diff --git a/core/src/com/cloud/agent/api/ManageSnapshotCommand.java b/core/src/com/cloud/agent/api/ManageSnapshotCommand.java index d3509c0cabc..7f49b3dab03 100644 --- a/core/src/com/cloud/agent/api/ManageSnapshotCommand.java +++ b/core/src/com/cloud/agent/api/ManageSnapshotCommand.java @@ -37,10 +37,11 @@ public class ManageSnapshotCommand extends Command { public ManageSnapshotCommand() {} - public ManageSnapshotCommand(String commandSwitch, long snapshotId, String path, String snapshotName) { + public ManageSnapshotCommand(String commandSwitch, long snapshotId, String path, String preSnapshot, String snapshotName) { _commandSwitch = commandSwitch; if (commandSwitch.equals(ManageSnapshotCommand.CREATE_SNAPSHOT)) { _volumePath = path; + _snapshotPath = preSnapshot; } else if (commandSwitch.equals(ManageSnapshotCommand.DESTROY_SNAPSHOT)) { _snapshotPath = path; diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 9c7cd7b66bb..11bc37a1d2e 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -5079,6 +5079,20 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR return new ValidateSnapshotAnswer(cmd, success, details, expectedSnapshotBackupUuid, actualSnapshotBackupUuid, actualSnapshotUuid); } + + 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 ManageSnapshotAnswer execute(final ManageSnapshotCommand cmd) { long snapshotId = cmd.getSnapshotId(); String snapshotName = cmd.getSnapshotName(); @@ -5111,6 +5125,25 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR } // Determine the UUID of the snapshot + 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; + } + + } + VDI.Record vdir = snapshot.getRecord(conn); snapshotUUID = vdir.uuid; diff --git a/core/src/com/cloud/server/ManagementServer.java b/core/src/com/cloud/server/ManagementServer.java index bc83aa15785..bc2e6d3212f 100644 --- a/core/src/com/cloud/server/ManagementServer.java +++ b/core/src/com/cloud/server/ManagementServer.java @@ -1734,19 +1734,7 @@ public interface ManagementServer { ResourceAllocationException, InternalErrorException; - /** - * @param userId The Id of the user who invoked this operation. - * @param volumeId The volume for which this snapshot is being taken - * @return The properties of the snapshot taken - */ - SnapshotVO createTemplateSnapshot(Long userId, long volumeId); - /** - * Destroy a snapshot - * @param snapshotId the id of the snapshot to destroy - * @return true if snapshot successfully destroyed, false otherwise - */ - boolean destroyTemplateSnapshot(Long userId, long snapshotId); long deleteSnapshotAsync(long userId, long snapshotId); long createVolumeFromSnapshotAsync(long userId, long accountId, long snapshotId, String volumeName) throws InternalErrorException, ResourceAllocationException; diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops index 2412122a80e..411c7fa31b2 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -2103,8 +2103,20 @@ def network_rules(session, args): except: util.SMlog("Failed to network rule !") +def getVhdParent(session, args): + util.SMlog("getParent with " + str(args)) + primaryStorageSRUuid = args['primaryStorageSRUuid'] + snapshotUuid = args['snapshotUuid'] + isISCSI = getIsTrueString(args['isISCSI']) + + primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI) + util.SMlog("primarySRPath: " + primarySRPath) + + baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) + + return baseCopyUuid if __name__ == "__main__": - XenAPIPlugin.dispatch({"pingtest": pingtest, "create_secondary_storage_folder":create_secondary_storage_folder, "setup_iscsi":setup_iscsi, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template": post_create_private_template, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "getnetwork": getnetwork, "preparemigration": preparemigration, "setIptables": setIptables, "patchdomr": patchdomr, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "checkMount": checkMount, "checkIscsi": checkIscsi, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "createVolumeFromSnapshot": createVolumeFromSnapshot, "networkUsage": networkUsage, "unmountSnapshotsDir": unmountSnapshotsDir, "deleteSnapshotsDir": deleteSnapshotsDir, "validatePreviousSnapshotBackup": validatePreviousSnapshotBackup, "validateSnapshot" : validateSnapshot, "network_rules":network_rules, "can_bridge_firewall":can_bridge_firewall, "default_network_rules":default_network_rules, "destroy_network_rules_for_vm":destroy_network_rules_for_vm, "default_network_rules_systemvm":default_network_rules_systemvm, "get_rule_logs_for_vms":get_rule_logs_for_vms, "setLinkLocalIP":setLinkLocalIP}) + XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "pingtest": pingtest, "create_secondary_storage_folder":create_secondary_storage_folder, "setup_iscsi":setup_iscsi, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template": post_create_private_template, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "getnetwork": getnetwork, "preparemigration": preparemigration, "setIptables": setIptables, "patchdomr": patchdomr, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "checkMount": checkMount, "checkIscsi": checkIscsi, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "createVolumeFromSnapshot": createVolumeFromSnapshot, "networkUsage": networkUsage, "unmountSnapshotsDir": unmountSnapshotsDir, "deleteSnapshotsDir": deleteSnapshotsDir, "validatePreviousSnapshotBackup": validatePreviousSnapshotBackup, "validateSnapshot" : validateSnapshot, "network_rules":network_rules, "can_bridge_firewall":can_bridge_firewall, "default_network_rules":default_network_rules, "destroy_network_rules_for_vm":destroy_network_rules_for_vm, "default_network_rules_systemvm":default_network_rules_systemvm, "get_rule_logs_for_vms":get_rule_logs_for_vms, "setLinkLocalIP":setLinkLocalIP}) diff --git a/server/src/com/cloud/async/executor/CreatePrivateTemplateExecutor.java b/server/src/com/cloud/async/executor/CreatePrivateTemplateExecutor.java index 87fef05d989..cd73537cce1 100644 --- a/server/src/com/cloud/async/executor/CreatePrivateTemplateExecutor.java +++ b/server/src/com/cloud/async/executor/CreatePrivateTemplateExecutor.java @@ -40,6 +40,7 @@ import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.UserVmManager; import com.google.gson.Gson; @@ -118,7 +119,8 @@ public class CreatePrivateTemplateExecutor extends VolumeOperationExecutor { if (snapshotId == null) { // We are create private template from volume. Create a snapshot, copy the vhd chain of the disk to secondary storage. // For template snapshot, we use a separate snapshot method. - snapshot = vmMgr.createTemplateSnapshot(param.getUserId(), param.getVolumeId()); + //snapshot = vmMgr.createTemplateSnapshot(param.getUserId(), param.getVolumeId()); + throw new CloudRuntimeException("Do not support create template from volume at this moment"); } else { // We are creating a private template from an already present snapshot. @@ -145,13 +147,6 @@ public class CreatePrivateTemplateExecutor extends VolumeOperationExecutor { 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()); - } } } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 3e1f0624635..9fa2762d1ac 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -6325,16 +6325,6 @@ public class ManagementServerImpl implements ManagementServer { return jobId; } - @Override - public SnapshotVO createTemplateSnapshot(Long userId, long volumeId) { - return _vmMgr.createTemplateSnapshot(userId, volumeId); - } - - @Override - public boolean destroyTemplateSnapshot(Long userId, long snapshotId) { - return _vmMgr.destroyTemplateSnapshot(userId, snapshotId); - } - @Override public long deleteSnapshotAsync(long userId, long snapshotId) { Snapshot snapshot = findSnapshotById(snapshotId); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 275d594c9e9..0debbc65135 100644 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -115,6 +115,7 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.UserVmDao; import com.google.gson.Gson; @@ -362,9 +363,19 @@ public class SnapshotManagerImpl implements SnapshotManager { } txn.commit(); + + // get previous snapshot Path + long preId = _snapshotDao.getLastSnapshot(volumeId, id); + String preSnapshotPath = null; + SnapshotVO preSnapshotVO = null; + if( preId != 0) { + preSnapshotVO = _snapshotDao.findById(preId); + preSnapshotPath = preSnapshotVO.getPath(); + + } // Send a ManageSnapshotCommand to the agent - ManageSnapshotCommand cmd = new ManageSnapshotCommand(ManageSnapshotCommand.CREATE_SNAPSHOT, id, volume.getPath(), snapshotName); + ManageSnapshotCommand cmd = new ManageSnapshotCommand(ManageSnapshotCommand.CREATE_SNAPSHOT, id, volume.getPath(), preSnapshotPath, snapshotName); String basicErrMsg = "Failed to create snapshot for volume: " + volume.getId(); ManageSnapshotAnswer answer = (ManageSnapshotAnswer) _storageMgr.sendToHostsOnStoragePool(volume.getPoolId(), cmd, basicErrMsg, _totalRetries, _pauseInterval, _shouldBeSnapshotCapable); @@ -374,7 +385,16 @@ public class SnapshotManagerImpl implements SnapshotManager { // Update the snapshot in the database if ((answer != null) && answer.getResult()) { // The snapshot was successfully created - createdSnapshot = updateDBOnCreate(id, answer.getSnapshotPath()); + if( preSnapshotPath != null && preSnapshotPath == answer.getSnapshotPath() ){ + //empty snapshot + s_logger.debug("CreateSnapshot: this is empty snapshot, remove it "); + // delete from the snapshots table + _snapshotDao.delete(id); + throw new CloudRuntimeException(" There is no change since last snapshot, please use last snapshot " + preSnapshotPath); + + } else { + createdSnapshot = updateDBOnCreate(id, answer.getSnapshotPath()); + } } else { if (answer != null) { s_logger.error(answer.getDetails()); diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java index cbd9fe3b43c..286b8b6034e 100644 --- a/server/src/com/cloud/vm/UserVmManager.java +++ b/server/src/com/cloud/vm/UserVmManager.java @@ -196,14 +196,6 @@ public interface UserVmManager extends Manager, VirtualMachineManager */ VMTemplateVO createPrivateTemplate(VMTemplateVO template, Long userId, long snapshotId, 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); - /** * Clean the network rules for the given VM * @param userId diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 7222e27c5d9..c6d0d78f53d 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -2021,87 +2021,7 @@ public class UserVmManagerImpl implements UserVmManager { } } - @Override - public boolean destroyTemplateSnapshot(Long userId, long snapshotId) { - boolean success = false; - SnapshotVO snapshot = _snapshotDao.findById(Long.valueOf(snapshotId)); - if (snapshot != null) { - VolumeVO volume = _volsDao.findById(snapshot.getVolumeId()); - ManageSnapshotCommand cmd = new ManageSnapshotCommand(ManageSnapshotCommand.DESTROY_SNAPSHOT, snapshotId, snapshot.getPath(), snapshot.getName()); - Answer answer = null; - String basicErrMsg = "Failed to destroy template snapshot: " + snapshot.getName(); - Long storagePoolId = volume.getPoolId(); - answer = _storageMgr.sendToHostsOnStoragePool(storagePoolId, cmd, basicErrMsg); - - if ((answer != null) && answer.getResult()) { - // delete the snapshot from the database - _snapshotDao.delete(snapshotId); - success = true; - } - if (answer != null) { - s_logger.error(answer.getDetails()); - } - } - - return success; - } - - @Override @DB - public SnapshotVO createTemplateSnapshot(long userId, long volumeId) { - SnapshotVO createdSnapshot = null; - VolumeVO volume = _volsDao.findById(volumeId); - - 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); - - 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.delete(id); - txn.commit(); - - createdSnapshot = null; - } - - return createdSnapshot; - } - - @Override public void cleanNetworkRules(long userId, long instanceId) { UserVmVO vm = _vmDao.findById(instanceId);