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 {
|
public class BackupSnapshotCommand extends SnapshotCommand {
|
||||||
private String prevSnapshotUuid;
|
private String prevSnapshotUuid;
|
||||||
private String prevBackupUuid;
|
private String prevBackupUuid;
|
||||||
private boolean isFirstSnapshotOfRootVolume;
|
|
||||||
private boolean isVolumeInactive;
|
private boolean isVolumeInactive;
|
||||||
private String firstBackupUuid;
|
private String firstBackupUuid;
|
||||||
private String vmName;
|
private String vmName;
|
||||||
@ -56,7 +55,6 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
|||||||
String prevSnapshotUuid,
|
String prevSnapshotUuid,
|
||||||
String prevBackupUuid,
|
String prevBackupUuid,
|
||||||
String firstBackupUuid,
|
String firstBackupUuid,
|
||||||
boolean isFirstSnapshotOfRootVolume,
|
|
||||||
boolean isVolumeInactive,
|
boolean isVolumeInactive,
|
||||||
String vmName)
|
String vmName)
|
||||||
{
|
{
|
||||||
@ -64,7 +62,6 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
|||||||
this.prevSnapshotUuid = prevSnapshotUuid;
|
this.prevSnapshotUuid = prevSnapshotUuid;
|
||||||
this.prevBackupUuid = prevBackupUuid;
|
this.prevBackupUuid = prevBackupUuid;
|
||||||
this.firstBackupUuid = firstBackupUuid;
|
this.firstBackupUuid = firstBackupUuid;
|
||||||
this.isFirstSnapshotOfRootVolume = isFirstSnapshotOfRootVolume;
|
|
||||||
this.isVolumeInactive = isVolumeInactive;
|
this.isVolumeInactive = isVolumeInactive;
|
||||||
this.vmName = vmName;
|
this.vmName = vmName;
|
||||||
}
|
}
|
||||||
@ -81,10 +78,6 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
|||||||
return firstBackupUuid;
|
return firstBackupUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFirstSnapshotOfRootVolume() {
|
|
||||||
return isFirstSnapshotOfRootVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVolumeInactive() {
|
public boolean isVolumeInactive() {
|
||||||
return 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.
|
* This currently assumes that both primary and secondary storage are mounted on the XenServer.
|
||||||
*/
|
*/
|
||||||
public class CreateVolumeFromSnapshotCommand extends SnapshotCommand {
|
public class CreateVolumeFromSnapshotCommand extends SnapshotCommand {
|
||||||
private String templatePath;
|
|
||||||
|
|
||||||
protected CreateVolumeFromSnapshotCommand() {
|
protected CreateVolumeFromSnapshotCommand() {
|
||||||
|
|
||||||
@ -51,18 +50,9 @@ public class CreateVolumeFromSnapshotCommand extends SnapshotCommand {
|
|||||||
Long accountId,
|
Long accountId,
|
||||||
Long volumeId,
|
Long volumeId,
|
||||||
String backedUpSnapshotUuid,
|
String backedUpSnapshotUuid,
|
||||||
String backedUpSnapshotName,
|
String backedUpSnapshotName)
|
||||||
String templatePath)
|
|
||||||
{
|
{
|
||||||
super(primaryStoragePoolNameLabel, secondaryStoragePoolURL, backedUpSnapshotUuid, backedUpSnapshotName, dcId, accountId, volumeId);
|
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.
|
* This currently assumes that the secondary storage are mounted on the XenServer.
|
||||||
*/
|
*/
|
||||||
public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
||||||
private String childUUID;
|
|
||||||
|
|
||||||
protected DeleteSnapshotBackupCommand() {
|
protected DeleteSnapshotBackupCommand() {
|
||||||
|
|
||||||
@ -59,18 +58,8 @@ public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
|||||||
Long accountId,
|
Long accountId,
|
||||||
Long volumeId,
|
Long volumeId,
|
||||||
String backupUUID,
|
String backupUUID,
|
||||||
String backupName,
|
String backupName)
|
||||||
String childUUID)
|
|
||||||
{
|
{
|
||||||
super(primaryStoragePoolNameLabel, secondaryStoragePoolURL, backupUUID, backupName, dcId, accountId, volumeId);
|
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() {}
|
||||||
|
|
||||||
public ManageSnapshotCommand(String commandSwitch, long snapshotId, String path, String snapshotName, String vmName) {
|
public ManageSnapshotCommand(long snapshotId, String volumePath, String preSnapshotPath ,String snapshotName, String vmName) {
|
||||||
_commandSwitch = commandSwitch;
|
_commandSwitch = ManageSnapshotCommand.CREATE_SNAPSHOT;
|
||||||
if (commandSwitch.equals(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
|
_volumePath = volumePath;
|
||||||
_volumePath = path;
|
_snapshotPath = preSnapshotPath;
|
||||||
}
|
|
||||||
else if (commandSwitch.equals(ManageSnapshotCommand.DESTROY_SNAPSHOT)) {
|
|
||||||
_snapshotPath = path;
|
|
||||||
}
|
|
||||||
_snapshotName = snapshotName;
|
_snapshotName = snapshotName;
|
||||||
_snapshotId = snapshotId;
|
_snapshotId = snapshotId;
|
||||||
_vmName = vmName;
|
_vmName = vmName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ManageSnapshotCommand(long snapshotId, String snapshotPath) {
|
||||||
|
_commandSwitch = ManageSnapshotCommand.DESTROY_SNAPSHOT;
|
||||||
|
_snapshotPath = snapshotPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean executeInSequence() {
|
public boolean executeInSequence() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -38,6 +38,10 @@ public class CreatePrivateTemplateAnswer extends Answer {
|
|||||||
_format = format;
|
_format = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CreatePrivateTemplateAnswer(Command cmd, boolean success, String result) {
|
||||||
|
super(cmd, success, result);
|
||||||
|
}
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return _path;
|
return _path;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,7 @@ import com.cloud.agent.api.CheckVirtualMachineAnswer;
|
|||||||
import com.cloud.agent.api.CheckVirtualMachineCommand;
|
import com.cloud.agent.api.CheckVirtualMachineCommand;
|
||||||
import com.cloud.agent.api.Command;
|
import com.cloud.agent.api.Command;
|
||||||
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
|
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
|
||||||
|
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
|
||||||
import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
|
import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
|
||||||
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
|
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
|
||||||
import com.cloud.agent.api.DeleteSnapshotBackupAnswer;
|
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.CreateAnswer;
|
||||||
import com.cloud.agent.api.storage.CreateCommand;
|
import com.cloud.agent.api.storage.CreateCommand;
|
||||||
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
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.DestroyCommand;
|
||||||
import com.cloud.agent.api.storage.DownloadAnswer;
|
import com.cloud.agent.api.storage.DownloadAnswer;
|
||||||
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
|
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
|
||||||
@ -607,8 +608,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
return execute((AttachVolumeCommand) cmd);
|
return execute((AttachVolumeCommand) cmd);
|
||||||
} else if (cmd instanceof AttachIsoCommand) {
|
} else if (cmd instanceof AttachIsoCommand) {
|
||||||
return execute((AttachIsoCommand) cmd);
|
return execute((AttachIsoCommand) cmd);
|
||||||
} else if (cmd instanceof ValidateSnapshotCommand) {
|
|
||||||
return execute((ValidateSnapshotCommand) cmd);
|
|
||||||
} else if (cmd instanceof ManageSnapshotCommand) {
|
} else if (cmd instanceof ManageSnapshotCommand) {
|
||||||
return execute((ManageSnapshotCommand) cmd);
|
return execute((ManageSnapshotCommand) cmd);
|
||||||
} else if (cmd instanceof BackupSnapshotCommand) {
|
} else if (cmd instanceof BackupSnapshotCommand) {
|
||||||
@ -619,8 +618,8 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
return execute((CreateVolumeFromSnapshotCommand) cmd);
|
return execute((CreateVolumeFromSnapshotCommand) cmd);
|
||||||
} else if (cmd instanceof DeleteSnapshotsDirCommand) {
|
} else if (cmd instanceof DeleteSnapshotsDirCommand) {
|
||||||
return execute((DeleteSnapshotsDirCommand) cmd);
|
return execute((DeleteSnapshotsDirCommand) cmd);
|
||||||
} else if (cmd instanceof CreatePrivateTemplateCommand) {
|
} else if (cmd instanceof CreatePrivateTemplateFromVolumeCommand) {
|
||||||
return execute((CreatePrivateTemplateCommand) cmd);
|
return execute((CreatePrivateTemplateFromVolumeCommand) cmd);
|
||||||
} else if (cmd instanceof CreatePrivateTemplateFromSnapshotCommand) {
|
} else if (cmd instanceof CreatePrivateTemplateFromSnapshotCommand) {
|
||||||
return execute((CreatePrivateTemplateFromSnapshotCommand) cmd);
|
return execute((CreatePrivateTemplateFromSnapshotCommand) cmd);
|
||||||
} else if (cmd instanceof GetStorageStatsCommand) {
|
} else if (cmd instanceof GetStorageStatsCommand) {
|
||||||
@ -891,7 +890,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
|
|
||||||
Pair<VM, String> v = createVmFromTemplate(conn, vmSpec, host);
|
Pair<VM, String> v = createVmFromTemplate(conn, vmSpec, host);
|
||||||
vm = v.first();
|
vm = v.first();
|
||||||
String vmUuid = v.second();
|
|
||||||
|
|
||||||
for (VolumeTO disk : vmSpec.getDisks()) {
|
for (VolumeTO disk : vmSpec.getDisks()) {
|
||||||
createVbd(conn, vmName, vm, disk, disk.getType() == VolumeType.ROOT && vmSpec.getType() != VirtualMachine.Type.User);
|
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 {
|
try {
|
||||||
Host myself = Host.getByUuid(conn, _host.uuid);
|
Host myself = Host.getByUuid(conn, _host.uuid);
|
||||||
|
|
||||||
boolean findsystemvmiso = false;
|
|
||||||
Set<SR> srs = SR.getByNameLabel(conn, "XenServer Tools");
|
Set<SR> srs = SR.getByNameLabel(conn, "XenServer Tools");
|
||||||
if( srs.size() != 1 ) {
|
if( srs.size() != 1 ) {
|
||||||
throw new CloudRuntimeException("There are " + srs.size() + " SRs with name XenServer Tools");
|
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");
|
throw new Exception("no attached PBD");
|
||||||
}
|
}
|
||||||
if (s_logger.isDebugEnabled()) {
|
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);
|
sr.scan(conn);
|
||||||
return sr;
|
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) {
|
protected ManageSnapshotAnswer execute(final ManageSnapshotCommand cmd) {
|
||||||
|
|
||||||
long snapshotId = cmd.getSnapshotId();
|
long snapshotId = cmd.getSnapshotId();
|
||||||
String snapshotName = cmd.getSnapshotName();
|
String snapshotName = cmd.getSnapshotName();
|
||||||
|
|
||||||
@ -5510,8 +5431,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
if (cmdSwitch.equals(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
|
if (cmdSwitch.equals(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
|
||||||
// Look up the volume
|
// Look up the volume
|
||||||
String volumeUUID = cmd.getVolumePath();
|
String volumeUUID = cmd.getVolumePath();
|
||||||
|
VDI volume = VDI.getByUuid(conn, volumeUUID);
|
||||||
VDI volume = getVDIbyUuid(volumeUUID);
|
|
||||||
|
|
||||||
// Create a snapshot
|
// Create a snapshot
|
||||||
VDI snapshot = volume.snapshot(conn, new HashMap<String, String>());
|
VDI snapshot = volume.snapshot(conn, new HashMap<String, String>());
|
||||||
@ -5519,10 +5439,26 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
if (snapshotName != null) {
|
if (snapshotName != null) {
|
||||||
snapshot.setNameLabel(conn, snapshotName);
|
snapshot.setNameLabel(conn, snapshotName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the UUID of the snapshot
|
// 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;
|
success = true;
|
||||||
details = null;
|
details = null;
|
||||||
@ -5547,74 +5483,63 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
return new ManageSnapshotAnswer(cmd, snapshotId, snapshotUUID, success, details);
|
return new ManageSnapshotAnswer(cmd, snapshotId, snapshotUUID, success, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateCommand cmd) {
|
protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateFromVolumeCommand cmd) {
|
||||||
String secondaryStorageURL = cmd.getSecondaryStorageURL();
|
String secondaryStoragePoolURL = cmd.getSecondaryStorageURL();
|
||||||
String snapshotUUID = cmd.getSnapshotPath();
|
String volumeUUID = cmd.getVolumePath();
|
||||||
|
Long accountId = cmd.getAccountId();
|
||||||
String userSpecifiedName = cmd.getTemplateName();
|
String userSpecifiedName = cmd.getTemplateName();
|
||||||
|
Long templateId = cmd.getTemplateId();
|
||||||
|
|
||||||
SR secondaryStorage = null;
|
String details = null;
|
||||||
VDI privateTemplate = null;
|
SR tmpltSR = null;
|
||||||
Connection conn = getConnection();
|
boolean result = false;
|
||||||
try {
|
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
|
URI uri = new URI(secondaryStoragePoolURL);
|
||||||
secondaryStorage = createNfsSRbyURI(new URI(secondaryStorageURL + "/template/" + templateDownloadFolder), false);
|
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
|
// copy volume to template SR
|
||||||
VDI snapshot = getVDIbyUuid(snapshotUUID);
|
VDI tmpltVDI = cloudVDIcopy(volume, tmpltSR);
|
||||||
privateTemplate = cloudVDIcopy(snapshot, secondaryStorage);
|
|
||||||
|
|
||||||
if (userSpecifiedName != null) {
|
if (userSpecifiedName != null) {
|
||||||
privateTemplate.setNameLabel(conn, userSpecifiedName);
|
tmpltVDI.setNameLabel(conn, userSpecifiedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the template file name and install path
|
String tmpltSrUUID = tmpltSR.getUuid(conn);
|
||||||
VDI.Record vdir = privateTemplate.getRecord(conn);
|
String tmpltUUID = tmpltVDI.getUuid(conn);
|
||||||
String templateName = vdir.uuid;
|
String tmpltFilename = tmpltUUID + ".vhd";
|
||||||
String templateFilename = templateName + ".vhd";
|
long virtualSize = tmpltVDI.getVirtualSize(conn);
|
||||||
String installPath = "template/" + templateInstallFolder + templateFilename;
|
long size = tmpltVDI.getPhysicalUtilisation(conn);
|
||||||
|
// create the template.properties file
|
||||||
// Determine the template's virtual size and then forget the VDI
|
result = postCreatePrivateTemplate(tmpltSrUUID, tmpltFilename, tmpltUUID, userSpecifiedName, null, size, virtualSize, templateId);
|
||||||
long virtualSize = privateTemplate.getVirtualSize(conn);
|
if (!result) {
|
||||||
// Create the template.properties file in the download folder, move
|
throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + tmpltURI);
|
||||||
// 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.");
|
|
||||||
}
|
}
|
||||||
|
return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, tmpltUUID, ImageFormat.VHD);
|
||||||
return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, templateName, ImageFormat.VHD);
|
|
||||||
} catch (XenAPIException e) {
|
} catch (XenAPIException e) {
|
||||||
if (privateTemplate != null) {
|
details = "Creating template from volume " + volumeUUID + " failed due to " + e.getMessage();
|
||||||
destroyVDI(privateTemplate);
|
s_logger.error(details, e);
|
||||||
}
|
|
||||||
|
|
||||||
s_logger.warn("CreatePrivateTemplate Failed due to " + e.toString(), e);
|
|
||||||
return new CreatePrivateTemplateAnswer(cmd, false, e.toString(), null, 0, null, null);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
s_logger.warn("CreatePrivateTemplate Failed due to " + e.getMessage(), e);
|
details = "Creating template from volume " + volumeUUID + " failed due to " + e.getMessage();
|
||||||
return new CreatePrivateTemplateAnswer(cmd, false, e.getMessage(), null, 0, null, null);
|
s_logger.error(details, e);
|
||||||
} finally {
|
} finally {
|
||||||
// Remove the secondary storage SR
|
// 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) {
|
protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateFromSnapshotCommand cmd) {
|
||||||
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
||||||
@ -5623,59 +5548,61 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
Long volumeId = cmd.getVolumeId();
|
Long volumeId = cmd.getVolumeId();
|
||||||
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
||||||
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
|
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
|
||||||
String origTemplateInstallPath = cmd.getOrigTemplateInstallPath();
|
|
||||||
Long newTemplateId = cmd.getNewTemplateId();
|
Long newTemplateId = cmd.getNewTemplateId();
|
||||||
String userSpecifiedName = cmd.getTemplateName();
|
String userSpecifiedName = cmd.getTemplateName();
|
||||||
|
|
||||||
// By default, assume failure
|
// By default, assume failure
|
||||||
String details = "Failed to create private template " + newTemplateId + " from snapshot for volume: " + volumeId + " with backupUuid: " + backedUpSnapshotUuid;
|
String details = null;
|
||||||
String newTemplatePath = null;
|
SR snapshotSR = null;
|
||||||
String templateName = null;
|
SR tmpltSR = null;
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
long virtualSize = 0;
|
|
||||||
try {
|
try {
|
||||||
URI uri = new URI(secondaryStoragePoolURL);
|
URI uri = new URI(secondaryStoragePoolURL);
|
||||||
String remoteTemplateMountPath = uri.getHost() + ":" + uri.getPath() + "/template/";
|
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
||||||
String templateFolder = cmd.getAccountId() + "/" + newTemplateId + "/";
|
String installPath = "template/tmpl/" + accountId + "/" + newTemplateId;
|
||||||
String templateDownloadFolder = createTemplateDownloadFolder(remoteTemplateMountPath, templateFolder);
|
if( !createSecondaryStorageFolder(secondaryStorageMountPath, installPath)) {
|
||||||
String templateInstallFolder = "tmpl/" + templateFolder;
|
details = " Filed to create folder " + installPath + " in secondary storage";
|
||||||
// Yes, create a template vhd
|
s_logger.warn(details);
|
||||||
Pair<VHDInfo, String> vhdDetails = createVHDFromSnapshot(primaryStorageNameLabel, dcId, accountId, volumeId, secondaryStoragePoolURL, backedUpSnapshotUuid,
|
return new CreatePrivateTemplateAnswer(cmd, false, details);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
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) {
|
} catch (XenAPIException e) {
|
||||||
details += ", reason: " + e.getMessage();
|
details = "Creating template from snapshot " + backedUpSnapshotUuid + " failed due to " + e.getMessage();
|
||||||
s_logger.error(details, e);
|
s_logger.error(details, e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
details += ", reason: " + e.getMessage();
|
details = "Creating template from snapshot " + backedUpSnapshotUuid + " failed due to " + e.getMessage();
|
||||||
s_logger.error(details, e);
|
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) {
|
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 snapshotUuid = cmd.getSnapshotUuid(); // not null: Precondition.
|
||||||
String prevSnapshotUuid = cmd.getPrevSnapshotUuid();
|
String prevSnapshotUuid = cmd.getPrevSnapshotUuid();
|
||||||
String prevBackupUuid = cmd.getPrevBackupUuid();
|
String prevBackupUuid = cmd.getPrevBackupUuid();
|
||||||
boolean isFirstSnapshotOfRootVolume = cmd.isFirstSnapshotOfRootVolume();
|
|
||||||
// By default assume failure
|
// By default assume failure
|
||||||
String details = null;
|
String details = null;
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
@ -5704,27 +5630,45 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
URI uri = new URI(secondaryStoragePoolURL);
|
URI uri = new URI(secondaryStoragePoolURL);
|
||||||
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
||||||
|
|
||||||
if (secondaryStorageMountPath == null) {
|
|
||||||
details = "Couldn't backup snapshot because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
if (prevBackupUuid == null) {
|
||||||
} else {
|
// the first snapshot is always a full snapshot
|
||||||
boolean gcHappened = true;
|
String folder = "snapshots/" + accountId + "/" + volumeId;
|
||||||
if (gcHappened) {
|
if( !createSecondaryStorageFolder(secondaryStorageMountPath, folder)) {
|
||||||
snapshotBackupUuid = backupSnapshot(primaryStorageSRUuid, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotUuid, prevSnapshotUuid, prevBackupUuid,
|
details = " Filed to create folder " + folder + " in secondary storage";
|
||||||
isFirstSnapshotOfRootVolume, isISCSI);
|
s_logger.warn(details);
|
||||||
success = (snapshotBackupUuid != null);
|
return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid);
|
||||||
} else {
|
|
||||||
s_logger.warn("GC hasn't happened yet for previousSnapshotUuid: " + prevSnapshotUuid + ". Will retry again after 1 min");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// Mark the snapshot as removed in the database.
|
||||||
// When the next snapshot is taken, it will be
|
// When the next snapshot is taken, it will be
|
||||||
// 1) deleted from the DB 2) The snapshotUuid will be deleted from the primary
|
// 1) deleted from the DB 2) The snapshotUuid will be deleted from the primary
|
||||||
// 3) the snapshotBackupUuid will be copied to secondary
|
// 3) the snapshotBackupUuid will be copied to secondary
|
||||||
// 4) if possible it will be coalesced with the next snapshot.
|
// 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.
|
// Destroy the previous snapshot, if it exists.
|
||||||
// We destroy the previous snapshot only if the current snapshot
|
// We destroy the previous snapshot only if the current snapshot
|
||||||
// backup succeeds.
|
// backup succeeds.
|
||||||
@ -5732,8 +5676,9 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
// so that it doesn't get merged with the
|
// so that it doesn't get merged with the
|
||||||
// new one
|
// new one
|
||||||
// and muddle the vhd chain on the secondary storage.
|
// 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) {
|
} catch (XenAPIException e) {
|
||||||
@ -5749,93 +5694,44 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
|
|
||||||
protected CreateVolumeFromSnapshotAnswer execute(final CreateVolumeFromSnapshotCommand cmd) {
|
protected CreateVolumeFromSnapshotAnswer execute(final CreateVolumeFromSnapshotCommand cmd) {
|
||||||
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
||||||
Long dcId = cmd.getDataCenterId();
|
|
||||||
Long accountId = cmd.getAccountId();
|
Long accountId = cmd.getAccountId();
|
||||||
Long volumeId = cmd.getVolumeId();
|
Long volumeId = cmd.getVolumeId();
|
||||||
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
||||||
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
|
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
|
||||||
String templatePath = cmd.getTemplatePath();
|
|
||||||
|
|
||||||
// By default, assume the command has failed and set the params to be
|
// By default, assume the command has failed and set the params to be
|
||||||
// passed to CreateVolumeFromSnapshotAnswer appropriately
|
// passed to CreateVolumeFromSnapshotAnswer appropriately
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
// Generic error message.
|
// Generic error message.
|
||||||
String details = "Failed to create volume from snapshot for volume: " + volumeId + " with backupUuid: " + backedUpSnapshotUuid;
|
String details = null;
|
||||||
String vhdUUID = null;
|
String volumeUUID = null;
|
||||||
SR temporarySROnSecondaryStorage = null;
|
SR snapshotSR = null;
|
||||||
String mountPointOfTemporaryDirOnSecondaryStorage = null;
|
|
||||||
|
if (secondaryStoragePoolURL == null) {
|
||||||
|
details += " because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
||||||
|
return new CreateVolumeFromSnapshotAnswer(cmd, result, details, volumeUUID);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
VDI vdi = null;
|
|
||||||
Connection conn = getConnection();
|
Connection conn = getConnection();
|
||||||
SR primaryStorageSR = getSRByNameLabelandHost(primaryStorageNameLabel);
|
SR primaryStorageSR = getSRByNameLabelandHost(primaryStorageNameLabel);
|
||||||
if (primaryStorageSR == null) {
|
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: "
|
throw new InternalErrorException("Could not create volume from snapshot because the primary Storage SR could not be created from the name label: "
|
||||||
+ primaryStorageNameLabel);
|
+ 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.
|
VDI volumeVDI = cloudVDIcopy(snapshotVDI, primaryStorageSR);
|
||||||
URI uri = new URI(secondaryStoragePoolURL);
|
|
||||||
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
|
||||||
|
|
||||||
if (secondaryStorageMountPath == null) {
|
volumeUUID = volumeVDI.getUuid(conn);
|
||||||
details += " because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
|
||||||
return new CreateVolumeFromSnapshotAnswer(cmd, result, details, vhdUUID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a volume and not a template
|
|
||||||
String templateDownloadFolder = "";
|
|
||||||
|
|
||||||
VHDInfo vhdInfo = createVHDFromSnapshot(dcId, accountId, volumeId, secondaryStorageMountPath, backedUpSnapshotUuid, templatePath, templateDownloadFolder, isISCSI);
|
result = true;
|
||||||
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;
|
|
||||||
|
|
||||||
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) {
|
} catch (XenAPIException e) {
|
||||||
details += " due to " + e.toString();
|
details += " due to " + e.toString();
|
||||||
s_logger.warn(details, e);
|
s_logger.warn(details, e);
|
||||||
@ -5844,13 +5740,8 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
s_logger.warn(details, e);
|
s_logger.warn(details, e);
|
||||||
} finally {
|
} finally {
|
||||||
// In all cases, if the temporary SR was created, forget it.
|
// In all cases, if the temporary SR was created, forget it.
|
||||||
if (temporarySROnSecondaryStorage != null) {
|
if (snapshotSR != null) {
|
||||||
removeSR(temporarySROnSecondaryStorage);
|
removeSR(snapshotSR);
|
||||||
// Delete the temporary directory created.
|
|
||||||
File folderPath = new File(mountPointOfTemporaryDirOnSecondaryStorage);
|
|
||||||
String remoteMountPath = folderPath.getParent();
|
|
||||||
String folder = folderPath.getName();
|
|
||||||
deleteSecondaryStorageFolder(remoteMountPath, folder);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
@ -5859,7 +5750,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
}
|
}
|
||||||
|
|
||||||
// In all cases return something.
|
// 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) {
|
protected DeleteSnapshotBackupAnswer execute(final DeleteSnapshotBackupCommand cmd) {
|
||||||
@ -5868,31 +5759,10 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
Long volumeId = cmd.getVolumeId();
|
Long volumeId = cmd.getVolumeId();
|
||||||
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
|
||||||
String backupUUID = cmd.getSnapshotUuid();
|
String backupUUID = cmd.getSnapshotUuid();
|
||||||
String childUUID = cmd.getChildUUID();
|
|
||||||
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
|
||||||
|
|
||||||
String details = null;
|
String details = null;
|
||||||
boolean success = false;
|
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;
|
URI uri = null;
|
||||||
try {
|
try {
|
||||||
uri = new URI(secondaryStoragePoolURL);
|
uri = new URI(secondaryStoragePoolURL);
|
||||||
@ -5906,14 +5776,13 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
if (secondaryStorageMountPath == null) {
|
if (secondaryStorageMountPath == null) {
|
||||||
details = "Couldn't delete snapshot because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
details = "Couldn't delete snapshot because the URL passed: " + secondaryStoragePoolURL + " is invalid.";
|
||||||
} else {
|
} else {
|
||||||
details = deleteSnapshotBackup(dcId, accountId, volumeId, secondaryStorageMountPath, backupUUID, childUUID, isISCSI);
|
details = deleteSnapshotBackup(dcId, accountId, volumeId, secondaryStorageMountPath, backupUUID);
|
||||||
success = (details != null && details.equals("1"));
|
success = (details != null && details.equals("1"));
|
||||||
if (success) {
|
if (success) {
|
||||||
s_logger.debug("Successfully deleted snapshot backup " + backupUUID);
|
s_logger.debug("Successfully deleted snapshot backup " + backupUUID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return new DeleteSnapshotBackupAnswer(cmd, success, details);
|
return new DeleteSnapshotBackupAnswer(cmd, success, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6134,8 +6003,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
return (result != null);
|
return (result != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean postCreatePrivateTemplate(String remoteTemplateMountPath, String templateDownloadFolder, String templateInstallFolder, String templateFilename,
|
protected boolean postCreatePrivateTemplate(String tmpltSrUUID,String tmpltFilename, String templateName, String templateDescription, String checksum, long size, long virtualSize, long templateId) {
|
||||||
String templateName, String templateDescription, String checksum, long virtualSize, long templateId) {
|
|
||||||
|
|
||||||
if (templateDescription == null) {
|
if (templateDescription == null) {
|
||||||
templateDescription = "";
|
templateDescription = "";
|
||||||
@ -6145,23 +6013,18 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
checksum = "";
|
checksum = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
String result = callHostPluginWithTimeOut("vmopsSnapshot", "post_create_private_template", 110*60, "remoteTemplateMountPath", remoteTemplateMountPath, "templateDownloadFolder", templateDownloadFolder,
|
String result = callHostPluginWithTimeOut("vmopsSnapshot", "post_create_private_template", 110*60, "tmpltSrUUID", tmpltSrUUID, "templateFilename", tmpltFilename, "templateName", templateName, "templateDescription", templateDescription,
|
||||||
"templateInstallFolder", templateInstallFolder, "templateFilename", templateFilename, "templateName", templateName, "templateDescription", templateDescription,
|
"checksum", checksum, "size", String.valueOf(size), "virtualSize", String.valueOf(virtualSize), "templateId", String.valueOf(templateId));
|
||||||
"checksum", checksum, "virtualSize", String.valueOf(virtualSize), "templateId", String.valueOf(templateId));
|
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
if (result != null && !result.isEmpty()) {
|
if (result != null && !result.isEmpty()) {
|
||||||
// Else, command threw an exception which has already been logged.
|
// Else, command threw an exception which has already been logged.
|
||||||
|
|
||||||
String[] tmp = result.split("#");
|
if (result.equalsIgnoreCase("1")) {
|
||||||
String status = tmp[0];
|
s_logger.debug("Successfully created template.properties file on secondary storage for " + tmpltFilename);
|
||||||
|
|
||||||
if (status != null && status.equalsIgnoreCase("1")) {
|
|
||||||
s_logger.debug("Successfully created template.properties file on secondary storage dir: " + templateInstallFolder);
|
|
||||||
success = true;
|
success = true;
|
||||||
} else {
|
} else {
|
||||||
s_logger.warn("Could not create template.properties file on secondary storage dir: " + templateInstallFolder + " for templateId: " + templateId
|
s_logger.warn("Could not create template.properties file on secondary storage for " + tmpltFilename + " for templateId: " + templateId);
|
||||||
+ ". Failed with status " + status);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6170,8 +6033,8 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
|
|
||||||
// Each argument is put in a separate line for readability.
|
// Each argument is put in a separate line for readability.
|
||||||
// Using more lines does not harm the environment.
|
// Using more lines does not harm the environment.
|
||||||
protected String backupSnapshot(String primaryStorageSRUuid, Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath, String snapshotUuid,
|
protected String backupSnapshot(String primaryStorageSRUuid, Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath,
|
||||||
String prevSnapshotUuid, String prevBackupUuid, Boolean isFirstSnapshotOfRootVolume, Boolean isISCSI) {
|
String snapshotUuid, String prevSnapshotUuid, String prevBackupUuid, Boolean isISCSI) {
|
||||||
String backupSnapshotUuid = null;
|
String backupSnapshotUuid = null;
|
||||||
|
|
||||||
if (prevSnapshotUuid == null) {
|
if (prevSnapshotUuid == null) {
|
||||||
@ -6185,7 +6048,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
// Using more lines does not harm the environment.
|
// Using more lines does not harm the environment.
|
||||||
String results = callHostPluginWithTimeOut("vmopsSnapshot", "backupSnapshot", 110*60, "primaryStorageSRUuid", primaryStorageSRUuid, "dcId", dcId.toString(), "accountId", accountId.toString(), "volumeId",
|
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",
|
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()) {
|
if (results == null || results.isEmpty()) {
|
||||||
// errString is already logged.
|
// errString is already logged.
|
||||||
@ -6210,6 +6073,19 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
return backupSnapshotUuid;
|
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) {
|
protected boolean destroySnapshotOnPrimaryStorage(String snapshotUuid) {
|
||||||
// Precondition snapshotUuid != null
|
// Precondition snapshotUuid != null
|
||||||
try {
|
try {
|
||||||
@ -6232,11 +6108,11 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
return false;
|
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
|
// 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(),
|
String result = callHostPlugin("vmopsSnapshot", "deleteSnapshotBackup", "backupUUID", backupUUID, "dcId", dcId.toString(), "accountId", accountId.toString(),
|
||||||
"volumeId", volumeId.toString(), "secondaryStorageMountPath", secondaryStorageMountPath, "isISCSI", isISCSI.toString());
|
"volumeId", volumeId.toString(), "secondaryStorageMountPath", secondaryStorageMountPath);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -6249,70 +6125,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
return result;
|
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
|
@Override
|
||||||
public boolean start() {
|
public boolean start() {
|
||||||
@ -6417,30 +6229,6 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
|
|||||||
public String pool;
|
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) {
|
protected String getGuestOsType(String stdType) {
|
||||||
return CitrixHelper.getGuestOsType(stdType);
|
return CitrixHelper.getGuestOsType(stdType);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1716,12 +1716,6 @@ public interface ManagementServer {
|
|||||||
ResourceAllocationException,
|
ResourceAllocationException,
|
||||||
InternalErrorException;
|
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
|
* Destroy a snapshot
|
||||||
@ -1761,7 +1755,7 @@ public interface ManagementServer {
|
|||||||
* @return valid template if success, null otherwise
|
* @return valid template if success, null otherwise
|
||||||
* @throws InvalidParameterValueException, ResourceAllocationException
|
* @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;
|
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,
|
Creating,
|
||||||
CreatedOnPrimary,
|
CreatedOnPrimary,
|
||||||
BackingUp,
|
BackingUp,
|
||||||
BackedUp;
|
BackedUp,
|
||||||
|
EmptySnapshot;
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.name();
|
return this.name();
|
||||||
|
|||||||
@ -79,6 +79,10 @@ public class SnapshotPolicyVO {
|
|||||||
return schedule;
|
return schedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setInterval(short interval) {
|
||||||
|
this.interval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTimezone(String timezone) {
|
public void setTimezone(String timezone) {
|
||||||
this.timezone = timezone;
|
this.timezone = timezone;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,14 +77,22 @@ public class SnapshotScheduleVO {
|
|||||||
return policyId;
|
return policyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setPolicyId(long policyId) {
|
||||||
|
this.policyId = policyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @return the scheduledTimestamp
|
* @return the scheduledTimestamp
|
||||||
*/
|
*/
|
||||||
public Date getScheduledTimestamp() {
|
public Date getScheduledTimestamp() {
|
||||||
return scheduledTimestamp;
|
return scheduledTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getAsyncJobId() {
|
public void setScheduledTimestamp(Date scheduledTimestamp) {
|
||||||
|
this.scheduledTimestamp = scheduledTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getAsyncJobId() {
|
||||||
return asyncJobId;
|
return asyncJobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -157,7 +157,7 @@ public class SnapshotVO implements Snapshot {
|
|||||||
return backupSnapshotId;
|
return backupSnapshotId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getPrevSnapshotId(){
|
public long getPrevSnapshotId(){
|
||||||
return prevSnapshotId;
|
return prevSnapshotId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,13 +169,12 @@ public class SnapshotVO implements Snapshot {
|
|||||||
this.prevSnapshotId = prevSnapshotId;
|
this.prevSnapshotId = prevSnapshotId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SnapshotType getSnapshotType(List<Long> policyIds) {
|
public static SnapshotType getSnapshotType(Long policyId) {
|
||||||
assert policyIds != null && !policyIds.isEmpty();
|
if (policyId.equals(MANUAL_POLICY_ID)) {
|
||||||
SnapshotType snapshotType = SnapshotType.RECURRING;
|
return SnapshotType.MANUAL;
|
||||||
if (policyIds.contains(MANUAL_POLICY_ID)) {
|
} else {
|
||||||
snapshotType = SnapshotType.MANUAL;
|
return SnapshotType.RECURRING;
|
||||||
}
|
}
|
||||||
return snapshotType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SnapshotType getSnapshotType(String 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);
|
List<SnapshotPolicyVO> listByVolumeId(long volumeId, Filter filter);
|
||||||
SnapshotPolicyVO findOneByVolumeInterval(long volumeId, short interval);
|
SnapshotPolicyVO findOneByVolumeInterval(long volumeId, short interval);
|
||||||
List<SnapshotPolicyVO> listActivePolicies();
|
List<SnapshotPolicyVO> listActivePolicies();
|
||||||
|
SnapshotPolicyVO findOneByVolume(long volumeId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,14 @@ public class SnapshotPolicyDaoImpl extends GenericDaoBase<SnapshotPolicyVO, Long
|
|||||||
return findOneIncludingRemovedBy(sc);
|
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
|
@Override
|
||||||
public List<SnapshotPolicyVO> listByVolumeId(long volumeId) {
|
public List<SnapshotPolicyVO> listByVolumeId(long volumeId) {
|
||||||
return listByVolumeId(volumeId, null);
|
return listByVolumeId(volumeId, null);
|
||||||
|
|||||||
@ -22,6 +22,7 @@ package com.cloud.storage.dao;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.cloud.storage.SnapshotPolicyVO;
|
||||||
import com.cloud.storage.SnapshotScheduleVO;
|
import com.cloud.storage.SnapshotScheduleVO;
|
||||||
import com.cloud.utils.db.GenericDao;
|
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 getCurrentSchedule(Long volumeId, Long policyId, boolean executing);
|
||||||
|
|
||||||
|
SnapshotScheduleVO findOneByVolume(long volumeId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.ejb.Local;
|
import javax.ejb.Local;
|
||||||
|
|
||||||
|
import com.cloud.storage.SnapshotPolicyVO;
|
||||||
import com.cloud.storage.SnapshotScheduleVO;
|
import com.cloud.storage.SnapshotScheduleVO;
|
||||||
import com.cloud.utils.db.GenericDaoBase;
|
import com.cloud.utils.db.GenericDaoBase;
|
||||||
import com.cloud.utils.db.SearchBuilder;
|
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 {
|
public class SnapshotScheduleDaoImpl extends GenericDaoBase<SnapshotScheduleVO, Long> implements SnapshotScheduleDao {
|
||||||
protected final SearchBuilder<SnapshotScheduleVO> executableSchedulesSearch;
|
protected final SearchBuilder<SnapshotScheduleVO> executableSchedulesSearch;
|
||||||
protected final SearchBuilder<SnapshotScheduleVO> coincidingSchedulesSearch;
|
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.
|
// 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.and("asyncJobId", coincidingSchedulesSearch.entity().getAsyncJobId(), SearchCriteria.Op.NULL);
|
||||||
coincidingSchedulesSearch.done();
|
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);
|
return listBy(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SnapshotScheduleVO findOneByVolume(long volumeId) {
|
||||||
|
SearchCriteria<SnapshotScheduleVO> sc = VolumeIdSearch.create();
|
||||||
|
sc.setParameters("volumeId", volumeId);
|
||||||
|
return findOneBy(sc);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -10,19 +10,12 @@ from util import CommandException
|
|||||||
import vhdutil
|
import vhdutil
|
||||||
import shutil
|
import shutil
|
||||||
import lvhdutil
|
import lvhdutil
|
||||||
import subprocess
|
|
||||||
from lvmcache import LVMCache
|
|
||||||
from journaler import Journaler
|
|
||||||
from lock import Lock
|
|
||||||
import errno
|
import errno
|
||||||
import subprocess
|
import subprocess
|
||||||
import xs_errors
|
import xs_errors
|
||||||
import cleanup
|
import cleanup
|
||||||
import hostvmstats
|
|
||||||
import socket
|
|
||||||
import stat
|
import stat
|
||||||
import random
|
import random
|
||||||
import tempfile
|
|
||||||
|
|
||||||
VHD_UTIL = '/usr/sbin/vhd-util'
|
VHD_UTIL = '/usr/sbin/vhd-util'
|
||||||
VHD_PREFIX = 'VHD-'
|
VHD_PREFIX = 'VHD-'
|
||||||
@ -107,85 +100,55 @@ def delete_secondary_storage_folder(session, args):
|
|||||||
|
|
||||||
return "1"
|
return "1"
|
||||||
|
|
||||||
@echo
|
|
||||||
def execute_script(session, args):
|
|
||||||
return ""
|
|
||||||
|
|
||||||
@echo
|
@echo
|
||||||
def post_create_private_template(session, args):
|
def post_create_private_template(session, args):
|
||||||
local_mount_path = None
|
local_mount_path = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
# get local template folder
|
||||||
# Mount the remote templates folder locally
|
sruuid = args["tmpltSrUUID"]
|
||||||
remote_mount_path = args["remoteTemplateMountPath"]
|
local_mount_path = os.path.join(SR.MOUNT_BASE, sruuid)
|
||||||
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")
|
|
||||||
|
|
||||||
# Retrieve args
|
# Retrieve args
|
||||||
filename = args["templateFilename"]
|
filename = args["templateFilename"]
|
||||||
name = args["templateName"]
|
name = args["templateName"]
|
||||||
description = args["templateDescription"]
|
description = args["templateDescription"]
|
||||||
checksum = args["checksum"]
|
checksum = args["checksum"]
|
||||||
virtual_size = args["virtualSize"]
|
size = args["size"]
|
||||||
template_id = args["templateId"]
|
virtual_size = args["virtualSize"]
|
||||||
|
template_id = args["templateId"]
|
||||||
|
|
||||||
# Determine the template size
|
# Determine the template size
|
||||||
template_download_folder = local_mount_path + "/" + args["templateDownloadFolder"]
|
template_install_path = local_mount_path + "/" + filename
|
||||||
template_download_path = template_download_folder + filename
|
file_size = os.path.getsize(template_install_path)
|
||||||
file_size = os.path.getsize(template_download_path)
|
util.SMlog("Got template file_size: " + str(file_size))
|
||||||
util.SMlog("Got template file_size: " + str(file_size))
|
|
||||||
|
|
||||||
# Create the template.properties file
|
# Create the template.properties file
|
||||||
template_properties_download_path = template_download_folder + "template.properties"
|
template_properties_install_path = local_mount_path + "/template.properties"
|
||||||
f = open(template_properties_download_path, "w")
|
f = open(template_properties_install_path, "w")
|
||||||
f.write("filename=" + filename + "\n")
|
f.write("filename=" + filename + "\n")
|
||||||
f.write("name=" + filename + "\n")
|
f.write("name=" + filename + "\n")
|
||||||
f.write("vhd=true\n")
|
f.write("vhd=true\n")
|
||||||
f.write("id=" + template_id + "\n")
|
f.write("id=" + template_id + "\n")
|
||||||
f.write("vhd.filename=" + filename + "\n")
|
f.write("vhd.filename=" + filename + "\n")
|
||||||
f.write("uniquename=" + name + "\n")
|
f.write("uniquename=" + name + "\n")
|
||||||
f.write("vhd.virtualsize=" + virtual_size + "\n")
|
f.write("vhd.virtualsize=" + virtual_size + "\n")
|
||||||
f.write("vhd.size=" + str(file_size) + "\n")
|
f.write("vhd.size=" + str(file_size) + "\n")
|
||||||
f.write("virtualsize=" + virtual_size + "\n")
|
f.write("virtualsize=" + virtual_size + "\n")
|
||||||
f.write("checksum=" + checksum + "\n")
|
f.write("checksum=" + checksum + "\n")
|
||||||
f.write("hvm=true\n")
|
f.write("hvm=true\n")
|
||||||
f.write("description=" + name + "\n")
|
f.write("description=" + name + "\n")
|
||||||
f.close()
|
f.close()
|
||||||
util.SMlog("Created template.properties file")
|
util.SMlog("Created template.properties file")
|
||||||
|
|
||||||
# Create the template install folder if necessary
|
# Set permissions
|
||||||
template_install_folder = local_mount_path + "/" + args["templateInstallFolder"]
|
permissions = stat.S_IREAD | stat.S_IWRITE | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH
|
||||||
if not os.path.isdir(template_install_folder):
|
os.chmod(template_properties_install_path, permissions)
|
||||||
current_umask = os.umask(0)
|
util.SMlog("Set permissions on template and template.properties")
|
||||||
os.makedirs(template_install_folder)
|
|
||||||
os.umask(current_umask)
|
|
||||||
|
|
||||||
# Move the template and the template.properties file to the install folder
|
except:
|
||||||
os.system("mv " + template_download_folder + "/" + filename + " " + template_install_folder)
|
errMsg = "post_create_private_template failed."
|
||||||
os.system("mv " + template_download_folder + "/template.properties " + template_install_folder)
|
util.SMlog(errMsg)
|
||||||
template_install_path = template_install_folder + filename
|
raise xs_errors.XenError(errMsg)
|
||||||
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)
|
|
||||||
|
|
||||||
return "1"
|
return "1"
|
||||||
|
|
||||||
@ -237,19 +200,6 @@ def chdir(path):
|
|||||||
util.SMlog("Chdired to " + path)
|
util.SMlog("Chdired to " + path)
|
||||||
return
|
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):
|
def scanParent(path):
|
||||||
# Do a scan for the parent for ISCSI volumes
|
# Do a scan for the parent for ISCSI volumes
|
||||||
# Note that the parent need not be visible on the XenServer
|
# Note that the parent need not be visible on the XenServer
|
||||||
@ -313,30 +263,14 @@ def rename(originalVHD, newVHD):
|
|||||||
raise xs_errors.XenError(errMsg)
|
raise xs_errors.XenError(errMsg)
|
||||||
return
|
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):
|
def makedirs(path):
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
try:
|
try:
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
except OSError, (errno, strerror):
|
except OSError, (errno, strerror):
|
||||||
|
umount(path)
|
||||||
|
if os.path.isdir(path):
|
||||||
|
return
|
||||||
errMsg = "OSError while creating " + path + " with errno: " + str(errno) + " and strerr: " + strerror
|
errMsg = "OSError while creating " + path + " with errno: " + str(errno) + " and strerr: " + strerror
|
||||||
util.SMlog(errMsg)
|
util.SMlog(errMsg)
|
||||||
raise xs_errors.XenError(errMsg)
|
raise xs_errors.XenError(errMsg)
|
||||||
@ -358,42 +292,17 @@ def mount(remoteDir, localDir):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def umount(localDir):
|
def umount(localDir):
|
||||||
success = False
|
try:
|
||||||
if os.path.isdir(localDir) and os.path.ismount(localDir):
|
cmd = ['umount', localDir]
|
||||||
try:
|
util.pread2(cmd)
|
||||||
cmd = ['umount', localDir]
|
except CommandException:
|
||||||
util.pread2(cmd)
|
errMsg = "CommandException raised while trying to umount " + localDir
|
||||||
except CommandException:
|
util.SMlog(errMsg)
|
||||||
errMsg = "CommandException raised while trying to umount " + localDir
|
return
|
||||||
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")
|
|
||||||
|
|
||||||
|
util.SMlog("Successfully unmounted " + localDir)
|
||||||
return
|
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):
|
def mountSnapshotsDir(secondaryStorageMountPath, relativeDir, dcId, accountId, instanceId):
|
||||||
# The aim is to mount secondaryStorageMountPath on
|
# The aim is to mount secondaryStorageMountPath on
|
||||||
# SR.MOUNT_BASE/<dcId>/<relativeDir>
|
# SR.MOUNT_BASE/<dcId>/<relativeDir>
|
||||||
@ -445,6 +354,9 @@ def getPrimarySRPath(primaryStorageSRUuid, isISCSI):
|
|||||||
else:
|
else:
|
||||||
return os.path.join(SR.MOUNT_BASE, primaryStorageSRUuid)
|
return os.path.join(SR.MOUNT_BASE, primaryStorageSRUuid)
|
||||||
|
|
||||||
|
def getBackupVHD(UUID):
|
||||||
|
return UUID + '.' + SR.DEFAULT_TAP
|
||||||
|
|
||||||
def getVHD(UUID, isISCSI):
|
def getVHD(UUID, isISCSI):
|
||||||
if isISCSI:
|
if isISCSI:
|
||||||
return VHD_PREFIX + UUID
|
return VHD_PREFIX + UUID
|
||||||
@ -529,122 +441,20 @@ def isVolumeAvailable(path):
|
|||||||
|
|
||||||
return (status == "1")
|
return (status == "1")
|
||||||
|
|
||||||
def getLastSnapshotUuid(volumeUuid, expectedLastSnapshotUuid):
|
def getVhdParent(session, args):
|
||||||
actualLastSnapshotUuid = ''
|
util.SMlog("getParent with " + str(args))
|
||||||
if volumeUuid:
|
primaryStorageSRUuid = args['primaryStorageSRUuid']
|
||||||
cmd = ['xe', 'vdi-param-get', 'uuid=' + volumeUuid, 'param-name=snapshots']
|
snapshotUuid = args['snapshotUuid']
|
||||||
stdout = ''
|
isISCSI = getIsTrueString(args['isISCSI'])
|
||||||
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'])
|
|
||||||
|
|
||||||
primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI)
|
primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI)
|
||||||
util.SMlog("primarySRPath: " + primarySRPath)
|
util.SMlog("primarySRPath: " + primarySRPath)
|
||||||
volumeVHD = getVHD(volumeUuid, isISCSI)
|
|
||||||
volumePath = os.path.join(primarySRPath, volumeVHD)
|
|
||||||
util.SMlog("volumePath: " + volumePath)
|
|
||||||
|
|
||||||
baseCopyUuid = ''
|
baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI)
|
||||||
wasVolumeAvailable = True
|
|
||||||
if isISCSI:
|
|
||||||
wasVolumeAvailable = isVolumeAvailable(volumePath)
|
|
||||||
if not wasVolumeAvailable:
|
|
||||||
# make it available
|
|
||||||
checkVolumeAvailablility(volumePath)
|
|
||||||
baseCopyUuid = scanParent(volumePath)
|
|
||||||
else:
|
|
||||||
baseCopyUuid = getParent(volumePath, isISCSI)
|
|
||||||
|
|
||||||
if baseCopyUuid is None:
|
return baseCopyUuid
|
||||||
# Make it an empty string so that it can be used in the return value
|
|
||||||
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):
|
def backupSnapshot(session, args):
|
||||||
util.SMlog("Called backupSnapshot with " + str(args))
|
util.SMlog("Called backupSnapshot with " + str(args))
|
||||||
primaryStorageSRUuid = args['primaryStorageSRUuid']
|
primaryStorageSRUuid = args['primaryStorageSRUuid']
|
||||||
@ -655,7 +465,6 @@ def backupSnapshot(session, args):
|
|||||||
snapshotUuid = args['snapshotUuid']
|
snapshotUuid = args['snapshotUuid']
|
||||||
prevSnapshotUuid = args['prevSnapshotUuid']
|
prevSnapshotUuid = args['prevSnapshotUuid']
|
||||||
prevBackupUuid = args['prevBackupUuid']
|
prevBackupUuid = args['prevBackupUuid']
|
||||||
isFirstSnapshotOfRootVolume = getIsTrueString(args['isFirstSnapshotOfRootVolume'])
|
|
||||||
isISCSI = getIsTrueString(args['isISCSI'])
|
isISCSI = getIsTrueString(args['isISCSI'])
|
||||||
|
|
||||||
primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI)
|
primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI)
|
||||||
@ -675,28 +484,23 @@ def backupSnapshot(session, args):
|
|||||||
# on it.
|
# on it.
|
||||||
backupsDir = mountSnapshotsDir(secondaryStorageMountPath, "snapshots", dcId, accountId, volumeId)
|
backupsDir = mountSnapshotsDir(secondaryStorageMountPath, "snapshots", dcId, accountId, volumeId)
|
||||||
util.SMlog("Backups dir " + backupsDir)
|
util.SMlog("Backups dir " + backupsDir)
|
||||||
# chdir to the backupsDir for convenience
|
|
||||||
chdir(backupsDir)
|
|
||||||
|
|
||||||
if baseCopyUuid == prevBaseCopyUuid:
|
if baseCopyUuid == prevBaseCopyUuid:
|
||||||
# There has been no change since the last snapshot so no need to backup
|
# 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)
|
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
|
# Set the uuid of the current backup to that of last backup
|
||||||
txt = "1#" + prevBaseCopyUuid
|
txt = "1#" + prevBaseCopyUuid
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
# Check existence of snapshot on primary storage
|
# Check existence of snapshot on primary storage
|
||||||
isfile(baseCopyPath, isISCSI)
|
isfile(baseCopyPath, isISCSI)
|
||||||
# copy baseCopyPath to backupsDir
|
# copy baseCopyPath to backupsDir with new uuid
|
||||||
backupFile = os.path.join(backupsDir, baseCopyVHD)
|
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)
|
copyfile(baseCopyPath, backupFile, isISCSI)
|
||||||
|
vhdutil.setHidden(backupFile, False)
|
||||||
|
|
||||||
# Now set the availability of the snapshotPath and the baseCopyPath to false
|
# Now set the availability of the snapshotPath and the baseCopyPath to false
|
||||||
makeUnavailable(snapshotUuid, primarySRPath, isISCSI)
|
makeUnavailable(snapshotUuid, primarySRPath, isISCSI)
|
||||||
@ -705,44 +509,18 @@ def backupSnapshot(session, args):
|
|||||||
makeUnavailable(prevSnapshotUuid, primarySRPath, isISCSI)
|
makeUnavailable(prevSnapshotUuid, primarySRPath, isISCSI)
|
||||||
makeUnavailable(prevBaseCopyUuid, 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.
|
# 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.
|
# 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
|
# So set the parent of the current baseCopyVHD to prevBackupVHD
|
||||||
if prevBackupUuid:
|
if prevBackupUuid:
|
||||||
# If there was a previous snapshot
|
# If there was a previous snapshot
|
||||||
prevBackupVHD = getVHD(prevBackupUuid, isISCSI)
|
prevBackupVHD = getBackupVHD(prevBackupUuid)
|
||||||
setParent(prevBackupVHD, backupFile)
|
prevBackupFile = os.path.join(backupsDir, prevBackupVHD)
|
||||||
|
setParent(prevBackupFile, backupFile)
|
||||||
|
|
||||||
txt = "1#" + baseCopyUuid
|
txt = "1#" + backupUuid
|
||||||
return txt
|
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
|
@echo
|
||||||
def deleteSnapshotBackup(session, args):
|
def deleteSnapshotBackup(session, args):
|
||||||
util.SMlog("Calling deleteSnapshotBackup with " + str(args))
|
util.SMlog("Calling deleteSnapshotBackup with " + str(args))
|
||||||
@ -751,14 +529,12 @@ def deleteSnapshotBackup(session, args):
|
|||||||
volumeId = args['volumeId']
|
volumeId = args['volumeId']
|
||||||
secondaryStorageMountPath = args['secondaryStorageMountPath']
|
secondaryStorageMountPath = args['secondaryStorageMountPath']
|
||||||
backupUUID = args['backupUUID']
|
backupUUID = args['backupUUID']
|
||||||
childUUID = args['childUUID']
|
|
||||||
isISCSI = getIsTrueString(args['isISCSI'])
|
|
||||||
|
|
||||||
backupsDir = mountSnapshotsDir(secondaryStorageMountPath, "snapshots", dcId, accountId, volumeId)
|
backupsDir = mountSnapshotsDir(secondaryStorageMountPath, "snapshots", dcId, accountId, volumeId)
|
||||||
# chdir to the backupsDir for convenience
|
# chdir to the backupsDir for convenience
|
||||||
chdir(backupsDir)
|
chdir(backupsDir)
|
||||||
|
|
||||||
backupVHD = getVHD(backupUUID, isISCSI)
|
backupVHD = getBackupVHD(backupUUID)
|
||||||
util.SMlog("checking existence of " + backupVHD)
|
util.SMlog("checking existence of " + backupVHD)
|
||||||
|
|
||||||
# The backupVHD is on secondary which is NFS and not ISCSI.
|
# The backupVHD is on secondary which is NFS and not ISCSI.
|
||||||
@ -767,211 +543,16 @@ def deleteSnapshotBackup(session, args):
|
|||||||
return "1"
|
return "1"
|
||||||
util.SMlog("backupVHD " + backupVHD + " exists.")
|
util.SMlog("backupVHD " + backupVHD + " exists.")
|
||||||
|
|
||||||
# Case 1) childUUID exists
|
# Just delete the backupVHD
|
||||||
if childUUID:
|
try:
|
||||||
coalesceToChild(backupVHD, childUUID, isISCSI)
|
os.remove(backupVHD)
|
||||||
else:
|
except OSError, (errno, strerror):
|
||||||
# Just delete the backupVHD
|
errMsg = "OSError while removing " + backupVHD + " with errno: " + str(errno) + " and strerr: " + strerror
|
||||||
try:
|
util.SMlog(errMsg)
|
||||||
os.remove(backupVHD)
|
raise xs_errors.XenError(errMsg)
|
||||||
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"
|
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):
|
def rmtree(path):
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
try:
|
try:
|
||||||
@ -1000,5 +581,5 @@ def deleteSnapshotsDir(session, args):
|
|||||||
return "1"
|
return "1"
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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
|
@Override @DB
|
||||||
public long submitAsyncJob(AsyncJobVO job, boolean scheduleJobExecutionInContext) {
|
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);
|
AsyncJobExecutor executor = getJobExecutor(job);
|
||||||
if(executor == null) {
|
if(executor == null) {
|
||||||
@ -108,6 +107,9 @@ public class AsyncJobManagerImpl implements AsyncJobManager {
|
|||||||
executor.setSyncSource(null);
|
executor.setSyncSource(null);
|
||||||
executor.setJob(job);
|
executor.setJob(job);
|
||||||
scheduleExecution(executor, scheduleJobExecutionInContext);
|
scheduleExecution(executor, scheduleJobExecutionInContext);
|
||||||
|
if(s_logger.isDebugEnabled()) {
|
||||||
|
s_logger.debug("submit async job-" + job.getId() + ", details: " + job.toString());
|
||||||
|
}
|
||||||
return job.getId();
|
return job.getId();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
s_logger.error("Unexpected 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.updateAsyncJobAttachment(job.getId(), "vm_template", template.getId());
|
||||||
asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, template.getId());
|
asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, template.getId());
|
||||||
Snapshot snapshot = null;
|
|
||||||
|
|
||||||
if (snapshotId == null) {
|
template = managerServer.createPrivateTemplate(template, param.getUserId(), snapshotId, volumeId, param.getName(), param.getDescription());
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snapshot == null) {
|
if (template != null) {
|
||||||
details += ", reason: Failed to create snapshot for basis of private template";
|
VMTemplateHostVO templateHostRef = managerServer.findTemplateHostRef(template.getId(),
|
||||||
} else {
|
volume.getDataCenterId());
|
||||||
param.setSnapshotId(snapshot.getId());
|
jobStatus = AsyncJobResult.STATUS_SUCCEEDED;
|
||||||
|
resultCode = 0;
|
||||||
template = managerServer.createPrivateTemplate(template,
|
details = null;
|
||||||
param.getUserId(),
|
String eventParams = "id=" + template.getId() + "\nname=" + template.getName() + "\nsize="
|
||||||
param.getSnapshotId(),
|
+ volume.getSize() + "\ndcId=" + volume.getDataCenterId();
|
||||||
param.getName(),
|
managerServer.saveEvent(param.getUserId(), param.getAccountId(), EventVO.LEVEL_INFO,
|
||||||
param.getDescription());
|
EventTypes.EVENT_TEMPLATE_CREATE, "Successfully created Template "
|
||||||
|
+ param.getName(), eventParams, param.getEventId());
|
||||||
if(template != null) {
|
resultObject = composeResultObject(template, templateHostRef, volume.getDataCenterId());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (InvalidParameterValueException e) {
|
} catch (InvalidParameterValueException e) {
|
||||||
details += ", reason: " + e.getMessage();
|
details += ", reason: " + e.getMessage();
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import com.cloud.storage.Snapshot;
|
|||||||
import com.cloud.storage.SnapshotVO;
|
import com.cloud.storage.SnapshotVO;
|
||||||
import com.cloud.storage.VolumeVO;
|
import com.cloud.storage.VolumeVO;
|
||||||
import com.cloud.storage.Snapshot.SnapshotType;
|
import com.cloud.storage.Snapshot.SnapshotType;
|
||||||
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
import com.cloud.storage.snapshot.SnapshotManager;
|
import com.cloud.storage.snapshot.SnapshotManager;
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@ -54,8 +55,10 @@ public class CreateSnapshotExecutor extends BaseAsyncJobExecutor {
|
|||||||
} else {
|
} else {
|
||||||
SnapshotOperationParam param = gson.fromJson(job.getCmdInfo(), SnapshotOperationParam.class);
|
SnapshotOperationParam param = gson.fromJson(job.getCmdInfo(), SnapshotOperationParam.class);
|
||||||
SnapshotManager snapshotManager = asyncMgr.getExecutorContext().getSnapshotMgr();
|
SnapshotManager snapshotManager = asyncMgr.getExecutorContext().getSnapshotMgr();
|
||||||
|
VolumeDao volumeDao = asyncMgr.getExecutorContext().getVolumeDao();
|
||||||
long volumeId = param.getVolumeId();
|
long volumeId = param.getVolumeId();
|
||||||
List<Long> policyIds = param.getPolicyIds();
|
long policyId = param.getPolicyId();
|
||||||
|
|
||||||
long snapshotId = 0;
|
long snapshotId = 0;
|
||||||
long userId = param.getUserId();
|
long userId = param.getUserId();
|
||||||
|
|
||||||
@ -65,34 +68,48 @@ public class CreateSnapshotExecutor extends BaseAsyncJobExecutor {
|
|||||||
int result = AsyncJobResult.STATUS_FAILED;
|
int result = AsyncJobResult.STATUS_FAILED;
|
||||||
int errorCode = BaseCmd.INTERNAL_ERROR;
|
int errorCode = BaseCmd.INTERNAL_ERROR;
|
||||||
Object resultObject = "Failed to create snapshot.";
|
Object resultObject = "Failed to create snapshot.";
|
||||||
|
VolumeVO vol = null;
|
||||||
|
|
||||||
try {
|
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) {
|
if (snapshot != null && snapshot.getStatus() == Snapshot.Status.CreatedOnPrimary) {
|
||||||
snapshotId = snapshot.getId();
|
snapshotId = snapshot.getId();
|
||||||
asyncMgr.updateAsyncJobStatus(jobId, BaseCmd.PROGRESS_INSTANCE_CREATED, snapshotId);
|
asyncMgr.updateAsyncJobStatus(jobId, BaseCmd.PROGRESS_INSTANCE_CREATED, snapshotId);
|
||||||
backedUp = snapshotManager.backupSnapshotToSecondaryStorage(userId, snapshot);
|
backedUp = snapshotManager.backupSnapshotToSecondaryStorage(userId, snapshot);
|
||||||
if (backedUp) {
|
if (backedUp) {
|
||||||
result = AsyncJobResult.STATUS_SUCCEEDED;
|
result = AsyncJobResult.STATUS_SUCCEEDED;
|
||||||
errorCode = 0; // Success
|
errorCode = 0; // Success
|
||||||
resultObject = composeResultObject(snapshot);
|
resultObject = composeResultObject(snapshot);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// More specific error
|
// More specific error
|
||||||
resultObject = "Created snapshot: " + snapshotId + " on primary but failed to backup on secondary";
|
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) {
|
} catch(Exception e) {
|
||||||
resultObject = "Unable to create snapshot: " + e.getMessage();
|
resultObject = "Unable to create snapshot: " + e.getMessage();
|
||||||
s_logger.warn(resultObject, e);
|
s_logger.warn(resultObject, e);
|
||||||
|
} finally {
|
||||||
|
if( vol != null ){
|
||||||
|
volumeDao.release(volumeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In all cases, ensure that we call completeAsyncJob to the asyncMgr.
|
// In all cases, ensure that we call completeAsyncJob to the asyncMgr.
|
||||||
asyncMgr.completeAsyncJob(jobId, result, errorCode, resultObject);
|
asyncMgr.completeAsyncJob(jobId, result, errorCode, resultObject);
|
||||||
|
|
||||||
// Cleanup jobs to do after the snapshot has been created.
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,6 @@ public class SnapshotOperationParam {
|
|||||||
private long accountId;
|
private long accountId;
|
||||||
private long userId;
|
private long userId;
|
||||||
private long snapshotId = 0;
|
private long snapshotId = 0;
|
||||||
private List<Long> policyIds = null;
|
|
||||||
private long policyId = 0;
|
private long policyId = 0;
|
||||||
private long volumeId;
|
private long volumeId;
|
||||||
private String name = null;
|
private String name = null;
|
||||||
@ -44,11 +43,11 @@ public class SnapshotOperationParam {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Used to create a snapshot
|
// 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);
|
setUserId(userId);
|
||||||
setAccountId(accountId);
|
setAccountId(accountId);
|
||||||
setVolumeId(volumeId);
|
setVolumeId(volumeId);
|
||||||
this.policyIds = policyIds;
|
this.policyId = policyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for CreateVolumeFromSnapshot
|
// Used for CreateVolumeFromSnapshot
|
||||||
@ -84,10 +83,6 @@ public class SnapshotOperationParam {
|
|||||||
this.snapshotId = snapshotId;
|
this.snapshotId = snapshotId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Long> getPolicyIds() {
|
|
||||||
return policyIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getPolicyId() {
|
public long getPolicyId() {
|
||||||
return policyId;
|
return policyId;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -355,7 +355,6 @@ public class ManagementServerImpl implements ManagementServer {
|
|||||||
private final AsyncJobManager _asyncMgr;
|
private final AsyncJobManager _asyncMgr;
|
||||||
private final TemplateManager _tmpltMgr;
|
private final TemplateManager _tmpltMgr;
|
||||||
private final SnapshotManager _snapMgr;
|
private final SnapshotManager _snapMgr;
|
||||||
private final SnapshotScheduler _snapshotScheduler;
|
|
||||||
private final NetworkGroupManager _networkGroupMgr;
|
private final NetworkGroupManager _networkGroupMgr;
|
||||||
private final int _purgeDelay;
|
private final int _purgeDelay;
|
||||||
private final boolean _directAttachNetworkExternalIpAllocator;
|
private final boolean _directAttachNetworkExternalIpAllocator;
|
||||||
@ -454,7 +453,6 @@ public class ManagementServerImpl implements ManagementServer {
|
|||||||
_asyncMgr = locator.getManager(AsyncJobManager.class);
|
_asyncMgr = locator.getManager(AsyncJobManager.class);
|
||||||
_tmpltMgr = locator.getManager(TemplateManager.class);
|
_tmpltMgr = locator.getManager(TemplateManager.class);
|
||||||
_snapMgr = locator.getManager(SnapshotManager.class);
|
_snapMgr = locator.getManager(SnapshotManager.class);
|
||||||
_snapshotScheduler = locator.getManager(SnapshotScheduler.class);
|
|
||||||
_networkGroupMgr = locator.getManager(NetworkGroupManager.class);
|
_networkGroupMgr = locator.getManager(NetworkGroupManager.class);
|
||||||
_uploadMonitor = locator.getManager(UploadMonitor.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");
|
throw new InvalidParameterValueException("Snapshots of volumes attached to System or router VM are not allowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
long policyId = Snapshot.MANUAL_POLICY_ID;
|
||||||
Long jobId = _snapshotScheduler.scheduleManualSnapshot(userId, volumeId);
|
long jobId = _snapMgr.createSnapshotAsync(userId, volumeId, policyId);
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return jobId;
|
return jobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SnapshotVO createTemplateSnapshot(Long userId, long volumeId) {
|
|
||||||
return _vmMgr.createTemplateSnapshot(userId, volumeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean destroyTemplateSnapshot(Long userId, long snapshotId) {
|
public boolean destroyTemplateSnapshot(Long userId, long snapshotId) {
|
||||||
return _vmMgr.destroyTemplateSnapshot(userId, snapshotId);
|
return _vmMgr.destroyTemplateSnapshot(userId, snapshotId);
|
||||||
@ -6963,7 +6952,7 @@ public class ManagementServerImpl implements ManagementServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Snapshot findSnapshotById(long snapshotId) {
|
public SnapshotVO findSnapshotById(long snapshotId) {
|
||||||
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
||||||
if (snapshot != null && snapshot.getRemoved() == null && snapshot.getStatus() == Snapshot.Status.BackedUp) {
|
if (snapshot != null && snapshot.getRemoved() == null && snapshot.getStatus() == Snapshot.Status.BackedUp) {
|
||||||
return snapshot;
|
return snapshot;
|
||||||
@ -6974,9 +6963,9 @@ public class ManagementServerImpl implements ManagementServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
|
|||||||
@ -490,7 +490,7 @@ public class StorageManagerImpl implements StorageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DB
|
@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;
|
VolumeVO createdVolume = null;
|
||||||
Long volumeId = null;
|
Long volumeId = null;
|
||||||
@ -506,10 +506,7 @@ public class StorageManagerImpl implements StorageManager {
|
|||||||
volume.setAccountId(accountId);
|
volume.setAccountId(accountId);
|
||||||
volume.setDomainId(account.getDomainId());
|
volume.setDomainId(account.getDomainId());
|
||||||
volume.setMirrorState(MirrorState.NOT_MIRRORED);
|
volume.setMirrorState(MirrorState.NOT_MIRRORED);
|
||||||
if (diskOffering != null) {
|
volume.setSize(virtualsize);
|
||||||
volume.setDiskOfferingId(diskOffering.getId());
|
|
||||||
}
|
|
||||||
volume.setSize(originalVolumeSize);
|
|
||||||
volume.setStorageResourceType(Storage.StorageResourceType.STORAGE_POOL);
|
volume.setStorageResourceType(Storage.StorageResourceType.STORAGE_POOL);
|
||||||
volume.setInstanceId(null);
|
volume.setInstanceId(null);
|
||||||
volume.setUpdated(new Date());
|
volume.setUpdated(new Date());
|
||||||
@ -538,8 +535,7 @@ public class StorageManagerImpl implements StorageManager {
|
|||||||
String volumeUUID = null;
|
String volumeUUID = null;
|
||||||
String details = 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
|
// Determine what pod to store the volume in
|
||||||
while ((pod = _agentMgr.findPod(null, null, dc, account.getId(), podsToAvoid)) != null) {
|
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.
|
// Get the newly created VDI from the snapshot.
|
||||||
// This will return a null volumePath if it could not be created
|
// 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();
|
volumeUUID = volumeDetails.first();
|
||||||
details = volumeDetails.second();
|
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.
|
SnapshotVO snapshot = _snapshotDao.findById(snapshotId); // Precondition: snapshot is not null and not removed.
|
||||||
Long origVolumeId = snapshot.getVolumeId();
|
Long origVolumeId = snapshot.getVolumeId();
|
||||||
VolumeVO originalVolume = _volsDao.findById(origVolumeId); // NOTE: Original volume could be destroyed and removed.
|
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.
|
// everything went well till now
|
||||||
// Why is the secondary storage a host? It's just an NFS mount point. Why do we need to look into the templateHostVO?
|
DataCenterVO dc = _dcDao.findById(originalVolume.getDataCenterId());
|
||||||
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;
|
|
||||||
|
|
||||||
if (originalVolume.getVolumeType() == VolumeType.DATADISK || originalVolume.getVolumeType() == VolumeType.ROOT) {
|
Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(userId, accountId, volumeName, dc,
|
||||||
Long diskOfferingId = originalVolume.getDiskOfferingId();
|
snapshot, originalVolume.getSize());
|
||||||
if (diskOfferingId != null) {
|
createdVolume = volumeDetails.first();
|
||||||
diskOffering = _diskOfferingDao.findById(diskOfferingId);
|
if (createdVolume != null) {
|
||||||
}
|
volumeId = createdVolume.getId();
|
||||||
}
|
|
||||||
// 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();
|
|
||||||
}
|
}
|
||||||
|
details = volumeDetails.second();
|
||||||
|
|
||||||
|
|
||||||
Transaction txn = Transaction.currentTxn();
|
Transaction txn = Transaction.currentTxn();
|
||||||
txn.start();
|
txn.start();
|
||||||
@ -719,7 +664,7 @@ public class StorageManagerImpl implements StorageManager {
|
|||||||
return createdVolume;
|
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;
|
String vdiUUID = null;
|
||||||
|
|
||||||
Long volumeId = snapshot.getVolumeId();
|
Long volumeId = snapshot.getVolumeId();
|
||||||
@ -738,8 +683,7 @@ public class StorageManagerImpl implements StorageManager {
|
|||||||
accountId,
|
accountId,
|
||||||
volumeId,
|
volumeId,
|
||||||
backedUpSnapshotUuid,
|
backedUpSnapshotUuid,
|
||||||
snapshot.getName(),
|
snapshot.getName());
|
||||||
templatePath);
|
|
||||||
|
|
||||||
String basicErrMsg = "Failed to create volume from " + snapshot.getName() + " for volume: " + volume.getId();
|
String basicErrMsg = "Failed to create volume from " + snapshot.getName() + " for volume: " + volume.getId();
|
||||||
CreateVolumeFromSnapshotAnswer answer = (CreateVolumeFromSnapshotAnswer) sendToHostsOnStoragePool(pool.getId(),
|
CreateVolumeFromSnapshotAnswer answer = (CreateVolumeFromSnapshotAnswer) sendToHostsOnStoragePool(pool.getId(),
|
||||||
|
|||||||
@ -47,12 +47,12 @@ public interface SnapshotManager extends Manager {
|
|||||||
* @throws ResourceAllocationException
|
* @throws ResourceAllocationException
|
||||||
* @throws InvalidParameterValueException
|
* @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
|
* 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
|
* 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 policyIds The list of policyIds to which this snapshot belongs to
|
||||||
* @param backedUp If true, the snapshot has been successfully created.
|
* @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
|
* 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);
|
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);
|
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.ejb.Local;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
import javax.persistence.EntityExistsException;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ import com.cloud.async.AsyncJobResult;
|
|||||||
import com.cloud.async.AsyncJobVO;
|
import com.cloud.async.AsyncJobVO;
|
||||||
import com.cloud.async.dao.AsyncJobDao;
|
import com.cloud.async.dao.AsyncJobDao;
|
||||||
import com.cloud.configuration.dao.ConfigurationDao;
|
import com.cloud.configuration.dao.ConfigurationDao;
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
import com.cloud.storage.Snapshot;
|
import com.cloud.storage.Snapshot;
|
||||||
import com.cloud.storage.SnapshotPolicyVO;
|
import com.cloud.storage.SnapshotPolicyVO;
|
||||||
import com.cloud.storage.SnapshotScheduleVO;
|
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
|
@DB
|
||||||
protected void scheduleSnapshots() {
|
protected void scheduleSnapshots() {
|
||||||
@ -237,72 +209,27 @@ public class SnapshotSchedulerImpl implements SnapshotScheduler {
|
|||||||
// Hence set user id to that of system
|
// Hence set user id to that of system
|
||||||
long userId = 1;
|
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) {
|
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 policyId = snapshotToBeExecuted.getPolicyId();
|
||||||
long volumeId = snapshotToBeExecuted.getVolumeId();
|
long volumeId = snapshotToBeExecuted.getVolumeId();
|
||||||
List<Long> coincidingPolicies = listOfVolumesSnapshotted.get(volumeId);
|
if (s_logger.isDebugEnabled()) {
|
||||||
if (coincidingPolicies != null) {
|
Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
|
||||||
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");
|
displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
|
||||||
// This can happen if this coincided with another schedule with a different policy
|
s_logger.debug("Scheduling 1 snapshot for volume " + volumeId + " for schedule id: "
|
||||||
// It would have added all the coinciding policies for the volume to the Map
|
+ snapshotToBeExecuted.getId() + " at " + displayTime);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
long snapshotScheId = snapshotToBeExecuted.getId();
|
||||||
coincidingPolicies = new ArrayList<Long>();
|
SnapshotScheduleVO tmpSnapshotScheduleVO = null;
|
||||||
List<SnapshotScheduleVO> coincidingSchedules = _snapshotScheduleDao.getCoincidingSnapshotSchedules(volumeId, _currentTimestamp);
|
try {
|
||||||
|
tmpSnapshotScheduleVO = _snapshotScheduleDao.acquire(snapshotScheId);
|
||||||
if (s_logger.isDebugEnabled()) {
|
long jobId = _snapshotManager.createSnapshotAsync(userId, volumeId, policyId);
|
||||||
Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
|
tmpSnapshotScheduleVO.setAsyncJobId(jobId);
|
||||||
displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
|
_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();
|
long policyId = policyInstance.getId();
|
||||||
Date nextSnapshotTimestamp = getNextScheduledTime(policyId, _currentTimestamp);
|
Date nextSnapshotTimestamp = getNextScheduledTime(policyId, _currentTimestamp);
|
||||||
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO(policyInstance.getVolumeId(), policyId, nextSnapshotTimestamp);
|
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;
|
return nextSnapshotTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override @DB
|
@Override @DB
|
||||||
public boolean removeSchedule(Long volumeId, Long policyId) {
|
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.
|
// 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
|
* @param description the user give description (aka display text) for the template
|
||||||
* @return a template if successfully created, null otherwise
|
* @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);
|
boolean destroyTemplateSnapshot(Long userId, long snapshotId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -20,7 +20,6 @@ package com.cloud.vm;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.Formatter;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
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.CheckVirtualMachineCommand;
|
||||||
import com.cloud.agent.api.Command;
|
import com.cloud.agent.api.Command;
|
||||||
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
|
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
|
||||||
|
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
|
||||||
import com.cloud.agent.api.GetVmStatsAnswer;
|
import com.cloud.agent.api.GetVmStatsAnswer;
|
||||||
import com.cloud.agent.api.GetVmStatsCommand;
|
import com.cloud.agent.api.GetVmStatsCommand;
|
||||||
import com.cloud.agent.api.ManageSnapshotAnswer;
|
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.StopCommand;
|
||||||
import com.cloud.agent.api.VmStatsEntry;
|
import com.cloud.agent.api.VmStatsEntry;
|
||||||
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
||||||
import com.cloud.agent.api.storage.CreatePrivateTemplateCommand;
|
|
||||||
import com.cloud.alert.AlertManager;
|
import com.cloud.alert.AlertManager;
|
||||||
import com.cloud.api.BaseCmd;
|
import com.cloud.api.BaseCmd;
|
||||||
import com.cloud.async.AsyncJobExecutor;
|
import com.cloud.async.AsyncJobExecutor;
|
||||||
@ -2232,7 +2231,7 @@ public class UserVmManagerImpl implements UserVmManager {
|
|||||||
SnapshotVO snapshot = _snapshotDao.findById(Long.valueOf(snapshotId));
|
SnapshotVO snapshot = _snapshotDao.findById(Long.valueOf(snapshotId));
|
||||||
if (snapshot != null) {
|
if (snapshot != null) {
|
||||||
VolumeVO volume = _volsDao.findById(snapshot.getVolumeId());
|
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;
|
Answer answer = null;
|
||||||
String basicErrMsg = "Failed to destroy template snapshot: " + snapshot.getName();
|
String basicErrMsg = "Failed to destroy template snapshot: " + snapshot.getName();
|
||||||
@ -2252,60 +2251,6 @@ public class UserVmManagerImpl implements UserVmManager {
|
|||||||
return success;
|
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
|
@Override
|
||||||
public void cleanNetworkRules(long userId, long instanceId) {
|
public void cleanNetworkRules(long userId, long instanceId) {
|
||||||
@ -2467,133 +2412,137 @@ public class UserVmManagerImpl implements UserVmManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override @DB
|
@Override @DB
|
||||||
public VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, long snapshotId, String name, String description) {
|
public VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, Long snapshotId, Long volumeId, String name, String description) {
|
||||||
VMTemplateVO privateTemplate = null;
|
String uniqueName = getRandomPrivateTemplateName();
|
||||||
long templateId = template.getId();
|
VMTemplateVO privateTemplate = null;
|
||||||
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
long templateId = template.getId();
|
||||||
if (snapshot != null) {
|
VolumeVO volume = null;
|
||||||
Long volumeId = snapshot.getVolumeId();
|
long zoneId;
|
||||||
VolumeVO volume = _volsDao.findById(volumeId);
|
HostVO secondaryStorageHost;
|
||||||
StringBuilder userFolder = new StringBuilder();
|
|
||||||
Formatter userFolderFormat = new Formatter(userFolder);
|
|
||||||
userFolderFormat.format("u%06d", snapshot.getAccountId());
|
|
||||||
|
|
||||||
String uniqueName = getRandomPrivateTemplateName();
|
Command cmd = null;
|
||||||
|
if( snapshotId != null ) {
|
||||||
long zoneId = volume.getDataCenterId();
|
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
||||||
HostVO secondaryStorageHost = _storageMgr.getSecondaryStorageHost(zoneId);
|
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);
|
String secondaryStorageURL = _storageMgr.getSecondaryStorageURL(zoneId);
|
||||||
|
|
||||||
if (secondaryStorageHost == null || secondaryStorageURL == null) {
|
if (secondaryStorageHost == null || secondaryStorageURL == null) {
|
||||||
s_logger.warn("Did not find the secondary storage URL in the database.");
|
throw new CloudRuntimeException("Did not find the secondary storage URL in the database for zoneId "
|
||||||
return null;
|
+ zoneId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Command cmd = null;
|
// We are creating a private template from a snapshot which has been
|
||||||
String backupSnapshotUUID = snapshot.getBackupSnapshotId();
|
// backed up to secondary storage.
|
||||||
if (backupSnapshotUUID != null) {
|
Long dcId = volume.getDataCenterId();
|
||||||
// We are creating a private template from a snapshot which has been backed up to secondary storage.
|
Long accountId = volume.getAccountId();
|
||||||
Long dcId = volume.getDataCenterId();
|
|
||||||
Long accountId = volume.getAccountId();
|
|
||||||
|
|
||||||
String origTemplateInstallPath = null;
|
String origTemplateInstallPath = null;
|
||||||
|
|
||||||
|
cmd = new CreatePrivateTemplateFromSnapshotCommand(_storageMgr.getPrimaryStorageNameLabel(volume),
|
||||||
if (ImageFormat.ISO != _snapshotMgr.getImageFormat(volumeId)) {
|
secondaryStorageURL, dcId, accountId, origVolumeId, backupSnapshotUUID, snapshot.getName(),
|
||||||
Long origTemplateId = volume.getTemplateId();
|
origTemplateInstallPath, templateId, name);
|
||||||
VMTemplateHostVO vmTemplateHostVO = _templateHostDao.findByHostTemplate(secondaryStorageHost.getId(), origTemplateId);
|
} else if( volumeId != null ) {
|
||||||
origTemplateInstallPath = vmTemplateHostVO.getInstallPath();
|
volume = _volsDao.findById(volumeId);
|
||||||
}
|
if( volume == null ) {
|
||||||
|
throw new CloudRuntimeException("Unable to find volume for Id " + volumeId);
|
||||||
cmd = new CreatePrivateTemplateFromSnapshotCommand(_storageMgr.getPrimaryStorageNameLabel(volume),
|
|
||||||
secondaryStorageURL,
|
|
||||||
dcId,
|
|
||||||
accountId,
|
|
||||||
volumeId,
|
|
||||||
backupSnapshotUUID,
|
|
||||||
snapshot.getName(),
|
|
||||||
origTemplateInstallPath,
|
|
||||||
templateId,
|
|
||||||
name);
|
|
||||||
}
|
}
|
||||||
else {
|
long instanceId = volume.getInstanceId();
|
||||||
cmd = new CreatePrivateTemplateCommand(secondaryStorageURL,
|
VMInstanceVO vm = _vmDao.findById(instanceId);
|
||||||
templateId,
|
State vmState = vm.getState();
|
||||||
volume.getAccountId(),
|
if( !vmState.equals(State.Stopped) && !vmState.equals(State.Destroyed)) {
|
||||||
name,
|
throw new CloudRuntimeException("Please put VM " + vm.getName() + " into Stopped state first");
|
||||||
uniqueName,
|
|
||||||
_storageMgr.getPrimaryStorageNameLabel(volume),
|
|
||||||
snapshot.getPath(),
|
|
||||||
snapshot.getName(),
|
|
||||||
userFolder.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
if (secondaryStorageHost == null || secondaryStorageURL == null) {
|
||||||
// This can be sent to a KVM host too.
|
throw new CloudRuntimeException("Did not find the secondary storage URL in the database for zoneId " + zoneId);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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;
|
return privateTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -998,7 +998,7 @@ CREATE TABLE `cloud`.`launch_permission` (
|
|||||||
|
|
||||||
CREATE TABLE `cloud`.`snapshot_policy` (
|
CREATE TABLE `cloud`.`snapshot_policy` (
|
||||||
`id` bigint unsigned NOT NULL auto_increment,
|
`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',
|
`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',
|
`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.',
|
`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` (
|
CREATE TABLE `cloud`.`snapshot_schedule` (
|
||||||
`id` bigint unsigned NOT NULL auto_increment,
|
`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',
|
`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',
|
`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',
|
`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',
|
`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`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user