Merge pull request #2108 from shapeblue/force-stop-vm-kvm

[4.9+] CLOUDSTACK-9860: Power off VMs when force stop is provided
This commit is contained in:
Rajani Karuturi 2017-06-06 15:30:24 +05:30 committed by GitHub
commit 5f35c15b6b
9 changed files with 77 additions and 18 deletions

View File

@ -54,7 +54,7 @@ public class StopVMCmd extends BaseAsyncCmd {
private Long id; private Long id;
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the VM " @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; private Boolean forced;
// /////////////////////////////////////////////////// // ///////////////////////////////////////////////////

View File

@ -28,6 +28,7 @@ public class StopCommand extends RebootCommand {
private String publicConsoleProxyIpAddress = null; private String publicConsoleProxyIpAddress = null;
private GPUDeviceTO gpuDevice; private GPUDeviceTO gpuDevice;
boolean checkBeforeCleanup = false; boolean checkBeforeCleanup = false;
boolean forceStop = false;
protected StopCommand() { protected StopCommand() {
} }
@ -45,6 +46,12 @@ public class StopCommand extends RebootCommand {
this.checkBeforeCleanup = checkBeforeCleanup; 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) { public StopCommand(String vmName, boolean executeInSequence, boolean checkBeforeCleanup) {
super(vmName, executeInSequence); super(vmName, executeInSequence);
this.checkBeforeCleanup = checkBeforeCleanup; this.checkBeforeCleanup = checkBeforeCleanup;
@ -82,4 +89,8 @@ public class StopCommand extends RebootCommand {
public boolean checkBeforeCleanup() { public boolean checkBeforeCleanup() {
return this.checkBeforeCleanup; return this.checkBeforeCleanup;
} }
public boolean isForceStop() {
return forceStop;
}
} }

View File

@ -1536,7 +1536,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
vmGuru.prepareStop(profile); 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; boolean stopped = false;
Answer answer = null; Answer answer = null;

View File

@ -2703,7 +2703,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
} }
} }
s_logger.debug(vmDef); s_logger.debug(vmDef);
msg = stopVM(conn, vmName); msg = stopVM(conn, vmName, false);
msg = startVM(conn, vmName, vmDef); msg = startVM(conn, vmName, vmDef);
return null; return null;
} catch (final LibvirtException e) { } catch (final LibvirtException e) {
@ -2725,14 +2725,17 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return msg; 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; DomainState state = null;
Domain dm = null; Domain dm = null;
s_logger.debug("Try to stop the vm at first"); s_logger.debug("Try to stop the vm at first");
if (forceStop) {
return stopVMInternal(conn, vmName, true);
}
String ret = stopVM(conn, vmName, false); String ret = stopVM(conn, vmName, false);
if (ret == Script.ERR_TIMEOUT) { if (ret == Script.ERR_TIMEOUT) {
ret = stopVM(conn, vmName, true); ret = stopVMInternal(conn, vmName, true);
} else if (ret != null) { } else if (ret != null) {
/* /*
* There is a race condition between libvirt and qemu: libvirt * 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) { if (state != DomainState.VIR_DOMAIN_SHUTOFF) {
s_logger.debug("Try to destroy the vm"); s_logger.debug("Try to destroy the vm");
ret = stopVM(conn, vmName, true); ret = stopVMInternal(conn, vmName, true);
if (ret != null) { if (ret != null) {
return ret; return ret;
} }
@ -2775,7 +2778,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return null; 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; Domain dm = null;
try { try {
dm = conn.domainLookupByName(vmName); dm = conn.domainLookupByName(vmName);

View File

@ -67,7 +67,8 @@ public final class LibvirtStopCommandWrapper extends CommandWrapper<StopCommand,
final List<InterfaceDef> ifaces = libvirtComputingResource.getInterfaces(conn, vmName); final List<InterfaceDef> ifaces = libvirtComputingResource.getInterfaces(conn, vmName);
libvirtComputingResource.destroyNetworkRulesForVM(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) { if (result == null) {
for (final DiskDef disk : disks) { for (final DiskDef disk : disks) {
libvirtComputingResource.cleanupDisk(disk); libvirtComputingResource.cleanupDisk(disk);

View File

@ -3150,13 +3150,18 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME, cmd.getVmName()); vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME, cmd.getVmName());
if (getVmPowerState(vmMo) != PowerState.PowerOff) { if (getVmPowerState(vmMo) != PowerState.PowerOff) {
if (vmMo.safePowerOff(_shutdownWaitMs)) { String msg = "Stop VM " + cmd.getVmName() + " Succeed";
return new StopAnswer(cmd, "Stop VM " + cmd.getVmName() + " Succeed", true); boolean success = false;
if (cmd.isForceStop()) {
success = vmMo.powerOff();
} else { } else {
String msg = "Have problem in powering off VM " + cmd.getVmName() + ", let the process continue"; success = vmMo.safePowerOff(_shutdownWaitMs);
s_logger.warn(msg);
return new StopAnswer(cmd, msg, true);
} }
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"; String msg = "VM " + cmd.getVmName() + " is already in stopped state";

View File

@ -4862,10 +4862,15 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return null; 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; Task task = null;
try { try {
task = vm.cleanShutdownAsync(conn); if (forcedStop) {
task = vm.hardShutdownAsync(conn);
} else {
task = vm.cleanShutdownAsync(conn);
}
try { try {
// poll every 1 seconds , timeout after 10 minutes // poll every 1 seconds , timeout after 10 minutes
waitForTask(conn, task, 1000, 10 * 60 * 1000); 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"); throw new CloudRuntimeException("Shutdown VM catch HandleInvalid and VM is not in HALTED state");
} }
} catch (final XenAPIException e) { } 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 { try {
VmPowerState state = vm.getPowerState(conn); VmPowerState state = vm.getPowerState(conn);
if (state == VmPowerState.RUNNING) { if (state == VmPowerState.RUNNING) {

View File

@ -113,7 +113,7 @@ public final class CitrixStopCommandWrapper extends CommandWrapper<StopCommand,
s_logger.info("Removed network rules for vm " + command.getVmName()); 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) { } catch (final Exception e) {
final String msg = "Catch exception " + e.getClass().getName() + " when stop VM:" + command.getVmName() + " due to " + e.toString(); final String msg = "Catch exception " + e.getClass().getName() + " when stop VM:" + command.getVmName() + " due to " + e.toString();

View File

@ -356,7 +356,7 @@ class TestVMLifeCycle(cloudstackTestCase):
return 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): def test_01_stop_vm(self):
"""Test Stop Virtual Machine """Test Stop Virtual Machine
""" """
@ -371,6 +371,40 @@ class TestVMLifeCycle(cloudstackTestCase):
self.fail("Failed to stop VM: %s" % e) self.fail("Failed to stop VM: %s" % e)
return 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") @attr(tags = ["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_02_start_vm(self): def test_02_start_vm(self):
"""Test Start Virtual Machine """Test Start Virtual Machine