Skip to content

Commit 81dfcbb

Browse files
server: Use ACPI event to reboot VM on KVM, and Use 'forced' reboot option to stop and start the VM(s) (#4681)
* Updated libvirt's native reboot operation for VM on KVM using ACPI event, and Added 'forced' reboot option to stop and start the VM (using rebootVirtualMachine API) * Added 'forced' reboot option for System VM and Router - New parameter 'forced' in rebootSystemVm API, to stop and then start System VM - New parameter 'forced' in rebootRouter API, to force stop and then start Router * Added force reboot tests for User VM, System VM and Router
1 parent 01d7b0e commit 81dfcbb

File tree

17 files changed

+303
-53
lines changed

17 files changed

+303
-53
lines changed

api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public interface VirtualNetworkApplianceService {
4444
* the command specifying router's id
4545
* @return router if successful
4646
*/
47-
VirtualRouter rebootRouter(long routerId, boolean reprogramNetwork) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
47+
VirtualRouter rebootRouter(long routerId, boolean reprogramNetwork, boolean forced) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
4848

4949
VirtualRouter upgradeRouter(UpgradeRouterCmd cmd);
5050

api/src/main/java/org/apache/cloudstack/api/command/admin/router/RebootRouterCmd.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ public class RebootRouterCmd extends BaseAsyncCmd {
4949
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DomainRouterResponse.class, required = true, description = "the ID of the router")
5050
private Long id;
5151

52+
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force reboot the router (Router is force Stopped and then Started)", since = "4.16.0")
53+
private Boolean forced;
54+
5255
/////////////////////////////////////////////////////
5356
/////////////////// Accessors ///////////////////////
5457
/////////////////////////////////////////////////////
@@ -96,10 +99,14 @@ public Long getInstanceId() {
9699
return getId();
97100
}
98101

102+
public boolean isForced() {
103+
return (forced != null) ? forced : false;
104+
}
105+
99106
@Override
100107
public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
101108
CallContext.current().setEventDetails("Router Id: " + this._uuidMgr.getUuid(VirtualMachine.class,getId()));
102-
VirtualRouter result = _routerService.rebootRouter(getId(), true);
109+
VirtualRouter result = _routerService.rebootRouter(getId(), true, isForced());
103110
if (result != null) {
104111
DomainRouterResponse response = _responseGenerator.createDomainRouterResponse(result);
105112
response.setResponseName("router");

api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/RebootSystemVmCmd.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public class RebootSystemVmCmd extends BaseAsyncCmd {
5252
description = "The ID of the system virtual machine")
5353
private Long id;
5454

55+
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force reboot the system VM (System VM is Stopped and then Started)", since = "4.16.0")
56+
private Boolean forced;
57+
5558
/////////////////////////////////////////////////////
5659
/////////////////// Accessors ///////////////////////
5760
/////////////////////////////////////////////////////
@@ -104,6 +107,10 @@ public Long getInstanceId() {
104107
return getId();
105108
}
106109

110+
public boolean isForced() {
111+
return (forced != null) ? forced : false;
112+
}
113+
107114
@Override
108115
public void execute() {
109116
CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId()));

api/src/main/java/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public class RebootVMCmd extends BaseAsyncCmd implements UserCmd {
5353
required=true, description="The ID of the virtual machine")
5454
private Long id;
5555

56+
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force reboot the VM (VM is Stopped and then Started)", since = "4.16.0")
57+
private Boolean forced;
58+
5659
@Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup menu or not", since = "4.15.0.0")
5760
private Boolean bootIntoSetup;
5861

@@ -64,6 +67,10 @@ public Long getId() {
6467
return id;
6568
}
6669

70+
public boolean isForced() {
71+
return (forced != null) ? forced : false;
72+
}
73+
6774
public Boolean getBootIntoSetup() {
6875
return bootIntoSetup;
6976
}

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3458,7 +3458,6 @@ private void orchestrateReboot(final String vmUuid, final Map<VirtualMachineProf
34583458
final DeployDestination dest = new DeployDestination(dc, pod, cluster, host);
34593459

34603460
try {
3461-
34623461
final Commands cmds = new Commands(Command.OnError.Stop);
34633462
RebootCommand rebootCmd = new RebootCommand(vm.getInstanceName(), getExecuteInSequence(vm.getHypervisorType()));
34643463
VirtualMachineTO vmTo = getVmTO(vm.getId());
@@ -3476,7 +3475,10 @@ private void orchestrateReboot(final String vmUuid, final Map<VirtualMachineProf
34763475
}
34773476
return;
34783477
}
3479-
s_logger.info("Unable to reboot VM " + vm + " on " + dest.getHost() + " due to " + (rebootAnswer == null ? " no reboot answer" : rebootAnswer.getDetails()));
3478+
3479+
String errorMsg = "Unable to reboot VM " + vm + " on " + dest.getHost() + " due to " + (rebootAnswer == null ? "no reboot response" : rebootAnswer.getDetails());
3480+
s_logger.info(errorMsg);
3481+
throw new CloudRuntimeException(errorMsg);
34803482
} catch (final OperationTimedoutException e) {
34813483
s_logger.warn("Unable to send the reboot command to host " + dest.getHost() + " for the vm " + vm + " due to operation timeout", e);
34823484
throw new CloudRuntimeException("Failed to reboot the vm on host " + dest.getHost());

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@
134134
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestResourceDef;
135135
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InputDef;
136136
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
137-
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.GuestNetType;
138137
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
139138
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
140139
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef;
@@ -3228,35 +3227,15 @@ public String rebootVM(final Connect conn, final String vmName) throws LibvirtEx
32283227
String msg = null;
32293228
try {
32303229
dm = conn.domainLookupByName(vmName);
3231-
// Get XML Dump including the secure information such as VNC password
3232-
// By passing 1, or VIR_DOMAIN_XML_SECURE flag
3233-
// https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainXMLFlags
3234-
String vmDef = dm.getXMLDesc(1);
3235-
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
3236-
parser.parseDomainXML(vmDef);
3237-
for (final InterfaceDef nic : parser.getInterfaces()) {
3238-
if (nic.getNetType() == GuestNetType.BRIDGE && nic.getBrName().startsWith("cloudVirBr")) {
3239-
try {
3240-
final int vnetId = Integer.parseInt(nic.getBrName().replaceFirst("cloudVirBr", ""));
3241-
final String pifName = getPif(_guestBridgeName);
3242-
final String newBrName = "br" + pifName + "-" + vnetId;
3243-
vmDef = vmDef.replace("'" + nic.getBrName() + "'", "'" + newBrName + "'");
3244-
s_logger.debug("VM bridge name is changed from " + nic.getBrName() + " to " + newBrName);
3245-
} catch (final NumberFormatException e) {
3246-
continue;
3247-
}
3248-
}
3249-
}
3250-
s_logger.debug(vmDef);
3251-
msg = stopVM(conn, vmName, false);
3252-
msg = startVM(conn, vmName, vmDef);
3230+
// Perform ACPI based reboot
3231+
// https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainReboot
3232+
// https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainRebootFlagValues
3233+
// Send ACPI event to Reboot
3234+
dm.reboot(0x1);
32533235
return null;
32543236
} catch (final LibvirtException e) {
32553237
s_logger.warn("Failed to create vm", e);
32563238
msg = e.getMessage();
3257-
} catch (final InternalErrorException e) {
3258-
s_logger.warn("Failed to create vm", e);
3259-
msg = e.getMessage();
32603239
} finally {
32613240
try {
32623241
if (dm != null) {

server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) {
510510

511511
@Override
512512
@ActionEvent(eventType = EventTypes.EVENT_ROUTER_REBOOT, eventDescription = "rebooting router Vm", async = true)
513-
public VirtualRouter rebootRouter(final long routerId, final boolean reprogramNetwork) throws ConcurrentOperationException, ResourceUnavailableException,
513+
public VirtualRouter rebootRouter(final long routerId, final boolean reprogramNetwork, final boolean forced) throws ConcurrentOperationException, ResourceUnavailableException,
514514
InsufficientCapacityException {
515515
final Account caller = CallContext.current().getCallingAccount();
516516

@@ -531,7 +531,7 @@ public VirtualRouter rebootRouter(final long routerId, final boolean reprogramNe
531531
final UserVO user = _userDao.findById(CallContext.current().getCallingUserId());
532532
s_logger.debug("Stopping and starting router " + router + " as a part of router reboot");
533533

534-
if (stop(router, false, user, caller) != null) {
534+
if (stop(router, forced, user, caller) != null) {
535535
return startRouter(routerId, reprogramNetwork);
536536
} else {
537537
throw new CloudRuntimeException("Failed to reboot router " + router);
@@ -1048,7 +1048,7 @@ protected void recoverRedundantNetwork(final DomainRouterVO masterRouter, final
10481048
}
10491049
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER, backupRouter.getDataCenterId(), backupRouter.getPodIdToDeployIn(), title, title);
10501050
try {
1051-
rebootRouter(backupRouter.getId(), true);
1051+
rebootRouter(backupRouter.getId(), true, false);
10521052
} catch (final ConcurrentOperationException e) {
10531053
s_logger.warn("Fail to reboot " + backupRouter.getInstanceName(), e);
10541054
} catch (final ResourceUnavailableException e) {

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2502,7 +2502,6 @@ private ConsoleProxyVO startConsoleProxy(final long instanceId) {
25022502
}
25032503

25042504
private ConsoleProxyVO stopConsoleProxy(final VMInstanceVO systemVm, final boolean isForced) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException {
2505-
25062505
_itMgr.advanceStop(systemVm.getUuid(), isForced);
25072506
return _consoleProxyDao.findById(systemVm.getId());
25082507
}
@@ -2512,6 +2511,11 @@ private ConsoleProxyVO rebootConsoleProxy(final long instanceId) {
25122511
return _consoleProxyDao.findById(instanceId);
25132512
}
25142513

2514+
private ConsoleProxyVO forceRebootConsoleProxy(final VMInstanceVO systemVm) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException {
2515+
_itMgr.advanceStop(systemVm.getUuid(), false);
2516+
return _consoleProxyMgr.startProxy(systemVm.getId(), true);
2517+
}
2518+
25152519
protected ConsoleProxyVO destroyConsoleProxy(final long instanceId) {
25162520
final ConsoleProxyVO proxy = _consoleProxyDao.findById(instanceId);
25172521

@@ -3401,6 +3405,11 @@ public SecondaryStorageVmVO rebootSecondaryStorageVm(final long instanceId) {
34013405
return _secStorageVmDao.findById(instanceId);
34023406
}
34033407

3408+
private SecondaryStorageVmVO forceRebootSecondaryStorageVm(final VMInstanceVO systemVm) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException {
3409+
_itMgr.advanceStop(systemVm.getUuid(), false);
3410+
return _secStorageVmMgr.startSecStorageVm(systemVm.getId());
3411+
}
3412+
34043413
protected SecondaryStorageVmVO destroySecondaryStorageVm(final long instanceId) {
34053414
final SecondaryStorageVmVO secStorageVm = _secStorageVmDao.findById(instanceId);
34063415
cleanupDownloadUrlsInZone(secStorageVm.getDataCenterId());
@@ -3570,12 +3579,24 @@ public VMInstanceVO rebootSystemVM(final RebootSystemVmCmd cmd) {
35703579
throw ex;
35713580
}
35723581

3573-
if (systemVm.getType().equals(VirtualMachine.Type.ConsoleProxy)) {
3574-
ActionEventUtils.startNestedActionEvent(EventTypes.EVENT_PROXY_REBOOT, "rebooting console proxy Vm");
3575-
return rebootConsoleProxy(cmd.getId());
3576-
} else {
3577-
ActionEventUtils.startNestedActionEvent(EventTypes.EVENT_SSVM_REBOOT, "rebooting secondary storage Vm");
3578-
return rebootSecondaryStorageVm(cmd.getId());
3582+
try {
3583+
if (systemVm.getType().equals(VirtualMachine.Type.ConsoleProxy)) {
3584+
ActionEventUtils.startNestedActionEvent(EventTypes.EVENT_PROXY_REBOOT, "rebooting console proxy Vm");
3585+
if (cmd.isForced()) {
3586+
return forceRebootConsoleProxy(systemVm);
3587+
}
3588+
return rebootConsoleProxy(cmd.getId());
3589+
} else {
3590+
ActionEventUtils.startNestedActionEvent(EventTypes.EVENT_SSVM_REBOOT, "rebooting secondary storage Vm");
3591+
if (cmd.isForced()) {
3592+
return forceRebootSecondaryStorageVm(systemVm);
3593+
}
3594+
return rebootSecondaryStorageVm(cmd.getId());
3595+
}
3596+
} catch (final ResourceUnavailableException e) {
3597+
throw new CloudRuntimeException("Unable to reboot " + systemVm, e);
3598+
} catch (final OperationTimedoutException e) {
3599+
throw new CloudRuntimeException("Operation timed out - Unable to reboot " + systemVm, e);
35793600
}
35803601
}
35813602

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,7 @@ private boolean resetVMPasswordInternal(Long vmId, String password) throws Resou
800800
return true;
801801
}
802802

803-
if (rebootVirtualMachine(userId, vmId, false) == null) {
803+
if (rebootVirtualMachine(userId, vmId, false, false) == null) {
804804
s_logger.warn("Failed to reboot the vm " + vmInstance);
805805
return false;
806806
} else {
@@ -911,7 +911,7 @@ private boolean resetVMSSHKeyInternal(Long vmId, String sshPublicKey, String pas
911911
s_logger.debug("Vm " + vmInstance + " is stopped, not rebooting it as a part of SSH Key reset");
912912
return true;
913913
}
914-
if (rebootVirtualMachine(userId, vmId, false) == null) {
914+
if (rebootVirtualMachine(userId, vmId, false, false) == null) {
915915
s_logger.warn("Failed to reboot the vm " + vmInstance);
916916
return false;
917917
} else {
@@ -948,7 +948,7 @@ public boolean stopVirtualMachine(long userId, long vmId) {
948948
return status;
949949
}
950950

951-
private UserVm rebootVirtualMachine(long userId, long vmId, boolean enterSetup) throws InsufficientCapacityException, ResourceUnavailableException {
951+
private UserVm rebootVirtualMachine(long userId, long vmId, boolean enterSetup, boolean forced) throws InsufficientCapacityException, ResourceUnavailableException {
952952
UserVmVO vm = _vmDao.findById(vmId);
953953

954954
if (s_logger.isTraceEnabled()) {
@@ -963,6 +963,15 @@ private UserVm rebootVirtualMachine(long userId, long vmId, boolean enterSetup)
963963
if (vm.getState() == State.Running && vm.getHostId() != null) {
964964
collectVmDiskStatistics(vm);
965965
collectVmNetworkStatistics(vm);
966+
967+
if (forced) {
968+
Host vmOnHost = _hostDao.findById(vm.getHostId());
969+
if (vmOnHost == null || vmOnHost.getResourceState() != ResourceState.Enabled || vmOnHost.getStatus() != Status.Up ) {
970+
throw new CloudRuntimeException("Unable to force reboot the VM as the host: " + vm.getHostId() + " is not in the right state");
971+
}
972+
return forceRebootVirtualMachine(vmId, vm.getHostId(), enterSetup);
973+
}
974+
966975
DataCenterVO dc = _dcDao.findById(vm.getDataCenterId());
967976
try {
968977
if (dc.getNetworkType() == DataCenter.NetworkType.Advanced) {
@@ -986,7 +995,7 @@ private UserVm rebootVirtualMachine(long userId, long vmId, boolean enterSetup)
986995
throw new CloudRuntimeException("Concurrent operations on starting router. " + e);
987996
} catch (Exception ex){
988997
throw new CloudRuntimeException("Router start failed due to" + ex);
989-
}finally {
998+
} finally {
990999
if (s_logger.isInfoEnabled()) {
9911000
s_logger.info(String.format("Rebooting vm %s%s.", vm.getInstanceName(), enterSetup? " entering hardware setup menu" : " as is"));
9921001
}
@@ -1007,6 +1016,24 @@ private UserVm rebootVirtualMachine(long userId, long vmId, boolean enterSetup)
10071016
}
10081017
}
10091018

1019+
private UserVm forceRebootVirtualMachine(long vmId, long hostId, boolean enterSetup) {
1020+
try {
1021+
if (stopVirtualMachine(vmId, false) != null) {
1022+
Map<VirtualMachineProfile.Param,Object> params = null;
1023+
if (enterSetup) {
1024+
params = new HashMap();
1025+
params.put(VirtualMachineProfile.Param.BootIntoSetup, Boolean.TRUE);
1026+
}
1027+
return startVirtualMachine(vmId, null, null, hostId, params, null).first();
1028+
}
1029+
} catch (ResourceUnavailableException e) {
1030+
throw new CloudRuntimeException("Unable to reboot the VM: " + vmId, e);
1031+
} catch (CloudException e) {
1032+
throw new CloudRuntimeException("Unable to reboot the VM: " + vmId, e);
1033+
}
1034+
return null;
1035+
}
1036+
10101037
@Override
10111038
@ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "upgrading Vm")
10121039
/*
@@ -2894,7 +2921,7 @@ public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityE
28942921
// Verify input parameters
28952922
UserVmVO vmInstance = _vmDao.findById(vmId);
28962923
if (vmInstance == null) {
2897-
throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
2924+
throw new InvalidParameterValueException("Unable to find a virtual machine with id " + vmId);
28982925
}
28992926

29002927
_accountMgr.checkAccess(caller, null, true, vmInstance);
@@ -2916,7 +2943,8 @@ public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityE
29162943
if (enterSetup != null && enterSetup && !HypervisorType.VMware.equals(vmInstance.getHypervisorType())) {
29172944
throw new InvalidParameterValueException("Booting into a hardware setup menu is not implemented on " + vmInstance.getHypervisorType());
29182945
}
2919-
UserVm userVm = rebootVirtualMachine(CallContext.current().getCallingUserId(), vmId, enterSetup == null ? false : cmd.getBootIntoSetup());
2946+
2947+
UserVm userVm = rebootVirtualMachine(CallContext.current().getCallingUserId(), vmId, enterSetup == null ? false : cmd.getBootIntoSetup(), cmd.isForced());
29202948
if (userVm != null ) {
29212949
// update the vmIdCountMap if the vm is in advanced shared network with out services
29222950
final List<NicVO> nics = _nicDao.listByVmId(vmId);

server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public VirtualRouter startRouter(final long routerId, final boolean reprogramNet
110110
* @see com.cloud.network.VirtualNetworkApplianceService#rebootRouter(long, boolean)
111111
*/
112112
@Override
113-
public VirtualRouter rebootRouter(final long routerId, final boolean reprogramNetwork) throws ConcurrentOperationException, ResourceUnavailableException {
113+
public VirtualRouter rebootRouter(final long routerId, final boolean reprogramNetwork, final boolean forced) throws ConcurrentOperationException, ResourceUnavailableException {
114114
// TODO Auto-generated method stub
115115
return null;
116116
}

0 commit comments

Comments
 (0)