Updated Branches: refs/heads/master 7cdd2ef6b -> 059e3beb2
CLOUDSTACK-4505: add ExpungeVM command to expunge a destroyed VM on demand Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/059e3beb Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/059e3beb Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/059e3beb Branch: refs/heads/master Commit: 059e3beb28642e1f886cb57f516170be884a2d1e Parents: 7cdd2ef Author: Wei Zhou <w.z...@leaseweb.com> Authored: Thu Oct 24 11:52:00 2013 +0200 Committer: Wei Zhou <w.z...@leaseweb.com> Committed: Thu Oct 24 11:52:00 2013 +0200 ---------------------------------------------------------------------- api/src/com/cloud/event/EventTypes.java | 1 + api/src/com/cloud/vm/UserVmService.java | 5 + .../api/command/admin/vm/ExpungeVMCmd.java | 116 +++++++++++++++++++ .../classes/resources/messages.properties | 4 + client/tomcatconf/commands.properties.in | 1 + .../com/cloud/server/ManagementServerImpl.java | 2 + server/src/com/cloud/vm/UserVmManagerImpl.java | 49 ++++++++ ui/css/cloudstack3.css | 2 + ui/dictionary.jsp | 4 + ui/scripts/instances.js | 42 +++++++ 10 files changed, 226 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/api/src/com/cloud/event/EventTypes.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index a762606..c7e7a45 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -76,6 +76,7 @@ public class EventTypes { public static final String EVENT_VM_MIGRATE = "VM.MIGRATE"; public static final String EVENT_VM_MOVE = "VM.MOVE"; public static final String EVENT_VM_RESTORE = "VM.RESTORE"; + public static final String EVENT_VM_EXPUNGE = "VM.EXPUNGE"; // Domain Router public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE"; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/api/src/com/cloud/vm/UserVmService.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index 7d459b9..0b142e8 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -23,6 +23,7 @@ import javax.naming.InsufficientResourcesException; import org.apache.cloudstack.api.BaseCmd.HTTPMethod; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; +import org.apache.cloudstack.api.command.admin.vm.ExpungeVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; @@ -463,4 +464,8 @@ public interface UserVmService { UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; + UserVm expungeVm(ExpungeVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException; + + UserVm expungeVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException; + } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/api/src/org/apache/cloudstack/api/command/admin/vm/ExpungeVMCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/admin/vm/ExpungeVMCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vm/ExpungeVMCmd.java new file mode 100644 index 0000000..387a0e9 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/vm/ExpungeVMCmd.java @@ -0,0 +1,116 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.vm; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.uservm.UserVm; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "expungeVirtualMachine", description="Expunge a virtual machine. Once expunged, it cannot be recoverd.", responseObject=SuccessResponse.class) +public class ExpungeVMCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(ExpungeVMCmd.class.getName()); + + private static final String s_name = "expungevirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class, + required=true, description="The ID of the virtual machine") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmById(getId()); + if (vm != null) { + return vm.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_EXPUNGE; + } + + @Override + public String getEventDescription() { + return "Expunging vm: " + getId(); + } + + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.VirtualMachine; + } + + public Long getInstanceId() { + return getId(); + } + + @Override + public void execute() throws ResourceUnavailableException, ConcurrentOperationException{ + CallContext.current().setEventDetails("Vm Id: "+getId()); + try { + UserVm result = _userVmService.expungeVm(this); + + if (result != null) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to expunge vm"); + } + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, cre.getMessage()); + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/client/WEB-INF/classes/resources/messages.properties ---------------------------------------------------------------------- diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index c4dbc41..e5b06ec 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -203,6 +203,8 @@ label.action.enable.user.processing=Enabling User.... label.action.enable.user=Enable User label.action.enable.zone.processing=Enabling Zone.... label.action.enable.zone=Enable Zone +label.action.expunge.instance=Expunge Instance +label.action.expunge.instance.processing=Expunging Instance.... label.action.force.reconnect.processing=Reconnecting.... label.action.force.reconnect=Force Reconnect label.action.generate.keys.processing=Generate Keys.... @@ -563,6 +565,7 @@ label.ESP.lifetime=ESP Lifetime (second) label.ESP.policy=ESP policy label.esx.host=ESX/ESXi Host label.example=Example +label.expunge=Expunge label.external.link=External link label.f5=F5 label.failed=Failed @@ -1281,6 +1284,7 @@ message.action.enable.nexusVswitch=Please confirm that you want to enable this n message.action.enable.physical.network=Please confirm that you want to enable this physical network. message.action.enable.pod=Please confirm that you want to enable this pod. message.action.enable.zone=Please confirm that you want to enable this zone. +message.action.expunge.instance=Please confirm that you want to expunge this instance. message.action.force.reconnect=Your host has been successfully forced to reconnect. This process can take up to several minutes. message.action.host.enable.maintenance.mode=Enabling maintenance mode will cause a live migration of all running instances on this host to any available host. message.action.instance.reset.password=Please confirm that you want to change the ROOT password for this virtual machine. http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/client/tomcatconf/commands.properties.in ---------------------------------------------------------------------- diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 0296de0..3fe0463 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -71,6 +71,7 @@ assignVirtualMachine=7 migrateVirtualMachine=1 migrateVirtualMachineWithVolume=1 recoverVirtualMachine=7 +expungeVirtualMachine=1 #### snapshot commands createSnapshot=15 http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/server/src/com/cloud/server/ManagementServerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 58513d7..1c68523 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -194,6 +194,7 @@ import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.ListVlanIpRangesCmd; import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; +import org.apache.cloudstack.api.command.admin.vm.ExpungeVMCmd; import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd; import org.apache.cloudstack.api.command.admin.vm.MigrateVirtualMachineWithVolumeCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; @@ -2753,6 +2754,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(AddNicToVMCmd.class); cmdList.add(DeployVMCmd.class); cmdList.add(DestroyVMCmd.class); + cmdList.add(ExpungeVMCmd.class); cmdList.add(GetVMPasswordCmd.class); cmdList.add(ListVMsCmd.class); cmdList.add(ScaleVMCmd.class); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/server/src/com/cloud/vm/UserVmManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 8de494c..e97dfd7 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -44,6 +44,7 @@ import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd.HTTPMethod; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; +import org.apache.cloudstack.api.command.admin.vm.ExpungeVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; @@ -1963,6 +1964,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_EXPUNGE, eventDescription = "expunging Vm", async = true) + public UserVm expungeVm(ExpungeVMCmd cmd) + throws ResourceUnavailableException, ConcurrentOperationException { + return expungeVm(cmd.getId()); + } + + @Override @DB public InstanceGroupVO createVmGroup(CreateVMGroupCmd cmd) { Account caller = CallContext.current().getCallingAccount(); @@ -3583,7 +3591,48 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } + @Override + public UserVm expungeVm(long vmId) throws ResourceUnavailableException, + ConcurrentOperationException { + Account caller = CallContext.current().getCallingAccount(); + Long userId = caller.getId(); + + // Verify input parameters + UserVmVO vm = _vmDao.findById(vmId); + if (vm == null) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Unable to find a virtual machine with specified vmId"); + ex.addProxyObject(String.valueOf(vmId), "vmId"); + throw ex; + } + + if (vm.getRemoved() != null) { + s_logger.trace("Vm id=" + vmId + " is already expunged"); + return vm; + } + + if ((vm.getState() != State.Destroyed) && (vm.getState() != State.Expunging)) { + CloudRuntimeException ex = new CloudRuntimeException( + "Please destroy vm with specified vmId before expunge"); + ex.addProxyObject(String.valueOf(vmId), "vmId"); + throw ex; + } + + _accountMgr.checkAccess(caller, null, true, vm); + boolean status; + + status = expunge(vm, userId, caller); + if (status) { + return _vmDao.findByIdIncludingRemoved(vmId); + } else { + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to expunge vm with specified vmId"); + ex.addProxyObject(String.valueOf(vmId), "vmId"); + throw ex; + } + + } @Override public Pair<List<UserVmJoinVO>, Integer> searchForUserVMs(Criteria c, Account caller, Long domainId, boolean isRecursive, http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/ui/css/cloudstack3.css ---------------------------------------------------------------------- diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 783b33e..fd6e6f3 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11844,6 +11844,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it } .destroy .icon, +.expunge .icon, .remove .icon, .delete .icon, .decline .icon, @@ -11852,6 +11853,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it } .destroy:hover .icon, +.expunge:hover .icon, .remove:hover .icon, .delete:hover .icon, .deleteacllist:hover .icon { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/ui/dictionary.jsp ---------------------------------------------------------------------- diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index 32313d8..f9fe088 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -213,6 +213,8 @@ dictionary = { 'label.action.enable.user.processing': '<fmt:message key="label.action.enable.user.processing" />', 'label.action.enable.zone': '<fmt:message key="label.action.enable.zone" />', 'label.action.enable.zone.processing': '<fmt:message key="label.action.enable.zone.processing" />', +'label.action.expunge.instance': '<fmt:message key="label.action.expunge.instance" />', +'label.action.expunge.instance.processing': '<fmt:message key="label.action.expunge.instance.processing" />', 'label.action.force.reconnect': '<fmt:message key="label.action.force.reconnect" />', 'label.action.force.reconnect.processing': '<fmt:message key="label.action.force.reconnect.processing" />', 'label.action.generate.keys': '<fmt:message key="label.action.generate.keys" />', @@ -564,6 +566,7 @@ dictionary = { 'label.ESP.policy': '<fmt:message key="label.ESP.policy" />', 'label.esx.host': '<fmt:message key="label.esx.host" />', 'label.example': '<fmt:message key="label.example" />', +'label.expunge': '<fmt:message key="label.expunge" />', 'label.external.link': '<fmt:message key="label.external.link" />', 'label.f5': '<fmt:message key="label.f5" />', 'label.failed': '<fmt:message key="label.failed" />', @@ -1248,6 +1251,7 @@ dictionary = { 'message.action.enable.physical.network': '<fmt:message key="message.action.enable.physical.network" />', 'message.action.enable.pod': '<fmt:message key="message.action.enable.pod" />', 'message.action.enable.zone': '<fmt:message key="message.action.enable.zone" />', +'message.action.expunge.instance': '<fmt:message key="message.action.expunge.instance" />', 'message.action.force.reconnect': '<fmt:message key="message.action.force.reconnect" />', 'message.action.host.enable.maintenance.mode': '<fmt:message key="message.action.host.enable.maintenance.mode" />', 'message.action.instance.reset.password': '<fmt:message key="message.action.instance.reset.password" />', http://git-wip-us.apache.org/repos/asf/cloudstack/blob/059e3beb/ui/scripts/instances.js ---------------------------------------------------------------------- diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 80f1b90..6e545fc 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -568,6 +568,39 @@ poll: pollAsyncJobResult } }, + expunge: { + label: 'label.action.expunge.instance', + compactLabel: 'label.expunge', + messages: { + confirm: function(args) { + return 'message.action.expunge.instance'; + }, + notification: function(args) { + return 'label.action.expunge.instance'; + } + }, + action: function(args) { + $.ajax({ + url: createURL("expungeVirtualMachine&id=" + args.context.instances[0].id), + dataType: "json", + async: true, + success: function(json) { + var jid = json.expungevirtualmachineresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getActionFilter: function() { + return vmActionfilter; + } + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + }, restore: { label: 'label.action.restore.instance', compactLabel: 'label.restore', @@ -1651,6 +1684,10 @@ var jsonObj; if (json.listvirtualmachinesresponse.virtualmachine != null && json.listvirtualmachinesresponse.virtualmachine.length > 0) jsonObj = json.listvirtualmachinesresponse.virtualmachine[0]; + else if (isAdmin()) + jsonObj = $.extend(args.context.instances[0], { + state: "Expunged" + }); //after root admin expunge a VM, listVirtualMachines API will no longer returns this expunged VM to all users. else jsonObj = $.extend(args.context.instances[0], { state: "Destroyed" @@ -1985,6 +2022,8 @@ if (isAdmin() || isDomainAdmin()) { allowedActions.push("restore"); } + if (isAdmin()) + allowedActions.push("expunge"); } else if (jsonObj.state == 'Running') { allowedActions.push("stop"); allowedActions.push("restart"); @@ -2042,6 +2081,9 @@ // allowedActions.push("stop"); } else if (jsonObj.state == 'Error') { allowedActions.push("destroy"); + } else if (jsonObj.state == 'Expunging') { + if (isAdmin()) + allowedActions.push("expunge"); } return allowedActions; }