From 4746c8c72606546ab4f17bb6ba397805d9998fbd Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 1 Sep 2020 10:24:48 +0200 Subject: [PATCH] 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. --- .../com/cloud/vm/VirtualMachineManager.java | 2 + .../cloud/vm/VirtualMachineManagerImpl.java | 112 ++++++++++++++++++ .../com/cloud/vm/VmWorkUpdateDefaultNic.java | 39 ++++++ .../java/com/cloud/vm/UserVmManagerImpl.java | 12 +- 4 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateDefaultNic.java diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index 50d78f43033..dc54f543c32 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -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 diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 8f722c92fde..7ffd023383c 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -5741,4 +5741,116 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return new Pair(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 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 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 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 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.SUCCEEDED, + _jobMgr.marshallResultObject(result)); + } + } diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateDefaultNic.java b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateDefaultNic.java new file mode 100644 index 00000000000..14f32399449 --- /dev/null +++ b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateDefaultNic.java @@ -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; + } +} diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 56eab1a065f..802ac1e0dd7 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -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);