mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
CLOUDSTACK-9860: Power off VMs when stopVM is called with forced=true
The 'force' option provided with the stopVirtualMachine API command is often assumed to be a hard shutdown sent to the hypervisor, when in fact it is for CloudStacks' internal use. CloudStack should be able to send the 'hard' power-off request to the hosts. When forced parameter on the stopVM API is true, power off (hard shutdown) a VM. This uses initial changes from #1635 to pass the forced parameter to hypervisor plugin via the StopCommand, and fixes force stop (poweroff) handling for KVM, VMware and XenServer. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
b65a58ba12
commit
8323a175f1
@ -54,7 +54,7 @@ public class StopVMCmd extends BaseAsyncCmd {
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the VM "
|
||||
+ "(vm is marked as Stopped even when command fails to be send to the backend). The caller knows the VM is stopped.")
|
||||
+ "(vm is marked as Stopped even when command fails to be send to the backend, otherwise a force poweroff is attempted). The caller knows the VM is stopped.")
|
||||
private Boolean forced;
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
|
||||
@ -28,6 +28,7 @@ public class StopCommand extends RebootCommand {
|
||||
private String publicConsoleProxyIpAddress = null;
|
||||
private GPUDeviceTO gpuDevice;
|
||||
boolean checkBeforeCleanup = false;
|
||||
boolean forceStop = false;
|
||||
|
||||
protected StopCommand() {
|
||||
}
|
||||
@ -45,6 +46,12 @@ public class StopCommand extends RebootCommand {
|
||||
this.checkBeforeCleanup = checkBeforeCleanup;
|
||||
}
|
||||
|
||||
public StopCommand(VirtualMachine vm, boolean executeInSequence, boolean checkBeforeCleanup, boolean forceStop) {
|
||||
super(vm.getInstanceName(), executeInSequence);
|
||||
this.checkBeforeCleanup = checkBeforeCleanup;
|
||||
this.forceStop = forceStop;
|
||||
}
|
||||
|
||||
public StopCommand(String vmName, boolean executeInSequence, boolean checkBeforeCleanup) {
|
||||
super(vmName, executeInSequence);
|
||||
this.checkBeforeCleanup = checkBeforeCleanup;
|
||||
@ -82,4 +89,8 @@ public class StopCommand extends RebootCommand {
|
||||
public boolean checkBeforeCleanup() {
|
||||
return this.checkBeforeCleanup;
|
||||
}
|
||||
|
||||
public boolean isForceStop() {
|
||||
return forceStop;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1536,7 +1536,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
||||
|
||||
vmGuru.prepareStop(profile);
|
||||
|
||||
final StopCommand stop = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), false);
|
||||
final StopCommand stop = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), false, cleanUpEvenIfUnableToStop);
|
||||
|
||||
boolean stopped = false;
|
||||
Answer answer = null;
|
||||
|
||||
@ -2703,7 +2703,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
}
|
||||
}
|
||||
s_logger.debug(vmDef);
|
||||
msg = stopVM(conn, vmName);
|
||||
msg = stopVM(conn, vmName, false);
|
||||
msg = startVM(conn, vmName, vmDef);
|
||||
return null;
|
||||
} catch (final LibvirtException e) {
|
||||
@ -2725,14 +2725,17 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
return msg;
|
||||
}
|
||||
|
||||
public String stopVM(final Connect conn, final String vmName) {
|
||||
public String stopVM(final Connect conn, final String vmName, final boolean forceStop) {
|
||||
DomainState state = null;
|
||||
Domain dm = null;
|
||||
|
||||
s_logger.debug("Try to stop the vm at first");
|
||||
if (forceStop) {
|
||||
return stopVMInternal(conn, vmName, true);
|
||||
}
|
||||
String ret = stopVM(conn, vmName, false);
|
||||
if (ret == Script.ERR_TIMEOUT) {
|
||||
ret = stopVM(conn, vmName, true);
|
||||
ret = stopVMInternal(conn, vmName, true);
|
||||
} else if (ret != null) {
|
||||
/*
|
||||
* There is a race condition between libvirt and qemu: libvirt
|
||||
@ -2765,7 +2768,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
if (state != DomainState.VIR_DOMAIN_SHUTOFF) {
|
||||
s_logger.debug("Try to destroy the vm");
|
||||
ret = stopVM(conn, vmName, true);
|
||||
ret = stopVMInternal(conn, vmName, true);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
@ -2775,7 +2778,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String stopVM(final Connect conn, final String vmName, final boolean force) {
|
||||
protected String stopVMInternal(final Connect conn, final String vmName, final boolean force) {
|
||||
Domain dm = null;
|
||||
try {
|
||||
dm = conn.domainLookupByName(vmName);
|
||||
|
||||
@ -67,7 +67,8 @@ public final class LibvirtStopCommandWrapper extends CommandWrapper<StopCommand,
|
||||
final List<InterfaceDef> ifaces = libvirtComputingResource.getInterfaces(conn, vmName);
|
||||
|
||||
libvirtComputingResource.destroyNetworkRulesForVM(conn, vmName);
|
||||
final String result = libvirtComputingResource.stopVM(conn, vmName);
|
||||
final String result = libvirtComputingResource.stopVM(conn, vmName, command.isForceStop());
|
||||
|
||||
if (result == null) {
|
||||
for (final DiskDef disk : disks) {
|
||||
libvirtComputingResource.cleanupDisk(disk);
|
||||
|
||||
@ -3150,13 +3150,18 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
||||
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME, cmd.getVmName());
|
||||
|
||||
if (getVmPowerState(vmMo) != PowerState.PowerOff) {
|
||||
if (vmMo.safePowerOff(_shutdownWaitMs)) {
|
||||
return new StopAnswer(cmd, "Stop VM " + cmd.getVmName() + " Succeed", true);
|
||||
String msg = "Stop VM " + cmd.getVmName() + " Succeed";
|
||||
boolean success = false;
|
||||
if (cmd.isForceStop()) {
|
||||
success = vmMo.powerOff();
|
||||
} else {
|
||||
String msg = "Have problem in powering off VM " + cmd.getVmName() + ", let the process continue";
|
||||
s_logger.warn(msg);
|
||||
return new StopAnswer(cmd, msg, true);
|
||||
success = vmMo.safePowerOff(_shutdownWaitMs);
|
||||
}
|
||||
if (!success) {
|
||||
msg = "Have problem in powering off VM " + cmd.getVmName() + ", let the process continue";
|
||||
s_logger.warn(msg);
|
||||
}
|
||||
return new StopAnswer(cmd, msg, true);
|
||||
}
|
||||
|
||||
String msg = "VM " + cmd.getVmName() + " is already in stopped state";
|
||||
|
||||
@ -4862,10 +4862,15 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
|
||||
return null;
|
||||
}
|
||||
|
||||
public void shutdownVM(final Connection conn, final VM vm, final String vmName) throws XmlRpcException {
|
||||
public void shutdownVM(final Connection conn, final VM vm, final String vmName, final boolean forcedStop) throws XmlRpcException {
|
||||
Task task = null;
|
||||
try {
|
||||
task = vm.cleanShutdownAsync(conn);
|
||||
if (forcedStop) {
|
||||
task = vm.hardShutdownAsync(conn);
|
||||
} else {
|
||||
task = vm.cleanShutdownAsync(conn);
|
||||
}
|
||||
|
||||
try {
|
||||
// poll every 1 seconds , timeout after 10 minutes
|
||||
waitForTask(conn, task, 1000, 10 * 60 * 1000);
|
||||
@ -4878,7 +4883,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
|
||||
throw new CloudRuntimeException("Shutdown VM catch HandleInvalid and VM is not in HALTED state");
|
||||
}
|
||||
} catch (final XenAPIException e) {
|
||||
s_logger.debug("Unable to cleanShutdown VM(" + vmName + ") on host(" + _host.getUuid() + ") due to " + e.toString());
|
||||
s_logger.debug("Unable to shutdown VM(" + vmName + ") with force=" + forcedStop + " on host(" + _host.getUuid() + ") due to " + e.toString());
|
||||
try {
|
||||
VmPowerState state = vm.getPowerState(conn);
|
||||
if (state == VmPowerState.RUNNING) {
|
||||
|
||||
@ -113,7 +113,7 @@ public final class CitrixStopCommandWrapper extends CommandWrapper<StopCommand,
|
||||
s_logger.info("Removed network rules for vm " + command.getVmName());
|
||||
}
|
||||
}
|
||||
citrixResourceBase.shutdownVM(conn, vm, vmName);
|
||||
citrixResourceBase.shutdownVM(conn, vm, vmName, command.isForceStop());
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
final String msg = "Catch exception " + e.getClass().getName() + " when stop VM:" + command.getVmName() + " due to " + e.toString();
|
||||
|
||||
@ -356,7 +356,7 @@ class TestVMLifeCycle(cloudstackTestCase):
|
||||
return
|
||||
|
||||
|
||||
@attr(tags = ["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false", BugId="CLOUDSTACK-6984")
|
||||
@attr(tags = ["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||
def test_01_stop_vm(self):
|
||||
"""Test Stop Virtual Machine
|
||||
"""
|
||||
@ -371,6 +371,40 @@ class TestVMLifeCycle(cloudstackTestCase):
|
||||
self.fail("Failed to stop VM: %s" % e)
|
||||
return
|
||||
|
||||
|
||||
@attr(tags = ["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||
def test_01_stop_vm_forced(self):
|
||||
"""Test Force Stop Virtual Machine
|
||||
"""
|
||||
try:
|
||||
self.small_virtual_machine.stop(self.apiclient, forced=True)
|
||||
except Exception as e:
|
||||
self.fail("Failed to stop VM: %s" % e)
|
||||
|
||||
list_vm_response = VirtualMachine.list(
|
||||
self.apiclient,
|
||||
id=self.small_virtual_machine.id
|
||||
)
|
||||
self.assertEqual(
|
||||
isinstance(list_vm_response, list),
|
||||
True,
|
||||
"Check list response returns a valid list"
|
||||
)
|
||||
|
||||
self.assertNotEqual(
|
||||
len(list_vm_response),
|
||||
0,
|
||||
"Check VM avaliable in List Virtual Machines"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
list_vm_response[0].state,
|
||||
"Stopped",
|
||||
"Check virtual machine is in stopped state"
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
@attr(tags = ["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||
def test_02_start_vm(self):
|
||||
"""Test Start Virtual Machine
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user