mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
snapshot redesign
1. use full snapshot and delta snapshot, there are maxsnashot/2 of delta snapshot between two full snapshot 2. only support one policy for one volume 3. remove all coleases in secondary storage.
This commit is contained in:
parent
72cb2ce4ca
commit
319d4f1aa0
@ -27,7 +27,6 @@ package com.cloud.agent.api;
|
||||
public class BackupSnapshotCommand extends SnapshotCommand {
|
||||
private String prevSnapshotUuid;
|
||||
private String prevBackupUuid;
|
||||
private boolean isFirstSnapshotOfRootVolume;
|
||||
private boolean isVolumeInactive;
|
||||
private String firstBackupUuid;
|
||||
private String vmName;
|
||||
@ -56,7 +55,6 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
||||
String prevSnapshotUuid,
|
||||
String prevBackupUuid,
|
||||
String firstBackupUuid,
|
||||
boolean isFirstSnapshotOfRootVolume,
|
||||
boolean isVolumeInactive,
|
||||
String vmName)
|
||||
{
|
||||
@ -64,7 +62,6 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
||||
this.prevSnapshotUuid = prevSnapshotUuid;
|
||||
this.prevBackupUuid = prevBackupUuid;
|
||||
this.firstBackupUuid = firstBackupUuid;
|
||||
this.isFirstSnapshotOfRootVolume = isFirstSnapshotOfRootVolume;
|
||||
this.isVolumeInactive = isVolumeInactive;
|
||||
this.vmName = vmName;
|
||||
}
|
||||
@ -81,10 +78,6 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
||||
return firstBackupUuid;
|
||||
}
|
||||
|
||||
public boolean isFirstSnapshotOfRootVolume() {
|
||||
return isFirstSnapshotOfRootVolume;
|
||||
}
|
||||
|
||||
public boolean isVolumeInactive() {
|
||||
return isVolumeInactive;
|
||||
}
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the GNU General Public License v3 or later.
|
||||
*
|
||||
* It is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.cloud.agent.api;
|
||||
|
||||
public class CreatePrivateTemplateFromVolumeCommand extends SnapshotCommand {
|
||||
private String _volumePath;
|
||||
private String _userSpecifiedName;
|
||||
private String _uniqueName;
|
||||
private long _templateId;
|
||||
private long _accountId;
|
||||
// For XenServer
|
||||
private String _secondaryStorageURL;
|
||||
|
||||
|
||||
public CreatePrivateTemplateFromVolumeCommand() {}
|
||||
|
||||
public CreatePrivateTemplateFromVolumeCommand(String secondaryStorageURL, long templateId, long accountId, String userSpecifiedName, String uniqueName, String volumePath) {
|
||||
_secondaryStorageURL = secondaryStorageURL;
|
||||
_templateId = templateId;
|
||||
_accountId = accountId;
|
||||
_userSpecifiedName = userSpecifiedName;
|
||||
_uniqueName = uniqueName;
|
||||
_volumePath = volumePath;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getSecondaryStorageURL() {
|
||||
return _secondaryStorageURL;
|
||||
}
|
||||
|
||||
public String getTemplateName() {
|
||||
return _userSpecifiedName;
|
||||
}
|
||||
|
||||
public String getUniqueName() {
|
||||
return _uniqueName;
|
||||
}
|
||||
|
||||
public long getTemplateId() {
|
||||
return _templateId;
|
||||
}
|
||||
|
||||
public void setVolumePath(String _volumePath) {
|
||||
this._volumePath = _volumePath;
|
||||
}
|
||||
|
||||
public String getVolumePath() {
|
||||
return _volumePath;
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return _accountId;
|
||||
}
|
||||
|
||||
public void setTemplateId(long templateId) {
|
||||
_templateId = templateId;
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,6 @@ package com.cloud.agent.api;
|
||||
* This currently assumes that both primary and secondary storage are mounted on the XenServer.
|
||||
*/
|
||||
public class CreateVolumeFromSnapshotCommand extends SnapshotCommand {
|
||||
private String templatePath;
|
||||
|
||||
protected CreateVolumeFromSnapshotCommand() {
|
||||
|
||||
@ -51,18 +50,9 @@ public class CreateVolumeFromSnapshotCommand extends SnapshotCommand {
|
||||
Long accountId,
|
||||
Long volumeId,
|
||||
String backedUpSnapshotUuid,
|
||||
String backedUpSnapshotName,
|
||||
String templatePath)
|
||||
String backedUpSnapshotName)
|
||||
{
|
||||
super(primaryStoragePoolNameLabel, secondaryStoragePoolURL, backedUpSnapshotUuid, backedUpSnapshotName, dcId, accountId, volumeId);
|
||||
this.templatePath = templatePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the templatePath
|
||||
*/
|
||||
public String getTemplatePath() {
|
||||
return templatePath;
|
||||
}
|
||||
|
||||
}
|
||||
@ -22,7 +22,6 @@ package com.cloud.agent.api;
|
||||
* This currently assumes that the secondary storage are mounted on the XenServer.
|
||||
*/
|
||||
public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
||||
private String childUUID;
|
||||
|
||||
protected DeleteSnapshotBackupCommand() {
|
||||
|
||||
@ -59,18 +58,8 @@ public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
||||
Long accountId,
|
||||
Long volumeId,
|
||||
String backupUUID,
|
||||
String backupName,
|
||||
String childUUID)
|
||||
String backupName)
|
||||
{
|
||||
super(primaryStoragePoolNameLabel, secondaryStoragePoolURL, backupUUID, backupName, dcId, accountId, volumeId);
|
||||
this.childUUID = childUUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the childUUID
|
||||
*/
|
||||
public String getChildUUID() {
|
||||
return childUUID;
|
||||
}
|
||||
|
||||
}
|
||||
@ -38,19 +38,21 @@ public class ManageSnapshotCommand extends Command {
|
||||
|
||||
public ManageSnapshotCommand() {}
|
||||
|
||||
public ManageSnapshotCommand(String commandSwitch, long snapshotId, String path, String snapshotName, String vmName) {
|
||||
_commandSwitch = commandSwitch;
|
||||
if (commandSwitch.equals(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
|
||||
_volumePath = path;
|
||||
}
|
||||
else if (commandSwitch.equals(ManageSnapshotCommand.DESTROY_SNAPSHOT)) {
|
||||
_snapshotPath = path;
|
||||
}
|
||||
public ManageSnapshotCommand(long snapshotId, String volumePath, String preSnapshotPath ,String snapshotName, String vmName) {
|
||||
_commandSwitch = ManageSnapshotCommand.CREATE_SNAPSHOT;
|
||||
_volumePath = volumePath;
|
||||
_snapshotPath = preSnapshotPath;
|
||||
_snapshotName = snapshotName;
|
||||
_snapshotId = snapshotId;
|
||||
_vmName = vmName;
|
||||
}
|
||||
|
||||
public ManageSnapshotCommand(long snapshotId, String snapshotPath) {
|
||||
_commandSwitch = ManageSnapshotCommand.DESTROY_SNAPSHOT;
|
||||
_snapshotPath = snapshotPath;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
|
||||
@ -38,6 +38,10 @@ public class CreatePrivateTemplateAnswer extends Answer {
|
||||
_format = format;
|
||||
}
|
||||
|
||||
public CreatePrivateTemplateAnswer(Command cmd, boolean success, String result) {
|
||||
super(cmd, success, result);
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return _path;
|
||||
}
|
||||
|
||||
@ -67,6 +67,7 @@ import com.cloud.agent.api.CheckVirtualMachineAnswer;
|
||||
import com.cloud.agent.api.CheckVirtualMachineCommand;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
|
||||
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
|
||||
import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
|
||||
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
|
||||
import com.cloud.agent.api.DeleteSnapshotBackupAnswer;
|
||||
@ -138,7 +139,7 @@ import com.cloud.agent.api.storage.CopyVolumeCommand;
|
||||
import com.cloud.agent.api.storage.CreateAnswer;
|
||||
import com.cloud.agent.api.storage.CreateCommand;
|
||||
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
||||
import com.cloud.agent.api.storage.CreatePrivateTemplateCommand;
|
||||
|
||||
import com.cloud.agent.api.storage.DestroyCommand;
|
||||
import com.cloud.agent.api.storage.DownloadAnswer;
|
||||
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
|
||||
@ -607,8 +608,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
return execute((AttachVolumeCommand) cmd);
|
||||
} else if (cmd instanceof AttachIsoCommand) {
|
||||
return execute((AttachIsoCommand) cmd);
|
||||
} else if (cmd instanceof ValidateSnapshotCommand) {
|
||||
return execute((ValidateSnapshotCommand) cmd);
|
||||
} else if (cmd instanceof ManageSnapshotCommand) {
|
||||
return execute((ManageSnapshotCommand) cmd);
|
||||
} else if (cmd instanceof BackupSnapshotCommand) {
|
||||
@ -619,8 +618,8 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
return execute((CreateVolumeFromSnapshotCommand) cmd);
|
||||
} else if (cmd instanceof DeleteSnapshotsDirCommand) {
|
||||
return execute((DeleteSnapshotsDirCommand) cmd);
|
||||
} else if (cmd instanceof CreatePrivateTemplateCommand) {
|
||||
return execute((CreatePrivateTemplateCommand) cmd);
|
||||
} else if (cmd instanceof CreatePrivateTemplateFromVolumeCommand) {
|
||||
return execute((CreatePrivateTemplateFromVolumeCommand) cmd);
|
||||
} else if (cmd instanceof CreatePrivateTemplateFromSnapshotCommand) {
|
||||
return execute((CreatePrivateTemplateFromSnapshotCommand) cmd);
|
||||
} else if (cmd instanceof GetStorageStatsCommand) {
|
||||
@ -891,7 +890,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
|
||||
Pair<VM, String> v = createVmFromTemplate(conn, vmSpec, host);
|
||||
vm = v.first();
|
||||
String vmUuid = v.second();
|
||||
|
||||
for (VolumeTO disk : vmSpec.getDisks()) {
|
||||
createVbd(conn, vmName, vm, disk, disk.getType() == VolumeType.ROOT && vmSpec.getType() != VirtualMachine.Type.User);
|
||||
@ -3943,7 +3941,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
try {
|
||||
Host myself = Host.getByUuid(conn, _host.uuid);
|
||||
|
||||
boolean findsystemvmiso = false;
|
||||
Set<SR> srs = SR.getByNameLabel(conn, "XenServer Tools");
|
||||
if( srs.size() != 1 ) {
|
||||
throw new CloudRuntimeException("There are " + srs.size() + " SRs with name XenServer Tools");
|
||||
@ -4812,7 +4809,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
throw new Exception("no attached PBD");
|
||||
}
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug(logX(sr, "Created a SR; UUID is " + sr.getUuid(conn)));
|
||||
s_logger.debug(logX(sr, "Created a SR; UUID is " + sr.getUuid(conn) + " device config is " + deviceConfig));
|
||||
}
|
||||
sr.scan(conn);
|
||||
return sr;
|
||||
@ -5412,84 +5409,8 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
}
|
||||
}
|
||||
|
||||
protected ValidateSnapshotAnswer execute(final ValidateSnapshotCommand cmd) {
|
||||
String primaryStoragePoolNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
||||
String volumeUuid = cmd.getVolumeUuid(); // Precondition: not null
|
||||
String firstBackupUuid = cmd.getFirstBackupUuid();
|
||||
String previousSnapshotUuid = cmd.getPreviousSnapshotUuid();
|
||||
String templateUuid = cmd.getTemplateUuid();
|
||||
|
||||
// By default assume failure
|
||||
String details = "Could not validate previous snapshot backup UUID " + "because the primary Storage SR could not be created from the name label: "
|
||||
+ primaryStoragePoolNameLabel;
|
||||
boolean success = false;
|
||||
String expectedSnapshotBackupUuid = null;
|
||||
String actualSnapshotBackupUuid = null;
|
||||
String actualSnapshotUuid = null;
|
||||
|
||||
Boolean isISCSI = false;
|
||||
String primaryStorageSRUuid = null;
|
||||
Connection conn = getConnection();
|
||||
try {
|
||||
SR primaryStorageSR = getSRByNameLabelandHost(primaryStoragePoolNameLabel);
|
||||
|
||||
if (primaryStorageSR != null) {
|
||||
primaryStorageSRUuid = primaryStorageSR.getUuid(conn);
|
||||
isISCSI = SRType.LVMOISCSI.equals(primaryStorageSR.getType(conn));
|
||||
}
|
||||
} catch (BadServerResponse e) {
|
||||
details += ", reason: " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
} catch (XenAPIException e) {
|
||||
details += ", reason: " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
} catch (XmlRpcException e) {
|
||||
details += ", reason: " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
}
|
||||
|
||||
if (primaryStorageSRUuid != null) {
|
||||
if (templateUuid == null) {
|
||||
templateUuid = "";
|
||||
}
|
||||
if (firstBackupUuid == null) {
|
||||
firstBackupUuid = "";
|
||||
}
|
||||
if (previousSnapshotUuid == null) {
|
||||
previousSnapshotUuid = "";
|
||||
}
|
||||
String result = callHostPlugin("vmopsSnapshot", "validateSnapshot", "primaryStorageSRUuid", primaryStorageSRUuid, "volumeUuid", volumeUuid, "firstBackupUuid", firstBackupUuid,
|
||||
"previousSnapshotUuid", previousSnapshotUuid, "templateUuid", templateUuid, "isISCSI", isISCSI.toString());
|
||||
if (result == null || result.isEmpty()) {
|
||||
details = "Validating snapshot backup for volume with UUID: " + volumeUuid + " failed because there was an exception in the plugin";
|
||||
// callHostPlugin exception which has been logged already
|
||||
} else {
|
||||
String[] uuids = result.split("#", -1);
|
||||
if (uuids.length >= 3) {
|
||||
expectedSnapshotBackupUuid = uuids[1];
|
||||
actualSnapshotBackupUuid = uuids[2];
|
||||
}
|
||||
if (uuids.length >= 4) {
|
||||
actualSnapshotUuid = uuids[3];
|
||||
} else {
|
||||
actualSnapshotUuid = "";
|
||||
}
|
||||
if (uuids[0].equals("1")) {
|
||||
success = true;
|
||||
details = null;
|
||||
} else {
|
||||
details = "Previous snapshot backup on the primary storage is invalid. " + "Expected: " + expectedSnapshotBackupUuid + " Actual: " + actualSnapshotBackupUuid;
|
||||
// success is still false
|
||||
}
|
||||
s_logger.debug("ValidatePreviousSnapshotBackup returned " + " success: " + success + " details: " + details + " expectedSnapshotBackupUuid: "
|
||||
+ expectedSnapshotBackupUuid + " actualSnapshotBackupUuid: " + actualSnapshotBackupUuid + " actualSnapshotUuid: " + actualSnapshotUuid);
|
||||
}
|
||||
}
|
||||
|
||||
return new ValidateSnapshotAnswer(cmd, success, details, expectedSnapshotBackupUuid, actualSnapshotBackupUuid, actualSnapshotUuid);
|
||||
}
|
||||
|
||||
protected ManageSnapshotAnswer execute(final ManageSnapshotCommand cmd) {
|
||||
|
||||
long snapshotId = cmd.getSnapshotId();
|
||||
String snapshotName = cmd.getSnapshotName();
|
||||
|
||||
@ -5510,8 +5431,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
if (cmdSwitch.equals(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
|
||||
// Look up the volume
|
||||
String volumeUUID = cmd.getVolumePath();
|
||||
|
||||
VDI volume = getVDIbyUuid(volumeUUID);
|
||||
VDI volume = VDI.getByUuid(conn, volumeUUID);
|
||||
|
||||
// Create a snapshot
|
||||
VDI snapshot = volume.snapshot(conn, new HashMap<String, String>());
|
||||
@ -5519,10 +5439,26 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
if (snapshotName != null) {
|
||||
snapshot.setNameLabel(conn, snapshotName);
|
||||
}
|
||||
|
||||
// Determine the UUID of the snapshot
|
||||
VDI.Record vdir = snapshot.getRecord(conn);
|
||||
snapshotUUID = vdir.uuid;
|
||||
|
||||
snapshotUUID = snapshot.getUuid(conn);
|
||||
String preSnapshotUUID = cmd.getSnapshotPath();
|
||||
//check if it is a empty snapshot
|
||||
if( preSnapshotUUID != null) {
|
||||
SR sr = volume.getSR(conn);
|
||||
String srUUID = sr.getUuid(conn);
|
||||
String type = sr.getType(conn);
|
||||
Boolean isISCSI = SRType.LVMOISCSI.equals(type);
|
||||
String snapshotParentUUID = getVhdParent(srUUID, snapshotUUID, isISCSI);
|
||||
|
||||
String preSnapshotParentUUID = getVhdParent(srUUID, preSnapshotUUID, isISCSI);
|
||||
if( snapshotParentUUID != null && snapshotParentUUID.equals(preSnapshotParentUUID)) {
|
||||
// this is empty snapshot, remove it
|
||||
snapshot.destroy(conn);
|
||||
snapshotUUID = preSnapshotUUID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
success = true;
|
||||
details = null;
|
||||
@ -5547,74 +5483,63 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
return new ManageSnapshotAnswer(cmd, snapshotId, snapshotUUID, success, details);
|
||||
}
|
||||
|
||||
protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateCommand cmd) {
|
||||
String secondaryStorageURL = cmd.getSecondaryStorageURL();
|
||||
String snapshotUUID = cmd.getSnapshotPath();
|
||||
protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateFromVolumeCommand cmd) {
|
||||
String secondaryStoragePoolURL = cmd.getSecondaryStorageURL();
|
||||
String volumeUUID = cmd.getVolumePath();
|
||||
Long accountId = cmd.getAccountId();
|
||||
String userSpecifiedName = cmd.getTemplateName();
|
||||
Long templateId = cmd.getTemplateId();
|
||||
|
||||
SR secondaryStorage = null;
|
||||
VDI privateTemplate = null;
|
||||
Connection conn = getConnection();
|
||||
String details = null;
|
||||
SR tmpltSR = null;
|
||||
boolean result = false;
|
||||
try {
|
||||
URI uri = new URI(secondaryStorageURL);
|
||||
String remoteTemplateMountPath = uri.getHost() + ":" + uri.getPath() + "/template/";
|
||||
String templateFolder = cmd.getAccountId() + "/" + cmd.getTemplateId() + "/";
|
||||
String templateDownloadFolder = createTemplateDownloadFolder(remoteTemplateMountPath, templateFolder);
|
||||
String templateInstallFolder = "tmpl/" + templateFolder;
|
||||
|
||||
// Create a SR for the secondary storage download folder
|
||||
secondaryStorage = createNfsSRbyURI(new URI(secondaryStorageURL + "/template/" + templateDownloadFolder), false);
|
||||
URI uri = new URI(secondaryStoragePoolURL);
|
||||
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
||||
String installPath = "template/tmpl/" + accountId + "/" + templateId;
|
||||
if( !createSecondaryStorageFolder(secondaryStorageMountPath, installPath)) {
|
||||
details = " Filed to create folder " + installPath + " in secondary storage";
|
||||
s_logger.warn(details);
|
||||
return new CreatePrivateTemplateAnswer(cmd, false, details);
|
||||
}
|
||||
Connection conn = getConnection();
|
||||
VDI volume = getVDIbyUuid(volumeUUID);
|
||||
// create template SR
|
||||
URI tmpltURI = new URI(secondaryStoragePoolURL + "/" + installPath);
|
||||
tmpltSR = createNfsSRbyURI(tmpltURI, false);
|
||||
|
||||
// Look up the snapshot and copy it to secondary storage
|
||||
VDI snapshot = getVDIbyUuid(snapshotUUID);
|
||||
privateTemplate = cloudVDIcopy(snapshot, secondaryStorage);
|
||||
// copy volume to template SR
|
||||
VDI tmpltVDI = cloudVDIcopy(volume, tmpltSR);
|
||||
|
||||
if (userSpecifiedName != null) {
|
||||
privateTemplate.setNameLabel(conn, userSpecifiedName);
|
||||
tmpltVDI.setNameLabel(conn, userSpecifiedName);
|
||||
}
|
||||
|
||||
// Determine the template file name and install path
|
||||
VDI.Record vdir = privateTemplate.getRecord(conn);
|
||||
String templateName = vdir.uuid;
|
||||
String templateFilename = templateName + ".vhd";
|
||||
String installPath = "template/" + templateInstallFolder + templateFilename;
|
||||
|
||||
// Determine the template's virtual size and then forget the VDI
|
||||
long virtualSize = privateTemplate.getVirtualSize(conn);
|
||||
// Create the template.properties file in the download folder, move
|
||||
// the template and the template.properties file
|
||||
// to the install folder, and then delete the download folder
|
||||
if (!postCreatePrivateTemplate(remoteTemplateMountPath, templateDownloadFolder, templateInstallFolder, templateFilename, templateName, userSpecifiedName, null,
|
||||
virtualSize, cmd.getTemplateId())) {
|
||||
throw new InternalErrorException("Failed to create the template.properties file.");
|
||||
String tmpltSrUUID = tmpltSR.getUuid(conn);
|
||||
String tmpltUUID = tmpltVDI.getUuid(conn);
|
||||
String tmpltFilename = tmpltUUID + ".vhd";
|
||||
long virtualSize = tmpltVDI.getVirtualSize(conn);
|
||||
long size = tmpltVDI.getPhysicalUtilisation(conn);
|
||||
// create the template.properties file
|
||||
result = postCreatePrivateTemplate(tmpltSrUUID, tmpltFilename, tmpltUUID, userSpecifiedName, null, size, virtualSize, templateId);
|
||||
if (!result) {
|
||||
throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + tmpltURI);
|
||||
}
|
||||
|
||||
return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, templateName, ImageFormat.VHD);
|
||||
return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, tmpltUUID, ImageFormat.VHD);
|
||||
} catch (XenAPIException e) {
|
||||
if (privateTemplate != null) {
|
||||
destroyVDI(privateTemplate);
|
||||
}
|
||||
|
||||
s_logger.warn("CreatePrivateTemplate Failed due to " + e.toString(), e);
|
||||
return new CreatePrivateTemplateAnswer(cmd, false, e.toString(), null, 0, null, null);
|
||||
details = "Creating template from volume " + volumeUUID + " failed due to " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
} catch (Exception e) {
|
||||
s_logger.warn("CreatePrivateTemplate Failed due to " + e.getMessage(), e);
|
||||
return new CreatePrivateTemplateAnswer(cmd, false, e.getMessage(), null, 0, null, null);
|
||||
details = "Creating template from volume " + volumeUUID + " failed due to " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
} finally {
|
||||
// Remove the secondary storage SR
|
||||
removeSR(secondaryStorage);
|
||||
removeSR(tmpltSR);
|
||||
}
|
||||
return new CreatePrivateTemplateAnswer(cmd, result, details);
|
||||
}
|
||||
|
||||
private String createTemplateDownloadFolder(String remoteTemplateMountPath, String templateFolder) throws InternalErrorException, URISyntaxException {
|
||||
String templateDownloadFolder = "download/" + _host.uuid + "/" + templateFolder;
|
||||
|
||||
// Create the download folder
|
||||
if (!createSecondaryStorageFolder(remoteTemplateMountPath, templateDownloadFolder)) {
|
||||
throw new InternalErrorException("Failed to create the template download folder.");
|
||||
}
|
||||
return templateDownloadFolder;
|
||||
}
|
||||
|
||||
protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateFromSnapshotCommand cmd) {
|
||||
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
||||
@ -5623,59 +5548,61 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
Long volumeId = cmd.getVolumeId();
|
||||
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
||||
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
|
||||
String origTemplateInstallPath = cmd.getOrigTemplateInstallPath();
|
||||
Long newTemplateId = cmd.getNewTemplateId();
|
||||
String userSpecifiedName = cmd.getTemplateName();
|
||||
|
||||
// By default, assume failure
|
||||
String details = "Failed to create private template " + newTemplateId + " from snapshot for volume: " + volumeId + " with backupUuid: " + backedUpSnapshotUuid;
|
||||
String newTemplatePath = null;
|
||||
String templateName = null;
|
||||
String details = null;
|
||||
SR snapshotSR = null;
|
||||
SR tmpltSR = null;
|
||||
boolean result = false;
|
||||
long virtualSize = 0;
|
||||
try {
|
||||
URI uri = new URI(secondaryStoragePoolURL);
|
||||
String remoteTemplateMountPath = uri.getHost() + ":" + uri.getPath() + "/template/";
|
||||
String templateFolder = cmd.getAccountId() + "/" + newTemplateId + "/";
|
||||
String templateDownloadFolder = createTemplateDownloadFolder(remoteTemplateMountPath, templateFolder);
|
||||
String templateInstallFolder = "tmpl/" + templateFolder;
|
||||
// Yes, create a template vhd
|
||||
Pair<VHDInfo, String> vhdDetails = createVHDFromSnapshot(primaryStorageNameLabel, dcId, accountId, volumeId, secondaryStoragePoolURL, backedUpSnapshotUuid,
|
||||
origTemplateInstallPath, templateDownloadFolder);
|
||||
|
||||
VHDInfo vhdInfo = vhdDetails.first();
|
||||
String failureDetails = vhdDetails.second();
|
||||
if (vhdInfo == null) {
|
||||
if (failureDetails != null) {
|
||||
details += failureDetails;
|
||||
}
|
||||
} else {
|
||||
templateName = vhdInfo.getUuid();
|
||||
String templateFilename = templateName + ".vhd";
|
||||
String templateInstallPath = templateInstallFolder + "/" + templateFilename;
|
||||
|
||||
newTemplatePath = "template" + "/" + templateInstallPath;
|
||||
|
||||
virtualSize = vhdInfo.getVirtualSize();
|
||||
// create the template.properties file
|
||||
result = postCreatePrivateTemplate(remoteTemplateMountPath, templateDownloadFolder, templateInstallFolder, templateFilename, templateName, userSpecifiedName, null,
|
||||
virtualSize, newTemplateId);
|
||||
if (!result) {
|
||||
details += ", reason: Could not create the template.properties file on secondary storage dir: " + templateInstallFolder;
|
||||
} else {
|
||||
// Aaah, success.
|
||||
details = null;
|
||||
}
|
||||
|
||||
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
||||
String installPath = "template/tmpl/" + accountId + "/" + newTemplateId;
|
||||
if( !createSecondaryStorageFolder(secondaryStorageMountPath, installPath)) {
|
||||
details = " Filed to create folder " + installPath + " in secondary storage";
|
||||
s_logger.warn(details);
|
||||
return new CreatePrivateTemplateAnswer(cmd, false, details);
|
||||
}
|
||||
Connection conn = getConnection();
|
||||
// create snapshot SR
|
||||
URI snapshotURI = new URI(secondaryStoragePoolURL + "/snapshots/" + accountId + "/" + volumeId );
|
||||
snapshotSR = createNfsSRbyURI(snapshotURI, false);
|
||||
snapshotSR.scan(conn);
|
||||
VDI snapshotVDI = getVDIbyUuid(backedUpSnapshotUuid);
|
||||
|
||||
// create template SR
|
||||
URI tmpltURI = new URI(secondaryStoragePoolURL + "/" + installPath);
|
||||
tmpltSR = createNfsSRbyURI(tmpltURI, false);
|
||||
// copy snapshotVDI to template SR
|
||||
VDI tmpltVDI = cloudVDIcopy(snapshotVDI, tmpltSR);
|
||||
|
||||
String tmpltSrUUID = tmpltSR.getUuid(conn);
|
||||
String tmpltUUID = tmpltVDI.getUuid(conn);
|
||||
String tmpltFilename = tmpltUUID + ".vhd";
|
||||
long virtualSize = tmpltVDI.getVirtualSize(conn);
|
||||
long size = tmpltVDI.getPhysicalUtilisation(conn);
|
||||
|
||||
// create the template.properties file
|
||||
result = postCreatePrivateTemplate(tmpltSrUUID, tmpltFilename, tmpltUUID, userSpecifiedName, null, size, virtualSize, newTemplateId);
|
||||
if (!result) {
|
||||
throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + tmpltURI);
|
||||
}
|
||||
|
||||
return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, tmpltUUID, ImageFormat.VHD);
|
||||
} catch (XenAPIException e) {
|
||||
details += ", reason: " + e.getMessage();
|
||||
details = "Creating template from snapshot " + backedUpSnapshotUuid + " failed due to " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
} catch (Exception e) {
|
||||
details += ", reason: " + e.getMessage();
|
||||
details = "Creating template from snapshot " + backedUpSnapshotUuid + " failed due to " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
} finally {
|
||||
// Remove the secondary storage SR
|
||||
removeSR(snapshotSR);
|
||||
removeSR(tmpltSR);
|
||||
}
|
||||
return new CreatePrivateTemplateAnswer(cmd, result, details, newTemplatePath, virtualSize, templateName, ImageFormat.VHD);
|
||||
return new CreatePrivateTemplateAnswer(cmd, result, details);
|
||||
}
|
||||
|
||||
protected BackupSnapshotAnswer execute(final BackupSnapshotCommand cmd) {
|
||||
@ -5687,7 +5614,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
String snapshotUuid = cmd.getSnapshotUuid(); // not null: Precondition.
|
||||
String prevSnapshotUuid = cmd.getPrevSnapshotUuid();
|
||||
String prevBackupUuid = cmd.getPrevBackupUuid();
|
||||
boolean isFirstSnapshotOfRootVolume = cmd.isFirstSnapshotOfRootVolume();
|
||||
// By default assume failure
|
||||
String details = null;
|
||||
boolean success = false;
|
||||
@ -5704,27 +5630,45 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
URI uri = new URI(secondaryStoragePoolURL);
|
||||
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
||||
|
||||
if (secondaryStorageMountPath == null) {
|
||||
details = "Couldn't backup snapshot because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
||||
} else {
|
||||
boolean gcHappened = true;
|
||||
if (gcHappened) {
|
||||
snapshotBackupUuid = backupSnapshot(primaryStorageSRUuid, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotUuid, prevSnapshotUuid, prevBackupUuid,
|
||||
isFirstSnapshotOfRootVolume, isISCSI);
|
||||
success = (snapshotBackupUuid != null);
|
||||
} else {
|
||||
s_logger.warn("GC hasn't happened yet for previousSnapshotUuid: " + prevSnapshotUuid + ". Will retry again after 1 min");
|
||||
|
||||
if (prevBackupUuid == null) {
|
||||
// the first snapshot is always a full snapshot
|
||||
String folder = "snapshots/" + accountId + "/" + volumeId;
|
||||
if( !createSecondaryStorageFolder(secondaryStorageMountPath, folder)) {
|
||||
details = " Filed to create folder " + folder + " in secondary storage";
|
||||
s_logger.warn(details);
|
||||
return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid);
|
||||
}
|
||||
|
||||
String snapshotMountpoint = secondaryStoragePoolURL + "/" + folder;
|
||||
SR snapshotSr = null;
|
||||
try {
|
||||
snapshotSr = createNfsSRbyURI(new URI(snapshotMountpoint), false);
|
||||
VDI snapshotVdi = getVDIbyUuid(snapshotUuid);
|
||||
VDI backedVdi = snapshotVdi.copy(conn, snapshotSr);
|
||||
snapshotBackupUuid = backedVdi.getUuid(conn);
|
||||
success = true;
|
||||
} finally {
|
||||
if( snapshotSr != null) {
|
||||
removeSR(snapshotSr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
snapshotBackupUuid = backupSnapshot(primaryStorageSRUuid, dcId, accountId, volumeId, secondaryStorageMountPath,
|
||||
snapshotUuid, prevSnapshotUuid, prevBackupUuid, isISCSI);
|
||||
success = (snapshotBackupUuid != null);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
if (success) {
|
||||
details = "Successfully backedUp the snapshotUuid: " + snapshotUuid + " to secondary storage.";
|
||||
|
||||
// Mark the snapshot as removed in the database.
|
||||
// When the next snapshot is taken, it will be
|
||||
// 1) deleted from the DB 2) The snapshotUuid will be deleted from the primary
|
||||
// 3) the snapshotBackupUuid will be copied to secondary
|
||||
// 4) if possible it will be coalesced with the next snapshot.
|
||||
|
||||
} else if (prevSnapshotUuid != null && !isFirstSnapshotOfRootVolume) {
|
||||
if (prevSnapshotUuid != null) {
|
||||
// Destroy the previous snapshot, if it exists.
|
||||
// We destroy the previous snapshot only if the current snapshot
|
||||
// backup succeeds.
|
||||
@ -5732,8 +5676,9 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
// so that it doesn't get merged with the
|
||||
// new one
|
||||
// and muddle the vhd chain on the secondary storage.
|
||||
details = "Successfully backedUp the snapshotUuid: " + snapshotUuid + " to secondary storage.";
|
||||
destroySnapshotOnPrimaryStorage(prevSnapshotUuid);
|
||||
|
||||
destroySnapshotOnPrimaryStorage(prevSnapshotUuid);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (XenAPIException e) {
|
||||
@ -5749,93 +5694,44 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
|
||||
protected CreateVolumeFromSnapshotAnswer execute(final CreateVolumeFromSnapshotCommand cmd) {
|
||||
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
||||
Long dcId = cmd.getDataCenterId();
|
||||
Long accountId = cmd.getAccountId();
|
||||
Long volumeId = cmd.getVolumeId();
|
||||
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
||||
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
|
||||
String templatePath = cmd.getTemplatePath();
|
||||
|
||||
// By default, assume the command has failed and set the params to be
|
||||
// passed to CreateVolumeFromSnapshotAnswer appropriately
|
||||
boolean result = false;
|
||||
// Generic error message.
|
||||
String details = "Failed to create volume from snapshot for volume: " + volumeId + " with backupUuid: " + backedUpSnapshotUuid;
|
||||
String vhdUUID = null;
|
||||
SR temporarySROnSecondaryStorage = null;
|
||||
String mountPointOfTemporaryDirOnSecondaryStorage = null;
|
||||
String details = null;
|
||||
String volumeUUID = null;
|
||||
SR snapshotSR = null;
|
||||
|
||||
if (secondaryStoragePoolURL == null) {
|
||||
details += " because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
||||
return new CreateVolumeFromSnapshotAnswer(cmd, result, details, volumeUUID);
|
||||
}
|
||||
try {
|
||||
VDI vdi = null;
|
||||
Connection conn = getConnection();
|
||||
SR primaryStorageSR = getSRByNameLabelandHost(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);
|
||||
}
|
||||
// Get the absolute path of the snapshot on the secondary storage.
|
||||
URI snapshotURI = new URI(secondaryStoragePoolURL + "/snapshots/" + accountId + "/" + volumeId );
|
||||
|
||||
Boolean isISCSI = SRType.LVMOISCSI.equals(primaryStorageSR.getType(conn));
|
||||
snapshotSR = createNfsSRbyURI(snapshotURI, false);
|
||||
snapshotSR.scan(conn);
|
||||
VDI snapshotVDI = getVDIbyUuid(backedUpSnapshotUuid);
|
||||
|
||||
// Get the absolute path of the template on the secondary storage.
|
||||
URI uri = new URI(secondaryStoragePoolURL);
|
||||
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
||||
VDI volumeVDI = cloudVDIcopy(snapshotVDI, primaryStorageSR);
|
||||
|
||||
if (secondaryStorageMountPath == null) {
|
||||
details += " because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
||||
return new CreateVolumeFromSnapshotAnswer(cmd, result, details, vhdUUID);
|
||||
}
|
||||
volumeUUID = volumeVDI.getUuid(conn);
|
||||
|
||||
// Create a volume and not a template
|
||||
String templateDownloadFolder = "";
|
||||
|
||||
VHDInfo vhdInfo = createVHDFromSnapshot(dcId, accountId, volumeId, secondaryStorageMountPath, backedUpSnapshotUuid, templatePath, templateDownloadFolder, isISCSI);
|
||||
if (vhdInfo == null) {
|
||||
details += " because the vmops plugin on XenServer failed at some point";
|
||||
} else {
|
||||
vhdUUID = vhdInfo.getUuid();
|
||||
String tempDirRelativePath = "snapshots" + File.separator + accountId + File.separator + volumeId + "_temp";
|
||||
mountPointOfTemporaryDirOnSecondaryStorage = secondaryStorageMountPath + File.separator + tempDirRelativePath;
|
||||
result = true;
|
||||
|
||||
uri = new URI("nfs://" + mountPointOfTemporaryDirOnSecondaryStorage);
|
||||
// No need to check if the SR already exists. It's a temporary
|
||||
// SR destroyed when this method exits.
|
||||
// And two createVolumeFromSnapshot operations cannot proceed at
|
||||
// the same time.
|
||||
temporarySROnSecondaryStorage = createNfsSRbyURI(uri, false);
|
||||
if (temporarySROnSecondaryStorage == null) {
|
||||
details += "because SR couldn't be created on " + mountPointOfTemporaryDirOnSecondaryStorage;
|
||||
} else {
|
||||
s_logger.debug("Successfully created temporary SR on secondary storage " + temporarySROnSecondaryStorage.getNameLabel(conn) + "with uuid "
|
||||
+ temporarySROnSecondaryStorage.getUuid(conn) + " and scanned it");
|
||||
// createNFSSRbyURI also scans the SR and introduces the VDI
|
||||
|
||||
vdi = getVDIbyUuid(vhdUUID);
|
||||
|
||||
if (vdi != null) {
|
||||
s_logger.debug("Successfully created VDI on secondary storage SR " + temporarySROnSecondaryStorage.getNameLabel(conn) + " with uuid " + vhdUUID);
|
||||
s_logger.debug("Copying VDI: " + vdi.getLocation(conn) + " from secondary to primary");
|
||||
VDI vdiOnPrimaryStorage = cloudVDIcopy(vdi, primaryStorageSR);
|
||||
// vdi.copy introduces the vdi into the database. Don't
|
||||
// need to do a scan on the primary
|
||||
// storage.
|
||||
|
||||
if (vdiOnPrimaryStorage != null) {
|
||||
vhdUUID = vdiOnPrimaryStorage.getUuid(conn);
|
||||
s_logger.debug("Successfully copied and introduced VDI on primary storage with path " + vdiOnPrimaryStorage.getLocation(conn) + " and uuid " + vhdUUID);
|
||||
result = true;
|
||||
details = null;
|
||||
|
||||
} else {
|
||||
details += ". Could not copy the vdi " + vhdUUID + " to primary storage";
|
||||
}
|
||||
|
||||
// The VHD on temporary was scanned and introduced as a VDI
|
||||
// destroy it as we don't need it anymore.
|
||||
vdi.destroy(conn);
|
||||
} else {
|
||||
details += ". Could not scan and introduce vdi with uuid: " + vhdUUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (XenAPIException e) {
|
||||
details += " due to " + e.toString();
|
||||
s_logger.warn(details, e);
|
||||
@ -5844,13 +5740,8 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
s_logger.warn(details, e);
|
||||
} finally {
|
||||
// In all cases, if the temporary SR was created, forget it.
|
||||
if (temporarySROnSecondaryStorage != null) {
|
||||
removeSR(temporarySROnSecondaryStorage);
|
||||
// Delete the temporary directory created.
|
||||
File folderPath = new File(mountPointOfTemporaryDirOnSecondaryStorage);
|
||||
String remoteMountPath = folderPath.getParent();
|
||||
String folder = folderPath.getName();
|
||||
deleteSecondaryStorageFolder(remoteMountPath, folder);
|
||||
if (snapshotSR != null) {
|
||||
removeSR(snapshotSR);
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
@ -5859,7 +5750,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
}
|
||||
|
||||
// In all cases return something.
|
||||
return new CreateVolumeFromSnapshotAnswer(cmd, result, details, vhdUUID);
|
||||
return new CreateVolumeFromSnapshotAnswer(cmd, result, details, volumeUUID);
|
||||
}
|
||||
|
||||
protected DeleteSnapshotBackupAnswer execute(final DeleteSnapshotBackupCommand cmd) {
|
||||
@ -5868,31 +5759,10 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
Long volumeId = cmd.getVolumeId();
|
||||
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
||||
String backupUUID = cmd.getSnapshotUuid();
|
||||
String childUUID = cmd.getChildUUID();
|
||||
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
||||
|
||||
String details = null;
|
||||
boolean success = false;
|
||||
|
||||
SR primaryStorageSR = null;
|
||||
Boolean isISCSI = false;
|
||||
try {
|
||||
Connection conn = getConnection();
|
||||
primaryStorageSR = getSRByNameLabelandHost(primaryStorageNameLabel);
|
||||
if (primaryStorageSR == null) {
|
||||
details = "Primary Storage SR could not be created from the name label: " + primaryStorageNameLabel;
|
||||
throw new InternalErrorException(details);
|
||||
}
|
||||
isISCSI = SRType.LVMOISCSI.equals(primaryStorageSR.getType(conn));
|
||||
} catch (XenAPIException e) {
|
||||
details = "Couldn't determine primary SR type " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
} catch (Exception e) {
|
||||
details = "Couldn't determine primary SR type " + e.getMessage();
|
||||
s_logger.error(details, e);
|
||||
}
|
||||
|
||||
if (primaryStorageSR != null) {
|
||||
URI uri = null;
|
||||
try {
|
||||
uri = new URI(secondaryStoragePoolURL);
|
||||
@ -5906,14 +5776,13 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
if (secondaryStorageMountPath == null) {
|
||||
details = "Couldn't delete snapshot because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
||||
} else {
|
||||
details = deleteSnapshotBackup(dcId, accountId, volumeId, secondaryStorageMountPath, backupUUID, childUUID, isISCSI);
|
||||
details = deleteSnapshotBackup(dcId, accountId, volumeId, secondaryStorageMountPath, backupUUID);
|
||||
success = (details != null && details.equals("1"));
|
||||
if (success) {
|
||||
s_logger.debug("Successfully deleted snapshot backup " + backupUUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new DeleteSnapshotBackupAnswer(cmd, success, details);
|
||||
}
|
||||
|
||||
@ -6134,8 +6003,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
return (result != null);
|
||||
}
|
||||
|
||||
protected boolean postCreatePrivateTemplate(String remoteTemplateMountPath, String templateDownloadFolder, String templateInstallFolder, String templateFilename,
|
||||
String templateName, String templateDescription, String checksum, long virtualSize, long templateId) {
|
||||
protected boolean postCreatePrivateTemplate(String tmpltSrUUID,String tmpltFilename, String templateName, String templateDescription, String checksum, long size, long virtualSize, long templateId) {
|
||||
|
||||
if (templateDescription == null) {
|
||||
templateDescription = "";
|
||||
@ -6145,23 +6013,18 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
checksum = "";
|
||||
}
|
||||
|
||||
String result = callHostPluginWithTimeOut("vmopsSnapshot", "post_create_private_template", 110*60, "remoteTemplateMountPath", remoteTemplateMountPath, "templateDownloadFolder", templateDownloadFolder,
|
||||
"templateInstallFolder", templateInstallFolder, "templateFilename", templateFilename, "templateName", templateName, "templateDescription", templateDescription,
|
||||
"checksum", checksum, "virtualSize", String.valueOf(virtualSize), "templateId", String.valueOf(templateId));
|
||||
String result = callHostPluginWithTimeOut("vmopsSnapshot", "post_create_private_template", 110*60, "tmpltSrUUID", tmpltSrUUID, "templateFilename", tmpltFilename, "templateName", templateName, "templateDescription", templateDescription,
|
||||
"checksum", checksum, "size", String.valueOf(size), "virtualSize", String.valueOf(virtualSize), "templateId", String.valueOf(templateId));
|
||||
|
||||
boolean success = false;
|
||||
if (result != null && !result.isEmpty()) {
|
||||
// Else, command threw an exception which has already been logged.
|
||||
|
||||
String[] tmp = result.split("#");
|
||||
String status = tmp[0];
|
||||
|
||||
if (status != null && status.equalsIgnoreCase("1")) {
|
||||
s_logger.debug("Successfully created template.properties file on secondary storage dir: " + templateInstallFolder);
|
||||
if (result.equalsIgnoreCase("1")) {
|
||||
s_logger.debug("Successfully created template.properties file on secondary storage for " + tmpltFilename);
|
||||
success = true;
|
||||
} else {
|
||||
s_logger.warn("Could not create template.properties file on secondary storage dir: " + templateInstallFolder + " for templateId: " + templateId
|
||||
+ ". Failed with status " + status);
|
||||
s_logger.warn("Could not create template.properties file on secondary storage for " + tmpltFilename + " for templateId: " + templateId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6170,8 +6033,8 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
|
||||
// Each argument is put in a separate line for readability.
|
||||
// Using more lines does not harm the environment.
|
||||
protected String backupSnapshot(String primaryStorageSRUuid, Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath, String snapshotUuid,
|
||||
String prevSnapshotUuid, String prevBackupUuid, Boolean isFirstSnapshotOfRootVolume, Boolean isISCSI) {
|
||||
protected String backupSnapshot(String primaryStorageSRUuid, Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath,
|
||||
String snapshotUuid, String prevSnapshotUuid, String prevBackupUuid, Boolean isISCSI) {
|
||||
String backupSnapshotUuid = null;
|
||||
|
||||
if (prevSnapshotUuid == null) {
|
||||
@ -6185,7 +6048,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
// Using more lines does not harm the environment.
|
||||
String results = callHostPluginWithTimeOut("vmopsSnapshot", "backupSnapshot", 110*60, "primaryStorageSRUuid", primaryStorageSRUuid, "dcId", dcId.toString(), "accountId", accountId.toString(), "volumeId",
|
||||
volumeId.toString(), "secondaryStorageMountPath", secondaryStorageMountPath, "snapshotUuid", snapshotUuid, "prevSnapshotUuid", prevSnapshotUuid, "prevBackupUuid",
|
||||
prevBackupUuid, "isFirstSnapshotOfRootVolume", isFirstSnapshotOfRootVolume.toString(), "isISCSI", isISCSI.toString());
|
||||
prevBackupUuid, "isISCSI", isISCSI.toString());
|
||||
|
||||
if (results == null || results.isEmpty()) {
|
||||
// errString is already logged.
|
||||
@ -6210,6 +6073,19 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
return backupSnapshotUuid;
|
||||
}
|
||||
|
||||
|
||||
protected String getVhdParent(String primaryStorageSRUuid, String snapshotUuid, Boolean isISCSI) {
|
||||
String parentUuid = callHostPlugin("vmopsSnapshot", "getVhdParent", "primaryStorageSRUuid", primaryStorageSRUuid,
|
||||
"snapshotUuid", snapshotUuid, "isISCSI", isISCSI.toString());
|
||||
|
||||
if (parentUuid == null || parentUuid.isEmpty()) {
|
||||
s_logger.debug("Unable to get parent of VHD " + snapshotUuid + " in SR " + primaryStorageSRUuid);
|
||||
// errString is already logged.
|
||||
return null;
|
||||
}
|
||||
return parentUuid;
|
||||
}
|
||||
|
||||
protected boolean destroySnapshotOnPrimaryStorage(String snapshotUuid) {
|
||||
// Precondition snapshotUuid != null
|
||||
try {
|
||||
@ -6232,11 +6108,11 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String deleteSnapshotBackup(Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath, String backupUUID, String childUUID, Boolean isISCSI) {
|
||||
protected String deleteSnapshotBackup(Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath, String backupUUID) {
|
||||
|
||||
// If anybody modifies the formatting below again, I'll skin them
|
||||
String result = callHostPlugin("vmopsSnapshot", "deleteSnapshotBackup", "backupUUID", backupUUID, "childUUID", childUUID, "dcId", dcId.toString(), "accountId", accountId.toString(),
|
||||
"volumeId", volumeId.toString(), "secondaryStorageMountPath", secondaryStorageMountPath, "isISCSI", isISCSI.toString());
|
||||
String result = callHostPlugin("vmopsSnapshot", "deleteSnapshotBackup", "backupUUID", backupUUID, "dcId", dcId.toString(), "accountId", accountId.toString(),
|
||||
"volumeId", volumeId.toString(), "secondaryStorageMountPath", secondaryStorageMountPath);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -6249,70 +6125,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
return result;
|
||||
}
|
||||
|
||||
// If anybody messes up with the formatting, I'll skin them
|
||||
protected Pair<VHDInfo, String> createVHDFromSnapshot(String primaryStorageNameLabel, Long dcId, Long accountId, Long volumeId, String secondaryStoragePoolURL,
|
||||
String backedUpSnapshotUuid, String templatePath, String templateDownloadFolder) throws XenAPIException, IOException, XmlRpcException, InternalErrorException,
|
||||
URISyntaxException {
|
||||
// Return values
|
||||
String details = null;
|
||||
Connection conn = getConnection();
|
||||
SR primaryStorageSR = getSRByNameLabelandHost(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);
|
||||
}
|
||||
|
||||
Boolean isISCSI = SRType.LVMOISCSI.equals(primaryStorageSR.getType(conn));
|
||||
|
||||
// Get the absolute path of the template on the secondary storage.
|
||||
URI uri = new URI(secondaryStoragePoolURL);
|
||||
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
||||
VHDInfo vhdInfo = null;
|
||||
if (secondaryStorageMountPath == null) {
|
||||
details = " because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
||||
} else {
|
||||
vhdInfo = createVHDFromSnapshot(dcId, accountId, volumeId, secondaryStorageMountPath, backedUpSnapshotUuid, templatePath, templateDownloadFolder, isISCSI);
|
||||
if (vhdInfo == null) {
|
||||
details = " because the vmops plugin on XenServer failed at some point";
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<VHDInfo, String>(vhdInfo, details);
|
||||
}
|
||||
|
||||
protected VHDInfo createVHDFromSnapshot(Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath, String backedUpSnapshotUuid, String templatePath,
|
||||
String templateDownloadFolder, Boolean isISCSI) {
|
||||
String vdiUUID = null;
|
||||
|
||||
String failureString = "Could not create volume from " + backedUpSnapshotUuid;
|
||||
templatePath = (templatePath == null) ? "" : templatePath;
|
||||
String results = callHostPluginWithTimeOut("vmopsSnapshot","createVolumeFromSnapshot", 110*60, "dcId", dcId.toString(), "accountId", accountId.toString(), "volumeId", volumeId.toString(),
|
||||
"secondaryStorageMountPath", secondaryStorageMountPath, "backedUpSnapshotUuid", backedUpSnapshotUuid, "templatePath", templatePath, "templateDownloadFolder",
|
||||
templateDownloadFolder, "isISCSI", isISCSI.toString());
|
||||
|
||||
if (results == null || results.isEmpty()) {
|
||||
// Command threw an exception which has already been logged.
|
||||
return null;
|
||||
}
|
||||
String[] tmp = results.split("#");
|
||||
String status = tmp[0];
|
||||
vdiUUID = tmp[1];
|
||||
Long virtualSizeInMB = 0L;
|
||||
if (tmp.length == 3) {
|
||||
virtualSizeInMB = Long.valueOf(tmp[2]);
|
||||
}
|
||||
// status == "1" if and only if vdiUUID != null
|
||||
// So we don't rely on status value but return vdiUUID as an indicator
|
||||
// of success.
|
||||
|
||||
if (status != null && status.equalsIgnoreCase("1") && vdiUUID != null) {
|
||||
s_logger.debug("Successfully created vhd file with all data on secondary storage : " + vdiUUID);
|
||||
} else {
|
||||
s_logger.debug(failureString + ". Failed with status " + status + " with vdiUuid " + vdiUUID);
|
||||
}
|
||||
return new VHDInfo(vdiUUID, virtualSizeInMB * MB);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
@ -6417,30 +6229,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
||||
public String pool;
|
||||
}
|
||||
|
||||
private class VHDInfo {
|
||||
private final String uuid;
|
||||
private final Long virtualSize;
|
||||
|
||||
public VHDInfo(String uuid, Long virtualSize) {
|
||||
this.uuid = uuid;
|
||||
this.virtualSize = virtualSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the uuid
|
||||
*/
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the virtualSize
|
||||
*/
|
||||
public Long getVirtualSize() {
|
||||
return virtualSize;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getGuestOsType(String stdType) {
|
||||
return CitrixHelper.getGuestOsType(stdType);
|
||||
}
|
||||
|
||||
@ -1716,12 +1716,6 @@ public interface ManagementServer {
|
||||
ResourceAllocationException,
|
||||
InternalErrorException;
|
||||
|
||||
/**
|
||||
* @param userId The Id of the user who invoked this operation.
|
||||
* @param volumeId The volume for which this snapshot is being taken
|
||||
* @return The properties of the snapshot taken
|
||||
*/
|
||||
SnapshotVO createTemplateSnapshot(Long userId, long volumeId);
|
||||
|
||||
/**
|
||||
* Destroy a snapshot
|
||||
@ -1761,7 +1755,7 @@ public interface ManagementServer {
|
||||
* @return valid template if success, null otherwise
|
||||
* @throws InvalidParameterValueException, ResourceAllocationException
|
||||
*/
|
||||
VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, long snapshotId, String name, String description) throws InvalidParameterValueException;
|
||||
VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, Long snapshotId, Long volumeId, String name, String description) throws InvalidParameterValueException;
|
||||
long createPrivateTemplateAsync(Long userId, long vmId, String name, String description, long guestOSId, Boolean requiresHvm, Integer bits, Boolean passwordEnabled, boolean isPublic, boolean featured, Long snapshotId) throws InvalidParameterValueException, ResourceAllocationException, InternalErrorException;
|
||||
|
||||
|
||||
|
||||
@ -39,7 +39,8 @@ public interface Snapshot {
|
||||
Creating,
|
||||
CreatedOnPrimary,
|
||||
BackingUp,
|
||||
BackedUp;
|
||||
BackedUp,
|
||||
EmptySnapshot;
|
||||
|
||||
public String toString() {
|
||||
return this.name();
|
||||
|
||||
@ -79,6 +79,10 @@ public class SnapshotPolicyVO {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
public void setInterval(short interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
public void setTimezone(String timezone) {
|
||||
this.timezone = timezone;
|
||||
}
|
||||
|
||||
@ -77,14 +77,22 @@ public class SnapshotScheduleVO {
|
||||
return policyId;
|
||||
}
|
||||
|
||||
/**
|
||||
public void setPolicyId(long policyId) {
|
||||
this.policyId = policyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the scheduledTimestamp
|
||||
*/
|
||||
public Date getScheduledTimestamp() {
|
||||
return scheduledTimestamp;
|
||||
}
|
||||
|
||||
public Long getAsyncJobId() {
|
||||
public void setScheduledTimestamp(Date scheduledTimestamp) {
|
||||
this.scheduledTimestamp = scheduledTimestamp;
|
||||
}
|
||||
|
||||
public Long getAsyncJobId() {
|
||||
return asyncJobId;
|
||||
}
|
||||
|
||||
|
||||
@ -157,7 +157,7 @@ public class SnapshotVO implements Snapshot {
|
||||
return backupSnapshotId;
|
||||
}
|
||||
|
||||
public long getPrevSnapshotId(){
|
||||
public long getPrevSnapshotId(){
|
||||
return prevSnapshotId;
|
||||
}
|
||||
|
||||
@ -169,13 +169,12 @@ public class SnapshotVO implements Snapshot {
|
||||
this.prevSnapshotId = prevSnapshotId;
|
||||
}
|
||||
|
||||
public static SnapshotType getSnapshotType(List<Long> policyIds) {
|
||||
assert policyIds != null && !policyIds.isEmpty();
|
||||
SnapshotType snapshotType = SnapshotType.RECURRING;
|
||||
if (policyIds.contains(MANUAL_POLICY_ID)) {
|
||||
snapshotType = SnapshotType.MANUAL;
|
||||
public static SnapshotType getSnapshotType(Long policyId) {
|
||||
if (policyId.equals(MANUAL_POLICY_ID)) {
|
||||
return SnapshotType.MANUAL;
|
||||
} else {
|
||||
return SnapshotType.RECURRING;
|
||||
}
|
||||
return snapshotType;
|
||||
}
|
||||
|
||||
public static SnapshotType getSnapshotType(String snapshotType) {
|
||||
|
||||
@ -32,4 +32,5 @@ public interface SnapshotPolicyDao extends GenericDao<SnapshotPolicyVO, Long> {
|
||||
List<SnapshotPolicyVO> listByVolumeId(long volumeId, Filter filter);
|
||||
SnapshotPolicyVO findOneByVolumeInterval(long volumeId, short interval);
|
||||
List<SnapshotPolicyVO> listActivePolicies();
|
||||
SnapshotPolicyVO findOneByVolume(long volumeId);
|
||||
}
|
||||
|
||||
@ -43,6 +43,14 @@ public class SnapshotPolicyDaoImpl extends GenericDaoBase<SnapshotPolicyVO, Long
|
||||
return findOneIncludingRemovedBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotPolicyVO findOneByVolume(long volumeId) {
|
||||
SearchCriteria<SnapshotPolicyVO> sc = VolumeIdSearch.create();
|
||||
sc.setParameters("volumeId", volumeId);
|
||||
sc.setParameters("active", true);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotPolicyVO> listByVolumeId(long volumeId) {
|
||||
return listByVolumeId(volumeId, null);
|
||||
|
||||
@ -22,6 +22,7 @@ package com.cloud.storage.dao;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.storage.SnapshotPolicyVO;
|
||||
import com.cloud.storage.SnapshotScheduleVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
@ -36,4 +37,6 @@ public interface SnapshotScheduleDao extends GenericDao<SnapshotScheduleVO, Long
|
||||
|
||||
SnapshotScheduleVO getCurrentSchedule(Long volumeId, Long policyId, boolean executing);
|
||||
|
||||
SnapshotScheduleVO findOneByVolume(long volumeId);
|
||||
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ import java.util.List;
|
||||
|
||||
import javax.ejb.Local;
|
||||
|
||||
import com.cloud.storage.SnapshotPolicyVO;
|
||||
import com.cloud.storage.SnapshotScheduleVO;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
@ -36,6 +37,7 @@ import com.cloud.utils.db.SearchCriteria;
|
||||
public class SnapshotScheduleDaoImpl extends GenericDaoBase<SnapshotScheduleVO, Long> implements SnapshotScheduleDao {
|
||||
protected final SearchBuilder<SnapshotScheduleVO> executableSchedulesSearch;
|
||||
protected final SearchBuilder<SnapshotScheduleVO> coincidingSchedulesSearch;
|
||||
private final SearchBuilder<SnapshotScheduleVO> VolumeIdSearch;
|
||||
// DB constraint: For a given volume and policyId, there will only be one entry in this table.
|
||||
|
||||
|
||||
@ -52,6 +54,10 @@ public class SnapshotScheduleDaoImpl extends GenericDaoBase<SnapshotScheduleVO,
|
||||
coincidingSchedulesSearch.and("asyncJobId", coincidingSchedulesSearch.entity().getAsyncJobId(), SearchCriteria.Op.NULL);
|
||||
coincidingSchedulesSearch.done();
|
||||
|
||||
VolumeIdSearch = createSearchBuilder();
|
||||
VolumeIdSearch.and("volumeId", VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
|
||||
VolumeIdSearch.done();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,6 +73,13 @@ public class SnapshotScheduleDaoImpl extends GenericDaoBase<SnapshotScheduleVO,
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SnapshotScheduleVO findOneByVolume(long volumeId) {
|
||||
SearchCriteria<SnapshotScheduleVO> sc = VolumeIdSearch.create();
|
||||
sc.setParameters("volumeId", volumeId);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@ -10,19 +10,12 @@ from util import CommandException
|
||||
import vhdutil
|
||||
import shutil
|
||||
import lvhdutil
|
||||
import subprocess
|
||||
from lvmcache import LVMCache
|
||||
from journaler import Journaler
|
||||
from lock import Lock
|
||||
import errno
|
||||
import subprocess
|
||||
import xs_errors
|
||||
import cleanup
|
||||
import hostvmstats
|
||||
import socket
|
||||
import stat
|
||||
import random
|
||||
import tempfile
|
||||
|
||||
VHD_UTIL = '/usr/sbin/vhd-util'
|
||||
VHD_PREFIX = 'VHD-'
|
||||
@ -107,85 +100,55 @@ def delete_secondary_storage_folder(session, args):
|
||||
|
||||
return "1"
|
||||
|
||||
@echo
|
||||
def execute_script(session, args):
|
||||
return ""
|
||||
|
||||
@echo
|
||||
def post_create_private_template(session, args):
|
||||
local_mount_path = None
|
||||
|
||||
try:
|
||||
try:
|
||||
# Mount the remote templates folder locally
|
||||
remote_mount_path = args["remoteTemplateMountPath"]
|
||||
local_mount_path = os.path.join(SR.MOUNT_BASE, "template" + str(int(random.random() * 10000)))
|
||||
mount(remote_mount_path, local_mount_path)
|
||||
util.SMlog("Mounted secondary storage template folder")
|
||||
# get local template folder
|
||||
sruuid = args["tmpltSrUUID"]
|
||||
local_mount_path = os.path.join(SR.MOUNT_BASE, sruuid)
|
||||
|
||||
# Retrieve args
|
||||
filename = args["templateFilename"]
|
||||
name = args["templateName"]
|
||||
description = args["templateDescription"]
|
||||
checksum = args["checksum"]
|
||||
virtual_size = args["virtualSize"]
|
||||
template_id = args["templateId"]
|
||||
# Retrieve args
|
||||
filename = args["templateFilename"]
|
||||
name = args["templateName"]
|
||||
description = args["templateDescription"]
|
||||
checksum = args["checksum"]
|
||||
size = args["size"]
|
||||
virtual_size = args["virtualSize"]
|
||||
template_id = args["templateId"]
|
||||
|
||||
# Determine the template size
|
||||
template_download_folder = local_mount_path + "/" + args["templateDownloadFolder"]
|
||||
template_download_path = template_download_folder + filename
|
||||
file_size = os.path.getsize(template_download_path)
|
||||
util.SMlog("Got template file_size: " + str(file_size))
|
||||
# Determine the template size
|
||||
template_install_path = local_mount_path + "/" + filename
|
||||
file_size = os.path.getsize(template_install_path)
|
||||
util.SMlog("Got template file_size: " + str(file_size))
|
||||
|
||||
# Create the template.properties file
|
||||
template_properties_download_path = template_download_folder + "template.properties"
|
||||
f = open(template_properties_download_path, "w")
|
||||
f.write("filename=" + filename + "\n")
|
||||
f.write("name=" + filename + "\n")
|
||||
f.write("vhd=true\n")
|
||||
f.write("id=" + template_id + "\n")
|
||||
f.write("vhd.filename=" + filename + "\n")
|
||||
f.write("uniquename=" + name + "\n")
|
||||
f.write("vhd.virtualsize=" + virtual_size + "\n")
|
||||
f.write("vhd.size=" + str(file_size) + "\n")
|
||||
f.write("virtualsize=" + virtual_size + "\n")
|
||||
f.write("checksum=" + checksum + "\n")
|
||||
f.write("hvm=true\n")
|
||||
f.write("description=" + name + "\n")
|
||||
f.close()
|
||||
util.SMlog("Created template.properties file")
|
||||
# Create the template.properties file
|
||||
template_properties_install_path = local_mount_path + "/template.properties"
|
||||
f = open(template_properties_install_path, "w")
|
||||
f.write("filename=" + filename + "\n")
|
||||
f.write("name=" + filename + "\n")
|
||||
f.write("vhd=true\n")
|
||||
f.write("id=" + template_id + "\n")
|
||||
f.write("vhd.filename=" + filename + "\n")
|
||||
f.write("uniquename=" + name + "\n")
|
||||
f.write("vhd.virtualsize=" + virtual_size + "\n")
|
||||
f.write("vhd.size=" + str(file_size) + "\n")
|
||||
f.write("virtualsize=" + virtual_size + "\n")
|
||||
f.write("checksum=" + checksum + "\n")
|
||||
f.write("hvm=true\n")
|
||||
f.write("description=" + name + "\n")
|
||||
f.close()
|
||||
util.SMlog("Created template.properties file")
|
||||
|
||||
# Create the template install folder if necessary
|
||||
template_install_folder = local_mount_path + "/" + args["templateInstallFolder"]
|
||||
if not os.path.isdir(template_install_folder):
|
||||
current_umask = os.umask(0)
|
||||
os.makedirs(template_install_folder)
|
||||
os.umask(current_umask)
|
||||
# Set permissions
|
||||
permissions = stat.S_IREAD | stat.S_IWRITE | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH
|
||||
os.chmod(template_properties_install_path, permissions)
|
||||
util.SMlog("Set permissions on template and template.properties")
|
||||
|
||||
# Move the template and the template.properties file to the install folder
|
||||
os.system("mv " + template_download_folder + "/" + filename + " " + template_install_folder)
|
||||
os.system("mv " + template_download_folder + "/template.properties " + template_install_folder)
|
||||
template_install_path = template_install_folder + filename
|
||||
template_properties_install_path = template_install_folder + "template.properties"
|
||||
|
||||
# Set permissions
|
||||
permissions = stat.S_IREAD | stat.S_IWRITE | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH
|
||||
os.chmod(template_install_path, permissions)
|
||||
os.chmod(template_properties_install_path, permissions)
|
||||
util.SMlog("Set permissions on template and template.properties")
|
||||
|
||||
# Delete the template download folder
|
||||
os.system("rm -rf " + template_download_folder)
|
||||
except:
|
||||
errMsg = "post_create_private_template failed."
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
finally:
|
||||
if local_mount_path != None:
|
||||
# Unmount the local templates folder
|
||||
umount(local_mount_path)
|
||||
# Remove the local templates folder
|
||||
os.system("rm -rf " + local_mount_path)
|
||||
except:
|
||||
errMsg = "post_create_private_template failed."
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
|
||||
return "1"
|
||||
|
||||
@ -237,19 +200,6 @@ def chdir(path):
|
||||
util.SMlog("Chdired to " + path)
|
||||
return
|
||||
|
||||
def coalesce(vhdPath):
|
||||
util.SMlog("Starting to coalesce " + vhdPath + " with its parent")
|
||||
try :
|
||||
cmd = [VHD_UTIL, "coalesce", "-n", vhdPath]
|
||||
txt = util.pread2(cmd)
|
||||
except:
|
||||
errMsg = "Unexpected error while trying to coalesce " + vhdPath + " to its parent"
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
util.SMlog("Successfully coalesced " + vhdPath + " with its parent ")
|
||||
|
||||
return
|
||||
|
||||
def scanParent(path):
|
||||
# Do a scan for the parent for ISCSI volumes
|
||||
# Note that the parent need not be visible on the XenServer
|
||||
@ -313,30 +263,14 @@ def rename(originalVHD, newVHD):
|
||||
raise xs_errors.XenError(errMsg)
|
||||
return
|
||||
|
||||
def coalesceToChild(backupVHD, childUUID, isISCSI):
|
||||
# coalesce childVHD with its parent
|
||||
childVHD = getVHD(childUUID, isISCSI)
|
||||
util.SMlog("childVHD: " + childVHD)
|
||||
# check for existence of childVHD
|
||||
isfile(childVHD, False)
|
||||
util.SMlog("childVHD " + childVHD + " exists")
|
||||
# No exception thrown, file exists
|
||||
coalesce(childVHD)
|
||||
|
||||
# rename the existing backupVHD file to childVHD
|
||||
# childVHD file automatically gets overwritten
|
||||
rename(backupVHD, childVHD)
|
||||
|
||||
# parent of the newly coalesced file still remains the same.
|
||||
# child of childVHD has it's parent name still set to childVHD.
|
||||
# So the VHD chain hasn't been broken.
|
||||
return
|
||||
|
||||
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
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
@ -358,42 +292,17 @@ def mount(remoteDir, localDir):
|
||||
return
|
||||
|
||||
def umount(localDir):
|
||||
success = False
|
||||
if os.path.isdir(localDir) and os.path.ismount(localDir):
|
||||
try:
|
||||
cmd = ['umount', localDir]
|
||||
util.pread2(cmd)
|
||||
except CommandException:
|
||||
errMsg = "CommandException raised while trying to umount " + localDir
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
util.SMlog("Successfully unmounted " + localDir)
|
||||
success = True
|
||||
else:
|
||||
util.SMlog("LocalDir: " + localDir + " doesn't exist or is not a mount point")
|
||||
try:
|
||||
cmd = ['umount', localDir]
|
||||
util.pread2(cmd)
|
||||
except CommandException:
|
||||
errMsg = "CommandException raised while trying to umount " + localDir
|
||||
util.SMlog(errMsg)
|
||||
return
|
||||
|
||||
util.SMlog("Successfully unmounted " + localDir)
|
||||
return
|
||||
|
||||
|
||||
def mountTemplatesDir(secondaryStorageMountPath, templatePath):
|
||||
# Aim is to mount <secondaryStorageMountPath>/<templatePath> on
|
||||
# SR.MOUNT_BASE/<random_uuid>
|
||||
# It will be unmounted after createVolumeFromSnapshot finishes.
|
||||
# It should be mounted read-only, but don't know how to do that
|
||||
# The random-uuid saves us from conflicts while restoring two different root volumes
|
||||
|
||||
absoluteTemplatePath = os.path.join(secondaryStorageMountPath, templatePath)
|
||||
absoluteTemplateDir = os.path.dirname(absoluteTemplatePath)
|
||||
|
||||
randomUUID = util.gen_uuid()
|
||||
localTemplateDir = os.path.join(SR.MOUNT_BASE, randomUUID)
|
||||
# create the temp dir
|
||||
makedirs(localTemplateDir)
|
||||
# mount
|
||||
mount(absoluteTemplateDir, localTemplateDir)
|
||||
|
||||
return localTemplateDir
|
||||
|
||||
def mountSnapshotsDir(secondaryStorageMountPath, relativeDir, dcId, accountId, instanceId):
|
||||
# The aim is to mount secondaryStorageMountPath on
|
||||
# SR.MOUNT_BASE/<dcId>/<relativeDir>
|
||||
@ -445,6 +354,9 @@ def getPrimarySRPath(primaryStorageSRUuid, isISCSI):
|
||||
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
|
||||
@ -529,122 +441,20 @@ def isVolumeAvailable(path):
|
||||
|
||||
return (status == "1")
|
||||
|
||||
def getLastSnapshotUuid(volumeUuid, expectedLastSnapshotUuid):
|
||||
actualLastSnapshotUuid = ''
|
||||
if volumeUuid:
|
||||
cmd = ['xe', 'vdi-param-get', 'uuid=' + volumeUuid, 'param-name=snapshots']
|
||||
stdout = ''
|
||||
try:
|
||||
stdout = util.pread2(cmd)
|
||||
except: #CommandException, (rc, cmdListStr, stderr):
|
||||
#errMsg = "CommandException thrown while executing: " + cmdListStr + " with return code: " + str(rc) + " and stderr: " + stderr
|
||||
errMsg = "Unexpected error while executing cmd: " + str(cmd)
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
if stdout:
|
||||
snapshots = stdout.split(';')
|
||||
if len(snapshots) == 1:
|
||||
if snapshots[0] == expectedLastSnapshotUuid:
|
||||
actualLastSnapshotUuid = expectedLastSnapshotUuid
|
||||
elif len(snapshots) == 2:
|
||||
# We expect only 1 snapshot to be present. If there is another that is unexpected and the last one
|
||||
if (expectedLastSnapshotUuid == snapshots[0].strip()):
|
||||
actualLastSnapshotUuid = snapshots[1].strip()
|
||||
else:
|
||||
# it should be equal to snapshots[1]. Else I have no idea.
|
||||
actualLastSnapshotUuid = snapshots[0].strip()
|
||||
else:
|
||||
# len(snapshots) > 2:
|
||||
errMsg = "Volume: " + volumeUuid + " has more than 2 snapshots: " + str(snapshots)
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
|
||||
return actualLastSnapshotUuid
|
||||
|
||||
@echo
|
||||
def validateSnapshot(session, args):
|
||||
util.SMlog("Called validateSnapshot with " + str(args))
|
||||
primaryStorageSRUuid = args['primaryStorageSRUuid']
|
||||
volumeUuid = args['volumeUuid']
|
||||
firstBackupUuid = args['firstBackupUuid']
|
||||
previousSnapshotUuid = args['previousSnapshotUuid']
|
||||
templateUuid = args['templateUuid']
|
||||
isISCSI = getIsTrueString(args['isISCSI'])
|
||||
def getVhdParent(session, args):
|
||||
util.SMlog("getParent with " + str(args))
|
||||
primaryStorageSRUuid = args['primaryStorageSRUuid']
|
||||
snapshotUuid = args['snapshotUuid']
|
||||
isISCSI = getIsTrueString(args['isISCSI'])
|
||||
|
||||
primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI)
|
||||
util.SMlog("primarySRPath: " + primarySRPath)
|
||||
volumeVHD = getVHD(volumeUuid, isISCSI)
|
||||
volumePath = os.path.join(primarySRPath, volumeVHD)
|
||||
util.SMlog("volumePath: " + volumePath)
|
||||
|
||||
baseCopyUuid = ''
|
||||
wasVolumeAvailable = True
|
||||
if isISCSI:
|
||||
wasVolumeAvailable = isVolumeAvailable(volumePath)
|
||||
if not wasVolumeAvailable:
|
||||
# make it available
|
||||
checkVolumeAvailablility(volumePath)
|
||||
baseCopyUuid = scanParent(volumePath)
|
||||
else:
|
||||
baseCopyUuid = getParent(volumePath, isISCSI)
|
||||
baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI)
|
||||
|
||||
if baseCopyUuid is None:
|
||||
# Make it an empty string so that it can be used in the return value
|
||||
baseCopyUuid = ''
|
||||
return baseCopyUuid
|
||||
|
||||
actualSnapshotUuid = getLastSnapshotUuid(volumeUuid, previousSnapshotUuid)
|
||||
expectedActual = firstBackupUuid + "#" + baseCopyUuid + "#" + actualSnapshotUuid
|
||||
if firstBackupUuid:
|
||||
# This is not the first snapshot
|
||||
if baseCopyUuid and (baseCopyUuid == firstBackupUuid):
|
||||
retval = "1#"
|
||||
else:
|
||||
retval = "0#"
|
||||
else:
|
||||
if templateUuid:
|
||||
# The DB thinks this is the first snapshot of a ROOT DISK
|
||||
# The parent of the volume should be the base template, which is also the parent of the given templateUuid.
|
||||
templateVHD = getVHD(templateUuid, isISCSI)
|
||||
templatePath = os.path.join(primarySRPath, templateVHD)
|
||||
baseTemplateUuid = ''
|
||||
wasTemplateAvailable = True
|
||||
if isISCSI:
|
||||
wasTemplateAvailable = isVolumeAvailable(templatePath)
|
||||
if not wasVolumeAvailable:
|
||||
# make it available
|
||||
checkVolumeAvailablility(templatePath)
|
||||
baseTemplateUuid = scanParent(templatePath)
|
||||
else:
|
||||
baseTemplateUuid = getParent(templatePath, isISCSI)
|
||||
|
||||
if baseTemplateUuid is None:
|
||||
# This will never happen.
|
||||
baseTemplateUuid = ''
|
||||
|
||||
expectedActual = baseTemplateUuid + "#" + baseCopyUuid + "#" + actualSnapshotUuid
|
||||
if baseTemplateUuid and (baseCopyUuid == baseTemplateUuid):
|
||||
retval = "1#"
|
||||
else:
|
||||
retval = "0#"
|
||||
|
||||
if isISCSI and not wasTemplateAvailable:
|
||||
manageAvailability(templatePath, '-an')
|
||||
|
||||
else:
|
||||
# The DB thinks this is the first snapshot of a DATA DISK.
|
||||
# The volume VDI should not have any parent.
|
||||
if not baseCopyUuid:
|
||||
retval = "1#"
|
||||
else:
|
||||
retval = "0#"
|
||||
|
||||
# Set the volume's visibility back to what it was.
|
||||
if isISCSI and not wasVolumeAvailable:
|
||||
manageAvailability(volumePath, '-an')
|
||||
|
||||
return retval + expectedActual
|
||||
|
||||
@echo
|
||||
def backupSnapshot(session, args):
|
||||
util.SMlog("Called backupSnapshot with " + str(args))
|
||||
primaryStorageSRUuid = args['primaryStorageSRUuid']
|
||||
@ -655,7 +465,6 @@ def backupSnapshot(session, args):
|
||||
snapshotUuid = args['snapshotUuid']
|
||||
prevSnapshotUuid = args['prevSnapshotUuid']
|
||||
prevBackupUuid = args['prevBackupUuid']
|
||||
isFirstSnapshotOfRootVolume = getIsTrueString(args['isFirstSnapshotOfRootVolume'])
|
||||
isISCSI = getIsTrueString(args['isISCSI'])
|
||||
|
||||
primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI)
|
||||
@ -675,28 +484,23 @@ def backupSnapshot(session, args):
|
||||
# on it.
|
||||
backupsDir = mountSnapshotsDir(secondaryStorageMountPath, "snapshots", dcId, accountId, volumeId)
|
||||
util.SMlog("Backups dir " + backupsDir)
|
||||
# chdir to the backupsDir for convenience
|
||||
chdir(backupsDir)
|
||||
|
||||
if baseCopyUuid == prevBaseCopyUuid:
|
||||
# There has been no change since the last snapshot so no need to backup
|
||||
util.SMlog("There has been no change since the last snapshot with backup: " + prevBaseCopyUuid)
|
||||
if isFirstSnapshotOfRootVolume:
|
||||
# baseCopyUuid is template. That is *NOT* the backup of any
|
||||
# snapshot. There is no backup. So create an empty dummyVHD representing the empty snapshot.
|
||||
# This will prevent deleteSnapshotBackup and createVolumeFromSnapshot from breaking.
|
||||
prevBaseCopyUuid = createDummyVHD(baseCopyPath, backupsDir, isISCSI)
|
||||
# The backup snapshot is the new dummy VHD created.
|
||||
|
||||
# Set the uuid of the current backup to that of last backup
|
||||
txt = "1#" + prevBaseCopyUuid
|
||||
return txt
|
||||
|
||||
# Check existence of snapshot on primary storage
|
||||
isfile(baseCopyPath, isISCSI)
|
||||
# copy baseCopyPath to backupsDir
|
||||
backupFile = os.path.join(backupsDir, baseCopyVHD)
|
||||
# copy baseCopyPath to backupsDir with new uuid
|
||||
backupUuid = util.gen_uuid()
|
||||
backupVHD = getBackupVHD(backupUuid)
|
||||
backupFile = os.path.join(backupsDir, backupVHD)
|
||||
util.SMlog("Back up " + baseCopyUuid + " to Secondary Storage as " + backupUuid)
|
||||
copyfile(baseCopyPath, backupFile, isISCSI)
|
||||
vhdutil.setHidden(backupFile, False)
|
||||
|
||||
# Now set the availability of the snapshotPath and the baseCopyPath to false
|
||||
makeUnavailable(snapshotUuid, primarySRPath, isISCSI)
|
||||
@ -705,44 +509,18 @@ def backupSnapshot(session, args):
|
||||
makeUnavailable(prevSnapshotUuid, primarySRPath, isISCSI)
|
||||
makeUnavailable(prevBaseCopyUuid, primarySRPath, isISCSI)
|
||||
|
||||
if isFirstSnapshotOfRootVolume:
|
||||
# First snapshot of the root volume.
|
||||
# It's parent is not null, but the template vhd.
|
||||
# Create a dummy empty vhd and set the parent of backupVHD to it.
|
||||
# This will prevent deleteSnapshotBackup and createVolumeFromSnapshot from breaking
|
||||
prevBackupUuid = createDummyVHD(baseCopyPath, backupsDir, isISCSI)
|
||||
|
||||
# Because the primary storage is always scanned, the parent of this base copy is always the first base copy.
|
||||
# We don't want that, we want a chain of VHDs each of which is a delta from the previous.
|
||||
# So set the parent of the current baseCopyVHD to prevBackupVHD
|
||||
if prevBackupUuid:
|
||||
# If there was a previous snapshot
|
||||
prevBackupVHD = getVHD(prevBackupUuid, isISCSI)
|
||||
setParent(prevBackupVHD, backupFile)
|
||||
prevBackupVHD = getBackupVHD(prevBackupUuid)
|
||||
prevBackupFile = os.path.join(backupsDir, prevBackupVHD)
|
||||
setParent(prevBackupFile, backupFile)
|
||||
|
||||
txt = "1#" + baseCopyUuid
|
||||
txt = "1#" + backupUuid
|
||||
return txt
|
||||
|
||||
def createDummyVHD(baseCopyPath, backupsDir, isISCSI):
|
||||
dummyUUID = ''
|
||||
try:
|
||||
dummyUUID = util.gen_uuid()
|
||||
dummyVHD = getVHD(dummyUUID, isISCSI)
|
||||
if isISCSI:
|
||||
checkVolumeAvailablility(baseCopyPath)
|
||||
cmd = [VHD_UTIL, 'query', '-v', '-n', baseCopyPath]
|
||||
virtualSizeInMB = util.pread2(cmd)
|
||||
util.SMlog("Virtual size of " + baseCopyPath + " is " + virtualSizeInMB)
|
||||
cmd = [VHD_UTIL, 'create', '-n', dummyVHD, '-s', virtualSizeInMB]
|
||||
util.pread2(cmd)
|
||||
except CommandException:
|
||||
errMsg = "Unexpected error while creating a dummy VHD " + dummyVHD + " on " + backupsDir
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
util.SMlog("Successfully created a new dummy VHD: " + dummyVHD + " on " + backupsDir)
|
||||
|
||||
return dummyUUID
|
||||
|
||||
@echo
|
||||
def deleteSnapshotBackup(session, args):
|
||||
util.SMlog("Calling deleteSnapshotBackup with " + str(args))
|
||||
@ -751,14 +529,12 @@ def deleteSnapshotBackup(session, args):
|
||||
volumeId = args['volumeId']
|
||||
secondaryStorageMountPath = args['secondaryStorageMountPath']
|
||||
backupUUID = args['backupUUID']
|
||||
childUUID = args['childUUID']
|
||||
isISCSI = getIsTrueString(args['isISCSI'])
|
||||
|
||||
backupsDir = mountSnapshotsDir(secondaryStorageMountPath, "snapshots", dcId, accountId, volumeId)
|
||||
# chdir to the backupsDir for convenience
|
||||
chdir(backupsDir)
|
||||
|
||||
backupVHD = getVHD(backupUUID, isISCSI)
|
||||
backupVHD = getBackupVHD(backupUUID)
|
||||
util.SMlog("checking existence of " + backupVHD)
|
||||
|
||||
# The backupVHD is on secondary which is NFS and not ISCSI.
|
||||
@ -767,211 +543,16 @@ def deleteSnapshotBackup(session, args):
|
||||
return "1"
|
||||
util.SMlog("backupVHD " + backupVHD + " exists.")
|
||||
|
||||
# Case 1) childUUID exists
|
||||
if childUUID:
|
||||
coalesceToChild(backupVHD, childUUID, isISCSI)
|
||||
else:
|
||||
# Just delete the backupVHD
|
||||
try:
|
||||
os.remove(backupVHD)
|
||||
except OSError, (errno, strerror):
|
||||
errMsg = "OSError while removing " + backupVHD + " with errno: " + str(errno) + " and strerr: " + strerror
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
# Just delete the backupVHD
|
||||
try:
|
||||
os.remove(backupVHD)
|
||||
except OSError, (errno, strerror):
|
||||
errMsg = "OSError while removing " + backupVHD + " with errno: " + str(errno) + " and strerr: " + strerror
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
|
||||
return "1"
|
||||
|
||||
@echo
|
||||
def createVolumeFromSnapshot(session, args):
|
||||
util.SMlog("Calling createVolumeFromSnapshot with " + str(args))
|
||||
dcId = args['dcId']
|
||||
accountId = args['accountId']
|
||||
volumeId = args['volumeId']
|
||||
secondaryStorageMountPath = args['secondaryStorageMountPath']
|
||||
backedUpSnapshotUuid = args['backedUpSnapshotUuid']
|
||||
templatePath = args['templatePath']
|
||||
templateDownloadFolder = args['templateDownloadFolder']
|
||||
isISCSI = getIsTrueString(args['isISCSI'])
|
||||
|
||||
backupsDir = mountSnapshotsDir(secondaryStorageMountPath, "snapshots", dcId, accountId, volumeId)
|
||||
util.SMlog("Backups dir " + backupsDir)
|
||||
|
||||
# chdir to the backupsDir for convenience
|
||||
chdir(backupsDir)
|
||||
|
||||
# Get the parent VHD chain of the backupSnapshotVHD
|
||||
vhdChain = []
|
||||
uuid = backedUpSnapshotUuid
|
||||
while uuid is not None:
|
||||
util.SMlog("Current uuid in parent chain " + uuid)
|
||||
vhd = getVHD(uuid, isISCSI)
|
||||
vhdChain.append(vhd)
|
||||
uuid = getParent(vhd, isISCSI)
|
||||
util.SMlog("successfully created the parent chain " + str(vhdChain))
|
||||
|
||||
destDirParent = ''
|
||||
destDir = ''
|
||||
if templateDownloadFolder:
|
||||
# Copy all the vhds to the final destination templates dir
|
||||
# It is some random directory on the primary created for templates.
|
||||
destDirParent = os.path.join(SR.MOUNT_BASE, "mount" + str(int(random.random() * 1000000)))
|
||||
destDir = os.path.join(destDirParent, templateDownloadFolder)
|
||||
# create the this directory, if it isn't there
|
||||
makedirs(destDir)
|
||||
else:
|
||||
# Copy all the vhds to a temp directory
|
||||
# Create a temp directory
|
||||
destDir = backupsDir + '_temp'
|
||||
|
||||
# Delete the temp directory if it already exists (from a previous createVolumeFromSnapshot)
|
||||
rmtree(destDir)
|
||||
|
||||
if templateDownloadFolder:
|
||||
# The destDir was created in create_secondary_storage_folder but is not mounted on the primary. Mount it again.
|
||||
remoteMountPoint = os.path.join(secondaryStorageMountPath, "template");
|
||||
remoteMountPoint = os.path.join(remoteMountPoint, templateDownloadFolder)
|
||||
mount(remoteMountPoint, destDir)
|
||||
else:
|
||||
# The parent of the destDir is the snapshots dir and is mounted on the primary. Just create the directory structure.
|
||||
makedirs(destDir)
|
||||
|
||||
# Copy
|
||||
for vhd in vhdChain:
|
||||
vhdPath = os.path.join(backupsDir, vhd)
|
||||
tempFile = os.path.join(destDir, vhd)
|
||||
# We are copying files on secondary storage which is NFS. Set isISCSI to false
|
||||
copyfile(vhdPath, tempFile, False)
|
||||
util.SMlog("Successfully copied all files")
|
||||
|
||||
# coalesce the vhd chains from bottom to top
|
||||
# chdir to destDir for convenience
|
||||
chdir(destDir)
|
||||
|
||||
# coalesce
|
||||
i = 0
|
||||
finalVhd = vhdChain[0]
|
||||
for vhd in vhdChain:
|
||||
finalVhd = vhdChain[i]
|
||||
last = len(vhdChain) - 1
|
||||
if i == last:
|
||||
# last vhd, has no parent. Don't coalesce
|
||||
break
|
||||
if templatePath and i == (last - 1):
|
||||
# Hack for root disks, the first Vhd is a dummy one.
|
||||
# Do not coalesce the actual disk with the dummy one.
|
||||
# Instead coalesce it with the templateVHD.
|
||||
break
|
||||
|
||||
i = i + 1
|
||||
# They are arranged from child to parent.
|
||||
util.SMlog("Starting to coalesce " + vhd + " with its parent")
|
||||
try:
|
||||
cmd = [VHD_UTIL, "coalesce", "-n", vhd]
|
||||
txt = util.pread2(cmd)
|
||||
except:
|
||||
errMsg = "Unexpected error while trying to coalesce " + vhd + " to its parent"
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
util.SMlog("Successfully coalesced " + vhd + " with its parent")
|
||||
|
||||
# Remove the child vhd
|
||||
try:
|
||||
os.remove(vhd)
|
||||
except OSError, (errno, strerror):
|
||||
errMsg = "OSError while removing " + vhd + " with errno: " + str(errno) + " and strerr: " + strerror
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
|
||||
util.SMlog("successfully coalesced all vhds to the parent " + finalVhd)
|
||||
finalVhdPath = os.path.join(destDir, finalVhd)
|
||||
|
||||
# This vhd has to be introduced with a new uuid because of the VDI UUID
|
||||
# uniqueness constraint
|
||||
newUUID = ''
|
||||
try:
|
||||
newUUID = util.gen_uuid()
|
||||
except:
|
||||
errMsg = "Unexpected error while trying to generate a uuid"
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
util.SMlog("generated a uuid " + newUUID)
|
||||
|
||||
# Now, at the Java layer an NFS SR is created with mount point destDir and scanned. The vhd
|
||||
# file is automatically introduced and a vdi.copy is done to move it to
|
||||
# primary storage.
|
||||
# new vhd file is created on NFS. So it should have NFS naming convention,
|
||||
# set isISCSI to false
|
||||
newVhd = getVHD(newUUID, False)
|
||||
rename(finalVhd, newVhd)
|
||||
|
||||
# For root disk
|
||||
if templatePath:
|
||||
# This will create a vhd on secondary storage destDir with name newVhd
|
||||
mergeTemplateAndSnapshot(secondaryStorageMountPath, templatePath, destDir, newVhd, isISCSI)
|
||||
|
||||
# set the hidden flag of the new VHD to false, so that it doesn't get deleted when the SR scan is done.
|
||||
try:
|
||||
vhdutil.setHidden(newVhd, False)
|
||||
except:
|
||||
errMsg = "Unexpected error while trying to set Hidden flag of " + newVhd
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
util.SMlog("Successfully set hidden flag of " + newVhd)
|
||||
|
||||
virtualSizeInMB = 0
|
||||
try:
|
||||
cmd = [VHD_UTIL, 'query', '-v', '-n', newVhd]
|
||||
virtualSizeInMB = util.pread2(cmd)
|
||||
util.SMlog("Virtual size of " + newVhd + " is " + virtualSizeInMB)
|
||||
except:
|
||||
errMsg = "Unexpected error while trying to get the virtual size of " + newVhd
|
||||
util.SMlog(errMsg)
|
||||
raise xs_errors.XenError(errMsg)
|
||||
|
||||
if templateDownloadFolder:
|
||||
# We are done with the destDir on the primary, unmount it and delete the random directory.
|
||||
# Current dir is destDir
|
||||
# cd to something else before unmounting
|
||||
chdir(backupsDir) # as good as anything else
|
||||
# unmount what was mounted.
|
||||
umount(destDir)
|
||||
# Remove the tree starting from the mountXXXX part, just the directories
|
||||
rmtree(destDirParent)
|
||||
# The coalesced data is still available on the secondary.
|
||||
|
||||
return "1#" + newUUID + "#" + virtualSizeInMB
|
||||
|
||||
def mergeTemplateAndSnapshot(secondaryStorageMountPath, templatePath, destDir, newVhd, isISCSI):
|
||||
# Mount the template directory present on the secondary to the primary
|
||||
templateDirOnPrimary = mountTemplatesDir(secondaryStorageMountPath, templatePath)
|
||||
|
||||
# Current dir is destDir
|
||||
templateVHD = os.path.basename(templatePath)
|
||||
templatePathOnPrimary = os.path.join(templateDirOnPrimary, templateVHD)
|
||||
templatePathOnTemp = os.path.join(destDir, templateVHD)
|
||||
# Copying from secondary to secondary, so set ISCSI to False
|
||||
copyfile(templatePathOnPrimary, templatePathOnTemp, False)
|
||||
|
||||
# unmount the temporary directory created for copying the template
|
||||
umount(templateDirOnPrimary)
|
||||
|
||||
# get the dummyVHD which is the parent of the new Vhd
|
||||
dummyUUID = getParent(newVhd, isISCSI)
|
||||
dummyVHD = getVHD(dummyUUID, isISCSI)
|
||||
# set the parent of the newVhd to the templateVHD on secondary
|
||||
setParent(templateVHD, newVhd)
|
||||
# remove the dummyVHD as we don't have any use for it and it wil
|
||||
# lie around after we do an SR scan
|
||||
os.remove(dummyVHD)
|
||||
|
||||
# coalesce the two VHDs into templateVHD
|
||||
coalesce(newVhd)
|
||||
|
||||
# rename templateVHD to newVhd
|
||||
rename(templateVHD, newVhd)
|
||||
|
||||
return
|
||||
|
||||
def rmtree(path):
|
||||
if os.path.isdir(path):
|
||||
try:
|
||||
@ -1000,5 +581,5 @@ def deleteSnapshotsDir(session, args):
|
||||
return "1"
|
||||
|
||||
if __name__ == "__main__":
|
||||
XenAPIPlugin.dispatch({"create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "createVolumeFromSnapshot": createVolumeFromSnapshot, "unmountSnapshotsDir": unmountSnapshotsDir, "deleteSnapshotsDir": deleteSnapshotsDir, "validateSnapshot" : validateSnapshot})
|
||||
XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir, "deleteSnapshotsDir": deleteSnapshotsDir})
|
||||
|
||||
|
||||
@ -91,8 +91,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
|
||||
|
||||
@Override @DB
|
||||
public long submitAsyncJob(AsyncJobVO job, boolean scheduleJobExecutionInContext) {
|
||||
if(s_logger.isDebugEnabled())
|
||||
s_logger.debug("submit async job-" + job.getId() + ", details: " + job.toString());
|
||||
|
||||
|
||||
AsyncJobExecutor executor = getJobExecutor(job);
|
||||
if(executor == null) {
|
||||
@ -108,6 +107,9 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
|
||||
executor.setSyncSource(null);
|
||||
executor.setJob(job);
|
||||
scheduleExecution(executor, scheduleJobExecutionInContext);
|
||||
if(s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("submit async job-" + job.getId() + ", details: " + job.toString());
|
||||
}
|
||||
return job.getId();
|
||||
} catch(Exception e) {
|
||||
s_logger.error("Unexpected exception: ", e);
|
||||
|
||||
@ -116,49 +116,24 @@ public class CreatePrivateTemplateExecutor extends VolumeOperationExecutor {
|
||||
|
||||
asyncMgr.updateAsyncJobAttachment(job.getId(), "vm_template", template.getId());
|
||||
asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, template.getId());
|
||||
Snapshot snapshot = null;
|
||||
|
||||
if (snapshotId == null) {
|
||||
// We are create private template from volume. Create a snapshot, copy the vhd chain of the disk to secondary storage.
|
||||
// For template snapshot, we use a separate snapshot method.
|
||||
snapshot = vmMgr.createTemplateSnapshot(param.getUserId(), param.getVolumeId());
|
||||
}
|
||||
else {
|
||||
// We are creating a private template from an already present snapshot.
|
||||
// This snapshot is already backed up on secondary storage.
|
||||
snapshot = managerServer.findSnapshotById(snapshotId);
|
||||
}
|
||||
template = managerServer.createPrivateTemplate(template, param.getUserId(), snapshotId, volumeId, param.getName(), param.getDescription());
|
||||
|
||||
if (snapshot == null) {
|
||||
details += ", reason: Failed to create snapshot for basis of private template";
|
||||
} else {
|
||||
param.setSnapshotId(snapshot.getId());
|
||||
|
||||
template = managerServer.createPrivateTemplate(template,
|
||||
param.getUserId(),
|
||||
param.getSnapshotId(),
|
||||
param.getName(),
|
||||
param.getDescription());
|
||||
|
||||
if(template != null) {
|
||||
VMTemplateHostVO templateHostRef = managerServer.findTemplateHostRef(template.getId(), volume.getDataCenterId());
|
||||
jobStatus = AsyncJobResult.STATUS_SUCCEEDED;
|
||||
resultCode = 0;
|
||||
details = null;
|
||||
String eventParams = "id="+template.getId()+"\nname=" + template.getName() +"\nsize="+volume.getSize()+ "\ndcId="+volume.getDataCenterId();
|
||||
managerServer.saveEvent(param.getUserId(), param.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_TEMPLATE_CREATE, "Successfully created Template " +param.getName(), eventParams ,param.getEventId());
|
||||
resultObject = composeResultObject(template, templateHostRef, volume.getDataCenterId());
|
||||
}
|
||||
|
||||
// Irrespective of whether the template was created or not,
|
||||
// cleanup the snapshot taken for this template. (If this template is created from a volume and not a snapshot)
|
||||
if(snapshotId == null) {
|
||||
// Template was created from volume
|
||||
// and snapshot is not null.
|
||||
managerServer.destroyTemplateSnapshot(param.getUserId(), snapshot.getId());
|
||||
}
|
||||
if (template != null) {
|
||||
VMTemplateHostVO templateHostRef = managerServer.findTemplateHostRef(template.getId(),
|
||||
volume.getDataCenterId());
|
||||
jobStatus = AsyncJobResult.STATUS_SUCCEEDED;
|
||||
resultCode = 0;
|
||||
details = null;
|
||||
String eventParams = "id=" + template.getId() + "\nname=" + template.getName() + "\nsize="
|
||||
+ volume.getSize() + "\ndcId=" + volume.getDataCenterId();
|
||||
managerServer.saveEvent(param.getUserId(), param.getAccountId(), EventVO.LEVEL_INFO,
|
||||
EventTypes.EVENT_TEMPLATE_CREATE, "Successfully created Template "
|
||||
+ param.getName(), eventParams, param.getEventId());
|
||||
resultObject = composeResultObject(template, templateHostRef, volume.getDataCenterId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (InvalidParameterValueException e) {
|
||||
details += ", reason: " + e.getMessage();
|
||||
|
||||
@ -33,6 +33,7 @@ import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.Snapshot.SnapshotType;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.snapshot.SnapshotManager;
|
||||
import com.cloud.user.Account;
|
||||
import com.google.gson.Gson;
|
||||
@ -54,8 +55,10 @@ public class CreateSnapshotExecutor extends BaseAsyncJobExecutor {
|
||||
} else {
|
||||
SnapshotOperationParam param = gson.fromJson(job.getCmdInfo(), SnapshotOperationParam.class);
|
||||
SnapshotManager snapshotManager = asyncMgr.getExecutorContext().getSnapshotMgr();
|
||||
VolumeDao volumeDao = asyncMgr.getExecutorContext().getVolumeDao();
|
||||
long volumeId = param.getVolumeId();
|
||||
List<Long> policyIds = param.getPolicyIds();
|
||||
long policyId = param.getPolicyId();
|
||||
|
||||
long snapshotId = 0;
|
||||
long userId = param.getUserId();
|
||||
|
||||
@ -65,34 +68,48 @@ public class CreateSnapshotExecutor extends BaseAsyncJobExecutor {
|
||||
int result = AsyncJobResult.STATUS_FAILED;
|
||||
int errorCode = BaseCmd.INTERNAL_ERROR;
|
||||
Object resultObject = "Failed to create snapshot.";
|
||||
VolumeVO vol = null;
|
||||
|
||||
try {
|
||||
SnapshotVO snapshot = snapshotManager.createSnapshot(userId, param.getVolumeId(), param.getPolicyIds());
|
||||
vol = volumeDao.acquire(volumeId, 10);
|
||||
if( vol != null) {
|
||||
SnapshotVO snapshot = snapshotManager.createSnapshot(userId, volumeId, policyId);
|
||||
|
||||
if (snapshot != null && snapshot.getStatus() == Snapshot.Status.CreatedOnPrimary) {
|
||||
snapshotId = snapshot.getId();
|
||||
asyncMgr.updateAsyncJobStatus(jobId, BaseCmd.PROGRESS_INSTANCE_CREATED, snapshotId);
|
||||
backedUp = snapshotManager.backupSnapshotToSecondaryStorage(userId, snapshot);
|
||||
if (backedUp) {
|
||||
result = AsyncJobResult.STATUS_SUCCEEDED;
|
||||
errorCode = 0; // Success
|
||||
resultObject = composeResultObject(snapshot);
|
||||
}
|
||||
else {
|
||||
// More specific error
|
||||
resultObject = "Created snapshot: " + snapshotId + " on primary but failed to backup on secondary";
|
||||
}
|
||||
}
|
||||
if (snapshot != null && snapshot.getStatus() == Snapshot.Status.CreatedOnPrimary) {
|
||||
snapshotId = snapshot.getId();
|
||||
asyncMgr.updateAsyncJobStatus(jobId, BaseCmd.PROGRESS_INSTANCE_CREATED, snapshotId);
|
||||
backedUp = snapshotManager.backupSnapshotToSecondaryStorage(userId, snapshot);
|
||||
if (backedUp) {
|
||||
result = AsyncJobResult.STATUS_SUCCEEDED;
|
||||
errorCode = 0; // Success
|
||||
resultObject = composeResultObject(snapshot);
|
||||
}
|
||||
else {
|
||||
// More specific error
|
||||
resultObject = "Created snapshot: " + snapshotId + " on primary but failed to backup on secondary";
|
||||
}
|
||||
} else if (snapshot != null && snapshot.getStatus() == Snapshot.Status.EmptySnapshot) {
|
||||
resultObject ="There is no change since last snapshot, please use last snapshot";
|
||||
s_logger.warn(resultObject);
|
||||
}
|
||||
} else {
|
||||
resultObject = "Another snapshot is being created for " + volumeId + " try another time ";
|
||||
s_logger.warn(resultObject);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
resultObject = "Unable to create snapshot: " + e.getMessage();
|
||||
s_logger.warn(resultObject, e);
|
||||
} finally {
|
||||
if( vol != null ){
|
||||
volumeDao.release(volumeId);
|
||||
}
|
||||
}
|
||||
|
||||
// In all cases, ensure that we call completeAsyncJob to the asyncMgr.
|
||||
asyncMgr.completeAsyncJob(jobId, result, errorCode, resultObject);
|
||||
|
||||
// Cleanup jobs to do after the snapshot has been created.
|
||||
snapshotManager.postCreateSnapshot(userId, volumeId, snapshotId, policyIds, backedUp);
|
||||
snapshotManager.postCreateSnapshot(userId, volumeId, snapshotId, policyId, backedUp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@ public class SnapshotOperationParam {
|
||||
private long accountId;
|
||||
private long userId;
|
||||
private long snapshotId = 0;
|
||||
private List<Long> policyIds = null;
|
||||
private long policyId = 0;
|
||||
private long volumeId;
|
||||
private String name = null;
|
||||
@ -44,11 +43,11 @@ public class SnapshotOperationParam {
|
||||
}
|
||||
|
||||
// Used to create a snapshot
|
||||
public SnapshotOperationParam(long userId, long accountId, long volumeId, List<Long> policyIds) {
|
||||
public SnapshotOperationParam(long userId, long accountId, long volumeId, long policyId) {
|
||||
setUserId(userId);
|
||||
setAccountId(accountId);
|
||||
setVolumeId(volumeId);
|
||||
this.policyIds = policyIds;
|
||||
this.policyId = policyId;
|
||||
}
|
||||
|
||||
// Used for CreateVolumeFromSnapshot
|
||||
@ -84,10 +83,6 @@ public class SnapshotOperationParam {
|
||||
this.snapshotId = snapshotId;
|
||||
}
|
||||
|
||||
public List<Long> getPolicyIds() {
|
||||
return policyIds;
|
||||
}
|
||||
|
||||
public long getPolicyId() {
|
||||
return policyId;
|
||||
}
|
||||
|
||||
@ -355,7 +355,6 @@ public class ManagementServerImpl implements ManagementServer {
|
||||
private final AsyncJobManager _asyncMgr;
|
||||
private final TemplateManager _tmpltMgr;
|
||||
private final SnapshotManager _snapMgr;
|
||||
private final SnapshotScheduler _snapshotScheduler;
|
||||
private final NetworkGroupManager _networkGroupMgr;
|
||||
private final int _purgeDelay;
|
||||
private final boolean _directAttachNetworkExternalIpAllocator;
|
||||
@ -454,7 +453,6 @@ public class ManagementServerImpl implements ManagementServer {
|
||||
_asyncMgr = locator.getManager(AsyncJobManager.class);
|
||||
_tmpltMgr = locator.getManager(TemplateManager.class);
|
||||
_snapMgr = locator.getManager(SnapshotManager.class);
|
||||
_snapshotScheduler = locator.getManager(SnapshotScheduler.class);
|
||||
_networkGroupMgr = locator.getManager(NetworkGroupManager.class);
|
||||
_uploadMonitor = locator.getManager(UploadMonitor.class);
|
||||
|
||||
@ -6862,21 +6860,12 @@ public class ManagementServerImpl implements ManagementServer {
|
||||
throw new InvalidParameterValueException("Snapshots of volumes attached to System or router VM are not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
Long jobId = _snapshotScheduler.scheduleManualSnapshot(userId, volumeId);
|
||||
if (jobId == null) {
|
||||
throw new InternalErrorException("Snapshot could not be scheduled because there is another snapshot underway for the same volume. " +
|
||||
"Please wait for some time.");
|
||||
}
|
||||
long policyId = Snapshot.MANUAL_POLICY_ID;
|
||||
long jobId = _snapMgr.createSnapshotAsync(userId, volumeId, policyId);
|
||||
|
||||
return jobId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotVO createTemplateSnapshot(Long userId, long volumeId) {
|
||||
return _vmMgr.createTemplateSnapshot(userId, volumeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean destroyTemplateSnapshot(Long userId, long snapshotId) {
|
||||
return _vmMgr.destroyTemplateSnapshot(userId, snapshotId);
|
||||
@ -6963,7 +6952,7 @@ public class ManagementServerImpl implements ManagementServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Snapshot findSnapshotById(long snapshotId) {
|
||||
public SnapshotVO findSnapshotById(long snapshotId) {
|
||||
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
||||
if (snapshot != null && snapshot.getRemoved() == null && snapshot.getStatus() == Snapshot.Status.BackedUp) {
|
||||
return snapshot;
|
||||
@ -6974,9 +6963,9 @@ public class ManagementServerImpl implements ManagementServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, long snapshotId, String name, String description) throws InvalidParameterValueException {
|
||||
public VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, Long snapshotId, Long volumeId, String name, String description) throws InvalidParameterValueException {
|
||||
|
||||
return _vmMgr.createPrivateTemplate(template, userId, snapshotId, name, description);
|
||||
return _vmMgr.createPrivateTemplate(template, userId, snapshotId, volumeId, name, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -490,7 +490,7 @@ public class StorageManagerImpl implements StorageManager {
|
||||
}
|
||||
|
||||
@DB
|
||||
protected Pair<VolumeVO, String> createVolumeFromSnapshot(long userId, long accountId, String userSpecifiedName, DataCenterVO dc, DiskOfferingVO diskOffering, SnapshotVO snapshot, String templatePath, Long originalVolumeSize, VMTemplateVO template) {
|
||||
protected Pair<VolumeVO, String> createVolumeFromSnapshot(long userId, long accountId, String userSpecifiedName, DataCenterVO dc, SnapshotVO snapshot, long virtualsize) {
|
||||
|
||||
VolumeVO createdVolume = null;
|
||||
Long volumeId = null;
|
||||
@ -506,10 +506,7 @@ public class StorageManagerImpl implements StorageManager {
|
||||
volume.setAccountId(accountId);
|
||||
volume.setDomainId(account.getDomainId());
|
||||
volume.setMirrorState(MirrorState.NOT_MIRRORED);
|
||||
if (diskOffering != null) {
|
||||
volume.setDiskOfferingId(diskOffering.getId());
|
||||
}
|
||||
volume.setSize(originalVolumeSize);
|
||||
volume.setSize(virtualsize);
|
||||
volume.setStorageResourceType(Storage.StorageResourceType.STORAGE_POOL);
|
||||
volume.setInstanceId(null);
|
||||
volume.setUpdated(new Date());
|
||||
@ -538,8 +535,7 @@ public class StorageManagerImpl implements StorageManager {
|
||||
String volumeUUID = null;
|
||||
String details = null;
|
||||
|
||||
DiskProfile dskCh = createDiskCharacteristics(volume, template, dc, diskOffering);
|
||||
|
||||
DiskProfile dskCh = new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), 0, virtualsize, null, false, false, null);
|
||||
|
||||
// Determine what pod to store the volume in
|
||||
while ((pod = _agentMgr.findPod(null, null, dc, account.getId(), podsToAvoid)) != null) {
|
||||
@ -552,7 +548,7 @@ public class StorageManagerImpl implements StorageManager {
|
||||
|
||||
// Get the newly created VDI from the snapshot.
|
||||
// This will return a null volumePath if it could not be created
|
||||
Pair<String, String> volumeDetails = createVDIFromSnapshot(userId, snapshot, pool, templatePath);
|
||||
Pair<String, String> volumeDetails = createVDIFromSnapshot(userId, snapshot, pool);
|
||||
volumeUUID = volumeDetails.first();
|
||||
details = volumeDetails.second();
|
||||
|
||||
@ -621,69 +617,18 @@ public class StorageManagerImpl implements StorageManager {
|
||||
SnapshotVO snapshot = _snapshotDao.findById(snapshotId); // Precondition: snapshot is not null and not removed.
|
||||
Long origVolumeId = snapshot.getVolumeId();
|
||||
VolumeVO originalVolume = _volsDao.findById(origVolumeId); // NOTE: Original volume could be destroyed and removed.
|
||||
String templatePath = null;
|
||||
VMTemplateVO template = null;
|
||||
if(originalVolume.getVolumeType().equals(Volume.VolumeType.ROOT)){
|
||||
if(originalVolume.getTemplateId() == null){
|
||||
details = "Null Template Id for Root Volume Id: " + origVolumeId + ". Cannot create volume from snapshot of root disk.";
|
||||
s_logger.error(details);
|
||||
}
|
||||
else {
|
||||
Long templateId = originalVolume.getTemplateId();
|
||||
template = _templateDao.findById(templateId);
|
||||
if(template == null) {
|
||||
details = "Unable find template id: " + templateId + " to create volume from root disk";
|
||||
s_logger.error(details);
|
||||
}
|
||||
else if (template.getFormat() != ImageFormat.ISO) {
|
||||
// For ISOs there is no base template VHD file. The root disk itself is the base template.
|
||||
// Creating a volume from an ISO Root Disk is the same as creating a volume for a Data Disk.
|
||||
|
||||
// Absolute crappy way of getting the template path on secondary storage.
|
||||
// Why is the secondary storage a host? It's just an NFS mount point. Why do we need to look into the templateHostVO?
|
||||
HostVO secondaryStorageHost = getSecondaryStorageHost(originalVolume.getDataCenterId());
|
||||
VMTemplateHostVO templateHostVO = _templateHostDao.findByHostTemplate(secondaryStorageHost.getId(), templateId);
|
||||
if (templateHostVO == null ||
|
||||
templateHostVO.getDownloadState() != VMTemplateStorageResourceAssoc.Status.DOWNLOADED ||
|
||||
(templatePath = templateHostVO.getInstallPath()) == null)
|
||||
{
|
||||
details = "Template id: " + templateId + " is not present on secondaryStorageHost Id: " + secondaryStorageHost.getId() + ". Can't create volume from ROOT DISK";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (details == null) {
|
||||
// everything went well till now
|
||||
DataCenterVO dc = _dcDao.findById(originalVolume.getDataCenterId());
|
||||
DiskOfferingVO diskOffering = null;
|
||||
// everything went well till now
|
||||
DataCenterVO dc = _dcDao.findById(originalVolume.getDataCenterId());
|
||||
|
||||
if (originalVolume.getVolumeType() == VolumeType.DATADISK || originalVolume.getVolumeType() == VolumeType.ROOT) {
|
||||
Long diskOfferingId = originalVolume.getDiskOfferingId();
|
||||
if (diskOfferingId != null) {
|
||||
diskOffering = _diskOfferingDao.findById(diskOfferingId);
|
||||
}
|
||||
}
|
||||
// else if (originalVolume.getVolumeType() == VolumeType.ROOT) {
|
||||
// // Create a temporary disk offering with the same size as the ROOT DISK
|
||||
// Long rootDiskSize = originalVolume.getSize();
|
||||
// Long rootDiskSizeInMB = rootDiskSize/(1024*1024);
|
||||
// Long sizeInGB = rootDiskSizeInMB/1024;
|
||||
// String name = "Root Disk Offering";
|
||||
// String displayText = "Temporary Disk Offering for Snapshot from Root Disk: " + originalVolume.getId() + "[" + sizeInGB + "GB Disk]";
|
||||
// diskOffering = new DiskOfferingVO(originalVolume.getDomainId(), name, displayText, rootDiskSizeInMB, false, null);
|
||||
// }
|
||||
else {
|
||||
// The code never reaches here.
|
||||
s_logger.error("Original volume must have been a ROOT DISK or a DATA DISK");
|
||||
return null;
|
||||
}
|
||||
Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(userId, accountId, volumeName, dc, diskOffering, snapshot, templatePath, originalVolume.getSize(), template);
|
||||
createdVolume = volumeDetails.first();
|
||||
if (createdVolume != null) {
|
||||
volumeId = createdVolume.getId();
|
||||
}
|
||||
details = volumeDetails.second();
|
||||
Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(userId, accountId, volumeName, dc,
|
||||
snapshot, originalVolume.getSize());
|
||||
createdVolume = volumeDetails.first();
|
||||
if (createdVolume != null) {
|
||||
volumeId = createdVolume.getId();
|
||||
}
|
||||
details = volumeDetails.second();
|
||||
|
||||
|
||||
Transaction txn = Transaction.currentTxn();
|
||||
txn.start();
|
||||
@ -719,7 +664,7 @@ public class StorageManagerImpl implements StorageManager {
|
||||
return createdVolume;
|
||||
}
|
||||
|
||||
protected Pair<String, String> createVDIFromSnapshot(long userId, SnapshotVO snapshot, StoragePoolVO pool, String templatePath) {
|
||||
protected Pair<String, String> createVDIFromSnapshot(long userId, SnapshotVO snapshot, StoragePoolVO pool) {
|
||||
String vdiUUID = null;
|
||||
|
||||
Long volumeId = snapshot.getVolumeId();
|
||||
@ -738,8 +683,7 @@ public class StorageManagerImpl implements StorageManager {
|
||||
accountId,
|
||||
volumeId,
|
||||
backedUpSnapshotUuid,
|
||||
snapshot.getName(),
|
||||
templatePath);
|
||||
snapshot.getName());
|
||||
|
||||
String basicErrMsg = "Failed to create volume from " + snapshot.getName() + " for volume: " + volume.getId();
|
||||
CreateVolumeFromSnapshotAnswer answer = (CreateVolumeFromSnapshotAnswer) sendToHostsOnStoragePool(pool.getId(),
|
||||
|
||||
@ -47,12 +47,12 @@ public interface SnapshotManager extends Manager {
|
||||
* @throws ResourceAllocationException
|
||||
* @throws InvalidParameterValueException
|
||||
*/
|
||||
SnapshotVO createSnapshot(long userId, long volumeId, List<Long> policies) throws InvalidParameterValueException, ResourceAllocationException;
|
||||
SnapshotVO createSnapshot(long userId, long volumeId, long policyId) throws InvalidParameterValueException, ResourceAllocationException;
|
||||
|
||||
/**
|
||||
* Creates a snapshot for the given volume
|
||||
*/
|
||||
long createSnapshotAsync(long userId, long volumeId, List<Long> policies);
|
||||
long createSnapshotAsync(long userId, long volumeId, long policyId);
|
||||
|
||||
/**
|
||||
* After successfully creating a snapshot of a volume, copy the snapshot to the secondary storage for
|
||||
@ -75,7 +75,7 @@ public interface SnapshotManager extends Manager {
|
||||
* @param policyIds The list of policyIds to which this snapshot belongs to
|
||||
* @param backedUp If true, the snapshot has been successfully created.
|
||||
*/
|
||||
void postCreateSnapshot(long userId, long volumeId, long snapshotId, List<Long> policyIds, boolean backedUp);
|
||||
void postCreateSnapshot(long userId, long volumeId, long snapshotId, long policyId, boolean backedUp);
|
||||
|
||||
/**
|
||||
* Creates a volume from the specified snapshot. A new volume is returned which is not attached to any VM Instance
|
||||
@ -158,4 +158,8 @@ public interface SnapshotManager extends Manager {
|
||||
|
||||
ImageFormat getImageFormat(Long volumeId);
|
||||
|
||||
SnapshotPolicyVO getPolicyForVolume(long volumeId);
|
||||
|
||||
boolean destroySnapshotBackUp(long userId, long snapshotId, long policyId);
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -45,6 +45,4 @@ public interface SnapshotScheduler extends Manager, Scheduler {
|
||||
*/
|
||||
boolean removeSchedule(Long volumeId, Long policyId);
|
||||
|
||||
Long scheduleManualSnapshot(Long userId, Long volumeId);
|
||||
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import java.util.TimerTask;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import javax.naming.ConfigurationException;
|
||||
import javax.persistence.EntityExistsException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -35,6 +36,7 @@ import com.cloud.async.AsyncJobResult;
|
||||
import com.cloud.async.AsyncJobVO;
|
||||
import com.cloud.async.dao.AsyncJobDao;
|
||||
import com.cloud.configuration.dao.ConfigurationDao;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotPolicyVO;
|
||||
import com.cloud.storage.SnapshotScheduleVO;
|
||||
@ -194,36 +196,6 @@ public class SnapshotSchedulerImpl implements SnapshotScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@DB
|
||||
public Long scheduleManualSnapshot(Long userId, Long volumeId) {
|
||||
// Check if there is another manual snapshot scheduled which hasn't been executed yet.
|
||||
SearchCriteria<SnapshotScheduleVO> sc = _snapshotScheduleDao.createSearchCriteria();
|
||||
sc.addAnd("volumeId", SearchCriteria.Op.EQ, volumeId);
|
||||
sc.addAnd("policyId", SearchCriteria.Op.EQ, Snapshot.MANUAL_POLICY_ID);
|
||||
|
||||
List<SnapshotScheduleVO> snapshotSchedules = _snapshotScheduleDao.search(sc, null);
|
||||
if (!snapshotSchedules.isEmpty()) {
|
||||
Date scheduledTimestamp = snapshotSchedules.get(0).getScheduledTimestamp();
|
||||
String dateDisplay = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
|
||||
s_logger.error("Can't execute another manual snapshot for volume: " + volumeId +
|
||||
" while another manual snapshot for the same volume is being created/backed up. " +
|
||||
"The older snapshot was scheduled at " + dateDisplay);
|
||||
return null;
|
||||
}
|
||||
|
||||
SnapshotScheduleVO snapshotSchedule = new SnapshotScheduleVO(volumeId, Snapshot.MANUAL_POLICY_ID, _currentTimestamp);
|
||||
// There is a race condition here. Two threads enter here.
|
||||
// Both find that there are no manual snapshots for the same volume scheduled.
|
||||
// Both try to schedule. One fails, which is what we wanted anyway.
|
||||
_snapshotScheduleDao.persist(snapshotSchedule);
|
||||
List<Long> policyIds = new ArrayList<Long>();
|
||||
policyIds.add(Snapshot.MANUAL_POLICY_ID);
|
||||
return _snapshotManager.createSnapshotAsync(userId, volumeId, policyIds);
|
||||
}
|
||||
|
||||
@DB
|
||||
protected void scheduleSnapshots() {
|
||||
@ -237,72 +209,27 @@ public class SnapshotSchedulerImpl implements SnapshotScheduler {
|
||||
// Hence set user id to that of system
|
||||
long userId = 1;
|
||||
|
||||
// The volumes which are going to be snapshotted now.
|
||||
// The value contains the list of policies associated with this new snapshot.
|
||||
// There can be more than one policy for a list if different policies coincide for the same volume.
|
||||
Map<Long, List<Long>> listOfVolumesSnapshotted = new HashMap<Long, List<Long>>();
|
||||
Calendar cal = Calendar.getInstance(DateUtil.GMT_TIMEZONE);
|
||||
cal.add(Calendar.MINUTE, -15);
|
||||
//Skip any snapshots older than 15mins
|
||||
Date graceTime = cal.getTime();
|
||||
|
||||
for (SnapshotScheduleVO snapshotToBeExecuted : snapshotsToBeExecuted) {
|
||||
Date scheduleTime = snapshotToBeExecuted.getScheduledTimestamp();
|
||||
if(scheduleTime.before(graceTime)){
|
||||
s_logger.info("Snapshot schedule older than 15mins. Skipping snapshot for volume: "+snapshotToBeExecuted.getVolumeId());
|
||||
scheduleNextSnapshotJob(snapshotToBeExecuted);
|
||||
continue;
|
||||
}
|
||||
long policyId = snapshotToBeExecuted.getPolicyId();
|
||||
long volumeId = snapshotToBeExecuted.getVolumeId();
|
||||
List<Long> coincidingPolicies = listOfVolumesSnapshotted.get(volumeId);
|
||||
if (coincidingPolicies != null) {
|
||||
s_logger.debug("The snapshot for this volume " + volumeId + " and policy " + policyId + " has already been sent for execution along with " + coincidingPolicies.size() + " policies in total");
|
||||
// This can happen if this coincided with another schedule with a different policy
|
||||
// It would have added all the coinciding policies for the volume to the Map
|
||||
|
||||
if (coincidingPolicies.contains(snapshotToBeExecuted.getPolicyId())) {
|
||||
// Don't need to do anything now. The snapshot is already scheduled for execution.
|
||||
s_logger.debug("coincidingPolicies contains snapshotToBeExecuted id: " + snapshotToBeExecuted.getId() + ". Don't need to do anything now. The snapshot is already scheduled for execution.");
|
||||
}
|
||||
else {
|
||||
// This will not happen
|
||||
s_logger.warn("Snapshot Schedule " + snapshotToBeExecuted.getId() +
|
||||
" is ready for execution now at timestamp " + _currentTimestamp +
|
||||
" but is not coincident with one being executed for volume " + volumeId);
|
||||
// Add this to the list of policies for the snapshot schedule
|
||||
coincidingPolicies.add(snapshotToBeExecuted.getPolicyId());
|
||||
listOfVolumesSnapshotted.put(volumeId, coincidingPolicies);
|
||||
|
||||
}
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
|
||||
displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
|
||||
s_logger.debug("Scheduling 1 snapshot for volume " + volumeId + " for schedule id: "
|
||||
+ snapshotToBeExecuted.getId() + " at " + displayTime);
|
||||
}
|
||||
else {
|
||||
coincidingPolicies = new ArrayList<Long>();
|
||||
List<SnapshotScheduleVO> coincidingSchedules = _snapshotScheduleDao.getCoincidingSnapshotSchedules(volumeId, _currentTimestamp);
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
|
||||
displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
|
||||
long snapshotScheId = snapshotToBeExecuted.getId();
|
||||
SnapshotScheduleVO tmpSnapshotScheduleVO = null;
|
||||
try {
|
||||
tmpSnapshotScheduleVO = _snapshotScheduleDao.acquire(snapshotScheId);
|
||||
long jobId = _snapshotManager.createSnapshotAsync(userId, volumeId, policyId);
|
||||
tmpSnapshotScheduleVO.setAsyncJobId(jobId);
|
||||
_snapshotScheduleDao.update(snapshotScheId, tmpSnapshotScheduleVO);
|
||||
} finally {
|
||||
if (tmpSnapshotScheduleVO != null) {
|
||||
_snapshotScheduleDao.release(snapshotScheId);
|
||||
}
|
||||
|
||||
Transaction txn = Transaction.currentTxn();
|
||||
txn.start();
|
||||
// There are more snapshots scheduled for this volume at the same time.
|
||||
// Club all the policies together and append them to the coincidingPolicies List
|
||||
StringBuilder coincidentSchedules = new StringBuilder();
|
||||
for (SnapshotScheduleVO coincidingSchedule : coincidingSchedules) {
|
||||
coincidingPolicies.add(coincidingSchedule.getPolicyId());
|
||||
coincidentSchedules.append(coincidingSchedule.getId() + ", ");
|
||||
}
|
||||
txn.commit();
|
||||
|
||||
s_logger.debug("Scheduling 1 snapshot for volume " + volumeId + " for schedule ids: " + coincidentSchedules + " at " + displayTime);
|
||||
long jobId = _snapshotManager.createSnapshotAsync(userId, volumeId, coincidingPolicies);
|
||||
|
||||
// Add this snapshot to the listOfVolumesSnapshotted
|
||||
// So that the coinciding schedules don't get scheduled again.
|
||||
listOfVolumesSnapshotted.put(volumeId, coincidingPolicies);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -328,10 +255,26 @@ public class SnapshotSchedulerImpl implements SnapshotScheduler {
|
||||
long policyId = policyInstance.getId();
|
||||
Date nextSnapshotTimestamp = getNextScheduledTime(policyId, _currentTimestamp);
|
||||
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO(policyInstance.getVolumeId(), policyId, nextSnapshotTimestamp);
|
||||
_snapshotScheduleDao.persist(snapshotScheduleVO);
|
||||
try{
|
||||
_snapshotScheduleDao.persist(snapshotScheduleVO);
|
||||
} catch (EntityExistsException e ) {
|
||||
snapshotScheduleVO = _snapshotScheduleDao.findOneByVolume(policyInstance.getVolumeId());
|
||||
try {
|
||||
snapshotScheduleVO = _snapshotScheduleDao.acquire(snapshotScheduleVO.getId());
|
||||
snapshotScheduleVO.setPolicyId(policyId);
|
||||
snapshotScheduleVO.setScheduledTimestamp(nextSnapshotTimestamp);
|
||||
_snapshotScheduleDao.update(snapshotScheduleVO.getId(), snapshotScheduleVO);
|
||||
} finally {
|
||||
if(snapshotScheduleVO != null ) {
|
||||
_snapshotScheduleDao.release(snapshotScheduleVO.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return nextSnapshotTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override @DB
|
||||
public boolean removeSchedule(Long volumeId, Long policyId) {
|
||||
// We can only remove schedules which are in the future. Not which are already executed in the past.
|
||||
|
||||
@ -197,14 +197,8 @@ public interface UserVmManager extends Manager, VirtualMachineManager<UserVmVO>
|
||||
* @param description the user give description (aka display text) for the template
|
||||
* @return a template if successfully created, null otherwise
|
||||
*/
|
||||
VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, long snapshotId, String name, String description);
|
||||
VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, Long snapshotId, Long volumeId, String name, String description);
|
||||
|
||||
/**
|
||||
* @param userId The Id of the user who invoked this operation.
|
||||
* @param volumeId The volume for which this snapshot is being taken
|
||||
* @return The properties of the snapshot taken
|
||||
*/
|
||||
SnapshotVO createTemplateSnapshot(long userId, long volumeId);
|
||||
boolean destroyTemplateSnapshot(Long userId, long snapshotId);
|
||||
|
||||
/**
|
||||
|
||||
@ -20,7 +20,6 @@ package com.cloud.vm;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Formatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -48,6 +47,7 @@ import com.cloud.agent.api.CheckVirtualMachineAnswer;
|
||||
import com.cloud.agent.api.CheckVirtualMachineCommand;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
|
||||
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
|
||||
import com.cloud.agent.api.GetVmStatsAnswer;
|
||||
import com.cloud.agent.api.GetVmStatsCommand;
|
||||
import com.cloud.agent.api.ManageSnapshotAnswer;
|
||||
@ -60,7 +60,6 @@ import com.cloud.agent.api.StartCommand;
|
||||
import com.cloud.agent.api.StopCommand;
|
||||
import com.cloud.agent.api.VmStatsEntry;
|
||||
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
||||
import com.cloud.agent.api.storage.CreatePrivateTemplateCommand;
|
||||
import com.cloud.alert.AlertManager;
|
||||
import com.cloud.api.BaseCmd;
|
||||
import com.cloud.async.AsyncJobExecutor;
|
||||
@ -2232,7 +2231,7 @@ public class UserVmManagerImpl implements UserVmManager {
|
||||
SnapshotVO snapshot = _snapshotDao.findById(Long.valueOf(snapshotId));
|
||||
if (snapshot != null) {
|
||||
VolumeVO volume = _volsDao.findById(snapshot.getVolumeId());
|
||||
ManageSnapshotCommand cmd = new ManageSnapshotCommand(ManageSnapshotCommand.DESTROY_SNAPSHOT, snapshotId, snapshot.getPath(), snapshot.getName(), null);
|
||||
ManageSnapshotCommand cmd = new ManageSnapshotCommand(snapshotId, snapshot.getPath());
|
||||
|
||||
Answer answer = null;
|
||||
String basicErrMsg = "Failed to destroy template snapshot: " + snapshot.getName();
|
||||
@ -2252,60 +2251,6 @@ public class UserVmManagerImpl implements UserVmManager {
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override @DB
|
||||
public SnapshotVO createTemplateSnapshot(long userId, long volumeId) {
|
||||
SnapshotVO createdSnapshot = null;
|
||||
VolumeVO volume = _volsDao.lock(volumeId, true);
|
||||
|
||||
Long id = null;
|
||||
|
||||
// Determine the name for this snapshot
|
||||
String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT);
|
||||
String snapshotName = volume.getName() + "_" + timeString;
|
||||
// Create the Snapshot object and save it so we can return it to the user
|
||||
SnapshotType snapshotType = SnapshotType.TEMPLATE;
|
||||
SnapshotVO snapshot = new SnapshotVO(volume.getAccountId(), volume.getId(), null, snapshotName, (short)snapshotType.ordinal(), snapshotType.name());
|
||||
snapshot = _snapshotDao.persist(snapshot);
|
||||
id = snapshot.getId();
|
||||
|
||||
// Send a ManageSnapshotCommand to the agent
|
||||
ManageSnapshotCommand cmd = new ManageSnapshotCommand(ManageSnapshotCommand.CREATE_SNAPSHOT, id, volume.getPath(), snapshotName, null);
|
||||
|
||||
String basicErrMsg = "Failed to create snapshot for volume: " + volume.getId();
|
||||
// This can be sent to a KVM host too. We are only taking snapshots on primary storage, which doesn't require XenServer.
|
||||
// So shouldBeSnapshotCapable is set to false.
|
||||
ManageSnapshotAnswer answer = (ManageSnapshotAnswer) _storageMgr.sendToHostsOnStoragePool(volume.getPoolId(), cmd, basicErrMsg);
|
||||
|
||||
// Update the snapshot in the database
|
||||
if ((answer != null) && answer.getResult()) {
|
||||
// The snapshot was successfully created
|
||||
|
||||
Transaction txn = Transaction.currentTxn();
|
||||
txn.start();
|
||||
createdSnapshot = _snapshotDao.findById(id);
|
||||
createdSnapshot.setPath(answer.getSnapshotPath());
|
||||
createdSnapshot.setStatus(Snapshot.Status.CreatedOnPrimary);
|
||||
_snapshotDao.update(id, createdSnapshot);
|
||||
txn.commit();
|
||||
|
||||
// Don't Create an event for Template Snapshots for now.
|
||||
} else {
|
||||
if (answer != null) {
|
||||
s_logger.error(answer.getDetails());
|
||||
}
|
||||
// The snapshot was not successfully created
|
||||
Transaction txn = Transaction.currentTxn();
|
||||
txn.start();
|
||||
createdSnapshot = _snapshotDao.findById(id);
|
||||
_snapshotDao.expunge(id);
|
||||
txn.commit();
|
||||
|
||||
createdSnapshot = null;
|
||||
}
|
||||
|
||||
return createdSnapshot;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void cleanNetworkRules(long userId, long instanceId) {
|
||||
@ -2467,133 +2412,137 @@ public class UserVmManagerImpl implements UserVmManager {
|
||||
}
|
||||
|
||||
@Override @DB
|
||||
public VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, long snapshotId, String name, String description) {
|
||||
VMTemplateVO privateTemplate = null;
|
||||
long templateId = template.getId();
|
||||
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
||||
if (snapshot != null) {
|
||||
Long volumeId = snapshot.getVolumeId();
|
||||
VolumeVO volume = _volsDao.findById(volumeId);
|
||||
StringBuilder userFolder = new StringBuilder();
|
||||
Formatter userFolderFormat = new Formatter(userFolder);
|
||||
userFolderFormat.format("u%06d", snapshot.getAccountId());
|
||||
public VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, Long snapshotId, Long volumeId, String name, String description) {
|
||||
String uniqueName = getRandomPrivateTemplateName();
|
||||
VMTemplateVO privateTemplate = null;
|
||||
long templateId = template.getId();
|
||||
VolumeVO volume = null;
|
||||
long zoneId;
|
||||
HostVO secondaryStorageHost;
|
||||
|
||||
String uniqueName = getRandomPrivateTemplateName();
|
||||
|
||||
long zoneId = volume.getDataCenterId();
|
||||
HostVO secondaryStorageHost = _storageMgr.getSecondaryStorageHost(zoneId);
|
||||
Command cmd = null;
|
||||
if( snapshotId != null ) {
|
||||
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
||||
if( snapshot == null ) {
|
||||
throw new CloudRuntimeException("Unable to find snapshot for Id " + snapshotId);
|
||||
}
|
||||
String backupSnapshotUUID = snapshot.getBackupSnapshotId();
|
||||
if (backupSnapshotUUID == null) {
|
||||
throw new CloudRuntimeException("Unable to create private template from snapshot " + snapshotId + " due to there is no backupSnapshotUUID for this snapshot");
|
||||
}
|
||||
Long origVolumeId = snapshot.getVolumeId();
|
||||
volume = _volsDao.findById(volumeId);
|
||||
zoneId = volume.getDataCenterId();
|
||||
secondaryStorageHost = _storageMgr.getSecondaryStorageHost(zoneId);
|
||||
String secondaryStorageURL = _storageMgr.getSecondaryStorageURL(zoneId);
|
||||
|
||||
if (secondaryStorageHost == null || secondaryStorageURL == null) {
|
||||
s_logger.warn("Did not find the secondary storage URL in the database.");
|
||||
return null;
|
||||
throw new CloudRuntimeException("Did not find the secondary storage URL in the database for zoneId "
|
||||
+ zoneId);
|
||||
}
|
||||
|
||||
Command cmd = null;
|
||||
String backupSnapshotUUID = snapshot.getBackupSnapshotId();
|
||||
if (backupSnapshotUUID != null) {
|
||||
// We are creating a private template from a snapshot which has been backed up to secondary storage.
|
||||
Long dcId = volume.getDataCenterId();
|
||||
Long accountId = volume.getAccountId();
|
||||
// We are creating a private template from a snapshot which has been
|
||||
// backed up to secondary storage.
|
||||
Long dcId = volume.getDataCenterId();
|
||||
Long accountId = volume.getAccountId();
|
||||
|
||||
String origTemplateInstallPath = null;
|
||||
String origTemplateInstallPath = null;
|
||||
|
||||
|
||||
if (ImageFormat.ISO != _snapshotMgr.getImageFormat(volumeId)) {
|
||||
Long origTemplateId = volume.getTemplateId();
|
||||
VMTemplateHostVO vmTemplateHostVO = _templateHostDao.findByHostTemplate(secondaryStorageHost.getId(), origTemplateId);
|
||||
origTemplateInstallPath = vmTemplateHostVO.getInstallPath();
|
||||
}
|
||||
|
||||
cmd = new CreatePrivateTemplateFromSnapshotCommand(_storageMgr.getPrimaryStorageNameLabel(volume),
|
||||
secondaryStorageURL,
|
||||
dcId,
|
||||
accountId,
|
||||
volumeId,
|
||||
backupSnapshotUUID,
|
||||
snapshot.getName(),
|
||||
origTemplateInstallPath,
|
||||
templateId,
|
||||
name);
|
||||
cmd = new CreatePrivateTemplateFromSnapshotCommand(_storageMgr.getPrimaryStorageNameLabel(volume),
|
||||
secondaryStorageURL, dcId, accountId, origVolumeId, backupSnapshotUUID, snapshot.getName(),
|
||||
origTemplateInstallPath, templateId, name);
|
||||
} else if( volumeId != null ) {
|
||||
volume = _volsDao.findById(volumeId);
|
||||
if( volume == null ) {
|
||||
throw new CloudRuntimeException("Unable to find volume for Id " + volumeId);
|
||||
}
|
||||
else {
|
||||
cmd = new CreatePrivateTemplateCommand(secondaryStorageURL,
|
||||
templateId,
|
||||
volume.getAccountId(),
|
||||
name,
|
||||
uniqueName,
|
||||
_storageMgr.getPrimaryStorageNameLabel(volume),
|
||||
snapshot.getPath(),
|
||||
snapshot.getName(),
|
||||
userFolder.toString());
|
||||
long instanceId = volume.getInstanceId();
|
||||
VMInstanceVO vm = _vmDao.findById(instanceId);
|
||||
State vmState = vm.getState();
|
||||
if( !vmState.equals(State.Stopped) && !vmState.equals(State.Destroyed)) {
|
||||
throw new CloudRuntimeException("Please put VM " + vm.getName() + " into Stopped state first");
|
||||
}
|
||||
|
||||
// FIXME: before sending the command, check if there's enough capacity on the storage server to create the template
|
||||
zoneId = volume.getDataCenterId();
|
||||
secondaryStorageHost = _storageMgr.getSecondaryStorageHost(zoneId);
|
||||
String secondaryStorageURL = _storageMgr.getSecondaryStorageURL(zoneId);
|
||||
|
||||
String basicErrMsg = "Failed to create template from snapshot: " + snapshot.getName();
|
||||
// This can be sent to a KVM host too.
|
||||
CreatePrivateTemplateAnswer answer = (CreatePrivateTemplateAnswer) _storageMgr.sendToHostsOnStoragePool(volume.getPoolId(), cmd, basicErrMsg);
|
||||
|
||||
if ((answer != null) && answer.getResult()) {
|
||||
|
||||
privateTemplate = _templateDao.findById(templateId);
|
||||
Long origTemplateId = volume.getTemplateId();
|
||||
VMTemplateVO origTemplate = null;
|
||||
if (origTemplateId != null) {
|
||||
origTemplate = _templateDao.findById(origTemplateId);
|
||||
}
|
||||
|
||||
if ((origTemplate != null) && !Storage.ImageFormat.ISO.equals(origTemplate.getFormat())) {
|
||||
// We made a template from a root volume that was cloned from a template
|
||||
privateTemplate.setFileSystem(origTemplate.getFileSystem());
|
||||
privateTemplate.setRequiresHvm(origTemplate.requiresHvm());
|
||||
privateTemplate.setBits(origTemplate.getBits());
|
||||
} else {
|
||||
// We made a template from a root volume that was not cloned from a template, or a data volume
|
||||
privateTemplate.setFileSystem(Storage.FileSystem.Unknown);
|
||||
privateTemplate.setRequiresHvm(true);
|
||||
privateTemplate.setBits(64);
|
||||
}
|
||||
|
||||
String answerUniqueName = answer.getUniqueName();
|
||||
if (answerUniqueName != null) {
|
||||
privateTemplate.setUniqueName(answerUniqueName);
|
||||
} else {
|
||||
privateTemplate.setUniqueName(uniqueName);
|
||||
}
|
||||
ImageFormat format = answer.getImageFormat();
|
||||
if (format != null) {
|
||||
privateTemplate.setFormat(format);
|
||||
}
|
||||
else {
|
||||
// This never occurs.
|
||||
// Specify RAW format makes it unusable for snapshots.
|
||||
privateTemplate.setFormat(ImageFormat.RAW);
|
||||
}
|
||||
|
||||
_templateDao.update(templateId, privateTemplate);
|
||||
|
||||
// add template zone ref for this template
|
||||
_templateDao.addTemplateToZone(privateTemplate, zoneId);
|
||||
VMTemplateHostVO templateHostVO = new VMTemplateHostVO(secondaryStorageHost.getId(), templateId);
|
||||
templateHostVO.setDownloadPercent(100);
|
||||
templateHostVO.setDownloadState(Status.DOWNLOADED);
|
||||
templateHostVO.setInstallPath(answer.getPath());
|
||||
templateHostVO.setLastUpdated(new Date());
|
||||
templateHostVO.setSize(answer.getVirtualSize());
|
||||
_templateHostDao.persist(templateHostVO);
|
||||
|
||||
// Increment the number of templates
|
||||
_accountMgr.incrementResourceCount(volume.getAccountId(), ResourceType.template);
|
||||
|
||||
} else {
|
||||
|
||||
// Remove the template record
|
||||
_templateDao.remove(templateId);
|
||||
if (secondaryStorageHost == null || secondaryStorageURL == null) {
|
||||
throw new CloudRuntimeException("Did not find the secondary storage URL in the database for zoneId " + zoneId);
|
||||
}
|
||||
cmd = new CreatePrivateTemplateFromVolumeCommand(secondaryStorageURL, templateId, volume.getAccountId(),
|
||||
name, uniqueName, volume.getPath());
|
||||
|
||||
|
||||
} else {
|
||||
throw new CloudRuntimeException("Creating private Template need to specify snapshotId or volumeId");
|
||||
}
|
||||
// FIXME: before sending the command, check if there's enough capacity
|
||||
// on the storage server to create the template
|
||||
|
||||
// This can be sent to a KVM host too.
|
||||
CreatePrivateTemplateAnswer answer = (CreatePrivateTemplateAnswer) _storageMgr.sendToHostsOnStoragePool(volume
|
||||
.getPoolId(), cmd, null);
|
||||
|
||||
if ((answer != null) && answer.getResult()) {
|
||||
|
||||
privateTemplate = _templateDao.findById(templateId);
|
||||
Long origTemplateId = volume.getTemplateId();
|
||||
VMTemplateVO origTemplate = null;
|
||||
if (origTemplateId != null) {
|
||||
origTemplate = _templateDao.findById(origTemplateId);
|
||||
}
|
||||
|
||||
if ((origTemplate != null) && !Storage.ImageFormat.ISO.equals(origTemplate.getFormat())) {
|
||||
// We made a template from a root volume that was cloned from a
|
||||
// template
|
||||
privateTemplate.setFileSystem(origTemplate.getFileSystem());
|
||||
privateTemplate.setRequiresHvm(origTemplate.requiresHvm());
|
||||
privateTemplate.setBits(origTemplate.getBits());
|
||||
} else {
|
||||
// We made a template from a root volume that was not cloned
|
||||
// from a template, or a data volume
|
||||
privateTemplate.setFileSystem(Storage.FileSystem.Unknown);
|
||||
privateTemplate.setRequiresHvm(true);
|
||||
privateTemplate.setBits(64);
|
||||
}
|
||||
|
||||
String answerUniqueName = answer.getUniqueName();
|
||||
if (answerUniqueName != null) {
|
||||
privateTemplate.setUniqueName(answerUniqueName);
|
||||
} else {
|
||||
privateTemplate.setUniqueName(uniqueName);
|
||||
}
|
||||
ImageFormat format = answer.getImageFormat();
|
||||
if (format != null) {
|
||||
privateTemplate.setFormat(format);
|
||||
} else {
|
||||
// This never occurs.
|
||||
// Specify RAW format makes it unusable for snapshots.
|
||||
privateTemplate.setFormat(ImageFormat.RAW);
|
||||
}
|
||||
|
||||
_templateDao.update(templateId, privateTemplate);
|
||||
|
||||
// add template zone ref for this template
|
||||
_templateDao.addTemplateToZone(privateTemplate, zoneId);
|
||||
VMTemplateHostVO templateHostVO = new VMTemplateHostVO(secondaryStorageHost.getId(), templateId);
|
||||
templateHostVO.setDownloadPercent(100);
|
||||
templateHostVO.setDownloadState(Status.DOWNLOADED);
|
||||
templateHostVO.setInstallPath(answer.getPath());
|
||||
templateHostVO.setLastUpdated(new Date());
|
||||
templateHostVO.setSize(answer.getVirtualSize());
|
||||
_templateHostDao.persist(templateHostVO);
|
||||
|
||||
// Increment the number of templates
|
||||
_accountMgr.incrementResourceCount(volume.getAccountId(), ResourceType.template);
|
||||
|
||||
} else {
|
||||
|
||||
// Remove the template record
|
||||
_templateDao.remove(templateId);
|
||||
throw new CloudRuntimeException("Creating private Template failed due to " + answer.getDetails());
|
||||
}
|
||||
|
||||
return privateTemplate;
|
||||
}
|
||||
|
||||
|
||||
@ -998,7 +998,7 @@ CREATE TABLE `cloud`.`launch_permission` (
|
||||
|
||||
CREATE TABLE `cloud`.`snapshot_policy` (
|
||||
`id` bigint unsigned NOT NULL auto_increment,
|
||||
`volume_id` bigint unsigned NOT NULL,
|
||||
`volume_id` bigint unsigned NOT NULL unique,
|
||||
`schedule` varchar(100) NOT NULL COMMENT 'schedule time of execution',
|
||||
`timezone` varchar(100) NOT NULL COMMENT 'the timezone in which the schedule time is specified',
|
||||
`interval` int(4) NOT NULL default 4 COMMENT 'backup schedule, e.g. hourly, daily, etc.',
|
||||
@ -1016,12 +1016,11 @@ CREATE TABLE `cloud`.`snapshot_policy_ref` (
|
||||
|
||||
CREATE TABLE `cloud`.`snapshot_schedule` (
|
||||
`id` bigint unsigned NOT NULL auto_increment,
|
||||
`volume_id` bigint unsigned NOT NULL COMMENT 'The volume for which this snapshot is being taken',
|
||||
`volume_id` bigint unsigned NOT NULL unique COMMENT 'The volume for which this snapshot is being taken',
|
||||
`policy_id` bigint unsigned NOT NULL COMMENT 'One of the policyIds for which this snapshot was taken',
|
||||
`scheduled_timestamp` datetime NOT NULL COMMENT 'Time at which the snapshot was scheduled for execution',
|
||||
`async_job_id` bigint unsigned COMMENT 'If this schedule is being executed, it is the id of the create aysnc_job. Before that it is null',
|
||||
`snapshot_id` bigint unsigned COMMENT 'If this schedule is being executed, then the corresponding snapshot has this id. Before that it is null',
|
||||
UNIQUE (volume_id, policy_id),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user