server: move UpdateDefaultNic to vm work job queue (#4020)

While remove secondary nic from a Running vm, if update the default nic to the secondary nic before the nic is removed, the vm will not have default nic (and cannot be started) when both operations are completed.

It is because UpdateDefaultNic api is not handled as a vm work job (AddNicToVMCmd and RemoveNicFromVMCmd are), it is processed before nic is removed. The result is that secondary nic becomes default nic and got removed.
This commit is contained in:
Wei Zhou 2020-09-01 10:24:48 +02:00 committed by GitHub
parent 749e302e0e
commit 4746c8c726
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 156 additions and 9 deletions

View File

@ -187,6 +187,8 @@ public interface VirtualMachineManager extends Manager {
*/
boolean removeNicFromVm(VirtualMachine vm, Nic nic) throws ConcurrentOperationException, ResourceUnavailableException;
Boolean updateDefaultNicForVM(VirtualMachine vm, Nic nic, Nic defaultNic);
/**
* @param vm
* @param network

View File

@ -5741,4 +5741,116 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(passwordMap));
}
@Override
public Boolean updateDefaultNicForVM(final VirtualMachine vm, final Nic nic, final Nic defaultNic) {
final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
VmWorkJobVO placeHolder = null;
placeHolder = createPlaceHolderWork(vm.getId());
try {
return orchestrateUpdateDefaultNicForVM(vm, nic, defaultNic);
} finally {
if (placeHolder != null) {
_workJobDao.expunge(placeHolder.getId());
}
}
} else {
final Outcome<VirtualMachine> outcome = updateDefaultNicForVMThroughJobQueue(vm, nic, defaultNic);
try {
outcome.get();
} catch (final InterruptedException e) {
throw new RuntimeException("Operation is interrupted", e);
} catch (final java.util.concurrent.ExecutionException e) {
throw new RuntimeException("Execution exception", e);
}
final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
if (jobResult != null) {
if (jobResult instanceof Boolean) {
return (Boolean)jobResult;
}
}
throw new RuntimeException("Unexpected job execution result");
}
}
private Boolean orchestrateUpdateDefaultNicForVM(final VirtualMachine vm, final Nic nic, final Nic defaultNic) {
s_logger.debug("Updating default nic of vm " + vm + " from nic " + defaultNic.getUuid() + " to nic " + nic.getUuid());
Integer chosenID = nic.getDeviceId();
Integer existingID = defaultNic.getDeviceId();
NicVO nicVO = _nicsDao.findById(nic.getId());
NicVO defaultNicVO = _nicsDao.findById(defaultNic.getId());
nicVO.setDefaultNic(true);
nicVO.setDeviceId(existingID);
defaultNicVO.setDefaultNic(false);
defaultNicVO.setDeviceId(chosenID);
_nicsDao.persist(nicVO);
_nicsDao.persist(defaultNicVO);
return true;
}
public Outcome<VirtualMachine> updateDefaultNicForVMThroughJobQueue(final VirtualMachine vm, final Nic nic, final Nic defaultNic) {
final CallContext context = CallContext.current();
final User user = context.getCallingUser();
final Account account = context.getCallingAccount();
final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
VirtualMachine.Type.Instance, vm.getId(),
VmWorkUpdateDefaultNic.class.getName());
VmWorkJobVO workJob = null;
if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
assert pendingWorkJobs.size() == 1;
workJob = pendingWorkJobs.get(0);
} else {
workJob = new VmWorkJobVO(context.getContextId());
workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
workJob.setCmd(VmWorkUpdateDefaultNic.class.getName());
workJob.setAccountId(account.getId());
workJob.setUserId(user.getId());
workJob.setVmType(VirtualMachine.Type.Instance);
workJob.setVmInstanceId(vm.getId());
workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
final VmWorkUpdateDefaultNic workInfo = new VmWorkUpdateDefaultNic(user.getId(), account.getId(), vm.getId(),
VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, nic.getId(), defaultNic.getId());
workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
_jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
}
AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
return new VmJobVirtualMachineOutcome(workJob, vm.getId());
}
@ReflectionUse
private Pair<JobInfo.Status, String> orchestrateUpdateDefaultNic(final VmWorkUpdateDefaultNic work) throws Exception {
final VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId());
if (vm == null) {
s_logger.info("Unable to find vm " + work.getVmId());
}
assert vm != null;
final NicVO nic = _entityMgr.findById(NicVO.class, work.getNicId());
if (nic == null) {
throw new CloudRuntimeException("Unable to find nic " + work.getNicId());
}
final NicVO defaultNic = _entityMgr.findById(NicVO.class, work.getDefaultNicId());
if (defaultNic == null) {
throw new CloudRuntimeException("Unable to find default nic " + work.getDefaultNicId());
}
final boolean result = orchestrateUpdateDefaultNicForVM(vm, nic, defaultNic);
return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED,
_jobMgr.marshallResultObject(result));
}
}

View File

@ -0,0 +1,39 @@
// 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 com.cloud.vm;
public class VmWorkUpdateDefaultNic extends VmWork {
private static final long serialVersionUID = -4265657031064437934L;
Long nicId;
Long defaultNicId;
public VmWorkUpdateDefaultNic(long userId, long accountId, long vmId, String handlerName, Long nicId, Long defaultNicId) {
super(userId, accountId, vmId, handlerName);
this.nicId = nicId;
this.defaultNicId = defaultNicId;
}
public Long getNicId() {
return nicId;
}
public Long getDefaultNicId() {
return defaultNicId;
}
}

View File

@ -1477,16 +1477,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Integer chosenID = nic.getDeviceId();
Integer existingID = existing.getDeviceId();
nic.setDefaultNic(true);
nic.setDeviceId(existingID);
existingVO.setDefaultNic(false);
existingVO.setDeviceId(chosenID);
nic = _nicDao.persist(nic);
existingVO = _nicDao.persist(existingVO);
Network newdefault = null;
newdefault = _networkModel.getDefaultNetworkForVm(vmId);
if (_itMgr.updateDefaultNicForVM(vmInstance, nic, existingVO)) {
newdefault = _networkModel.getDefaultNetworkForVm(vmId);
}
if (newdefault == null) {
nic.setDefaultNic(false);