From 6e7ad8830feface29051f48c5d6350ed58b3993e Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Thu, 11 Aug 2011 10:25:10 -0700 Subject: [PATCH] bug 11043: Propagating fix --- .../exception/CloudExecutionException.java | 63 +++++++++ api/src/com/cloud/exception/ErrorCode.java | 54 ++++++++ .../com/cloud/network/guru/NetworkGuru.java | 51 ++++--- .../cloud/vm/VirtualMachineManagerImpl.java | 124 ++++++++---------- .../src/com/cloud/utils/SerialVersionUID.java | 3 +- 5 files changed, 209 insertions(+), 86 deletions(-) create mode 100755 api/src/com/cloud/exception/CloudExecutionException.java create mode 100755 api/src/com/cloud/exception/ErrorCode.java diff --git a/api/src/com/cloud/exception/CloudExecutionException.java b/api/src/com/cloud/exception/CloudExecutionException.java new file mode 100755 index 00000000000..979043289e4 --- /dev/null +++ b/api/src/com/cloud/exception/CloudExecutionException.java @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.exception; + +import java.util.HashMap; + +import com.cloud.utils.SerialVersionUID; + +/** + * CloudExecutionException is a generic exception thrown by components in + * CloudStack. It indicates an error in the execution of the business logic. + * When using this exception, it is important to give detail information + * about the error. At the entry points, this exception is caught but the + * stack trace is not logged so the information has to be detail enough + * that one can find out what the error is simply by reading the error message. + * + */ +public class CloudExecutionException extends RuntimeException { + private final static long serialVersionUID = SerialVersionUID.CloudExecutionException; + + private final ErrorCode code; + private final HashMap details; + + public CloudExecutionException(ErrorCode code, String message, Throwable cause) { + super(message, cause); + this.code = code; + details = new HashMap(); + } + + public ErrorCode getErrorCode() { + return code; + } + + public String getErrorMessage() { + return new StringBuilder("Error Code=").append(code).append("; Error Message=").append(super.toString()).toString(); + } + + @Override + public String toString() { + StringBuilder buff = new StringBuilder(); + buff.append("Error Code=").append(code); + buff.append("; Error Message=").append(super.toString()); + if (details.size() > 0) { + buff.append("; Error Details=").append(details.toString()); + } + return buff.toString(); + } +} diff --git a/api/src/com/cloud/exception/ErrorCode.java b/api/src/com/cloud/exception/ErrorCode.java new file mode 100755 index 00000000000..046443c0bad --- /dev/null +++ b/api/src/com/cloud/exception/ErrorCode.java @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.exception; + +import java.util.HashSet; + +/** + * ErrorCode is a standard error code given by the API to represent the error. + */ +public class ErrorCode { + String code; + private static HashSet s_codes = new HashSet(); + + public ErrorCode(String code) { + this.code = code; + assert !s_codes.contains(this) : "There is already an error code registered for this code: " + code; + s_codes.add(this); + } + + public String getCode() { + return code; + } + + @Override + public int hashCode() { + return code.hashCode(); + } + + @Override + public boolean equals(Object that) { + if (!(that instanceof ErrorCode)) { + return false; + } + + return this.code.equals(((ErrorCode)that).code); + } + + public final static ErrorCode UnableToReachResource = new ErrorCode("resource.unavailable"); +} diff --git a/api/src/com/cloud/network/guru/NetworkGuru.java b/api/src/com/cloud/network/guru/NetworkGuru.java index 5ead472300e..b2d2842b82f 100644 --- a/api/src/com/cloud/network/guru/NetworkGuru.java +++ b/api/src/com/cloud/network/guru/NetworkGuru.java @@ -52,29 +52,37 @@ public interface NetworkGuru extends Adapter { * @return NetworkConfiguration */ Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner); - + /** - * allocate a nic in this network. This method implementation cannot take a long time as - * it is meant to allocate for the DB. - * @param network configuration to allocate the nic in. - * @param nic user specified - * @param vm virtual machine the network configuraiton will be in. + * allocate a nic in this network. This method implementation cannot take a long time as it is meant to allocate for + * the DB. + * + * @param network + * configuration to allocate the nic in. + * @param nic + * user specified + * @param vm + * virtual machine the network configuration will be in. * @return NicProfile. * @throws InsufficientVirtualNetworkCapcityException * @throws InsufficientAddressCapacityException */ NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException; - + /** * Fully implement the network configuration as specified. - * @param network network configuration - * @param offering offering that the network configuration was based on. - * @param destination where were deploying to. + * + * @param network + * network configuration + * @param offering + * offering that the network configuration was based on. + * @param destination + * where were deploying to. * @return a fully implemented NetworkConfiguration. - * @throws InsufficientVirtualNetworkCapcityException TODO + * @throws InsufficientVirtualNetworkCapcityException */ Network implement(Network network, NetworkOffering offering, DeployDestination destination, ReservationContext context) throws InsufficientVirtualNetworkCapcityException; - + /** * reserve a nic for this VM in this network. * @param nic @@ -89,13 +97,20 @@ public interface NetworkGuru extends Adapter { void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException; boolean release(NicProfile nic, VirtualMachineProfile vm, String reservationId); - + void deallocate(Network network, NicProfile nic, VirtualMachineProfile vm); - + + /** + * @deprecated This method should not be here in the first place. What does this really mean? Is it always persisted + * in the nic? When is it persisted in the nic? When is it called? No Idea. + * @param profile + * @param network + */ + @Deprecated void updateNicProfile(NicProfile profile, Network network); - + void shutdown(NetworkProfile network, NetworkOffering offering); - + /** * Throw away the design. * @param network @@ -104,7 +119,7 @@ public interface NetworkGuru extends Adapter { * @return */ boolean trash(Network network, NetworkOffering offering, Account owner); - + void updateNetworkProfile(NetworkProfile networkProfile); - + } diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 64f3f20ca0c..0e70a7c2e5b 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -684,7 +684,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } if (dest == null) { - throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId()); + throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId()); } long destHostId = dest.getHost().getId(); @@ -926,6 +926,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } VirtualMachineGuru vmGuru = getVmGuru(vm); + VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); try { if (!stateTransitTo(vm, Event.StopRequested, vm.getHostId())) { @@ -936,66 +937,55 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene throw new CloudRuntimeException("We cannot stop " + vm + " when it is in state " + vm.getState()); } s_logger.debug("Unable to transition the state but we're moving on because it's forced stop"); - } - - VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); - if ((vm.getState() == State.Starting || vm.getState() == State.Stopping || vm.getState() == State.Migrating) && forced) { - ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); - if (work == null) { - if (cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.StopRequested, forced, user, account)) { - try { - return stateTransitTo(vm, Event.AgentReportStopped, null); - } catch (NoTransitionException e) { - s_logger.warn("Unable to cleanup " + vm); - return false; - } - } - } else { - try { - return stateTransitTo(vm, Event.AgentReportStopped, null); - } catch (NoTransitionException e) { - s_logger.warn("Unable to cleanup " + vm); - return false; - } - } - } - - if (vm.getHostId() != null) { - String routerPrivateIp = null; - if (vm.getType() == VirtualMachine.Type.DomainRouter) { - routerPrivateIp = vm.getPrivateIpAddress(); - } - StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null, routerPrivateIp); - boolean stopped = false; - StopAnswer answer = null; - try { - answer = (StopAnswer) _agentMgr.send(vm.getHostId(), stop); - stopped = answer.getResult(); - if (!stopped) { - throw new CloudRuntimeException("Unable to stop the virtual machine due to " + answer.getDetails()); - } - vmGuru.finalizeStop(profile, answer); - - } catch (AgentUnavailableException e) { - } catch (OperationTimedoutException e) { - } finally { - if (!stopped) { - if (!forced) { - s_logger.warn("Unable to stop vm " + vm); + if (state == State.Starting || state == State.Stopping || state == State.Migrating) { + ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); + if (work != null) { + if (cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.StopRequested, forced, user, account)) { try { - stateTransitTo(vm, Event.OperationFailed, vm.getHostId()); + return stateTransitTo(vm, Event.AgentReportStopped, null); } catch (NoTransitionException e) { - s_logger.warn("Unable to transition the state " + vm); + s_logger.warn("Unable to cleanup " + vm); + return false; } - return false; - } else { - s_logger.warn("Unable to actually stop " + vm + " but continue with release because it's a force stop"); - vmGuru.finalizeStop(profile, answer); } } } } + String routerPrivateIp = null; + if (vm.getType() == VirtualMachine.Type.DomainRouter) { + routerPrivateIp = vm.getPrivateIpAddress(); + } + StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null, routerPrivateIp); + boolean stopped = false; + StopAnswer answer = null; + try { + answer = (StopAnswer) _agentMgr.send(vm.getHostId(), stop); + stopped = answer.getResult(); + if (!stopped) { + throw new CloudRuntimeException("Unable to stop the virtual machine due to " + answer.getDetails()); + } + vmGuru.finalizeStop(profile, answer); + + } catch (AgentUnavailableException e) { + } catch (OperationTimedoutException e) { + } finally { + if (!stopped) { + if (!forced) { + s_logger.warn("Unable to stop vm " + vm); + try { + stateTransitTo(vm, Event.OperationFailed, vm.getHostId()); + } catch (NoTransitionException e) { + s_logger.warn("Unable to transition the state " + vm); + } + return false; + } else { + s_logger.warn("Unable to actually stop " + vm + " but continue with release because it's a force stop"); + vmGuru.finalizeStop(profile, answer); + } + } + } + if (s_logger.isDebugEnabled()) { s_logger.debug(vm + " is stopped on the host. Proceeding to release resource held."); } @@ -1464,7 +1454,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene return commands; } - + protected Map convertDeltaToInfos(final Map states) { final HashMap map = new HashMap(); @@ -1495,7 +1485,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene return map; } - + protected Map convertToInfos(final Map states) { final HashMap map = new HashMap(); @@ -1698,7 +1688,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } public Commands fullSync(final long hostId, StartupRoutingCommand startup) { - + Commands commands = new Commands(OnError.Continue); Map infos = convertToInfos(startup.getVmStates()); if( startup.isPoolSync()) { @@ -1725,7 +1715,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene hId = host.getId(); } HypervisorGuru hvGuru = _hvGuruMgr.getGuru(castedVm.getHypervisorType()); - + Command command = compareState(hId, castedVm, info, true, hvGuru.trackVmHostChange()); if (command != null) { commands.addCommand(command); @@ -1735,13 +1725,13 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene s_logger.warn("Stopping a VM that we have no record of: " + left.name); commands.addCommand(cleanup(left.name)); } - + } else { final List vms = _vmDao.listByHostId(hostId); s_logger.debug("Found " + vms.size() + " VMs for host " + hostId); for (VMInstanceVO vm : vms) { AgentVmInfo info = infos.remove(vm.getId()); - + VMInstanceVO castedVm = null; if (info == null) { info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); @@ -1749,15 +1739,15 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } else { castedVm = info.vm; } - + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(castedVm.getHypervisorType()); - + Command command = compareState(hostId, castedVm, info, true, hvGuru.trackVmHostChange()); if (command != null) { commands.addCommand(command); } } - + for (final AgentVmInfo left : infos.values()) { boolean found = false; for (VirtualMachineGuru vmGuru : _vmGurus.values()) { @@ -1766,10 +1756,10 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene found = true; HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); if(hvGuru.trackVmHostChange()) { - Command command = compareState(hostId, vm, left, true, true); - if (command != null) { - commands.addCommand(command); - } + Command command = compareState(hostId, vm, left, true, true); + if (command != null) { + commands.addCommand(command); + } } else { s_logger.warn("Stopping a VM, VM " + left.name + " migrate from Host " + vm.getHostId() + " to Host " + hostId ); commands.addCommand(cleanup(left.name)); @@ -1937,7 +1927,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene this.guru = (VirtualMachineGuru) guru; this.host = host; } - + public AgentVmInfo(String name, VirtualMachineGuru guru, VMInstanceVO vm, State state) { this(name, guru, vm, state, null); } diff --git a/utils/src/com/cloud/utils/SerialVersionUID.java b/utils/src/com/cloud/utils/SerialVersionUID.java index bbc401d5281..5bfbb84b339 100755 --- a/utils/src/com/cloud/utils/SerialVersionUID.java +++ b/utils/src/com/cloud/utils/SerialVersionUID.java @@ -64,5 +64,6 @@ public interface SerialVersionUID { public static final long VirtualMachineMigrationException = Base | 0x24; public static final long DiscoveredWithErrorException = Base | 0x25; public static final long NoTransitionException = Base | 0x26; - public static final long CallFailedException = Base | 0x26; + public static final long CloudExecutionException = Base | 0x27; + public static final long CallFailedException = Base | 0x28; }