From 6b80c79986cf5012ea951bb88040a747dd8f2556 Mon Sep 17 00:00:00 2001 From: wilderrodrigues Date: Tue, 24 Mar 2015 11:15:37 +0100 Subject: [PATCH] Refactoring the StopCommand on CitrixResourceBase to cope with new design - Added basic tests --- .../resource/CitrixResourceBase.java | 18 +- .../wrapper/CitrixRequestWrapper.java | 2 + .../resource/wrapper/StopCommandWrapper.java | 173 ++++++++++++++++++ .../wrapper/CitrixRequestWrapperTest.java | 13 ++ 4 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/StopCommandWrapper.java diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java index c7405fc942f..7c80f645c8b 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java @@ -339,6 +339,10 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return _host; } + public boolean canBridgeFirewall() { + return _canBridgeFirewall; + } + private static boolean isAlienVm(final VM vm, final Connection conn) throws XenAPIException, XmlRpcException { // TODO : we need a better way to tell whether or not the VM belongs to CloudStack final String vmName = vm.getNameLabel(conn); @@ -374,7 +378,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return success; } - protected boolean isRefNull(final XenAPIObject object) { + public boolean isRefNull(final XenAPIObject object) { return object == null || object.toWireString().equals("OpaqueRef:NULL") || object.toWireString().equals(""); } @@ -1311,7 +1315,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } - protected HashMap> getGPUGroupDetails(final Connection conn) throws XenAPIException, XmlRpcException { + public HashMap> getGPUGroupDetails(final Connection conn) throws XenAPIException, XmlRpcException { return null; } @@ -2877,7 +2881,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } - protected void removeSR(final Connection conn, final SR sr) { + public void removeSR(final Connection conn, final SR sr) { if (sr == null) { return; } @@ -3272,7 +3276,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } - 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) throws XmlRpcException { Task task = null; try { task = vm.cleanShutdownAsync(conn); @@ -3672,7 +3676,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe throw new CloudRuntimeException("Could not find an available slot in VM with name to attach a new disk."); } - protected String callHostPlugin(final Connection conn, final String plugin, final String cmd, final String... params) { + public String callHostPlugin(final Connection conn, final String plugin, final String cmd, final String... params) { final Map args = new HashMap(); String msg; try { @@ -4056,7 +4060,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return vlanNetwork; } - protected void disableVlanNetwork(final Connection conn, final Network network) { + public void disableVlanNetwork(final Connection conn, final Network network) { } protected SR getLocalLVMSR(final Connection conn) { @@ -5736,7 +5740,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } - protected SR getISOSRbyVmName(final Connection conn, final String vmName) { + public SR getISOSRbyVmName(final Connection conn, final String vmName) { try { final Set srs = SR.getByNameLabel(conn, vmName + "-ISO"); if (srs.size() == 0) { diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/CitrixRequestWrapper.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/CitrixRequestWrapper.java index b3996c19e06..5eb3882f627 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/CitrixRequestWrapper.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/CitrixRequestWrapper.java @@ -29,6 +29,7 @@ import com.cloud.agent.api.GetVmDiskStatsCommand; import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.RebootRouterCommand; +import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand; import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand; import com.cloud.agent.api.storage.CreateCommand; @@ -63,6 +64,7 @@ public class CitrixRequestWrapper extends RequestWrapper { map.put(GetVmStatsCommand.class, new GetVmStatsCommandWrapper()); map.put(GetVmDiskStatsCommand.class, new GetVmDiskStatsCommandWrapper()); map.put(CheckHealthCommand.class, new CheckHealthCommandWrapper()); + map.put(StopCommand.class, new StopCommandWrapper()); } public static CitrixRequestWrapper getInstance() { diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/StopCommandWrapper.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/StopCommandWrapper.java new file mode 100644 index 00000000000..bca90b54661 --- /dev/null +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/StopCommandWrapper.java @@ -0,0 +1,173 @@ +// +// 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.hypervisor.xenserver.resource.wrapper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.VgpuTypesInfo; +import com.cloud.agent.api.to.GPUDeviceTO; +import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase; +import com.cloud.resource.CommandWrapper; +import com.cloud.utils.StringUtils; +import com.xensource.xenapi.Connection; +import com.xensource.xenapi.Network; +import com.xensource.xenapi.SR; +import com.xensource.xenapi.Types.VmPowerState; +import com.xensource.xenapi.Types.XenAPIException; +import com.xensource.xenapi.VGPU; +import com.xensource.xenapi.VIF; +import com.xensource.xenapi.VM; + +public final class StopCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(StopCommandWrapper.class); + + @Override + public Answer execute(final StopCommand command, final CitrixResourceBase citrixResourceBase) { + final String vmName = command.getVmName(); + String platformstring = null; + try { + final Connection conn = citrixResourceBase.getConnection(); + final Set vms = VM.getByNameLabel(conn, vmName); + // stop vm which is running on this host or is in halted state + final Iterator iter = vms.iterator(); + while (iter.hasNext()) { + final VM vm = iter.next(); + final VM.Record vmr = vm.getRecord(conn); + if (vmr.powerState != VmPowerState.RUNNING) { + continue; + } + if (citrixResourceBase.isRefNull(vmr.residentOn)) { + continue; + } + if (vmr.residentOn.getUuid(conn).equals(citrixResourceBase.getHost().getUuid())) { + continue; + } + iter.remove(); + } + + if (vms.size() == 0) { + return new StopAnswer(command, "VM does not exist", true); + } + for (final VM vm : vms) { + final VM.Record vmr = vm.getRecord(conn); + platformstring = StringUtils.mapToString(vmr.platform); + if (vmr.isControlDomain) { + final String msg = "Tring to Shutdown control domain"; + s_logger.warn(msg); + return new StopAnswer(command, msg, false); + } + + if (vmr.powerState == VmPowerState.RUNNING && !citrixResourceBase.isRefNull(vmr.residentOn) && !vmr.residentOn.getUuid(conn).equals(citrixResourceBase.getHost().getUuid())) { + final String msg = "Stop Vm " + vmName + " failed due to this vm is not running on this host: " + citrixResourceBase.getHost().getUuid() + " but host:" + vmr.residentOn.getUuid(conn); + s_logger.warn(msg); + return new StopAnswer(command, msg, platformstring, false); + } + + if (command.checkBeforeCleanup() && vmr.powerState == VmPowerState.RUNNING) { + final String msg = "Vm " + vmName + " is running on host and checkBeforeCleanup flag is set, so bailing out"; + s_logger.debug(msg); + return new StopAnswer(command, msg, false); + } + + s_logger.debug("9. The VM " + vmName + " is in Stopping state"); + + try { + if (vmr.powerState == VmPowerState.RUNNING) { + /* when stop a vm, set affinity to current xenserver */ + vm.setAffinity(conn, vm.getResidentOn(conn)); + + if (citrixResourceBase.canBridgeFirewall()) { + final String result = citrixResourceBase.callHostPlugin(conn, "vmops", "destroy_network_rules_for_vm", "vmName", command.getVmName()); + if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { + s_logger.warn("Failed to remove network rules for vm " + command.getVmName()); + } else { + s_logger.info("Removed network rules for vm " + command.getVmName()); + } + } + citrixResourceBase.shutdownVM(conn, vm, vmName); + } + } catch (final Exception e) { + final String msg = "Catch exception " + e.getClass().getName() + " when stop VM:" + command.getVmName() + " due to " + e.toString(); + s_logger.debug(msg); + return new StopAnswer(command, msg, platformstring, false); + } finally { + + try { + if (vm.getPowerState(conn) == VmPowerState.HALTED) { + Set vGPUs = null; + // Get updated GPU details + try { + vGPUs = vm.getVGPUs(conn); + } catch (final XenAPIException e2) { + s_logger.debug("VM " + vmName + " does not have GPU support."); + } + if (vGPUs != null && !vGPUs.isEmpty()) { + final HashMap> groupDetails = citrixResourceBase.getGPUGroupDetails(conn); + command.setGpuDevice(new GPUDeviceTO(null, null, groupDetails)); + } + + final Set vifs = vm.getVIFs(conn); + final List networks = new ArrayList(); + for (final VIF vif : vifs) { + networks.add(vif.getNetwork(conn)); + } + vm.destroy(conn); + final SR sr = citrixResourceBase.getISOSRbyVmName(conn, command.getVmName()); + citrixResourceBase.removeSR(conn, sr); + // Disable any VLAN networks that aren't used + // anymore + for (final Network network : networks) { + try { + if (network.getNameLabel(conn).startsWith("VLAN")) { + citrixResourceBase.disableVlanNetwork(conn, network); + } + } catch (final Exception e) { + // network might be destroyed by other host + } + } + return new StopAnswer(command, "Stop VM " + vmName + " Succeed", platformstring, true); + } + } catch (final Exception e) { + final String msg = "VM destroy failed in Stop " + vmName + " Command due to " + e.getMessage(); + s_logger.warn(msg, e); + } finally { + s_logger.debug("10. The VM " + vmName + " is in Stopped state"); + } + } + } + + } catch (final Exception e) { + final String msg = "Stop Vm " + vmName + " fail due to " + e.toString(); + s_logger.warn(msg, e); + return new StopAnswer(command, msg, platformstring, false); + } + return new StopAnswer(command, "Stop VM failed", platformstring, false); + } +} \ No newline at end of file diff --git a/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/CitrixRequestWrapperTest.java b/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/CitrixRequestWrapperTest.java index 69071c0e074..74f7cdc091a 100644 --- a/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/CitrixRequestWrapperTest.java +++ b/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/CitrixRequestWrapperTest.java @@ -26,6 +26,7 @@ import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.RebootRouterCommand; +import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand; import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand; import com.cloud.agent.api.storage.CreateAnswer; @@ -177,6 +178,18 @@ public class CitrixRequestWrapperTest { assertFalse(answer.getResult()); } + + @Test + public void testStopCommandCommand() { + final StopCommand statsCommand = new StopCommand("Test", false, false); + + final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance(); + assertNotNull(wrapper); + + final Answer answer = wrapper.execute(statsCommand, citrixResourceBase); + + assertFalse(answer.getResult()); + } } class NotAValidCommand extends Command {