diff --git a/agent/src/com/cloud/agent/Agent.java b/agent/src/com/cloud/agent/Agent.java index 6d3f9df9403..855cd5bcafe 100755 --- a/agent/src/com/cloud/agent/Agent.java +++ b/agent/src/com/cloud/agent/Agent.java @@ -49,7 +49,6 @@ import com.cloud.agent.api.UpgradeAnswer; import com.cloud.agent.api.UpgradeCommand; import com.cloud.agent.transport.Request; import com.cloud.agent.transport.Response; -import com.cloud.agent.transport.UpgradeResponse; import com.cloud.exception.AgentControlChannelException; import com.cloud.resource.ServerResource; import com.cloud.utils.PropertiesUtil; @@ -302,7 +301,7 @@ public class Agent implements HandlerFactory, IAgentControl { commands[i] = startup[i]; } - final Request request = new Request(getNextSequence(), _id != null ? _id : -1, -1, commands, false); + final Request request = new Request(getNextSequence(), _id != null ? _id : -1, -1, commands, false, false, false); if (s_logger.isDebugEnabled()) { s_logger.debug("Sending Startup: " + request.toString()); @@ -624,7 +623,7 @@ public class Agent implements HandlerFactory, IAgentControl { @Override public AgentControlAnswer sendRequest(AgentControlCommand cmd, int timeoutInMilliseconds) throws AgentControlChannelException { Request request = new Request(this.getNextSequence(), this.getId(), - -1, new Command[] {cmd}, true, false); + -1, new Command[] {cmd}, true, false, false); AgentControlListener listener = new AgentControlListener(request); registerControlListener(listener); @@ -647,7 +646,7 @@ public class Agent implements HandlerFactory, IAgentControl { @Override public void postRequest(AgentControlCommand cmd) throws AgentControlChannelException { Request request = new Request(this.getNextSequence(), this.getId(), - -1, new Command[] {cmd}, true, false); + -1, new Command[] {cmd}, true, false, false); postRequest(request); } @@ -676,11 +675,13 @@ public class Agent implements HandlerFactory, IAgentControl { return _answer; } - public Answer processControlRequest(Request request, AgentControlCommand cmd) { + @Override + public Answer processControlRequest(Request request, AgentControlCommand cmd) { return null; } - public void processControlResponse(Response response, AgentControlAnswer answer) { + @Override + public void processControlResponse(Response response, AgentControlAnswer answer) { if(_request.getSequence() == response.getSequence()) { _answer = answer; synchronized(this) { @@ -778,9 +779,7 @@ public class Agent implements HandlerFactory, IAgentControl { Request request; try { request = Request.parse(task.getData()); - if (request instanceof UpgradeResponse) { - upgradeAgent(((UpgradeResponse)request).getUpgradeUrl(), null); - } else if (request instanceof Response) { + if (request instanceof Response) { processResponse((Response)request, task.getLink()); } else { processRequest(request, task.getLink()); diff --git a/api/src/com/cloud/deploy/DeploymentPlanner.java b/api/src/com/cloud/deploy/DeploymentPlanner.java index d60f466314f..83c8c76ed5d 100644 --- a/api/src/com/cloud/deploy/DeploymentPlanner.java +++ b/api/src/com/cloud/deploy/DeploymentPlanner.java @@ -3,11 +3,92 @@ */ package com.cloud.deploy; +import java.util.HashSet; import java.util.Set; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.host.Host; import com.cloud.utils.component.Adapter; import com.cloud.vm.VirtualMachineProfile; +/** + * Returns a deployment destination for the VM. + */ public interface DeploymentPlanner extends Adapter { - DeployDestination plan(VirtualMachineProfile vm, DeploymentPlan plan, Set avoid); + /** + * plan is called to determine where a virtual machine should be running. + * + * @param vm virtual machine. + * @param plan deployment plan that tells you where it's being deployed to. + * @param avoid avoid these data centers, pods, clusters, or hosts. + * @return DeployDestination for that virtual machine. + */ + DeployDestination plan(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException; + + /** + * check() is called right before the virtual machine starts to make sure + * the host has enough capacity. + * + * @param vm virtual machine in question. + * @param plan deployment plan used to determined the deploy destination. + * @param dest destination returned by plan. + * @param avoid what to avoid. + * @return true if it's okay to start; false if not. If false, the exclude list will include what should be excluded. + */ + boolean check(VirtualMachineProfile vm, DeploymentPlan plan, DeployDestination dest, ExcludeList exclude); + + public static class ExcludeList { + Set _dcIds; + Set _podIds; + Set _clusterIds; + Set _hostIds; + + public void adddDataCenter(long dataCenterId) { + if (_dcIds == null) { + _dcIds = new HashSet(); + } + _dcIds.add(dataCenterId); + } + + public void addPod(long podId) { + if (_podIds == null) { + _podIds = new HashSet(); + } + _podIds.add(podId); + } + + public void addCluster(long clusterId) { + if (_clusterIds == null) { + _clusterIds = new HashSet(); + } + _clusterIds.add(clusterId); + } + + public void addHost(long hostId) { + if (_hostIds == null) { + _hostIds = new HashSet(); + } + _hostIds.add(hostId); + } + + public boolean shouldAvoid(Host host) { + if (_dcIds != null && _dcIds.contains(host.getDataCenterId())) { + return true; + } + + if (_podIds != null && _podIds.contains(host.getPodId())) { + return true; + } + + if (_clusterIds != null && _clusterIds.contains(host.getClusterId())) { + return true; + } + + if (_hostIds != null && _hostIds.contains(host.getId())) { + return true; + } + + return false; + } + } } diff --git a/core/src/com/cloud/host/InsufficientServerCapacityException.java b/api/src/com/cloud/exception/InsufficientServerCapacityException.java similarity index 78% rename from core/src/com/cloud/host/InsufficientServerCapacityException.java rename to api/src/com/cloud/exception/InsufficientServerCapacityException.java index 83dc611243a..b20424a537f 100755 --- a/core/src/com/cloud/host/InsufficientServerCapacityException.java +++ b/api/src/com/cloud/exception/InsufficientServerCapacityException.java @@ -15,11 +15,9 @@ * along with this program. If not, see . * */ -package com.cloud.host; +package com.cloud.exception; -import com.cloud.exception.InsufficientCapacityException; import com.cloud.utils.SerialVersionUID; -import com.cloud.vm.VirtualMachine; /** * This exception is thrown when a server cannot be found to host the @@ -30,13 +28,7 @@ public class InsufficientServerCapacityException extends InsufficientCapacityExc private static final long serialVersionUID = SerialVersionUID.InsufficientServerCapacityException; - VirtualMachine.Type type; - public InsufficientServerCapacityException(VirtualMachine.Type type, String msg) { + public InsufficientServerCapacityException(String msg) { super(msg); - this.type = type; - } - - VirtualMachine.Type getType() { - return type; } } diff --git a/api/src/com/cloud/storage/StoragePool.java b/api/src/com/cloud/storage/StoragePool.java index dbbb8c61492..6341118f4e8 100644 --- a/api/src/com/cloud/storage/StoragePool.java +++ b/api/src/com/cloud/storage/StoragePool.java @@ -76,6 +76,9 @@ public interface StoragePool { * @return available storage in bytes */ long getAvailableBytes(); + + + Long getClusterId(); /** * @return the fqdn or ip address of the storage host diff --git a/api/src/com/cloud/vm/VirtualMachineProfiler.java b/api/src/com/cloud/vm/VirtualMachineProfiler.java deleted file mode 100644 index 1156346bb22..00000000000 --- a/api/src/com/cloud/vm/VirtualMachineProfiler.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * - */ -package com.cloud.vm; - -import com.cloud.offering.ServiceOffering; -import com.cloud.template.VirtualMachineTemplate; -import com.cloud.utils.component.Adapter; - -public interface VirtualMachineProfiler extends Adapter { - - VirtualMachineProfile convert(ServiceOffering offering, VirtualMachineTemplate template); -} diff --git a/core/src/com/cloud/agent/Listener.java b/core/src/com/cloud/agent/Listener.java index 260465edcf1..19f0babd116 100755 --- a/core/src/com/cloud/agent/Listener.java +++ b/core/src/com/cloud/agent/Listener.java @@ -26,7 +26,7 @@ import com.cloud.host.HostVO; import com.cloud.host.Status; /** - * Listener is a multipurpose interface for hooking into the AgentManager. + * Listener is a multi-purpose interface for hooking into the AgentManager. * There are several types of events that the AgentManager forwards * to the listener. * @@ -45,7 +45,7 @@ public interface Listener { * @param answers answers to the commands. * @return true if processed. false if not. */ - boolean processAnswer(long agentId, long seq, Answer[] answers); + boolean processAnswers(long agentId, long seq, Answer[] answers); /** * This method is called by the AgentManager when an agent sent @@ -57,7 +57,7 @@ public interface Listener { * @param commands commands that were sent. * @return true if you processed the commands. false if not. */ - boolean processCommand(long agentId, long seq, Command[] commands); + boolean processCommands(long agentId, long seq, Command[] commands); /** * process control command sent from agent under its management @@ -89,7 +89,7 @@ public interface Listener { boolean processDisconnect(long agentId, Status state); /** - * If ths Listener is passed to the send() method, this method + * If this Listener is passed to the send() method, this method * is called by AgentManager after processing an answer * from the agent. Returning true means you're expecting more * answers from the agent using the same sequence number. diff --git a/core/src/com/cloud/agent/transport/Request.java b/core/src/com/cloud/agent/transport/Request.java index 8267ad84799..3910ade0164 100755 --- a/core/src/com/cloud/agent/transport/Request.java +++ b/core/src/com/cloud/agent/transport/Request.java @@ -49,7 +49,6 @@ import com.google.gson.reflect.TypeToken; * 6. AgentId - 8 bytes; * 7. Data Package. * - * Currently flags has only if it is a request or response. */ public class Request { private static final Logger s_logger = Logger.getLogger(Request.class); @@ -72,8 +71,7 @@ public class Request { protected static final short FLAG_REQUEST = 0x1; protected static final short FLAG_STOP_ON_ERROR = 0x2; protected static final short FLAG_IN_SEQUENCE = 0x4; - protected static final short FLAG_WATCH = 0x8; - protected static final short FLAG_UPDATE = 0x10; + protected static final short FLAG_REVERT_ON_ERROR = 0x8; protected static final short FLAG_FROM_SERVER = 0x20; protected static final short FLAG_CONTROL = 0x40; @@ -93,86 +91,88 @@ public class Request { protected Version _ver; protected long _seq; - protected Command[] _cmds; - protected boolean _inSequence; - protected boolean _stopOnError; - protected boolean _fromServer; - protected boolean _control; + protected short _flags; protected long _mgmtId; protected long _agentId; + protected Command[] _cmds; protected String _content; - public Request(long seq, long agentId, long mgmtId, final Command command, boolean fromServer) { - this(seq, agentId, mgmtId, new Command[] {command}, true, fromServer); - } - - public Request(long seq, long agentId, long mgmtId, final Command[] commands, boolean fromServer) { - this(seq, agentId, mgmtId, commands, true, fromServer); + protected Request() { } - protected Request(Version ver, long seq, long agentId, long mgmtId, final Command[] cmds, final Boolean inSequence, final boolean stopOnError, boolean fromServer) { + protected Request(Version ver, long seq, long agentId, long mgmtId, short flags, final Command[] cmds) { _ver = ver; _cmds = cmds; - _stopOnError = stopOnError; - if (inSequence != null) { - _inSequence = inSequence; - } else { - for (final Command cmd : cmds) { - if (cmd.executeInSequence()) { - _inSequence = true; - break; - } - } - } + _flags = flags; _seq = seq; _agentId = agentId; _mgmtId = mgmtId; - _fromServer = fromServer; } - protected Request(Version ver, long seq, long agentId, long mgmtId, final String content, final boolean inSequence, final boolean stopOnError, final boolean fromServer, final boolean control) { - _ver = ver; - _cmds = null; + protected Request(Version ver, long seq, long agentId, long mgmtId, short flags, final String content) { + this(ver, seq, agentId, mgmtId, flags, (Command[])null); _content = content; - _stopOnError = stopOnError; - _inSequence = inSequence; - _seq = seq; - _agentId = agentId; - _mgmtId = mgmtId; - _fromServer = fromServer; - _control = control; } - public Request(long seq, long agentId, long mgmtId, final Command[] cmds, final boolean stopOnError, boolean fromServer) { - this(Version.v3, seq, agentId, mgmtId, cmds, null, stopOnError, fromServer); + public Request(long seq, long agentId, long mgmtId, final Command command, boolean fromServer) { + this(seq, agentId, mgmtId, new Command[] {command}, true, fromServer, true); } + public Request(long seq, long agentId, long mgmtId, Command[] cmds, boolean stopOnError, boolean fromServer, boolean revert) { + this(Version.v3, seq, agentId, mgmtId, (short)0, cmds); + setStopOnError(stopOnError); + setFromServer(fromServer); + setRevertOnError(revert); + } + + protected Request(final Request that, final Command[] cmds) { + this._ver = that._ver; + this._seq = that._seq; + setInSequence(that.executeInSequence()); + setStopOnError(that.stopOnError()); + this._cmds = cmds; + this._mgmtId = that._mgmtId; + this._agentId = that._agentId; + setFromServer(!that.isFromServer()); + } + + private final void setStopOnError(boolean stopOnError) { + _flags |= (stopOnError ? 1 : 0) << FLAG_STOP_ON_ERROR; + } + + private final void setInSequence(boolean inSequence) { + _flags |= (inSequence ? 1 : 0) << FLAG_IN_SEQUENCE; + } + + public boolean isControl() { - return _control; + return (_flags & FLAG_CONTROL) > 0; } - public void setControl() { - _control = true; + public void setControl(boolean control) { + _flags |= (control ? 1 : 0) << FLAG_CONTROL; + } + + public boolean revertOnError() { + return (_flags & FLAG_CONTROL) > 0; + } + + private final void setRevertOnError(boolean revertOnError) { + _flags |= (revertOnError ? 1 : 0) << FLAG_REVERT_ON_ERROR; + } + + private final void setFromServer(boolean fromServer) { + _flags |= (fromServer ? 1 : 0) << FLAG_FROM_SERVER; } public long getManagementServerId() { return _mgmtId; } - protected Request(final Request that, final Command[] cmds) { - this._ver = that._ver; - this._seq = that._seq; - this._inSequence = that._inSequence; - this._stopOnError = that._stopOnError; - this._cmds = cmds; - this._mgmtId = that._mgmtId; - this._agentId = that._agentId; - this._fromServer = !that._fromServer; + public boolean isFromServer() { + return (_flags & FLAG_FROM_SERVER) > 0; } - protected Request() { - } - public Version getVersion() { return _ver; } @@ -182,7 +182,7 @@ public class Request { } public boolean executeInSequence() { - return _inSequence; + return (_flags & FLAG_IN_SEQUENCE) > 0; } public long getSequence() { @@ -190,7 +190,7 @@ public class Request { } public boolean stopOnError() { - return _stopOnError; + return (_flags & FLAG_STOP_ON_ERROR) > 0; } public Command getCommand() { @@ -269,25 +269,7 @@ public class Request { } protected short getFlags() { - short flags = 0; - if (!(this instanceof Response)) { - flags = FLAG_REQUEST; - } else { - flags = FLAG_RESPONSE; - } - if (_inSequence) { - flags = (short)(flags | FLAG_IN_SEQUENCE); - } - if (_stopOnError) { - flags = (short)(flags | FLAG_STOP_ON_ERROR); - } - if (_fromServer) { - flags = (short)(flags | FLAG_FROM_SERVER); - } - if (_control) { - flags = (short)(flags | FLAG_CONTROL); - } - return flags; + return (short)(((this instanceof Response) ? FLAG_RESPONSE : FLAG_REQUEST) | _flags); } /** @@ -311,12 +293,6 @@ public class Request { final byte reserved = buff.get(); // tossed away for now. final Short flags = buff.getShort(); final boolean isRequest = (flags & FLAG_REQUEST) > 0; - final boolean isControl = (flags & FLAG_IN_SEQUENCE) > 0; - final boolean isStopOnError = (flags & FLAG_STOP_ON_ERROR) > 0; - final boolean isWatch = (flags & FLAG_WATCH) > 0; - final boolean fromServer = (flags & FLAG_FROM_SERVER) > 0; - final boolean needsUpdate = (flags & FLAG_UPDATE) > 0; - final boolean control = (flags & FLAG_CONTROL) > 0; final long seq = buff.getLong(); final int size = buff.getInt(); @@ -335,14 +311,11 @@ public class Request { } final String content = new String(command, offset, command.length - offset); - if (needsUpdate && !isRequest) { - return new UpgradeResponse(Version.get(ver), seq, content); - } if (isRequest) { - return new Request(version, seq, agentId, mgmtId, content, isControl, isStopOnError, fromServer, control); + return new Request(version, seq, agentId, mgmtId, flags, content); } else { - return new Response(Version.get(ver), seq, agentId, mgmtId, content, isControl, isStopOnError, fromServer, control); + return new Response(Version.get(ver), seq, agentId, mgmtId, flags, content); } } @@ -371,8 +344,6 @@ public class Request { } public static boolean fromServer(final byte[] bytes) { - // int flags = NumbersUtil.bytesToShort(bytes, 2); - return (bytes[3] & FLAG_FROM_SERVER) > 0; } @@ -385,7 +356,6 @@ public class Request { } public static boolean isControl(final byte[] bytes) { -// int flags = NumbersUtil.bytesToShort(bytes, 2); return (bytes[3] & FLAG_CONTROL) > 0; } } diff --git a/core/src/com/cloud/agent/transport/Response.java b/core/src/com/cloud/agent/transport/Response.java index 59eb2263810..da59e48350a 100755 --- a/core/src/com/cloud/agent/transport/Response.java +++ b/core/src/com/cloud/agent/transport/Response.java @@ -46,8 +46,8 @@ public class Response extends Request { _agentId = agentId; } - protected Response(Version ver, long seq, long agentId, long mgmtId, String ans, boolean inSequence, boolean stopOnError, boolean fromServer, boolean control) { - super(ver, seq, agentId, mgmtId, ans, inSequence, stopOnError, fromServer, control); + protected Response(Version ver, long seq, long agentId, long mgmtId, short flags, String ans) { + super(ver, seq, agentId, mgmtId, flags, ans); } public Answer getAnswer() { diff --git a/core/src/com/cloud/agent/transport/UpgradeResponse.java b/core/src/com/cloud/agent/transport/UpgradeResponse.java deleted file mode 100644 index 5930b0d5d0d..00000000000 --- a/core/src/com/cloud/agent/transport/UpgradeResponse.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (C) 2010 Cloud.com, 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.agent.transport; - -import java.nio.ByteBuffer; - -import com.cloud.agent.api.Answer; -import com.cloud.utils.NumbersUtil; - -/** - * An UpgradeResponse is sent when there is a protocol version mismatched. - * It has the same header as the request but contains an url to the - * updated agent. - */ -public class UpgradeResponse extends Response { - byte[] _requestBytes; - - public UpgradeResponse(Request request, String url) { - super(request, new Answer[0]); - _requestBytes = null; - } - - public UpgradeResponse(byte[] request, String url) { - super(Version.v2, -1, -1, -1, url, true, true, true, false); - _requestBytes = request; - } - - protected UpgradeResponse(Version ver, long seq, String url) { - super(ver, seq, -1, -1, url, true, false, true, false); - _requestBytes = null; - } - - @Override - protected ByteBuffer serializeHeader(int contentSize) { - if (_requestBytes == null) { - return super.serializeHeader(contentSize); - } - - byte[] responseHeader = new byte[16]; - ByteBuffer buffer = ByteBuffer.wrap(responseHeader); - - buffer.put(_requestBytes[0]); // version number - buffer.put((byte)0); - buffer.putShort(getFlags()); - buffer.put(_requestBytes, 4, 8); // sequence number - buffer.putInt(contentSize); - buffer.flip(); - - return buffer; - } - - @Override - public String toString() { - if (_requestBytes == null) { - return super.toString(); - } - - final StringBuilder buffer = new StringBuilder(); - buffer.append("{ ").append(getType()); - buffer.append(", Seq: ").append(NumbersUtil.bytesToLong(_requestBytes, 4)).append(", Ver: ").append(_requestBytes[0]).append(", Flags: ").append(Integer.toBinaryString(getFlags())); - buffer.append(", ").append(_content).append(" }"); - return buffer.toString(); - } - - @Override - public ByteBuffer[] toBytes() { - ByteBuffer[] buffers = new ByteBuffer[2]; - - buffers[1] = ByteBuffer.wrap(_content.getBytes()); - buffers[0] = serializeHeader(buffers[1].capacity()); - - return buffers; - } - - public String getUpgradeUrl() { - return _content; - } - - @Override - protected short getFlags() { - return FLAG_RESPONSE | FLAG_UPDATE; - } -} diff --git a/core/src/com/cloud/vm/dao/DomainRouterDaoImpl.java b/core/src/com/cloud/vm/dao/DomainRouterDaoImpl.java index c6589d48715..dbe113e118f 100755 --- a/core/src/com/cloud/vm/dao/DomainRouterDaoImpl.java +++ b/core/src/com/cloud/vm/dao/DomainRouterDaoImpl.java @@ -35,10 +35,10 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.DomainRouter.Role; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.State; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.DomainRouter.Role; @Local(value = { DomainRouterDao.class }) public class DomainRouterDaoImpl extends GenericDaoBase implements DomainRouterDao { @@ -287,13 +287,13 @@ public class DomainRouterDaoImpl extends GenericDaoBase im public List listByDomain(Long domainId) { SearchCriteria sc = DomainIdSearch.create(); sc.setParameters("domainId", domainId); - return listIncludingRemovedBy(sc); + return listBy(sc); } @Override public List listByVlanDbId(Long vlanDbId) { SearchCriteria sc = VlanDbIdSearch.create(); sc.setParameters("vlanDbId", vlanDbId); - return listIncludingRemovedBy(sc); + return listBy(sc); } } diff --git a/core/src/com/cloud/vm/dao/VMInstanceDaoImpl.java b/core/src/com/cloud/vm/dao/VMInstanceDaoImpl.java index 813e056c2ea..992fb999263 100644 --- a/core/src/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/core/src/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -229,6 +229,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return search(sc, null); } + @Override public Integer[] countRoutersAndProxies(Long hostId) { Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; diff --git a/core/src/com/cloud/agent/AgentManager.java b/server/src/com/cloud/agent/AgentManager.java similarity index 94% rename from core/src/com/cloud/agent/AgentManager.java rename to server/src/com/cloud/agent/AgentManager.java index 11740cdaa34..b8495c4af5e 100755 --- a/core/src/com/cloud/agent/AgentManager.java +++ b/server/src/com/cloud/agent/AgentManager.java @@ -23,6 +23,7 @@ import java.util.Set; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; +import com.cloud.agent.manager.Commands; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.PodCluster; @@ -51,6 +52,11 @@ import com.cloud.vm.VirtualMachineProfile; * DAOs and the connections it manages. */ public interface AgentManager extends Manager { + public enum OnError { + Revert, + Continue, + Stop + } /** * easy send method that returns null if there's any errors. It handles all exceptions. @@ -81,9 +87,9 @@ public interface AgentManager extends Manager { * @param stopOnError should the agent stop execution on the first error. * @return an array of Answer */ - Answer[] send(Long hostId, Command [] cmds, boolean stopOnError) throws AgentUnavailableException, OperationTimedoutException; + Answer[] send(Long hostId, Commands cmds) throws AgentUnavailableException, OperationTimedoutException; - Answer[] send(Long hostId, Command [] cmds, boolean stopOnError, int timeout) throws AgentUnavailableException, OperationTimedoutException; + Answer[] send(Long hostId, Commands cmds, int timeout) throws AgentUnavailableException, OperationTimedoutException; /** * Asynchronous sending of a command to the agent. @@ -102,7 +108,7 @@ public interface AgentManager extends Manager { * @param listener the listener to process the answer. * @return sequence number. */ - long send(Long hostId, Command[] cmds, boolean stopOnError, Listener listener) throws AgentUnavailableException; + long send(Long hostId, Commands cmds, Listener listener) throws AgentUnavailableException; /** * Register to listen for host events. These are mostly connection and diff --git a/server/src/com/cloud/agent/manager/AgentAttache.java b/server/src/com/cloud/agent/manager/AgentAttache.java index c95b4488edc..88d2629d9fc 100644 --- a/server/src/com/cloud/agent/manager/AgentAttache.java +++ b/server/src/com/cloud/agent/manager/AgentAttache.java @@ -245,7 +245,7 @@ public abstract class AgentAttache { s_logger.debug(log(seq, "Unable to find listener.")); } } else { - processed = monitor.processAnswer(_id, seq, answers); + processed = monitor.processAnswers(_id, seq, answers); if (s_logger.isTraceEnabled()) { s_logger.trace(log(seq, (processed ? "" : " did not ") + " processed ")); } diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index dedca6a8e46..a17ce1c5e90 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -387,7 +387,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory { public void handleCommands(AgentAttache attache, final long sequence, final Command[] cmds) { for (Pair listener : _cmdMonitors) { - boolean processed = listener.second().processCommand(attache.getId(), sequence, cmds); + boolean processed = listener.second().processCommands(attache.getId(), sequence, cmds); if (s_logger.isTraceEnabled()) { s_logger.trace("SeqA " + attache.getId() + "-" + sequence + ": " + (processed ? "processed" : "not processed") + " by " + listener.getClass()); } @@ -674,8 +674,11 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory { } @Override - public Answer send(final Long hostId, final Command cmd, final int timeout) throws AgentUnavailableException, OperationTimedoutException { - Answer[] answers = send(hostId, new Command[] { cmd }, true, timeout); + public Answer send(Long hostId, Command cmd, int timeout) throws AgentUnavailableException, OperationTimedoutException { + Commands cmds = new Commands(OnError.Revert); + cmds.addCommand(cmd); + send(hostId, cmds, timeout); + Answer[] answers = cmds.getAnswers(); if (answers != null && !(answers[0] instanceof UnsupportedAnswer)) { return answers[0]; } @@ -689,17 +692,18 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory { } @Override - public Answer[] send(final Long hostId, final Command[] cmds, final boolean stopOnError, final int timeout) throws AgentUnavailableException, - OperationTimedoutException { + public Answer[] send(Long hostId, Commands commands, int timeout) throws AgentUnavailableException, OperationTimedoutException { assert hostId != null : "Who's not checking the agent id before sending? ... (finger wagging)"; if (hostId == null) { throw new AgentUnavailableException(-1); } + + Command[] cmds = commands.toCommands(); assert cmds.length > 0 : "Ask yourself this about a hundred times. Why am I sending zero length commands?"; if (cmds.length == 0) { - return new Answer[0]; + commands.setAnswers(new Answer[0]); } final AgentAttache agent = getAttache(hostId); @@ -708,8 +712,10 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory { } long seq = _hostDao.getNextSequence(hostId); - Request req = new Request(seq, hostId, _nodeId, cmds, stopOnError, true); - return agent.send(req, timeout); + Request req = new Request(seq, hostId, _nodeId, cmds, commands.stopOnError(), true, commands.revertOnError()); + Answer[] answers = agent.send(req, timeout); + commands.setAnswers(answers); + return answers; } protected Status investigate(AgentAttache agent) { @@ -720,7 +726,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory { try { long seq = _hostDao.getNextSequence(hostId); - Request req = new Request(seq, hostId, _nodeId, new Command[] { new CheckHealthCommand() }, true, true); + Request req = new Request(seq, hostId, _nodeId, new CheckHealthCommand(), true); Answer[] answers = agent.send(req, 50 * 1000); if (answers != null && answers[0] != null ) { Status status = answers[0].getResult() ? Status.Up : Status.Down; @@ -754,27 +760,28 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory { } @Override - public long send(final Long hostId, final Command[] cmds, final boolean stopOnError, final Listener listener) throws AgentUnavailableException { + public long send(Long hostId, Commands commands, Listener listener) throws AgentUnavailableException { final AgentAttache agent = getAttache(hostId); if (agent.isClosed()) { return -1; } + Command[] cmds = commands.toCommands(); + assert cmds.length > 0 : "Why are you sending zero length commands?"; if (cmds.length == 0) { return -1; - } + } long seq = _hostDao.getNextSequence(hostId); - Request req = new Request(seq, hostId, _nodeId, cmds, stopOnError, true); + Request req = new Request(seq, hostId, _nodeId, cmds, commands.stopOnError(), true, commands.revertOnError()); agent.send(req, listener); return seq; } @Override public long gatherStats(final Long hostId, final Command cmd, final Listener listener) { - final Command[] cmds = new Command[] { cmd }; try { - return send(hostId, cmds, true, listener); + return send(hostId, new Commands(cmd), listener); } catch (final AgentUnavailableException e) { return -1; } @@ -1042,7 +1049,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory { AgentAttache attache = null; if (s_logger.isDebugEnabled()) { - s_logger.debug("Startup request from directly connected host: " + new Request(0, -1, -1, cmds, false).toString()); + s_logger.debug("Startup request from directly connected host: " + new Request(0l, -1l, -1l, cmds, true, false, true).toString()); } try { attache = handleDirectConnect(resource, cmds, details, old); @@ -1231,8 +1238,8 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory { } @Override - public Answer[] send(final Long hostId, final Command[] cmds, final boolean stopOnError) throws AgentUnavailableException, OperationTimedoutException { - return send(hostId, cmds, stopOnError, _wait); + public Answer[] send(final Long hostId, Commands cmds) throws AgentUnavailableException, OperationTimedoutException { + return send(hostId, cmds, _wait); } @Override diff --git a/server/src/com/cloud/agent/manager/AgentMonitor.java b/server/src/com/cloud/agent/manager/AgentMonitor.java index 2b110420e96..237aa66c9ff 100755 --- a/server/src/com/cloud/agent/manager/AgentMonitor.java +++ b/server/src/com/cloud/agent/manager/AgentMonitor.java @@ -166,12 +166,12 @@ public class AgentMonitor extends Thread implements Listener { } @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { return false; } @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { boolean processed = false; for (Command cmd : commands) { if (cmd instanceof PingCommand) { diff --git a/server/src/com/cloud/agent/manager/Commands.java b/server/src/com/cloud/agent/manager/Commands.java new file mode 100644 index 00000000000..d5a1ae0775c --- /dev/null +++ b/server/src/com/cloud/agent/manager/Commands.java @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2010 Cloud.com, 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.agent.manager; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.agent.AgentManager.OnError; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.utils.exception.CloudRuntimeException; + +public class Commands { + OnError _handler; + private ArrayList _ids = new ArrayList(); + private ArrayList _cmds = new ArrayList(); + private Answer[] _answers; + + public Commands(OnError handler) { + _handler = handler; + } + + public Commands(Command cmd) { + this(OnError.Revert); + addCommand(cmd); + } + + public void addCommands(List cmds) { + int i = 0; + for (Command cmd : cmds) { + addCommand(Integer.toString(i++), cmd); + } + } + + public int size() { + return _cmds.size(); + } + + public void addCommand(String id, Command cmd) { + _ids.add(id); + _cmds.add(cmd); + } + + public void addCommand(Command cmd) { + addCommand(null, cmd); + } + + public Answer getAnswer(String id) { + int i = _ids.indexOf(id); + return _answers[i]; + } + + @SuppressWarnings("unchecked") + public T getAnswer(Class clazz) { + assert(clazz != Answer.class) : "How do you expect to get a unique answer in this case? huh? How? How? How?....one more time....How?"; + for (Answer answer : _answers) { + if (answer.getClass() == clazz) { + return (T)answer; + } + } + throw new CloudRuntimeException("Unable to get answer that is of " + clazz); + } + + public Answer getAnswerFor(Class clazz) { + assert (clazz != Command.class) : "You passed in a generic Command. Seriously, you think you did that?"; + int i = 0; + for (Command cmd : _cmds) { + if (cmd.getClass() == clazz) { + break; + } + i++; + } + + assert i < _cmds.size() : "You sure you actually sent this command " + clazz; + + return _answers[i]; + } + + public Command[] toCommands() { + return _cmds.toArray(new Command[_cmds.size()]); + } + + public void setAnswers(Answer[] answers) { + assert answers.length == _cmds.size() : "We didn't get back the same number of answers as commands sent"; + _answers = answers; + } + + public OnError getErrorHandling() { + return _handler; + } + + public boolean stopOnError() { + return _handler == OnError.Revert || _handler == OnError.Stop; + } + + public boolean revertOnError() { + return _handler == OnError.Revert; + } + + public Answer[] getAnswers() { + return _answers; + } + + @SuppressWarnings("unchecked") + public T getCommand(Class clazz) { + for (Command cmd : _cmds) { + if (cmd.getClass() == clazz) { + return (T)cmd; + } + } + return null; + } +} diff --git a/server/src/com/cloud/agent/manager/SynchronousListener.java b/server/src/com/cloud/agent/manager/SynchronousListener.java index 9eb33efe99b..dd5d1d001fd 100755 --- a/server/src/com/cloud/agent/manager/SynchronousListener.java +++ b/server/src/com/cloud/agent/manager/SynchronousListener.java @@ -63,7 +63,7 @@ public class SynchronousListener implements Listener { } @Override - public synchronized boolean processAnswer(long agentId, long seq, Answer[] resp) { + public synchronized boolean processAnswers(long agentId, long seq, Answer[] resp) { _answers = resp; notifyAll(); return true; @@ -85,7 +85,7 @@ public class SynchronousListener implements Listener { } @Override - public boolean processCommand(long agentId, long seq, Command[] req) { + public boolean processCommands(long agentId, long seq, Command[] req) { return false; } diff --git a/server/src/com/cloud/alert/AlertManagerImpl.java b/server/src/com/cloud/alert/AlertManagerImpl.java index bc8481c89e9..4b98dd5cdb6 100644 --- a/server/src/com/cloud/alert/AlertManagerImpl.java +++ b/server/src/com/cloud/alert/AlertManagerImpl.java @@ -58,7 +58,6 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.offering.ServiceOffering; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; -import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.dao.StoragePoolDao; @@ -78,7 +77,6 @@ import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.VMInstanceDao; import com.sun.mail.smtp.SMTPMessage; import com.sun.mail.smtp.SMTPSSLTransport; import com.sun.mail.smtp.SMTPTransport; @@ -94,22 +92,21 @@ public class AlertManagerImpl implements AlertManager { private String _name = null; private EmailAlert _emailAlert; - private AlertDao _alertDao; - private HostDao _hostDao; + @Inject private AlertDao _alertDao; + @Inject private HostDao _hostDao; @Inject protected StorageManager _storageMgr; - private ServiceOfferingDao _offeringsDao; - private CapacityDao _capacityDao; - private VMInstanceDao _vmDao; - private DomainRouterDao _routerDao; - private ConsoleProxyDao _consoleProxyDao; - private SecondaryStorageVmDao _secStorgaeVmDao; - private UserVmDao _userVmDao; - private DataCenterDao _dcDao; - private HostPodDao _podDao; - private VolumeDao _volumeDao; - private IPAddressDao _publicIPAddressDao; - private DataCenterIpAddressDaoImpl _privateIPAddressDao; - private StoragePoolDao _storagePoolDao; + @Inject private ServiceOfferingDao _offeringsDao; + @Inject private CapacityDao _capacityDao; + @Inject private DomainRouterDao _routerDao; + @Inject private ConsoleProxyDao _consoleProxyDao; + @Inject private SecondaryStorageVmDao _secStorgaeVmDao; + @Inject private UserVmDao _userVmDao; + @Inject private DataCenterDao _dcDao; + @Inject private HostPodDao _podDao; + @Inject private VolumeDao _volumeDao; + @Inject private IPAddressDao _publicIPAddressDao; + @Inject private DataCenterIpAddressDaoImpl _privateIPAddressDao; + @Inject private StoragePoolDao _storagePoolDao; private Timer _timer = null; private float _cpuOverProvisioningFactor = 1; @@ -183,79 +180,6 @@ public class AlertManagerImpl implements AlertManager { _privateIPCapacityThreshold = Double.parseDouble(privateIPCapacityThreshold); } - _hostDao = locator.getDao(HostDao.class); - if (_hostDao == null) { - s_logger.error("Unable to get the host dao."); - return false; - } - - _vmDao = locator.getDao(VMInstanceDao.class); - if (_vmDao == null) { - s_logger.error("Unable to get the VM Instance dao."); - return false; - } - _routerDao = locator.getDao(DomainRouterDao.class); - _consoleProxyDao = locator.getDao(ConsoleProxyDao.class); - _secStorgaeVmDao = locator.getDao(SecondaryStorageVmDao.class); - - _userVmDao = locator.getDao(UserVmDao.class); - if (_userVmDao == null) { - s_logger.error("Unable to get the UserVm dao."); - return false; - } - - _offeringsDao = locator.getDao(ServiceOfferingDao.class); - if (_offeringsDao == null) { - s_logger.error("Unable to get the ServiceOffering dao."); - return false; - } - - _capacityDao = locator.getDao(CapacityDao.class); - if (_capacityDao == null) { - s_logger.error("Unable to get the capacity dao."); - return false; - } - - _alertDao = locator.getDao(AlertDao.class); - if (_alertDao == null) { - s_logger.error("Unable to get the alert dao."); - return false; - } - - - _dcDao = locator.getDao(DataCenterDao.class); - if (_dcDao == null) { - s_logger.error("Unable to get the DataCenter dao."); - return false; - } - - _podDao = locator.getDao(HostPodDao.class); - if (_podDao == null) { - s_logger.error("Unable to get the Pod dao."); - return false; - } - - _volumeDao = locator.getDao(VolumeDao.class); - if (_volumeDao == null) { - s_logger.error("Unable to get the Volume dao."); - return false; - } - - _publicIPAddressDao = locator.getDao(IPAddressDao.class); - if (_publicIPAddressDao == null) { - throw new ConfigurationException("Unable to get " + IPAddressDao.class.getName()); - } - - _privateIPAddressDao = locator.getDao(DataCenterIpAddressDaoImpl.class); - if (_privateIPAddressDao == null) { - throw new ConfigurationException("Unable to get " + DataCenterIpAddressDaoImpl.class.getName()); - } - - _storagePoolDao = locator.getDao(StoragePoolDao.class); - if (_storagePoolDao == null) { - throw new ConfigurationException("Unable to get " + StoragePoolDao.class.getName()); - } - String capacityCheckPeriodStr = configs.get("capacity.check.period"); if (capacityCheckPeriodStr != null) { _capacityCheckPeriod = Long.parseLong(capacityCheckPeriodStr); diff --git a/server/src/com/cloud/async/executor/VMOperationListener.java b/server/src/com/cloud/async/executor/VMOperationListener.java index 60f0bf55532..c37ddec25bc 100644 --- a/server/src/com/cloud/async/executor/VMOperationListener.java +++ b/server/src/com/cloud/async/executor/VMOperationListener.java @@ -50,7 +50,7 @@ public class VMOperationListener implements Listener { _cookie = cookie; } - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { Answer answer = null; if(answers != null) answer = answers[0]; @@ -61,7 +61,7 @@ public class VMOperationListener implements Listener { return true; } - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { return true; } diff --git a/server/src/com/cloud/async/executor/VolumeOperationListener.java b/server/src/com/cloud/async/executor/VolumeOperationListener.java index ef19a03a1a5..39f5668396a 100644 --- a/server/src/com/cloud/async/executor/VolumeOperationListener.java +++ b/server/src/com/cloud/async/executor/VolumeOperationListener.java @@ -52,7 +52,7 @@ public class VolumeOperationListener implements Listener { _cookie = cookie; } - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { Answer answer = null; if(answers != null) answer = answers[0]; @@ -64,7 +64,7 @@ public class VolumeOperationListener implements Listener { return true; } - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { return true; } diff --git a/server/src/com/cloud/capacity/CapacityManager.java b/server/src/com/cloud/capacity/CapacityManager.java new file mode 100644 index 00000000000..4dcec032d68 --- /dev/null +++ b/server/src/com/cloud/capacity/CapacityManager.java @@ -0,0 +1,12 @@ +package com.cloud.capacity; + +import com.cloud.utils.component.Manager; + +/** + * Capacity Manager manages the different capacities + * available within the Cloud Stack. + * + */ +public interface CapacityManager extends Manager { + +} diff --git a/server/src/com/cloud/vm/VirtualMachineChecker.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java similarity index 53% rename from server/src/com/cloud/vm/VirtualMachineChecker.java rename to server/src/com/cloud/capacity/CapacityManagerImpl.java index 9f8038b833b..97cd56eef33 100644 --- a/server/src/com/cloud/vm/VirtualMachineChecker.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -15,15 +15,37 @@ * along with this program. If not, see . * */ -package com.cloud.vm; +package com.cloud.capacity; -import java.util.List; +import java.util.Map; -import com.cloud.agent.api.to.VirtualMachineTO; -import com.cloud.deploy.DeployDestination; -import com.cloud.utils.Pair; +import javax.naming.ConfigurationException; -public interface VirtualMachineChecker { - boolean finalizeDeployment(VirtualMachineTO vm, VirtualMachineProfile profile, DeployDestination dest); - boolean finalizeDeployments(List> deployments); +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.utils.component.Inject; + +public class CapacityManagerImpl implements CapacityManager { + String _name; + @Inject CapacityDao _capacityDao; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java b/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java index a8a9d992bb4..762ca4ee382 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyListener.java @@ -45,12 +45,12 @@ public class ConsoleProxyListener implements Listener { } @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { return true; } @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { return false; } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 84e64df0721..5ea92cedf88 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -52,6 +52,7 @@ import com.cloud.agent.api.ConsoleProxyLoadReportCommand; import com.cloud.agent.api.MigrateCommand; import com.cloud.agent.api.PrepareForMigrationCommand; import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.Start2Command; import com.cloud.agent.api.StartConsoleProxyAnswer; import com.cloud.agent.api.StartConsoleProxyCommand; import com.cloud.agent.api.StartupCommand; @@ -61,6 +62,7 @@ import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VirtualMachineTO.SshMonitor; +import com.cloud.agent.manager.Commands; import com.cloud.async.AsyncJobExecutor; import com.cloud.async.AsyncJobManager; import com.cloud.async.AsyncJobVO; @@ -149,7 +151,7 @@ import com.cloud.vm.State; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Event; -import com.cloud.vm.VirtualMachineChecker; +import com.cloud.vm.VirtualMachineGuru; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineName; import com.cloud.vm.VirtualMachineProfile; @@ -178,7 +180,7 @@ import com.google.gson.GsonBuilder; // because sooner or later, it will be driven into Running state // @Local(value = { ConsoleProxyManager.class }) -public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMachineManager, AgentHook, VirtualMachineChecker { +public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMachineManager, AgentHook, VirtualMachineGuru { private static final Logger s_logger = Logger.getLogger(ConsoleProxyManagerImpl.class); private static final int DEFAULT_FIND_HOST_RETRY_COUNT = 2; @@ -2322,7 +2324,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach haMgr.registerHandler(VirtualMachine.Type.ConsoleProxy, this); } - boolean useLocalStorage = Boolean.parseBoolean((String) params.get(Config.SystemVMUseLocalStorage.key())); + boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); String networkRateStr = _configDao.getValue("network.throttling.rate"); String multicastRateStr = _configDao.getValue("multicast.throttling.rate"); _networkRate = ((networkRateStr == null) ? 200 : Integer.parseInt(networkRateStr)); @@ -2347,7 +2349,10 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach } @Override - public boolean finalizeDeployment(VirtualMachineTO vm, VirtualMachineProfile profile, DeployDestination dest) { + public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest) { + Start2Command cmd = cmds.getCommand(Start2Command.class); + VirtualMachineTO vm = cmd.getVirtualMachine(); + StringBuilder buf = new StringBuilder(); buf.append(" template=domP type=consoleproxy"); buf.append(" host=").append(_mgmt_host); @@ -2372,7 +2377,6 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach buf.append(" dns2=").append(nic.getDns2()); } } -// buf.append(" bootproto=dhcp"); //FIXME: Not sure what the private ip address is suppose to be. if (nic.getType() == TrafficType.Management) { buf.append(" localgw=").append(dest.getPod().getGateway()); } else if (nic.getType() == TrafficType.Control) { @@ -2398,7 +2402,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach } @Override - public boolean finalizeDeployments(List> deployments) { - return false; + public boolean checkDeploymentResult(Commands cmds, VirtualMachineProfile profile, DeployDestination dest) { + return true; } } diff --git a/server/src/com/cloud/deploy/SimplePlanner.java b/server/src/com/cloud/deploy/SimplePlanner.java index c2f4c6674e8..35beb8d86fa 100644 --- a/server/src/com/cloud/deploy/SimplePlanner.java +++ b/server/src/com/cloud/deploy/SimplePlanner.java @@ -18,7 +18,6 @@ package com.cloud.deploy; import java.util.List; -import java.util.Set; import javax.ejb.Local; @@ -43,7 +42,7 @@ public class SimplePlanner extends PlannerBase implements DeploymentPlanner { @Inject ClusterDao _clusterDao; @Override - public DeployDestination plan(VirtualMachineProfile vm, DeploymentPlan plan, Set avoid) { + public DeployDestination plan(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) { DataCenterVO dc = _dcDao.findById(plan.getDataCenterId()); List hosts = _hostDao.listBy(Type.Routing, plan.getDataCenterId()); @@ -61,7 +60,10 @@ public class SimplePlanner extends PlannerBase implements DeploymentPlanner { return new DeployDestination(dc, pod, cluster, host); } - + + public boolean check(VirtualMachineProfile vm, DeploymentPlan plan, DeployDestination dest, ExcludeList avoid) { + return true; + } protected SimplePlanner() { super(); diff --git a/server/src/com/cloud/ha/VmSyncListener.java b/server/src/com/cloud/ha/VmSyncListener.java index dd31f706770..eb179dce44b 100644 --- a/server/src/com/cloud/ha/VmSyncListener.java +++ b/server/src/com/cloud/ha/VmSyncListener.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; import com.cloud.agent.api.AgentControlCommand; @@ -30,6 +31,7 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.manager.Commands; import com.cloud.exception.AgentUnavailableException; import com.cloud.host.HostVO; import com.cloud.host.Status; @@ -59,7 +61,7 @@ public class VmSyncListener implements Listener { } @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { for (final Answer answer : answers) { if (!answer.getResult()) { s_logger.warn("Cleanup failed due to " + answer.getDetails()); @@ -83,7 +85,7 @@ public class VmSyncListener implements Listener { } @Override - public boolean processCommand(long agentId, long seq, Command[] req) { + public boolean processCommands(long agentId, long seq, Command[] req) { boolean processed = false; for (Command cmd : req) { if (cmd instanceof PingRoutingCommand) { @@ -92,7 +94,9 @@ public class VmSyncListener implements Listener { List commands = _haMgr.deltaSync(agentId, ping.getNewStates()); if (commands.size() > 0) { try { - _agentMgr.send(agentId, commands.toArray(new Command[commands.size()]), false, this); + Commands cmds = new Commands(OnError.Continue); + cmds.addCommands(commands); + _agentMgr.send(agentId, cmds, this); } catch (final AgentUnavailableException e) { s_logger.warn("Agent is now unavailable", e); } @@ -128,9 +132,10 @@ public class VmSyncListener implements Listener { s_logger.debug("Sending clean commands to the agent"); if (commands.size() > 0) { - final Command[] cmds = commands.toArray(new Command[commands.size()]); + Commands cmds = new Commands(OnError.Continue); + cmds.addCommands(commands); try { - _agentMgr.send(agentId, cmds, false, this); + _agentMgr.send(agentId, cmds, this); } catch (final AgentUnavailableException e) { s_logger.warn("Agent is unavailable now", e); } diff --git a/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java b/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java index f8c6bcb3394..cb3b993b921 100644 --- a/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java +++ b/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java @@ -52,13 +52,13 @@ public class KvmServerDiscoverer extends DiscovererBase implements Discoverer, @Inject ClusterDao _clusterDao; @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { // TODO Auto-generated method stub return false; } @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { // TODO Auto-generated method stub return false; } diff --git a/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java b/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java index ab3e7166627..da94ffc6c69 100644 --- a/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java +++ b/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java @@ -486,12 +486,12 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L } @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { return false; } @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { return false; } diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index fd73165c7e4..dc3552c02cd 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -34,6 +34,7 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CheckVirtualMachineAnswer; import com.cloud.agent.api.CheckVirtualMachineCommand; @@ -57,6 +58,7 @@ import com.cloud.agent.api.routing.SavePasswordCommand; import com.cloud.agent.api.routing.SetFirewallRuleCommand; import com.cloud.agent.api.routing.VmDataCommand; import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.api.BaseCmd; import com.cloud.async.AsyncJobExecutor; @@ -216,6 +218,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager @Inject NetworkConfigurationDao _networkProfileDao = null; @Inject NicDao _nicDao; @Inject GuestOSDao _guestOSDao = null; +// @Inject DomainRouterManager _routerMgr; @Inject(adapter=NetworkGuru.class) Adapters _networkGurus; @@ -1183,28 +1186,27 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager private boolean resendDhcpEntries(final DomainRouterVO router){ final List vms = _vmDao.listBy(router.getId(), State.Creating, State.Starting, State.Running, State.Stopping, State.Stopped, State.Migrating); - final List cmdList = new ArrayList(); + Commands cmds = new Commands(OnError.Continue); for (UserVmVO vm: vms) { if (vm.getGuestIpAddress() == null || vm.getGuestMacAddress() == null || vm.getName() == null) continue; DhcpEntryCommand decmd = new DhcpEntryCommand(vm.getGuestMacAddress(), vm.getGuestIpAddress(), router.getPrivateIpAddress(), vm.getName()); - cmdList.add(decmd); + cmds.addCommand(decmd); } - if (cmdList.size() > 0) { - final Command [] cmds = new Command[cmdList.size()]; - Answer [] answers = null; + if (cmds.size() > 0) { try { - answers = _agentMgr.send(router.getHostId(), cmdList.toArray(cmds), false); + _agentMgr.send(router.getHostId(), cmds); } catch (final AgentUnavailableException e) { s_logger.warn("agent unavailable", e); } catch (final OperationTimedoutException e) { s_logger.warn("Timed Out", e); } + Answer[] answers = cmds.getAnswers(); if (answers == null ){ return false; } int i=0; - while (i < cmdList.size()) { + while (i < cmds.size()) { Answer ans = answers[i]; i++; if ((ans != null) && (ans.getResult())) { @@ -1398,7 +1400,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager @Override public boolean associateIP(final DomainRouterVO router, final List ipAddrList, final boolean add, long vmId) { - final Command [] cmds = new Command[ipAddrList.size()]; + Commands cmds = new Commands(OnError.Continue); int i=0; boolean sourceNat = false; for (final String ipAddress: ipAddrList) { @@ -1423,14 +1425,14 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager vmGuestAddress = _vmDao.findById(vmId).getGuestIpAddress(); } - cmds[i++] = new IPAssocCommand(router.getInstanceName(), router.getPrivateIpAddress(), ipAddress, add, firstIP, sourceNat, vlanId, vlanGateway, vlanNetmask, vifMacAddress, vmGuestAddress); + cmds.addCommand(new IPAssocCommand(router.getInstanceName(), router.getPrivateIpAddress(), ipAddress, add, firstIP, sourceNat, vlanId, vlanGateway, vlanNetmask, vifMacAddress, vmGuestAddress)); sourceNat = false; } Answer[] answers = null; try { - answers = _agentMgr.send(router.getHostId(), cmds, false); + answers = _agentMgr.send(router.getHostId(), cmds); } catch (final AgentUnavailableException e) { s_logger.warn("Agent unavailable", e); return false; @@ -1500,7 +1502,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager return result; } - final List cmdList = new ArrayList(); + Commands cmds = new Commands(OnError.Continue); final List lbRules = new ArrayList(); final List fwdRules = new ArrayList(); @@ -1515,7 +1517,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager if (rule.isForwarding()) { fwdRules.add(rule); final SetFirewallRuleCommand cmd = new SetFirewallRuleCommand(routerName, routerIp, rule); - cmdList.add(cmd); + cmds.addCommand(cmd); } else { lbRules.add(rule); } @@ -1526,12 +1528,11 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager final String [] cfg = cfgrtr.generateConfiguration(fwRules); final String [][] addRemoveRules = cfgrtr.generateFwRules(fwRules); final LoadBalancerCfgCommand cmd = new LoadBalancerCfgCommand(cfg, addRemoveRules, routerName, routerIp); - cmdList.add(cmd); + cmds.addCommand(cmd); } - final Command [] cmds = new Command[cmdList.size()]; Answer [] answers = null; try { - answers = _agentMgr.send(host.getId(), cmdList.toArray(cmds), false); + answers = _agentMgr.send(host.getId(), cmds); } catch (final AgentUnavailableException e) { s_logger.warn("agent unavailable", e); } catch (final OperationTimedoutException e) { @@ -1585,7 +1586,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager return result; } - final Command [] cmds = new Command[fwRules.size()]; + Commands cmds = new Commands(OnError.Continue); int i=0; for (final FirewallRuleVO rule: fwRules) { IPAddressVO ip = _ipAddressDao.findById(rule.getPublicIpAddress()); @@ -1595,17 +1596,17 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager if (rule.isForwarding()) { fwdRules.add(rule); final SetFirewallRuleCommand cmd = new SetFirewallRuleCommand(router.getInstanceName(), router.getPrivateIpAddress(), rule); - cmds[i++] = cmd; + cmds.addCommand(cmd); } } - final Answer [] answers = null; try { - _agentMgr.send(hostId, cmds, false); + _agentMgr.send(hostId, cmds); } catch (final AgentUnavailableException e) { s_logger.warn("agent unavailable", e); } catch (final OperationTimedoutException e) { s_logger.warn("Timed Out", e); } + Answer[] answers = cmds.getAnswers(); if (answers == null ){ return result; } @@ -1890,7 +1891,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager _agentMgr.registerForHostEvents(new SshKeysDistriMonitor(this, _hostDao, _configDao), true, false, false); _haMgr.registerHandler(VirtualMachine.Type.DomainRouter, this); - boolean useLocalStorage = Boolean.parseBoolean((String)params.get(Config.SystemVMUseLocalStorage.key())); + boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); String networkRateStr = _configDao.getValue("network.throttling.rate"); String multicastRateStr = _configDao.getValue("multicast.throttling.rate"); _networkRate = ((networkRateStr == null) ? 200 : Integer.parseInt(networkRateStr)); @@ -2297,14 +2298,14 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager } String userData = vm.getUserData(); int cmdsLength = (password == null ? 0:1) + 1; - Command[] cmds = new Command[++cmdsLength]; + Commands cmds = new Commands(OnError.Stop); int cmdIndex = 0; int passwordIndex = -1; int vmDataIndex = -1; - cmds[cmdIndex] = new DhcpEntryCommand(vm.getGuestMacAddress(), vm.getGuestIpAddress(), router.getPrivateIpAddress(), vm.getName()); + cmds.addCommand(new DhcpEntryCommand(vm.getGuestMacAddress(), vm.getGuestIpAddress(), router.getPrivateIpAddress(), vm.getName())); if (password != null) { final String encodedPassword = rot13(password); - cmds[++cmdIndex] = new SavePasswordCommand(encodedPassword, vm.getPrivateIpAddress(), router.getPrivateIpAddress(), vm.getName()); + cmds.addCommand(new SavePasswordCommand(encodedPassword, vm.getPrivateIpAddress(), router.getPrivateIpAddress(), vm.getName())); passwordIndex = cmdIndex; } @@ -2313,10 +2314,10 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager String zoneName = _dcDao.findById(vm.getDataCenterId()).getName(); String routerPublicIpAddress = (router.getPublicIpAddress() != null) ? router.getPublicIpAddress() : vm.getGuestIpAddress(); - cmds[++cmdIndex] = generateVmDataCommand(router.getPrivateIpAddress(), routerPublicIpAddress, vm.getPrivateIpAddress(), userData, serviceOffering, zoneName, vm.getGuestIpAddress(), vm.getName(), vm.getInstanceName(), vm.getId()); + cmds.addCommand(generateVmDataCommand(router.getPrivateIpAddress(), routerPublicIpAddress, vm.getPrivateIpAddress(), userData, serviceOffering, zoneName, vm.getGuestIpAddress(), vm.getName(), vm.getInstanceName(), vm.getId())); vmDataIndex = cmdIndex; - Answer[] answers = _agentMgr.send(router.getHostId(), cmds, true); + Answer[] answers = _agentMgr.send(router.getHostId(), cmds); if (!answers[0].getResult()) { s_logger.error("Unable to set dhcp entry for " + vm.getId() + " - " + vm.getName() +" on domR: " + router.getName() + " due to " + answers[0].getDetails()); return null; diff --git a/server/src/com/cloud/network/SshKeysDistriMonitor.java b/server/src/com/cloud/network/SshKeysDistriMonitor.java index 80737a9813a..fb7f123da17 100644 --- a/server/src/com/cloud/network/SshKeysDistriMonitor.java +++ b/server/src/com/cloud/network/SshKeysDistriMonitor.java @@ -56,7 +56,7 @@ public class SshKeysDistriMonitor implements Listener { } @Override - public synchronized boolean processAnswer(long agentId, long seq, Answer[] resp) { + public synchronized boolean processAnswers(long agentId, long seq, Answer[] resp) { return true; } @@ -96,7 +96,7 @@ public class SshKeysDistriMonitor implements Listener { @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { // TODO Auto-generated method stub return false; } diff --git a/server/src/com/cloud/network/element/DomainRouterElement.java b/server/src/com/cloud/network/element/DomainRouterElement.java new file mode 100644 index 00000000000..7f039c13b62 --- /dev/null +++ b/server/src/com/cloud/network/element/DomainRouterElement.java @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2010 Cloud.com, 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.network.element; + +import javax.ejb.Local; + +import com.cloud.network.NetworkConfiguration; +import com.cloud.offering.NetworkOffering; +import com.cloud.utils.component.AdapterBase; +import com.cloud.vm.NicProfile; +import com.cloud.vm.VirtualMachineProfile; + + +@Local(value=NetworkElement.class) +public class DomainRouterElement extends AdapterBase implements NetworkElement { + + @Override + public boolean implement(NetworkConfiguration config, NetworkOffering offering) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean prepare(NetworkConfiguration config, NicProfile nic, VirtualMachineProfile vm, NetworkOffering offering) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean release(NetworkConfiguration config, NicProfile nic, VirtualMachineProfile vm, NetworkOffering offering) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean shutdown(NetworkConfiguration config, NetworkOffering offering) { + // TODO Auto-generated method stub + return false; + } + + protected DomainRouterElement() { + super(); + } +} diff --git a/server/src/com/cloud/network/router/DomainRouterManager.java b/server/src/com/cloud/network/router/DomainRouterManager.java index e8ec0add3f3..89bf7f40be6 100644 --- a/server/src/com/cloud/network/router/DomainRouterManager.java +++ b/server/src/com/cloud/network/router/DomainRouterManager.java @@ -1,10 +1,243 @@ /** + * Copyright (C) 2010 Cloud.com, 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.network.router; +import java.util.List; +import java.util.Map; + +import com.cloud.agent.api.to.NicTO; +import com.cloud.async.executor.AssignToLoadBalancerExecutor; +import com.cloud.async.executor.LoadBalancerParam; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.VlanVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.FirewallRuleVO; +import com.cloud.network.IPAddressVO; +import com.cloud.network.NetworkConfiguration; +import com.cloud.network.NetworkConfigurationVO; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.user.AccountVO; +import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; +import com.cloud.vm.DomainRouter; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineProfile; +/** + * NetworkManager manages the network for the different end users. + * + */ public interface DomainRouterManager extends Manager { + public static final int DEFAULT_ROUTER_VM_RAMSIZE = 128; // 128M + public static final boolean USE_POD_VLAN = false; + /** + * Assigns a router to the user. + * + * @param userId user id. + * @param dcId data center id. + * @param podId pod id. + * @param domain domain to use + * @return DomainRouter if one is assigned. + * @throws InsufficientCapacityException if assigning causes any capacity issues. + */ + DomainRouterVO assignRouter(long userId, long accountId, long dcId, long podId, String domain, String instance) throws InsufficientCapacityException; + + /** + * create the router. + * + * @param accountId account Id the router belongs to. + * @param ipAddress public ip address the router should use to access the internet. + * @param dcId data center id the router should live in. + * @param domain domain name of this network. + * @param offering service offering associated with this request + * @return DomainRouterVO if created. null if not. + */ + DomainRouterVO createRouter(long accountId, String ipAddress, long dcId, String domain, ServiceOfferingVO offering, long startEventId) throws ConcurrentOperationException; + /** + * create a DHCP server/user data server for directly connected VMs + * @param userId the user id of the user creating the router. + * @param accountId the account id of the user creating the router. + * @param dcId data center id the router should live in. + * @param domain domain name of this network. + * @return DomainRouterVO if created. null if not. + */ + DomainRouterVO createDhcpServerForDirectlyAttachedGuests(long userId, long accountId, DataCenterVO dc, HostPodVO pod, Long candidateHost, VlanVO vlan) throws ConcurrentOperationException; + + /** + /* + * Send ssh public/private key pair to specified host + * @param hostId + * @param pubKey + * @param prvKey + */ + boolean sendSshKeysToHost(Long hostId, String pubKey, String prvKey); + + /** + * save a vm password on the router. + * + * @param routerId the ID of the router to save the password to + * @param vmIpAddress the IP address of the User VM that will use the password + * @param password the password to save to the router + */ + boolean savePasswordToRouter(long routerId, String vmIpAddress, String password); + + DomainRouterVO startRouter(long routerId, long eventId); + + boolean releaseRouter(long routerId); + + boolean destroyRouter(long routerId); + + boolean stopRouter(long routerId, long eventId); + + boolean getRouterStatistics(long vmId, Map netStats, Map diskStats); + + boolean rebootRouter(long routerId, long eventId); + /** + * @param hostId get all of the virtual machine routers on a host. + * @return collection of VirtualMachineRouter + */ + List getRouters(long hostId); + + /** + * @param routerId id of the router + * @return VirtualMachineRouter + */ + DomainRouterVO getRouter(long routerId); + + /** + * Do all of the work of releasing public ip addresses. Note that + * if this method fails, there can be side effects. + * @param userId + * @param ipAddress + * @return true if it did; false if it didn't + */ + public boolean releasePublicIpAddress(long userId, String ipAddress); + + /** + * Find or create the source nat ip address a user uses within the + * data center. + * + * @param account account + * @param dc data center + * @param domain domain used for user's network. + * @param so service offering associated with this request + * @return public ip address. + */ + public String assignSourceNatIpAddress(AccountVO account, DataCenterVO dc, String domain, ServiceOfferingVO so, long startEventId, HypervisorType hyperType) throws ResourceAllocationException; + + /** + * @param fwRules list of rules to be updated + * @param router router where the rules have to be updated + * @return list of rules successfully updated + */ + public List updatePortForwardingRules(List fwRules, DomainRouterVO router, Long hostId); + + /** + * @param fwRules list of rules to be updated + * @param router router where the rules have to be updated + * @return success + */ + public boolean updateLoadBalancerRules(List fwRules, DomainRouterVO router, Long hostId); + + /** + * @param publicIpAddress public ip address associated with the fwRules + * @param fwRules list of rules to be updated + * @param router router where the rules have to be updated + * @return list of rules successfully updated + */ + public List updateFirewallRules(String publicIpAddress, List fwRules, DomainRouterVO router); + + /** + * Associates or disassociates a list of public IP address for a router. + * @param router router object to send the association to + * @param ipAddrList list of public IP addresses + * @param add true if associate, false if disassociate + * @param vmId + * @return + */ + boolean associateIP(DomainRouterVO router, List ipAddrList, boolean add, long vmId) throws ResourceAllocationException; + + boolean updateFirewallRule(FirewallRuleVO fwRule, String oldPrivateIP, String oldPrivatePort); + boolean executeAssignToLoadBalancer(AssignToLoadBalancerExecutor executor, LoadBalancerParam param); + + /** + * Add a DHCP entry on the domr dhcp server + * @param routerHostId - the host id of the domr + * @param routerIp - the private ip address of the domr + * @param vmName - the name of the VM (e.g., i-10-TEST) + * @param vmMac - the mac address of the eth0 interface of the VM + * @param vmIp - the ip address to hand out. + * @return success or failure + */ + public boolean addDhcpEntry(long routerHostId, String routerIp, String vmName, String vmMac, String vmIp); + + /** + * Adds a virtual machine into the guest network. + * 1. Starts the domR + * 2. Sets the dhcp Entry on the domR + * 3. Sets the domR + * + * @param vm user vm to add to the guest network + * @param password password for this vm. Can be null + * @return DomainRouterVO if everything is successful. null if not. + * + * @throws ConcurrentOperationException if multiple starts are being attempted. + */ + public DomainRouterVO addVirtualMachineToGuestNetwork(UserVmVO vm, String password, long startEventId) throws ConcurrentOperationException; + + String createZoneVlan(DomainRouterVO router); + + /** + * Lists IP addresses that belong to VirtualNetwork VLANs + * @param accountId - account that the IP address should belong to + * @param dcId - zone that the IP address should belong to + * @param sourceNat - (optional) true if the IP address should be a source NAT address + * @return - list of IP addresses + */ + List listPublicIpAddressesInVirtualNetwork(long accountId, long dcId, Boolean sourceNat); + + NetworkConfigurationVO setupNetworkConfiguration(AccountVO owner, NetworkOfferingVO offering, DeploymentPlan plan); + NetworkConfigurationVO setupNetworkConfiguration(AccountVO owner, NetworkOfferingVO offering, NetworkConfiguration predefined, DeploymentPlan plan); + List setupNetworkConfigurations(AccountVO owner, List offerings, DeploymentPlan plan); + + List getSystemAccountNetworkOfferings(String... offeringNames); + + List allocate(VirtualMachineProfile vm, List> networks) throws InsufficientCapacityException; + + NicTO[] prepare(VirtualMachineProfile profile, DeployDestination dest) throws InsufficientAddressCapacityException, InsufficientVirtualNetworkCapcityException; + void release(VirtualMachineProfile vmProfile); + + void create(K vm); + + List getNics(K vm); + boolean upgradeRouter(long routerId, long serviceOfferingId); } diff --git a/server/src/com/cloud/network/router/DomainRouterManagerImpl.java b/server/src/com/cloud/network/router/DomainRouterManagerImpl.java index 6374652e535..48fb6d550d3 100644 --- a/server/src/com/cloud/network/router/DomainRouterManagerImpl.java +++ b/server/src/com/cloud/network/router/DomainRouterManagerImpl.java @@ -1,25 +1,1950 @@ /** + * Copyright (C) 2010 Cloud.com, 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.network.router; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import javax.ejb.Local; import javax.naming.ConfigurationException; -@Local(value=DomainRouterManager.class) -public class DomainRouterManagerImpl implements DomainRouterManager { +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckVirtualMachineAnswer; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.CreateZoneVlanAnswer; +import com.cloud.agent.api.CreateZoneVlanCommand; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.ModifySshKeysCommand; +import com.cloud.agent.api.NetworkUsageAnswer; +import com.cloud.agent.api.NetworkUsageCommand; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.RebootAnswer; +import com.cloud.agent.api.RebootRouterCommand; +import com.cloud.agent.api.StartRouterAnswer; +import com.cloud.agent.api.StartRouterCommand; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.agent.api.routing.IPAssocCommand; +import com.cloud.agent.api.routing.LoadBalancerCfgCommand; +import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.SetFirewallRuleCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.manager.Commands; +import com.cloud.alert.AlertManager; +import com.cloud.api.BaseCmd; +import com.cloud.async.AsyncJobExecutor; +import com.cloud.async.AsyncJobManager; +import com.cloud.async.AsyncJobResult; +import com.cloud.async.AsyncJobVO; +import com.cloud.async.BaseAsyncJobExecutor; +import com.cloud.async.executor.AssignToLoadBalancerExecutor; +import com.cloud.async.executor.LoadBalancerParam; +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.ResourceCount.ResourceType; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.configuration.dao.ResourceLimitDao; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.Vlan; +import com.cloud.dc.Vlan.VlanType; +import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.event.EventState; +import com.cloud.event.EventTypes; +import com.cloud.event.EventVO; +import com.cloud.event.dao.EventDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.exception.InternalErrorException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.StorageUnavailableException; +import com.cloud.ha.HighAvailabilityManager; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.FirewallRuleVO; +import com.cloud.network.HAProxyConfigurator; +import com.cloud.network.IPAddressVO; +import com.cloud.network.LoadBalancerConfigurator; +import com.cloud.network.LoadBalancerVO; +import com.cloud.network.Network.TrafficType; +import com.cloud.network.NetworkConfiguration; +import com.cloud.network.NetworkConfigurationVO; +import com.cloud.network.SecurityGroupVMMapVO; +import com.cloud.network.SshKeysDistriMonitor; +import com.cloud.network.configuration.NetworkGuru; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.NetworkConfigurationDao; +import com.cloud.network.dao.SecurityGroupVMMapDao; +import com.cloud.network.element.NetworkElement; +import com.cloud.offering.NetworkOffering; +import com.cloud.offering.NetworkOffering.GuestIpType; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.resource.Resource; +import com.cloud.resource.Resource.ReservationStrategy; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.StorageManager; +import com.cloud.storage.StoragePoolVO; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.DiskTemplateDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.StoragePoolDao; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VMTemplateHostDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserStatisticsVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; +import com.cloud.user.dao.UserStatisticsDao; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.Inject; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.DomainRouter; +import com.cloud.vm.DomainRouter.Role; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.State; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Event; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VirtualMachineName; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; + +/** + * NetworkManagerImpl implements NetworkManager. + */ +@Local(value={DomainRouterManager.class}) +public class DomainRouterManagerImpl implements DomainRouterManager, VirtualMachineManager { + private static final Logger s_logger = Logger.getLogger(DomainRouterManagerImpl.class); + String _name; + @Inject DataCenterDao _dcDao = null; + @Inject VlanDao _vlanDao = null; + @Inject FirewallRulesDao _rulesDao = null; + @Inject SecurityGroupVMMapDao _securityGroupVMMapDao = null; + @Inject LoadBalancerDao _loadBalancerDao = null; + @Inject IPAddressDao _ipAddressDao = null; + @Inject VMTemplateDao _templateDao = null; + @Inject DiskTemplateDao _diskDao = null; + @Inject DomainRouterDao _routerDao = null; + @Inject UserDao _userDao = null; + @Inject AccountDao _accountDao = null; + @Inject DomainDao _domainDao = null; + @Inject UserStatisticsDao _userStatsDao = null; + @Inject VolumeDao _volsDao = null; + @Inject HostDao _hostDao = null; + @Inject EventDao _eventDao = null; + @Inject ConfigurationDao _configDao; + @Inject HostPodDao _podDao = null; + @Inject VMTemplateHostDao _vmTemplateHostDao = null; + @Inject UserVmDao _vmDao = null; + @Inject ResourceLimitDao _limitDao = null; + @Inject CapacityDao _capacityDao = null; + @Inject AgentManager _agentMgr; + @Inject StorageManager _storageMgr; + @Inject HighAvailabilityManager _haMgr; + @Inject AlertManager _alertMgr; + @Inject AccountManager _accountMgr; + @Inject ConfigurationManager _configMgr; + @Inject AsyncJobManager _asyncMgr; + @Inject StoragePoolDao _storagePoolDao = null; + @Inject ServiceOfferingDao _serviceOfferingDao = null; + @Inject UserStatisticsDao _statsDao = null; + @Inject NetworkOfferingDao _networkOfferingDao = null; + @Inject NetworkConfigurationDao _networkProfileDao = null; + @Inject NicDao _nicDao; + @Inject GuestOSDao _guestOSDao = null; + @Inject DomainRouterManager _routerMgr; + + @Inject(adapter=NetworkGuru.class) + Adapters _networkGurus; + @Inject(adapter=NetworkElement.class) + Adapters _networkElements; + + long _routerTemplateId = -1; + int _routerRamSize; + // String _privateNetmask; + int _retry = 2; + String _domain; + String _instance; + int _routerCleanupInterval = 3600; + int _routerStatsInterval = 300; + private ServiceOfferingVO _offering; + private int _networkRate; + private int _multicastRate; + private HashMap _systemNetworks = new HashMap(5); + + private VMTemplateVO _template; + + ScheduledExecutorService _executor; + + @Override + public boolean destroy(DomainRouterVO router) { + return destroyRouter(router.getId()); + } @Override - public boolean configure(String name, Map params) throws ConfigurationException { - _name = name; + public boolean sendSshKeysToHost(Long hostId, String pubKey, String prvKey) { + ModifySshKeysCommand cmd = new ModifySshKeysCommand(pubKey, prvKey); + final Answer answer = _agentMgr.easySend(hostId, cmd); + + if (answer != null) + return true; + else + return false; + } + + @Override @DB + public String assignSourceNatIpAddress(AccountVO account, final DataCenterVO dc, final String domain, final ServiceOfferingVO serviceOffering, long startEventId, HypervisorType hyperType) throws ResourceAllocationException { + if (serviceOffering.getGuestIpType() == NetworkOffering.GuestIpType.DirectDual || serviceOffering.getGuestIpType() == NetworkOffering.GuestIpType.DirectSingle) { + return null; + } + final long dcId = dc.getId(); + String sourceNat = null; + + final long accountId = account.getId(); + + Transaction txn = Transaction.currentTxn(); + try { + final EventVO event = new EventVO(); + event.setUserId(1L); // system user performed the action... + event.setAccountId(account.getId()); + event.setType(EventTypes.EVENT_NET_IP_ASSIGN); + + txn.start(); + + account = _accountDao.acquire(accountId); + if (account == null) { + s_logger.warn("Unable to lock account " + accountId); + return null; + } + if(s_logger.isDebugEnabled()) + s_logger.debug("lock account " + accountId + " is acquired"); + + boolean isAccountIP = false; + List addrs = listPublicIpAddressesInVirtualNetwork(account.getId(), dcId, true); + if (addrs.size() == 0) { + + // Check that the maximum number of public IPs for the given accountId will not be exceeded + if (_accountMgr.resourceLimitExceeded(account, ResourceType.public_ip)) { + ResourceAllocationException rae = new ResourceAllocationException("Maximum number of public IP addresses for account: " + account.getAccountName() + " has been exceeded."); + rae.setResourceType("ip"); + throw rae; + } + + //check for account specific IP pool. + addrs = listPublicIpAddressesInVirtualNetwork(account.getId(), dcId, null); + if (addrs.size() == 0){ + + if (s_logger.isDebugEnabled()) { + s_logger.debug("assigning a new ip address"); + } + Pair ipAndVlan = _vlanDao.assignIpAddress(dc.getId(), accountId, account.getDomainId(), VlanType.VirtualNetwork, true); + + if (ipAndVlan != null) { + sourceNat = ipAndVlan.first(); + + // Increment the number of public IPs for this accountId in the database + _accountMgr.incrementResourceCount(accountId, ResourceType.public_ip); + event.setParameters("address=" + sourceNat + "\nsourceNat=true\ndcId="+dcId); + event.setDescription("Acquired a public ip: " + sourceNat); + _eventDao.persist(event); + } + }else{ + isAccountIP = true; + sourceNat = addrs.get(0).getAddress(); + _ipAddressDao.setIpAsSourceNat(sourceNat); + s_logger.debug("assigning a new ip address " +sourceNat); + + // Increment the number of public IPs for this accountId in the database + _accountMgr.incrementResourceCount(accountId, ResourceType.public_ip); + event.setParameters("address=" + sourceNat + "\nsourceNat=true\ndcId="+dcId); + event.setDescription("Acquired a public ip: " + sourceNat); + _eventDao.persist(event); + } + + } else { + sourceNat = addrs.get(0).getAddress(); + } + + if (sourceNat == null) { + txn.rollback(); + event.setLevel(EventVO.LEVEL_ERROR); + event.setParameters("dcId=" + dcId); + event.setDescription("Failed to acquire a public ip."); + _eventDao.persist(event); + s_logger.error("Unable to get source nat ip address for account " + account.getId()); + return null; + } + + UserStatisticsVO stats = _userStatsDao.findBy(account.getId(), dcId); + if (stats == null) { + stats = new UserStatisticsVO(account.getId(), dcId); + _userStatsDao.persist(stats); + } + + txn.commit(); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Source Nat is " + sourceNat); + } + + DomainRouterVO router = null; + try { + router = createRouter(account.getId(), sourceNat, dcId, domain, serviceOffering, startEventId); + } catch (final Exception e) { + s_logger.error("Unable to create router for " + account.getAccountName(), e); + } + + if (router != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router is " + router.getName()); + } + return sourceNat; + } + + s_logger.warn("releasing the source nat because router was not created: " + sourceNat); + txn.start(); + if(isAccountIP){ + _ipAddressDao.unassignIpAsSourceNat(sourceNat); + }else{ + _ipAddressDao.unassignIpAddress(sourceNat); + } + + _accountMgr.decrementResourceCount(accountId, ResourceType.public_ip); + EventVO event2 = new EventVO(); + event2.setUserId(1L); + event2.setAccountId(account.getId()); + event2.setType(EventTypes.EVENT_NET_IP_RELEASE); + event2.setParameters("address=" + sourceNat + "\nsourceNat=true"); + event2.setDescription("released source nat ip " + sourceNat + " since router could not be started"); + _eventDao.persist(event2); + txn.commit(); + return null; + } finally { + if (account != null) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Releasing lock account " + accountId); + + _accountDao.release(accountId); + } + } + } + + @Override + @DB + public DomainRouterVO createDhcpServerForDirectlyAttachedGuests(long userId, long accountId, DataCenterVO dc, HostPodVO pod, Long candidateHost, VlanVO guestVlan) throws ConcurrentOperationException{ + + final AccountVO account = _accountDao.findById(accountId); + boolean podVlan = guestVlan.getVlanType().equals(VlanType.DirectAttached) && guestVlan.getVlanId().equals(Vlan.UNTAGGED); + long accountIdForDHCPServer = podVlan ? Account.ACCOUNT_ID_SYSTEM : accountId; + long domainIdForDHCPServer = podVlan ? DomainVO.ROOT_DOMAIN : account.getDomainId(); + String domainNameForDHCPServer = podVlan ? "root" : _domainDao.findById(account.getDomainId()).getName(); + + final VMTemplateVO rtrTemplate = _templateDao.findRoutingTemplate(); + + final Transaction txn = Transaction.currentTxn(); + DomainRouterVO router = null; + Long podId = pod.getId(); + pod = _podDao.acquire(podId, 20*60); + if (pod == null) { + throw new ConcurrentOperationException("Unable to acquire lock on pod " + podId ); + } + if(s_logger.isDebugEnabled()) + s_logger.debug("Lock on pod " + podId + " is acquired"); + + final long id = _routerDao.getNextInSequence(Long.class, "id"); + final String[] macAddresses = _dcDao.getNextAvailableMacAddressPair(dc.getId()); + final String mgmtMacAddress = macAddresses[0]; + final String guestMacAddress = macAddresses[1]; + final String name = VirtualMachineName.getRouterName(id, _instance).intern(); + + boolean routerLockAcquired = false; + try { + List rtrs = _routerDao.listByVlanDbId(guestVlan.getId()); + assert rtrs.size() < 2 : "How did we get more than one router per vlan?"; + if (rtrs.size() == 1) { + return rtrs.get(0); + } + String mgmtNetmask = NetUtils.getCidrNetmask(pod.getCidrSize()); + final String guestIp = _ipAddressDao.assignIpAddress(accountIdForDHCPServer, domainIdForDHCPServer, guestVlan.getId(), false); + + router = + new DomainRouterVO(id, + _offering.getId(), + name, + mgmtMacAddress, + null, + mgmtNetmask, + _routerTemplateId, + rtrTemplate.getGuestOSId(), + guestMacAddress, + guestIp, + guestVlan.getVlanNetmask(), + accountIdForDHCPServer, + domainIdForDHCPServer, + "FE:FF:FF:FF:FF:FF", + null, + "255.255.255.255", + guestVlan.getId(), + guestVlan.getVlanId(), + pod.getId(), + dc.getId(), + _routerRamSize, + guestVlan.getVlanGateway(), + domainNameForDHCPServer, + dc.getDns1(), + dc.getDns2()); + router.setRole(Role.DHCP_USERDATA); + router.setVnet(guestVlan.getVlanId()); + + router.setLastHostId(candidateHost); + + txn.start(); + router = _routerDao.persist(router); + router = _routerDao.acquire(router.getId()); + if (router == null) { + s_logger.debug("Unable to acquire lock on router " + id); + throw new CloudRuntimeException("Unable to acquire lock on router " + id); + } + + routerLockAcquired = true; + + txn.commit(); + + List vols = _storageMgr.create(account, router, rtrTemplate, dc, pod, _offering, null,0); + if (vols == null){ + _ipAddressDao.unassignIpAddress(guestIp); + _routerDao.expunge(router.getId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to create dhcp server in storage host or pool in pod " + pod.getName() + " (id:" + pod.getId() + ")"); + } + } + + final EventVO event = new EventVO(); + event.setUserId(userId); + event.setAccountId(accountIdForDHCPServer); + event.setType(EventTypes.EVENT_ROUTER_CREATE); + + if (vols == null) { + event.setDescription("failed to create DHCP Server : " + router.getName()); + event.setLevel(EventVO.LEVEL_ERROR); + _eventDao.persist(event); + throw new ExecutionException("Unable to create DHCP Server"); + } + _routerDao.updateIf(router, Event.OperationSucceeded, null); + + s_logger.info("DHCP server created: id=" + router.getId() + "; name=" + router.getName() + "; vlan=" + guestVlan.getVlanId() + "; pod=" + pod.getName()); + + event.setDescription("successfully created DHCP Server : " + router.getName() + " with ip : " + router.getGuestIpAddress()); + _eventDao.persist(event); + + return router; + } catch (final Throwable th) { + if (th instanceof ExecutionException) { + s_logger.error("Error while starting router due to " + th.getMessage()); + } else { + s_logger.error("Unable to create router", th); + } + txn.rollback(); + + if (router.getState() == State.Creating) { + _routerDao.expunge(router.getId()); + } + return null; + } finally { + if (routerLockAcquired) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Releasing lock on router " + id); + } + _routerDao.release(id); + } + if (pod != null) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Releasing lock on pod " + podId); + _podDao.release(pod.getId()); + } + } + } + + @Override + public DomainRouterVO assignRouter(final long userId, final long accountId, final long dataCenterId, final long podId, final String domain, final String instance) throws InsufficientCapacityException { + return null; + } + + @Override + public boolean releaseRouter(final long routerId) { + return destroyRouter(routerId); + } + + @Override @DB + public DomainRouterVO createRouter(final long accountId, final String publicIpAddress, final long dataCenterId, + String domain, final ServiceOfferingVO offering, long startEventId) + throws ConcurrentOperationException { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating a router for account=" + accountId + "; publicIpAddress=" + publicIpAddress + "; dc=" + dataCenterId + "domain=" + domain); + } + + final AccountVO account = _accountDao.acquire(accountId); + if (account == null) { + throw new ConcurrentOperationException("Unable to acquire account " + accountId); + } + + if(s_logger.isDebugEnabled()) + s_logger.debug("lock on account " + accountId + " for createRouter is acquired"); + + final Transaction txn = Transaction.currentTxn(); + DomainRouterVO router = null; + boolean success = false; + try { + router = _routerDao.findBy(accountId, dataCenterId); + if (router != null && router.getState() != State.Creating) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router " + router.toString() + " found for account " + accountId + " in data center " + dataCenterId); + } + success = true; + return router; + } + EventVO event = new EventVO(); + event.setUserId(1L); + event.setAccountId(accountId); + event.setType(EventTypes.EVENT_ROUTER_CREATE); + event.setState(EventState.Started); + event.setStartId(startEventId); + event.setDescription("Creating Router for account with Id: "+accountId); + event = _eventDao.persist(event); + + final DataCenterVO dc = _dcDao.findById(dataCenterId); + final VMTemplateVO template = _templateDao.findRoutingTemplate(); + + String[] macAddresses = getMacAddressPair(dataCenterId); + String privateMacAddress = macAddresses[0]; + String publicMacAddress = macAddresses[1]; + + final long id = _routerDao.getNextInSequence(Long.class, "id"); + + if (domain == null) { + domain = "v" + Long.toHexString(accountId) + "." + _domain; + } + + final String name = VirtualMachineName.getRouterName(id, _instance).intern(); + long routerMacAddress = NetUtils.mac2Long(dc.getRouterMacAddress()) | ((dc.getId() & 0xff) << 32); + + //set the guestNetworkCidr from the dc obj + String guestNetworkCidr = dc.getGuestNetworkCidr(); + String[] cidrTuple = guestNetworkCidr.split("\\/"); + String guestIpAddress = NetUtils.getIpRangeStartIpFromCidr(cidrTuple[0], Long.parseLong(cidrTuple[1])); + String guestNetmask = NetUtils.getCidrNetmask(Long.parseLong(cidrTuple[1])); + +// String path = null; +// final int numVolumes = offering.isMirroredVolumes()?2:1; +// long routerId = 0; + + // Find the VLAN ID, VLAN gateway, and VLAN netmask for publicIpAddress + IPAddressVO ipVO = _ipAddressDao.findById(publicIpAddress); + VlanVO vlan = _vlanDao.findById(ipVO.getVlanDbId()); + String vlanId = vlan.getVlanId(); + String vlanGateway = vlan.getVlanGateway(); + String vlanNetmask = vlan.getVlanNetmask(); + + Pair pod = null; + Set avoids = new HashSet(); + boolean found = false; + while ((pod = _agentMgr.findPod(template, offering, dc, accountId, avoids)) != null) { + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Attempting to create in pod " + pod.first().getName()); + } + + router = new DomainRouterVO(id, + _offering.getId(), + name, + privateMacAddress, + null, + null, + _routerTemplateId, + template.getGuestOSId(), + NetUtils.long2Mac(routerMacAddress), + guestIpAddress, + guestNetmask, + accountId, + account.getDomainId(), + publicMacAddress, + publicIpAddress, + vlanNetmask, + vlan.getId(), + vlanId, + pod.first().getId(), + dataCenterId, + _routerRamSize, + vlanGateway, + domain, + dc.getDns1(), + dc.getDns2()); + router.setMirroredVols(offering.isMirrored()); + + router.setLastHostId(pod.second()); + router = _routerDao.persist(router); + + List vols = _storageMgr.create(account, router, template, dc, pod.first(), _offering, null,0); + if(vols != null) { + found = true; + break; + } + + _routerDao.expunge(router.getId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find storage host or pool in pod " + pod.first().getName() + " (id:" + pod.first().getId() + "), checking other pods"); + } + avoids.add(pod.first().getId()); + } + + if (!found) { + event.setDescription("failed to create Domain Router : " + name); + event.setLevel(EventVO.LEVEL_ERROR); + _eventDao.persist(event); + throw new ExecutionException("Unable to create DomainRouter"); + } + _routerDao.updateIf(router, Event.OperationSucceeded, null); + + s_logger.debug("Router created: id=" + router.getId() + "; name=" + router.getName()); + + event = new EventVO(); + event.setUserId(1L); // system user performed the action + event.setAccountId(accountId); + event.setType(EventTypes.EVENT_ROUTER_CREATE); + event.setStartId(startEventId); + event.setDescription("successfully created Domain Router : " + router.getName() + " with ip : " + publicIpAddress); + _eventDao.persist(event); + success = true; + return router; + } catch (final Throwable th) { + if (th instanceof ExecutionException) { + s_logger.error("Error while starting router due to " + th.getMessage()); + } else { + s_logger.error("Unable to create router", th); + } + txn.rollback(); + + if (router != null && router.getState() == State.Creating) { + _routerDao.expunge(router.getId()); + } + return null; + } finally { + if (account != null) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Releasing lock on account " + account.getId() + " for createRouter"); + _accountDao.release(account.getId()); + } + if(!success){ + EventVO event = new EventVO(); + event.setUserId(1L); // system user performed the action + event.setAccountId(accountId); + event.setType(EventTypes.EVENT_ROUTER_CREATE); + event.setStartId(startEventId); + event.setLevel(EventVO.LEVEL_ERROR); + event.setDescription("Failed to create router for account " + accountId + " in data center " + dataCenterId); + _eventDao.persist(event); + } + } + } + + @Override + public boolean destroyRouter(final long routerId) { + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Attempting to destroy router " + routerId); + } + + DomainRouterVO router = _routerDao.acquire(routerId); + + if (router == null) { + s_logger.debug("Unable to acquire lock on router " + routerId); + return false; + } + + EventVO event = new EventVO(); + event.setUserId(User.UID_SYSTEM); + event.setAccountId(router.getAccountId()); + event.setType(EventTypes.EVENT_ROUTER_DESTROY); + event.setState(EventState.Started); + event.setParameters("id=" + routerId); + event.setDescription("Starting to destroy router : " + router.getName()); + event = _eventDao.persist(event); + + try { + if (router.getState() == State.Destroyed || router.getState() == State.Expunging || router.getRemoved() != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find router or router is destroyed: " + routerId); + } + return true; + } + + if (!stop(router, 0)) { + s_logger.debug("Unable to stop the router: " + routerId); + return false; + } + router = _routerDao.findById(routerId); + if (!_routerDao.updateIf(router, Event.DestroyRequested, router.getHostId())) { + s_logger.debug("VM " + router.toString() + " is not in a state to be destroyed."); + return false; + } + } finally { + if (s_logger.isDebugEnabled()) + s_logger.debug("Release lock on router " + routerId + " for stop"); + _routerDao.release(routerId); + } + + router.setPublicIpAddress(null); + router.setVlanDbId(null); + _routerDao.update(router.getId(), router); + _routerDao.remove(router.getId()); + + List vols = _volsDao.findByInstance(routerId); + _storageMgr.destroy(router, vols); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Successfully destroyed router: " + routerId); + } + + EventVO completedEvent = new EventVO(); + completedEvent.setUserId(User.UID_SYSTEM); + completedEvent.setAccountId(router.getAccountId()); + completedEvent.setType(EventTypes.EVENT_ROUTER_DESTROY); + completedEvent.setStartId(event.getId()); + completedEvent.setParameters("id=" + routerId); + completedEvent.setDescription("successfully destroyed router : " + router.getName()); + _eventDao.persist(completedEvent); + + return true; + } + + @Override + @DB + public boolean upgradeRouter(long routerId, long serviceOfferingId) { + DomainRouterVO router = _routerDao.acquire(routerId); + + router.setServiceOfferingId(serviceOfferingId); + return _routerDao.update(routerId, router); + } + + private String rot13(final String password) { + final StringBuffer newPassword = new StringBuffer(""); + + for (int i = 0; i < password.length(); i++) { + char c = password.charAt(i); + + if ((c >= 'a' && c <= 'm') || ((c >= 'A' && c <= 'M'))) { + c += 13; + } else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) { + c -= 13; + } + + newPassword.append(c); + } + + return newPassword.toString(); + } + + @Override + public boolean savePasswordToRouter(final long routerId, final String vmIpAddress, final String password) { + + final DomainRouterVO router = _routerDao.findById(routerId); + final String routerPrivateIpAddress = router.getPrivateIpAddress(); + final String vmName = router.getName(); + final String encodedPassword = rot13(password); + final SavePasswordCommand cmdSavePassword = new SavePasswordCommand(encodedPassword, vmIpAddress, routerPrivateIpAddress, vmName); + + if (router != null && router.getHostId() != null) { + final Answer answer = _agentMgr.easySend(router.getHostId(), cmdSavePassword); + return (answer != null && answer.getResult()); + } else { + // either the router doesn't exist or router isn't running at all + return false; + } + } + + @Override + public DomainRouterVO startRouter(final long routerId, long eventId) { + try { + return start(routerId, eventId); + } catch (final StorageUnavailableException e) { + s_logger.debug(e.getMessage()); + return null; + } catch (final ConcurrentOperationException e) { + s_logger.debug(e.getMessage()); + return null; + } + } + + @Override @DB + public DomainRouterVO start(long routerId, long startEventId) throws StorageUnavailableException, ConcurrentOperationException { + AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); + if (asyncExecutor != null) { + AsyncJobVO job = asyncExecutor.getJob(); + if (s_logger.isInfoEnabled()) + s_logger.info("Start router " + routerId + ", update async job-" + job.getId()); + _asyncMgr.updateAsyncJobAttachment(job.getId(), "domain_router", routerId); + } + + DomainRouterVO router = _routerDao.acquire(routerId); + if (router == null) { + s_logger.debug("Unable to lock the router " + routerId); + return router; + } + + if(s_logger.isDebugEnabled()) + s_logger.debug("Lock on router " + routerId + " is acquired"); + + boolean started = false; + String vnet = null; + boolean vnetAllocated = false; + try { + final State state = router.getState(); + if (state == State.Running) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router is already started: " + router.toString()); + } + started = true; + return router; + } + + EventVO event = new EventVO(); + event.setUserId(1L); + event.setAccountId(router.getAccountId()); + event.setType(EventTypes.EVENT_ROUTER_START); + event.setState(EventState.Started); + event.setDescription("Starting Router with Id: "+routerId); + event.setStartId(startEventId); + event = _eventDao.persist(event); + + if(startEventId == 0){ + // When route start is not asynchronous, use id of the Started event instead of Scheduled event + startEventId = event.getId(); + } + + + if (state == State.Destroyed || state == State.Expunging || router.getRemoved() != null) { + s_logger.debug("Starting a router that's can not be started: " + router.toString()); + return null; + } + + if (state.isTransitional()) { + throw new ConcurrentOperationException("Someone else is starting the router: " + router.toString()); + } + + final HostPodVO pod = _podDao.findById(router.getPodId()); + final HashSet avoid = new HashSet(); + final VMTemplateVO template = _templateDao.findById(router.getTemplateId()); + final DataCenterVO dc = _dcDao.findById(router.getDataCenterId()); + ServiceOfferingVO offering = _serviceOfferingDao.findById(router.getServiceOfferingId()); + List sps = _storageMgr.getStoragePoolsForVm(router.getId()); + StoragePoolVO sp = sps.get(0); // FIXME + + HostVO routingHost = (HostVO)_agentMgr.findHost(Host.Type.Routing, dc, pod, sp, offering, template, router, null, avoid); + + if (routingHost == null) { + s_logger.error("Unable to find a host to start " + router.toString()); + return null; + } + + if (!_routerDao.updateIf(router, Event.StartRequested, routingHost.getId())) { + s_logger.debug("Unable to start router " + router.toString() + " because it is not in a startable state"); + throw new ConcurrentOperationException("Someone else is starting the router: " + router.toString()); + } + + final boolean mirroredVols = router.isMirroredVols(); + try { + event = new EventVO(); + event.setUserId(1L); + event.setAccountId(router.getAccountId()); + event.setType(EventTypes.EVENT_ROUTER_START); + event.setStartId(startEventId); + + final List vms = _vmDao.listBy(routerId, State.Starting, State.Running, State.Stopped, State.Stopping); + if (vms.size() != 0) { // Find it in the existing network. + for (final UserVmVO vm : vms) { + if (vm.getVnet() != null) { + vnet = vm.getVnet(); + break; + } + } + } + + if (vnet != null) { + s_logger.debug("Router: " + router.getName() + " discovered vnet: " + vnet + " from existing VMs."); + } else { + s_logger.debug("Router: " + router.getName() + " was unable to discover vnet from existing VMs. Acquiring new vnet."); + } + + String routerMacAddress = null; + if (vnet == null && router.getRole() == Role.DHCP_FIREWALL_LB_PASSWD_USERDATA) { // If not found, then get another one. + if(USE_POD_VLAN){ + vnet = _dcDao.allocatePodVlan(router.getPodId(), router.getAccountId()); + } else { + vnet = _dcDao.allocateVnet(router.getDataCenterId(), router.getAccountId()); + } + vnetAllocated = true; + if(vnet != null){ + routerMacAddress = getRouterMacForVnet(dc, vnet); + } + } else if (router.getRole() == Role.DHCP_USERDATA) { + if (!Vlan.UNTAGGED.equals(router.getVlanId())) { + vnet = router.getVlanId().trim(); + } else { + vnet = Vlan.UNTAGGED; + } + routerMacAddress = router.getGuestMacAddress(); + } else if (vnet != null && router.getRole() == Role.DHCP_FIREWALL_LB_PASSWD_USERDATA) { + routerMacAddress = getRouterMacForVnet(dc, vnet); + } + + if (vnet == null) { + s_logger.error("Unable to get another vnet while starting router " + router.getName()); + return null; + } else { + s_logger.debug("Router: " + router.getName() + " is using vnet: " + vnet); + } + + Answer answer = null; + int retry = _retry; + + do { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Trying to start router on host " + routingHost.getName()); + } + + String privateIpAddress = _dcDao.allocateLinkLocalPrivateIpAddress(router.getDataCenterId(), routingHost.getPodId(), router.getId()); + if (privateIpAddress == null) { + s_logger.error("Unable to allocate a private ip address while creating router for pod " + routingHost.getPodId()); + avoid.add(routingHost); + continue; + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Private Ip Address allocated: " + privateIpAddress); + } + + router.setPrivateIpAddress(privateIpAddress); + router.setPrivateNetmask(NetUtils.getLinkLocalNetMask()); + router.setGuestMacAddress(routerMacAddress); + router.setVnet(vnet); + final String name = VirtualMachineName.attachVnet(router.getName(), vnet); + router.setInstanceName(name); + + _routerDao.updateIf(router, Event.OperationRetry, routingHost.getId()); + + List vols = _storageMgr.prepare(router, routingHost); + if (vols == null) { + s_logger.debug("Couldn't get storage working for " + routingHost); + continue; + } + /* + if( !_storageMgr.share(router, vols, routingHost, true) ) { + s_logger.debug("Unable to share volumes to host " + routingHost.getId()); + continue; + } + */ + + try { + String[] storageIps = new String[2]; + + // Determine the VM's OS description + String guestOSDescription; + GuestOSVO guestOS = _guestOSDao.findById(router.getGuestOSId()); + if (guestOS == null) { + String msg = "Could not find guest OS description for OSId " + + router.getGuestOSId() + " for vm: " + router.getName(); + s_logger.debug(msg); + throw new CloudRuntimeException(msg); + } else { + guestOSDescription = guestOS.getDisplayName(); + } + + final StartRouterCommand cmdStartRouter = new StartRouterCommand(router, _networkRate, + _multicastRate, name, storageIps, vols, mirroredVols, guestOSDescription); + answer = _agentMgr.send(routingHost.getId(), cmdStartRouter); + if (answer != null && answer.getResult()) { + if (answer instanceof StartRouterAnswer){ + StartRouterAnswer rAnswer = (StartRouterAnswer)answer; + if (rAnswer.getPrivateIpAddress() != null) { + router.setPrivateIpAddress(rAnswer.getPrivateIpAddress()); + } + if (rAnswer.getPrivateMacAddress() != null) { + router.setPrivateMacAddress(rAnswer.getPrivateMacAddress()); + } + } + if (resendRouterState(router)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router " + router.getName() + " started on " + routingHost.getName()); + } + started = true; + break; + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router " + router.getName() + " started on " + routingHost.getName() + " but failed to program rules"); + } + sendStopCommand(router); + } + } + s_logger.debug("Unable to start " + router.toString() + " on host " + routingHost.toString() + " due to " + answer.getDetails()); + } catch (OperationTimedoutException e) { + if (e.isActive()) { + s_logger.debug("Unable to start vm " + router.getName() + " due to operation timed out and it is active so scheduling a restart."); + _haMgr.scheduleRestart(router, true); + return null; + } + } catch (AgentUnavailableException e) { + s_logger.debug("Agent " + routingHost.toString() + " was unavailable to start VM " + router.getName()); + } + avoid.add(routingHost); + + router.setPrivateIpAddress(null); + _dcDao.releaseLinkLocalPrivateIpAddress(privateIpAddress, router.getDataCenterId(), router.getId()); + + _storageMgr.unshare(router, vols, routingHost); + } while (--retry > 0 && (routingHost = (HostVO)_agentMgr.findHost(Host.Type.Routing, dc, pod, sp, offering, template, router, null, avoid)) != null); + + + if (routingHost == null || retry <= 0) { + throw new ExecutionException("Couldn't find a routingHost"); + } + + _routerDao.updateIf(router, Event.OperationSucceeded, routingHost.getId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router " + router.toString() + " is now started on " + routingHost.toString()); + } + + event.setDescription("successfully started Domain Router: " + router.getName()); + _eventDao.persist(event); + + return _routerDao.findById(routerId); + } catch (final Throwable th) { + + if (th instanceof ExecutionException) { + s_logger.error("Error while starting router due to " + th.getMessage()); + } else if (th instanceof ConcurrentOperationException) { + throw (ConcurrentOperationException)th; + } else if (th instanceof StorageUnavailableException) { + throw (StorageUnavailableException)th; + } else { + s_logger.error("Error while starting router", th); + } + return null; + } + } finally { + + if (!started){ + Transaction txn = Transaction.currentTxn(); + txn.start(); + if (vnetAllocated == true && vnet != null) { + _dcDao.releaseVnet(vnet, router.getDataCenterId(), router.getAccountId()); + } + + router.setVnet(null); + String privateIpAddress = router.getPrivateIpAddress(); + + router.setPrivateIpAddress(null); + + if (privateIpAddress != null) { + _dcDao.releasePrivateIpAddress(privateIpAddress, router.getDataCenterId(), router.getId()); + } + + + if (_routerDao.updateIf(router, Event.OperationFailed, null)) { + txn.commit(); + } + + EventVO event = new EventVO(); + event.setUserId(1L); + event.setAccountId(router.getAccountId()); + event.setType(EventTypes.EVENT_ROUTER_START); + event.setDescription("Failed to start Router with Id: "+routerId); + event.setLevel(EventVO.LEVEL_ERROR); + event.setStartId(startEventId); + _eventDao.persist(event); + } + + if (router != null) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Releasing lock on router " + routerId); + _routerDao.release(routerId); + } + + } + } + + private String getRouterMacForVnet(final DataCenterVO dc, final String vnet) { + final long vnetId = Long.parseLong(vnet); + //ToDo: There could be 2 DomR in 2 diff pods of the zone with same vnet. Add podId to the mix to make them unique + final long routerMac = (NetUtils.mac2Long(dc.getRouterMacAddress()) & (0x00ffff0000ffffl)) | ((vnetId & 0xffff) << 16); + return NetUtils.long2Mac(routerMac); + } + + private String getRouterMacForZoneVlan(final DataCenterVO dc, final String vlan) { + final long vnetId = Long.parseLong(vlan); + final long routerMac = (NetUtils.mac2Long(dc.getRouterMacAddress()) & (0x00ffff0000ffffl)) | ((vnetId & 0xffff) << 16); + return NetUtils.long2Mac(routerMac); + } + + private String[] getMacAddressPair(long dataCenterId) { + return _dcDao.getNextAvailableMacAddressPair(dataCenterId); + } + + private boolean resendRouterState(final DomainRouterVO router) { + if (router.getRole() == Role.DHCP_FIREWALL_LB_PASSWD_USERDATA) { + //source NAT address is stored in /proc/cmdline of the domR and gets + //reassigned upon powerup. Source NAT rule gets configured in StartRouter command + final List ipAddrs = listPublicIpAddressesInVirtualNetwork(router.getAccountId(), router.getDataCenterId(), null); + final List ipAddrList = new ArrayList(); + for (final IPAddressVO ipVO : ipAddrs) { + ipAddrList.add(ipVO.getAddress()); + } + if (!ipAddrList.isEmpty()) { + final boolean success = associateIP(router, ipAddrList, true, 0); + if (!success) { + return false; + } + } + final List fwRules = new ArrayList(); + for (final IPAddressVO ipVO : ipAddrs) { + fwRules.addAll(_rulesDao.listIPForwarding(ipVO.getAddress())); + } + final List result = updateFirewallRules(router + .getPublicIpAddress(), fwRules, router); + if (result.size() != fwRules.size()) { + return false; + } + } + return resendDhcpEntries(router); + + } + + private boolean resendDhcpEntries(final DomainRouterVO router){ + final List vms = _vmDao.listBy(router.getId(), State.Creating, State.Starting, State.Running, State.Stopping, State.Stopped, State.Migrating); + Commands cmds = new Commands(OnError.Continue); + for (UserVmVO vm: vms) { + if (vm.getGuestIpAddress() == null || vm.getGuestMacAddress() == null || vm.getName() == null) + continue; + DhcpEntryCommand decmd = new DhcpEntryCommand(vm.getGuestMacAddress(), vm.getGuestIpAddress(), router.getPrivateIpAddress(), vm.getName()); + cmds.addCommand(decmd); + } + if (cmds.size() > 0) { + try { + _agentMgr.send(router.getHostId(), cmds); + } catch (final AgentUnavailableException e) { + s_logger.warn("agent unavailable", e); + } catch (final OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + } + Answer[] answers = cmds.getAnswers(); + if (answers == null ){ + return false; + } + int i=0; + while (i < cmds.size()) { + Answer ans = answers[i]; + i++; + if ((ans != null) && (ans.getResult())) { + continue; + } else { + return false; + } + } + } + return true; + } + + /* + private boolean resendUserData(final DomainRouterVO router){ + final List vms = _vmDao.listByRouterId(router.getId()); + final List cmdList = new ArrayList(); + for (UserVmVO vm: vms) { + if (vm.getGuestIpAddress() == null || vm.getGuestMacAddress() == null || vm.getName() == null) + continue; + if (vm.getUserData() == null) + continue; + UserDataCommand userDataCmd = new UserDataCommand(vm.getUserData(), vm.getGuestIpAddress(), router.getPrivateIpAddress(), vm.getName()); + cmdList.add(userDataCmd); + } + final Command [] cmds = new Command[cmdList.size()]; + Answer [] answers = null; + try { + answers = _agentMgr.send(router.getHostId(), cmdList.toArray(cmds), false); + } catch (final AgentUnavailableException e) { + s_logger.warn("agent unavailable", e); + } catch (final OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + } + if (answers == null ){ + return false; + } + int i=0; + while (i < cmdList.size()) { + Answer ans = answers[i]; + i++; + if ((ans != null) && (ans.getResult())) { + continue; + } else { + return false; + } + } + return true; + } + */ + + @Override + public boolean stopRouter(final long routerId, long eventId) { + AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); + if (asyncExecutor != null) { + AsyncJobVO job = asyncExecutor.getJob(); + + if (s_logger.isInfoEnabled()) + s_logger.info("Stop router " + routerId + ", update async job-" + job.getId()); + _asyncMgr.updateAsyncJobAttachment(job.getId(), "domain_router", routerId); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Stopping router " + routerId); + } + + return stop(_routerDao.findById(routerId), eventId); + } + + @DB + public void processStopOrRebootAnswer(final DomainRouterVO router, Answer answer) { + final Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + final UserStatisticsVO userStats = _userStatsDao.lock(router.getAccountId(), router.getDataCenterId()); + if (userStats != null) { + final RebootAnswer sa = (RebootAnswer)answer; + final Long received = sa.getBytesReceived(); + long netBytes = 0; + if (received != null) { + if (received.longValue() >= userStats.getCurrentBytesReceived()) { + netBytes = received.longValue(); + } else { + netBytes = userStats.getCurrentBytesReceived() + received; + } + } else { + netBytes = userStats.getCurrentBytesReceived(); + } + userStats.setCurrentBytesReceived(0); + userStats.setNetBytesReceived(userStats.getNetBytesReceived() + netBytes); + + final Long sent = sa.getBytesSent(); + + if (sent != null) { + if (sent.longValue() >= userStats.getCurrentBytesSent()) { + netBytes = sent.longValue(); + } else { + netBytes = userStats.getCurrentBytesSent() + sent; + } + } else { + netBytes = userStats.getCurrentBytesSent(); + } + userStats.setNetBytesSent(userStats.getNetBytesSent() + netBytes); + userStats.setCurrentBytesSent(0); + _userStatsDao.update(userStats.getId(), userStats); + } else { + s_logger.warn("User stats were not created for account " + router.getAccountId() + " and dc " + router.getDataCenterId()); + } + txn.commit(); + } catch (final Exception e) { + throw new CloudRuntimeException("Problem getting stats after reboot/stop ", e); + } + } + + @Override + public boolean getRouterStatistics(final long vmId, final Map netStats, final Map diskStats) { + final DomainRouterVO router = _routerDao.findById(vmId); + + if (router == null || router.getState() != State.Running || router.getHostId() == null) { + return true; + } + + /* + final GetVmStatsCommand cmd = new GetVmStatsCommand(router, router.getInstanceName()); + final Answer answer = _agentMgr.easySend(router.getHostId(), cmd); + if (answer == null) { + return false; + } + + final GetVmStatsAnswer stats = (GetVmStatsAnswer)answer; + + netStats.putAll(stats.getNetworkStats()); + diskStats.putAll(stats.getDiskStats()); + */ + + return true; + } + + + @Override + public boolean rebootRouter(final long routerId, long startEventId) { + AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); + if (asyncExecutor != null) { + AsyncJobVO job = asyncExecutor.getJob(); + + if (s_logger.isInfoEnabled()) + s_logger.info("Reboot router " + routerId + ", update async job-" + job.getId()); + _asyncMgr.updateAsyncJobAttachment(job.getId(), "domain_router", routerId); + } + + final DomainRouterVO router = _routerDao.findById(routerId); + + if (router == null || router.getState() == State.Destroyed) { + return false; + } + + EventVO event = new EventVO(); + event.setUserId(1L); + event.setAccountId(router.getAccountId()); + event.setType(EventTypes.EVENT_ROUTER_REBOOT); + event.setState(EventState.Started); + event.setDescription("Rebooting Router with Id: "+routerId); + event.setStartId(startEventId); + _eventDao.persist(event); + + + event = new EventVO(); + event.setUserId(1L); + event.setAccountId(router.getAccountId()); + event.setType(EventTypes.EVENT_ROUTER_REBOOT); + event.setStartId(startEventId); + + if (router.getState() == State.Running && router.getHostId() != null) { + final RebootRouterCommand cmd = new RebootRouterCommand(router.getInstanceName(), router.getPrivateIpAddress()); + final RebootAnswer answer = (RebootAnswer)_agentMgr.easySend(router.getHostId(), cmd); + + if (answer != null && resendRouterState(router)) { + processStopOrRebootAnswer(router, answer); + event.setDescription("successfully rebooted Domain Router : " + router.getName()); + _eventDao.persist(event); + return true; + } else { + event.setDescription("failed to reboot Domain Router : " + router.getName()); + event.setLevel(EventVO.LEVEL_ERROR); + _eventDao.persist(event); + return false; + } + } else { + return startRouter(routerId, 0) != null; + } + } + + @Override + public boolean associateIP(final DomainRouterVO router, final List ipAddrList, final boolean add, long vmId) { + Commands cmds = new Commands(OnError.Continue); + int i=0; + boolean sourceNat = false; + for (final String ipAddress: ipAddrList) { + if (ipAddress.equalsIgnoreCase(router.getPublicIpAddress())) + sourceNat=true; + + IPAddressVO ip = _ipAddressDao.findById(ipAddress); + VlanVO vlan = _vlanDao.findById(ip.getVlanDbId()); + String vlanId = vlan.getVlanId(); + String vlanGateway = vlan.getVlanGateway(); + String vlanNetmask = vlan.getVlanNetmask(); + boolean firstIP = (!sourceNat && (_ipAddressDao.countIPs(vlan.getDataCenterId(), vlan.getVlanId(), vlan.getVlanGateway(), vlan.getVlanNetmask(), true) == 1)); + + String vifMacAddress = null; + if (firstIP) { + String[] macAddresses = _dcDao.getNextAvailableMacAddressPair(ip.getDataCenterId()); + vifMacAddress = macAddresses[1]; + } + + String vmGuestAddress = null; + if(vmId!=0){ + vmGuestAddress = _vmDao.findById(vmId).getGuestIpAddress(); + } + + cmds.addCommand(new IPAssocCommand(router.getInstanceName(), router.getPrivateIpAddress(), ipAddress, add, firstIP, sourceNat, vlanId, vlanGateway, vlanNetmask, vifMacAddress, vmGuestAddress)); + + sourceNat = false; + } + + Answer[] answers = null; + try { + answers = _agentMgr.send(router.getHostId(), cmds); + } catch (final AgentUnavailableException e) { + s_logger.warn("Agent unavailable", e); + return false; + } catch (final OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + return false; + } + + if (answers == null) { + return false; + } + + if (answers.length != ipAddrList.size()) { + return false; + } + + for (int i1=0; i1 < answers.length; i1++) { + Answer ans = answers[i1]; + return ans.getResult(); + } + return true; } + @Override + public boolean updateFirewallRule(final FirewallRuleVO rule, String oldPrivateIP, String oldPrivatePort) { + + final IPAddressVO ipVO = _ipAddressDao.findById(rule.getPublicIpAddress()); + if (ipVO == null || ipVO.getAllocated() == null) { + return false; + } + + final DomainRouterVO router = _routerDao.findBy(ipVO.getAccountId(), ipVO.getDataCenterId()); + Long hostId = router.getHostId(); + if (router == null || router.getHostId() == null) { + return true; + } + + if (rule.isForwarding()) { + return updatePortForwardingRule(rule, router, hostId, oldPrivateIP, oldPrivatePort); + } else { + final List fwRules = _rulesDao.listIPForwarding(ipVO.getAccountId(), ipVO.getDataCenterId()); + + return updateLoadBalancerRules(fwRules, router, hostId); + } + } + + @Override + public List updateFirewallRules(final String publicIpAddress, final List fwRules, final DomainRouterVO router) { + final List result = new ArrayList(); + if (fwRules.size() == 0) { + return result; + } + + if (router == null || router.getHostId() == null) { + return fwRules; + } else { + final HostVO host = _hostDao.findById(router.getHostId()); + return updateFirewallRules(host, router.getInstanceName(), router.getPrivateIpAddress(), fwRules); + } + } + + public List updateFirewallRules(final HostVO host, final String routerName, final String routerIp, final List fwRules) { + final List result = new ArrayList(); + if (fwRules.size() == 0) { + s_logger.debug("There are no firewall rules"); + return result; + } + + Commands cmds = new Commands(OnError.Continue); + final List lbRules = new ArrayList(); + final List fwdRules = new ArrayList(); + + int i=0; + for (FirewallRuleVO rule : fwRules) { + // Determine the VLAN ID and netmask of the rule's public IP address + IPAddressVO ip = _ipAddressDao.findById(rule.getPublicIpAddress()); + VlanVO vlan = _vlanDao.findById(new Long(ip.getVlanDbId())); + String vlanNetmask = vlan.getVlanNetmask(); + rule.setVlanNetmask(vlanNetmask); + + if (rule.isForwarding()) { + fwdRules.add(rule); + final SetFirewallRuleCommand cmd = new SetFirewallRuleCommand(routerName, routerIp, rule); + cmds.addCommand(cmd); + } else { + lbRules.add(rule); + } + + } + if (lbRules.size() > 0) { //at least one load balancer rule + final LoadBalancerConfigurator cfgrtr = new HAProxyConfigurator(); + final String [] cfg = cfgrtr.generateConfiguration(fwRules); + final String [][] addRemoveRules = cfgrtr.generateFwRules(fwRules); + final LoadBalancerCfgCommand cmd = new LoadBalancerCfgCommand(cfg, addRemoveRules, routerName, routerIp); + cmds.addCommand(cmd); + } + Answer [] answers = null; + try { + answers = _agentMgr.send(host.getId(), cmds); + } catch (final AgentUnavailableException e) { + s_logger.warn("agent unavailable", e); + } catch (final OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + } + if (answers == null ){ + return result; + } + i=0; + for (final FirewallRuleVO rule:fwdRules){ + final Answer ans = answers[i++]; + if (ans != null) { + if (ans.getResult()) { + result.add(rule); + } else { + s_logger.warn("Unable to update firewall rule: " + rule.toString()); + } + } + } + if (i == (answers.length-1)) { + final Answer lbAnswer = answers[i]; + if (lbAnswer.getResult()) { + result.addAll(lbRules); + } else { + s_logger.warn("Unable to update lb rules."); + } + } + return result; + } + + private boolean updatePortForwardingRule(final FirewallRuleVO rule, final DomainRouterVO router, Long hostId, String oldPrivateIP, String oldPrivatePort) { + IPAddressVO ip = _ipAddressDao.findById(rule.getPublicIpAddress()); + VlanVO vlan = _vlanDao.findById(new Long(ip.getVlanDbId())); + rule.setVlanNetmask(vlan.getVlanNetmask()); + + final SetFirewallRuleCommand cmd = new SetFirewallRuleCommand(router.getInstanceName(), router.getPrivateIpAddress(), rule, oldPrivateIP, oldPrivatePort); + final Answer ans = _agentMgr.easySend(hostId, cmd); + if (ans == null) { + return false; + } else { + return ans.getResult(); + } + } + + @Override + public List updatePortForwardingRules(final List fwRules, final DomainRouterVO router, Long hostId ){ + final List fwdRules = new ArrayList(); + final List result = new ArrayList(); + + if (fwRules.size() == 0) { + return result; + } + + Commands cmds = new Commands(OnError.Continue); + int i=0; + for (final FirewallRuleVO rule: fwRules) { + IPAddressVO ip = _ipAddressDao.findById(rule.getPublicIpAddress()); + VlanVO vlan = _vlanDao.findById(new Long(ip.getVlanDbId())); + String vlanNetmask = vlan.getVlanNetmask(); + rule.setVlanNetmask(vlanNetmask); + if (rule.isForwarding()) { + fwdRules.add(rule); + final SetFirewallRuleCommand cmd = new SetFirewallRuleCommand(router.getInstanceName(), router.getPrivateIpAddress(), rule); + cmds.addCommand(cmd); + } + } + try { + _agentMgr.send(hostId, cmds); + } catch (final AgentUnavailableException e) { + s_logger.warn("agent unavailable", e); + } catch (final OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + } + Answer[] answers = cmds.getAnswers(); + if (answers == null ){ + return result; + } + i=0; + for (final FirewallRuleVO rule:fwdRules){ + final Answer ans = answers[i++]; + if (ans != null) { + if (ans.getResult()) { + result.add(rule); + } + } + } + return result; + } + + @Override + public boolean executeAssignToLoadBalancer(AssignToLoadBalancerExecutor executor, LoadBalancerParam param) { + try { + executor.getAsyncJobMgr().getExecutorContext().getManagementServer().assignToLoadBalancer(param.getUserId(), param.getLoadBalancerId(), param.getInstanceIdList()); + executor.getAsyncJobMgr().completeAsyncJob(executor.getJob().getId(), AsyncJobResult.STATUS_SUCCEEDED, 0, "success"); + } catch (InvalidParameterValueException e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to assign vms " + StringUtils.join(param.getInstanceIdList(), ",") + " to load balancer " + param.getLoadBalancerId() + ": " + e.getMessage()); + } + + executor.getAsyncJobMgr().completeAsyncJob(executor.getJob().getId(), AsyncJobResult.STATUS_FAILED, BaseCmd.PARAM_ERROR, "Unable to assign vms " + StringUtils.join(param.getInstanceIdList(), ",") + " to load balancer " + param.getLoadBalancerId()); + } catch (NetworkRuleConflictException e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to assign vms " + StringUtils.join(param.getInstanceIdList(), ",") + " to load balancer " + param.getLoadBalancerId() + ": " + e.getMessage()); + } + + executor.getAsyncJobMgr().completeAsyncJob(executor.getJob().getId(), AsyncJobResult.STATUS_FAILED, BaseCmd.NET_CONFLICT_LB_RULE_ERROR, e.getMessage()); + } catch (InternalErrorException e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to assign vms " + StringUtils.join(param.getInstanceIdList(), ",") + " to load balancer " + param.getLoadBalancerId() + ": " + e.getMessage()); + } + + executor.getAsyncJobMgr().completeAsyncJob(executor.getJob().getId(), AsyncJobResult.STATUS_FAILED, BaseCmd.INTERNAL_ERROR, e.getMessage()); + } catch (PermissionDeniedException e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to assign vms " + StringUtils.join(param.getInstanceIdList(), ",") + " to load balancer " + param.getLoadBalancerId() + ": " + e.getMessage()); + } + + executor.getAsyncJobMgr().completeAsyncJob(executor.getJob().getId(), + AsyncJobResult.STATUS_FAILED, BaseCmd.ACCOUNT_ERROR, e.getMessage()); + } catch(Exception e) { + s_logger.warn("Unable to assign vms " + StringUtils.join(param.getInstanceIdList(), ",") + " to load balancer " + param.getLoadBalancerId() + ": " + e.getMessage(), e); + executor.getAsyncJobMgr().completeAsyncJob(executor.getJob().getId(), + AsyncJobResult.STATUS_FAILED, BaseCmd.INTERNAL_ERROR, e.getMessage()); + } + return true; + } + + @Override @DB + public boolean releasePublicIpAddress(long userId, final String ipAddress) { + IPAddressVO ip = null; + try { + ip = _ipAddressDao.acquire(ipAddress); + + if (ip == null) { + s_logger.warn("Unable to find allocated ip: " + ipAddress); + return false; + } + + if(s_logger.isDebugEnabled()) + s_logger.debug("lock on ip " + ipAddress + " is acquired"); + + if (ip.getAllocated() == null) { + s_logger.warn("ip: " + ipAddress + " is already released"); + return false; + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Releasing ip " + ipAddress + "; sourceNat = " + ip.isSourceNat()); + } + + final List ipAddrs = new ArrayList(); + ipAddrs.add(ip.getAddress()); + final List firewallRules = _rulesDao.listIPForwardingForUpdate(ipAddress); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found firewall rules: " + firewallRules.size()); + } + + for (final FirewallRuleVO fw: firewallRules) { + fw.setEnabled(false); + } + + DomainRouterVO router = null; + if (ip.isSourceNat()) { + router = _routerDao.findByPublicIpAddress(ipAddress); + if (router != null) { + if (router.getPublicIpAddress() != null) { + return false; + } + } + } else { + router = _routerDao.findBy(ip.getAccountId(), ip.getDataCenterId()); + } + + // Now send the updates down to the domR (note: we still hold locks on address and firewall) + updateFirewallRules(ipAddress, firewallRules, router); + + for (final FirewallRuleVO rule: firewallRules) { + _rulesDao.remove(rule.getId()); + + // Save and create the event + String ruleName = (rule.isForwarding() ? "ip forwarding" : "load balancer"); + String description = "deleted " + ruleName + " rule [" + rule.getPublicIpAddress() + ":" + rule.getPublicPort() + + "]->[" + rule.getPrivateIpAddress() + ":" + rule.getPrivatePort() + "]" + " " + + rule.getProtocol(); + + // save off an event for removing the network rule + EventVO event = new EventVO(); + event.setUserId(userId); + event.setAccountId(ip.getAccountId()); + event.setType(EventTypes.EVENT_NET_RULE_DELETE); + event.setDescription(description); + event.setLevel(EventVO.LEVEL_INFO); + _eventDao.persist(event); + } + + // We've deleted all the rules for the given public IP, so remove any security group mappings for that public IP + List securityGroupMappings = _securityGroupVMMapDao.listByIp(ipAddress); + for (SecurityGroupVMMapVO securityGroupMapping : securityGroupMappings) { + _securityGroupVMMapDao.remove(securityGroupMapping.getId()); + + // save off an event for removing the security group + EventVO event = new EventVO(); + event.setUserId(userId); + event.setAccountId(ip.getAccountId()); + event.setType(EventTypes.EVENT_PORT_FORWARDING_SERVICE_REMOVE); + String params = "sgId="+securityGroupMapping.getId()+"\nvmId="+securityGroupMapping.getInstanceId(); + event.setParameters(params); + event.setDescription("Successfully removed security group " + Long.valueOf(securityGroupMapping.getSecurityGroupId()).toString() + " from virtual machine " + Long.valueOf(securityGroupMapping.getInstanceId()).toString()); + event.setLevel(EventVO.LEVEL_INFO); + _eventDao.persist(event); + } + + List loadBalancers = _loadBalancerDao.listByIpAddress(ipAddress); + for (LoadBalancerVO loadBalancer : loadBalancers) { + _loadBalancerDao.remove(loadBalancer.getId()); + + // save off an event for removing the load balancer + EventVO event = new EventVO(); + event.setUserId(userId); + event.setAccountId(ip.getAccountId()); + event.setType(EventTypes.EVENT_LOAD_BALANCER_DELETE); + String params = "id="+loadBalancer.getId(); + event.setParameters(params); + event.setDescription("Successfully deleted load balancer " + loadBalancer.getId().toString()); + event.setLevel(EventVO.LEVEL_INFO); + _eventDao.persist(event); + } + + if ((router != null) && (router.getState() == State.Running)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Disassociate ip " + router.getName()); + } + + if (associateIP(router, ipAddrs, false, 0)) { + _ipAddressDao.unassignIpAddress(ipAddress); + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to dissociate IP : " + ipAddress + " due to failing to dissociate with router: " + router.getName()); + } + + final EventVO event = new EventVO(); + event.setUserId(userId); + event.setAccountId(ip.getAccountId()); + event.setType(EventTypes.EVENT_NET_IP_RELEASE); + event.setLevel(EventVO.LEVEL_ERROR); + event.setParameters("address=" + ipAddress + "\nsourceNat="+ip.isSourceNat()); + event.setDescription("failed to released a public ip: " + ipAddress + " due to failure to disassociate with router " + router.getName()); + _eventDao.persist(event); + + return false; + } + } else { + _ipAddressDao.unassignIpAddress(ipAddress); + } + s_logger.debug("released a public ip: " + ipAddress); + final EventVO event = new EventVO(); + event.setUserId(userId); + event.setAccountId(ip.getAccountId()); + event.setType(EventTypes.EVENT_NET_IP_RELEASE); + event.setParameters("address=" + ipAddress + "\nsourceNat="+ip.isSourceNat()); + event.setDescription("released a public ip: " + ipAddress); + _eventDao.persist(event); + + return true; + } catch (final Throwable e) { + s_logger.warn("ManagementServer error", e); + return false; + } finally { + if(ip != null) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Releasing lock on ip " + ipAddress); + _ipAddressDao.release(ipAddress); + } + } + } + + @Override + public DomainRouterVO getRouter(final long routerId) { + return _routerDao.findById(routerId); + } + + @Override + public List getRouters(final long hostId) { + return _routerDao.listByHostId(hostId); + } + + @Override + public boolean updateLoadBalancerRules(final List fwRules, final DomainRouterVO router, Long hostId) { + + for (FirewallRuleVO rule : fwRules) { + // Determine the the VLAN ID and netmask of the rule's public IP address + IPAddressVO ip = _ipAddressDao.findById(rule.getPublicIpAddress()); + VlanVO vlan = _vlanDao.findById(new Long(ip.getVlanDbId())); + String vlanNetmask = vlan.getVlanNetmask(); + + rule.setVlanNetmask(vlanNetmask); + } + + final LoadBalancerConfigurator cfgrtr = new HAProxyConfigurator(); + final String [] cfg = cfgrtr.generateConfiguration(fwRules); + final String [][] addRemoveRules = cfgrtr.generateFwRules(fwRules); + final LoadBalancerCfgCommand cmd = new LoadBalancerCfgCommand(cfg, addRemoveRules, router.getInstanceName(), router.getPrivateIpAddress()); + final Answer ans = _agentMgr.easySend(hostId, cmd); + if (ans == null) { + return false; + } else { + return ans.getResult(); + } + } + + @Override + public boolean configure(final String name, final Map params) throws ConfigurationException { + _name = name; + + _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("RouterMonitor")); + + final ComponentLocator locator = ComponentLocator.getCurrentLocator(); + + final Map configs = _configDao.getConfiguration("AgentManager", params); + + _routerRamSize = NumbersUtil.parseInt(configs.get("router.ram.size"), 128); + +// String value = configs.get("guest.ip.network"); +// _guestIpAddress = value != null ? value : "10.1.1.1"; +// +// value = configs.get("guest.netmask"); +// _guestNetmask = value != null ? value : "255.255.255.0"; + + String value = configs.get("start.retry"); + _retry = NumbersUtil.parseInt(value, 2); + + value = configs.get("router.stats.interval"); + _routerStatsInterval = NumbersUtil.parseInt(value, 300); + + value = configs.get("router.cleanup.interval"); + _routerCleanupInterval = NumbersUtil.parseInt(value, 3600); + + _domain = configs.get("domain"); + if (_domain == null) { + _domain = "foo.com"; + } + + _instance = configs.get("instance.name"); + if (_instance == null) { + _instance = "DEFAULT"; + } + + s_logger.info("Router configurations: " + "ramsize=" + _routerRamSize + "; templateId=" + _routerTemplateId); + + final UserStatisticsDao statsDao = locator.getDao(UserStatisticsDao.class); + if (statsDao == null) { + throw new ConfigurationException("Unable to get " + UserStatisticsDao.class.getName()); + } + + _agentMgr.registerForHostEvents(new SshKeysDistriMonitor(null, _hostDao, _configDao), true, false, false); + _haMgr.registerHandler(VirtualMachine.Type.DomainRouter, this); + + boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); + String networkRateStr = _configDao.getValue("network.throttling.rate"); + String multicastRateStr = _configDao.getValue("multicast.throttling.rate"); + _networkRate = ((networkRateStr == null) ? 200 : Integer.parseInt(networkRateStr)); + _multicastRate = ((multicastRateStr == null) ? 10 : Integer.parseInt(multicastRateStr)); + _offering = new ServiceOfferingVO("Fake Offering For DomR", 1, _routerRamSize, 0, 0, 0, false, null, NetworkOffering.GuestIpType.Virtualized, useLocalStorage, true, null); + _offering.setUniqueName("Cloud.Com-SoftwareRouter"); + _offering = _serviceOfferingDao.persistSystemServiceOffering(_offering); + _template = _templateDao.findRoutingTemplate(); + if (_template == null) { + s_logger.error("Unable to find system vm template."); + } else { + _routerTemplateId = _template.getId(); + } + + NetworkOfferingVO publicNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemVmPublicNetwork, TrafficType.Public, null); + publicNetworkOffering = _networkOfferingDao.persistSystemNetworkOffering(publicNetworkOffering); + _systemNetworks.put(NetworkOfferingVO.SystemVmPublicNetwork, publicNetworkOffering); + NetworkOfferingVO managementNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemVmManagementNetwork, TrafficType.Management, null); + managementNetworkOffering = _networkOfferingDao.persistSystemNetworkOffering(managementNetworkOffering); + _systemNetworks.put(NetworkOfferingVO.SystemVmManagementNetwork, managementNetworkOffering); + NetworkOfferingVO controlNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemVmControlNetwork, TrafficType.Control, null); + controlNetworkOffering = _networkOfferingDao.persistSystemNetworkOffering(controlNetworkOffering); + _systemNetworks.put(NetworkOfferingVO.SystemVmControlNetwork, controlNetworkOffering); + NetworkOfferingVO guestNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemVmGuestNetwork, TrafficType.Guest, GuestIpType.Virtualized); + guestNetworkOffering = _networkOfferingDao.persistSystemNetworkOffering(guestNetworkOffering); + _systemNetworks.put(NetworkOfferingVO.SystemVmGuestNetwork, guestNetworkOffering); + NetworkOfferingVO storageNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemVmStorageNetwork, TrafficType.Storage, null); + storageNetworkOffering = _networkOfferingDao.persistSystemNetworkOffering(storageNetworkOffering); + _systemNetworks.put(NetworkOfferingVO.SystemVmGuestNetwork, storageNetworkOffering); + + s_logger.info("Network Manager is configured."); + + return true; + } + + @Override + public String getName() { + return _name; + } + @Override public boolean start() { + _executor.scheduleAtFixedRate(new RouterCleanupTask(), _routerCleanupInterval, _routerCleanupInterval, TimeUnit.SECONDS); + _executor.scheduleAtFixedRate(new NetworkUsageTask(), _routerStatsInterval, _routerStatsInterval, TimeUnit.SECONDS); return true; } @@ -28,8 +1953,790 @@ public class DomainRouterManagerImpl implements DomainRouterManager { return true; } - @Override - public String getName() { - return _name; + protected DomainRouterManagerImpl() { } + + @Override + public Command cleanup(final DomainRouterVO vm, final String vmName) { + if (vmName != null) { + return new StopCommand(vm, vmName, VirtualMachineName.getVnet(vmName)); + } else if (vm != null) { + final DomainRouterVO vo = vm; + return new StopCommand(vo, vo.getVnet()); + } else { + throw new CloudRuntimeException("Shouldn't even be here!"); + } + } + + @Override + public void completeStartCommand(final DomainRouterVO router) { + _routerDao.updateIf(router, Event.AgentReportRunning, router.getHostId()); + } + + @Override + public void completeStopCommand(final DomainRouterVO router) { + completeStopCommand(router, Event.AgentReportStopped); + } + + @DB + public void completeStopCommand(final DomainRouterVO router, final Event ev) { + final long routerId = router.getId(); + + final Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + if (_vmDao.listBy(routerId, State.Starting, State.Running).size() == 0) { + _dcDao.releaseVnet(router.getVnet(), router.getDataCenterId(), router.getAccountId()); + } + + router.setVnet(null); + + String privateIpAddress = router.getPrivateIpAddress(); + + if (privateIpAddress != null) { + _dcDao.releaseLinkLocalPrivateIpAddress(privateIpAddress, router.getDataCenterId(), router.getId()); + } + router.setPrivateIpAddress(null); + + if (!_routerDao.updateIf(router, ev, null)) { + s_logger.debug("Router is not updated"); + return; + } + txn.commit(); + } catch (final Exception e) { + throw new CloudRuntimeException("Unable to complete stop", e); + } + + if (_storageMgr.unshare(router, null) == null) { + s_logger.warn("Unable to set share to false for " + router.getId() + " on host "); + } + } + + @Override + public DomainRouterVO get(final long id) { + return getRouter(id); + } + + @Override + public Long convertToId(final String vmName) { + if (!VirtualMachineName.isValidRouterName(vmName, _instance)) { + return null; + } + + return VirtualMachineName.getRouterId(vmName); + } + + private boolean sendStopCommand(DomainRouterVO router) { + final StopCommand stop = new StopCommand(router, router.getInstanceName(), router.getVnet()); + + Answer answer = null; + boolean stopped = false; + try { + answer = _agentMgr.send(router.getHostId(), stop); + if (!answer.getResult()) { + s_logger.error("Unable to stop router"); + } else { + stopped = true; + } + } catch (AgentUnavailableException e) { + s_logger.warn("Unable to reach agent to stop vm: " + router.getId()); + } catch (OperationTimedoutException e) { + s_logger.warn("Unable to reach agent to stop vm: " + router.getId()); + s_logger.error("Unable to stop router"); + } + + return stopped; + } + + @Override + @DB + public boolean stop(DomainRouterVO router, long eventId) { + long routerId = router.getId(); + + router = _routerDao.acquire(routerId); + if (router == null) { + s_logger.debug("Unable to acquire lock on router " + routerId); + return false; + } + + EventVO event = new EventVO(); + event.setUserId(1L); + event.setAccountId(router.getAccountId()); + event.setType(EventTypes.EVENT_ROUTER_STOP); + event.setState(EventState.Started); + event.setDescription("Stopping Router with Id: "+routerId); + event.setStartId(eventId); + event = _eventDao.persist(event); + if(eventId == 0){ + eventId = event.getId(); + } + + try { + + if(s_logger.isDebugEnabled()) + s_logger.debug("Lock on router " + routerId + " for stop is acquired"); + + if (router.getRemoved() != null) { + s_logger.debug("router " + routerId + " is removed"); + return false; + } + + final Long hostId = router.getHostId(); + final State state = router.getState(); + if (state == State.Stopped || state == State.Destroyed || state == State.Expunging || router.getRemoved() != null) { + s_logger.debug("Router was either not found or the host id is null"); + return true; + } + + event = new EventVO(); + event.setUserId(1L); + event.setAccountId(router.getAccountId()); + event.setType(EventTypes.EVENT_ROUTER_STOP); + event.setStartId(eventId); + + if (!_routerDao.updateIf(router, Event.StopRequested, hostId)) { + s_logger.debug("VM " + router.toString() + " is not in a state to be stopped."); + return false; + } + + if (hostId == null) { + s_logger.debug("VM " + router.toString() + " doesn't have a host id"); + return false; + } + + final StopCommand stop = new StopCommand(router, router.getInstanceName(), router.getVnet(), router.getPrivateIpAddress()); + + Answer answer = null; + boolean stopped = false; + try { + answer = _agentMgr.send(hostId, stop); + if (!answer.getResult()) { + s_logger.error("Unable to stop router"); + event.setDescription("failed to stop Domain Router : " + router.getName()); + event.setLevel(EventVO.LEVEL_ERROR); + _eventDao.persist(event); + } else { + stopped = true; + } + } catch (AgentUnavailableException e) { + s_logger.warn("Unable to reach agent to stop vm: " + router.getId()); + } catch (OperationTimedoutException e) { + s_logger.warn("Unable to reach agent to stop vm: " + router.getId()); + s_logger.error("Unable to stop router"); + } + + if (!stopped) { + event.setDescription("failed to stop Domain Router : " + router.getName()); + event.setLevel(EventVO.LEVEL_ERROR); + _eventDao.persist(event); + _routerDao.updateIf(router, Event.OperationFailed, router.getHostId()); + return false; + } + + completeStopCommand(router, Event.OperationSucceeded); + event.setDescription("successfully stopped Domain Router : " + router.getName()); + _eventDao.persist(event); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router " + router.toString() + " is stopped"); + } + + processStopOrRebootAnswer(router, answer); + } finally { + if(s_logger.isDebugEnabled()) + s_logger.debug("Release lock on router " + routerId + " for stop"); + _routerDao.release(routerId); + } + return true; + } + + @Override + public HostVO prepareForMigration(final DomainRouterVO router) throws StorageUnavailableException { + final long routerId = router.getId(); + final boolean mirroredVols = router.isMirroredVols(); + final DataCenterVO dc = _dcDao.findById(router.getDataCenterId()); + final HostPodVO pod = _podDao.findById(router.getPodId()); + final ServiceOfferingVO offering = _serviceOfferingDao.findById(router.getServiceOfferingId()); + List sps = _storageMgr.getStoragePoolsForVm(router.getId()); + StoragePoolVO sp = sps.get(0); // FIXME + + final List vols = _volsDao.findCreatedByInstance(routerId); + + final String [] storageIps = new String[2]; + final VolumeVO vol = vols.get(0); + storageIps[0] = vol.getHostIp(); + if (mirroredVols && (vols.size() == 2)) { + storageIps[1] = vols.get(1).getHostIp(); + } + + final PrepareForMigrationCommand cmd = new PrepareForMigrationCommand(router.getInstanceName(), router.getVnet(), storageIps, vols, mirroredVols); + + HostVO routingHost = null; + final HashSet avoid = new HashSet(); + + final HostVO fromHost = _hostDao.findById(router.getHostId()); + if (fromHost.getHypervisorType() != HypervisorType.KVM && fromHost.getClusterId() == null) { + s_logger.debug("The host is not in a cluster"); + return null; + } + avoid.add(fromHost); + + while ((routingHost = (HostVO)_agentMgr.findHost(Host.Type.Routing, dc, pod, sp, offering, _template, router, fromHost, avoid)) != null) { + avoid.add(routingHost); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Trying to migrate router to host " + routingHost.getName()); + } + + if( ! _storageMgr.share(router, vols, routingHost, false) ) { + s_logger.warn("Can not share " + vol.getPath() + " to " + router.getName() ); + throw new StorageUnavailableException("Can not share " + vol.getPath() + " to " + router.getName(), vol); + } + + final Answer answer = _agentMgr.easySend(routingHost.getId(), cmd); + if (answer != null && answer.getResult()) { + return routingHost; + } + + _storageMgr.unshare(router, vols, routingHost); + } + + return null; + } + + @Override + public boolean migrate(final DomainRouterVO router, final HostVO host) { + final HostVO fromHost = _hostDao.findById(router.getHostId()); + + if (!_routerDao.updateIf(router, Event.MigrationRequested, router.getHostId())) { + s_logger.debug("State for " + router.toString() + " has changed so migration can not take place."); + return false; + } + + final MigrateCommand cmd = new MigrateCommand(router.getInstanceName(), host.getPrivateIpAddress(), false); + final Answer answer = _agentMgr.easySend(fromHost.getId(), cmd); + if (answer == null) { + return false; + } + + final List vols = _volsDao.findCreatedByInstance(router.getId()); + if (vols.size() == 0) { + return true; + } + + _storageMgr.unshare(router, vols, fromHost); + + return true; + } + + @Override + public boolean completeMigration(final DomainRouterVO router, final HostVO host) throws OperationTimedoutException, AgentUnavailableException { + final CheckVirtualMachineCommand cvm = new CheckVirtualMachineCommand(router.getInstanceName()); + final CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer)_agentMgr.send(host.getId(), cvm); + if (answer == null || !answer.getResult()) { + s_logger.debug("Unable to complete migration for " + router.getId()); + _routerDao.updateIf(router, Event.AgentReportStopped, null); + return false; + } + + final State state = answer.getState(); + if (state == State.Stopped) { + s_logger.warn("Unable to complete migration as we can not detect it on " + host.getId()); + _routerDao.updateIf(router, Event.AgentReportStopped, null); + return false; + } + + _routerDao.updateIf(router, Event.OperationSucceeded, host.getId()); + + return true; + } + + protected class RouterCleanupTask implements Runnable { + + public RouterCleanupTask() { + } + + @Override + public void run() { + try { + final List ids = _routerDao.findLonelyRouters(); + s_logger.info("Found " + ids.size() + " routers to stop. "); + + for (final Long id : ids) { + stopRouter(id, 0); + } + s_logger.info("Done my job. Time to rest."); + } catch (Exception e) { + s_logger.warn("Unable to stop routers. Will retry. ", e); + } + } + } + + @Override + public boolean addDhcpEntry(final long routerHostId, final String routerIp, String vmName, String vmMac, String vmIp) { + final DhcpEntryCommand dhcpEntry = new DhcpEntryCommand(vmMac, vmIp, routerIp, vmName); + + + final Answer answer = _agentMgr.easySend(routerHostId, dhcpEntry); + return (answer != null && answer.getResult()); + } + + @Override + public DomainRouterVO addVirtualMachineToGuestNetwork(UserVmVO vm, String password, long startEventId) throws ConcurrentOperationException { + try { + DomainRouterVO router = start(vm.getDomainRouterId(), 0); + if (router == null) { + s_logger.error("Can't find a domain router to start VM: " + vm.getName()); + return null; + } + + if (vm.getGuestMacAddress() == null){ + String routerGuestMacAddress = null; + if(USE_POD_VLAN){ + if((vm.getPodId() == router.getPodId())){ + routerGuestMacAddress = router.getGuestMacAddress(); + } else { + //Not in the same pod use guest zone mac address + routerGuestMacAddress = router.getGuestZoneMacAddress(); + } + String vmMacAddress = NetUtils.long2Mac((NetUtils.mac2Long(routerGuestMacAddress) & 0xffffffff0000L) | (NetUtils.ip2Long(vm.getGuestIpAddress()) & 0xffff)); + vm.setGuestMacAddress(vmMacAddress); + } + else { + String vmMacAddress = NetUtils.long2Mac((NetUtils.mac2Long(router.getGuestMacAddress()) & 0xffffffff0000L) | (NetUtils.ip2Long(vm.getGuestIpAddress()) & 0xffff)); + vm.setGuestMacAddress(vmMacAddress); + } + } + String userData = vm.getUserData(); + int cmdsLength = (password == null ? 0:1) + 1; + Commands cmds = new Commands(OnError.Stop); + int cmdIndex = 0; + int passwordIndex = -1; + int vmDataIndex = -1; + cmds.addCommand(new DhcpEntryCommand(vm.getGuestMacAddress(), vm.getGuestIpAddress(), router.getPrivateIpAddress(), vm.getName())); + if (password != null) { + final String encodedPassword = rot13(password); + cmds.addCommand(new SavePasswordCommand(encodedPassword, vm.getPrivateIpAddress(), router.getPrivateIpAddress(), vm.getName())); + passwordIndex = cmdIndex; + } + + + String serviceOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId()).getDisplayText(); + String zoneName = _dcDao.findById(vm.getDataCenterId()).getName(); + String routerPublicIpAddress = (router.getPublicIpAddress() != null) ? router.getPublicIpAddress() : vm.getGuestIpAddress(); + + cmds.addCommand(generateVmDataCommand(router.getPrivateIpAddress(), routerPublicIpAddress, vm.getPrivateIpAddress(), userData, serviceOffering, zoneName, vm.getGuestIpAddress(), vm.getName(), vm.getInstanceName(), vm.getId())); + vmDataIndex = cmdIndex; + + Answer[] answers = _agentMgr.send(router.getHostId(), cmds); + if (!answers[0].getResult()) { + s_logger.error("Unable to set dhcp entry for " + vm.getId() + " - " + vm.getName() +" on domR: " + router.getName() + " due to " + answers[0].getDetails()); + return null; + } + + if (password != null && !answers[passwordIndex].getResult()) { + s_logger.error("Unable to set password for " + vm.getId() + " - " + vm.getName() + " due to " + answers[passwordIndex].getDetails()); + return null; + } + + if (vmDataIndex > 0 && !answers[vmDataIndex].getResult()) { + s_logger.error("Unable to set VM data for " + vm.getId() + " - " + vm.getName() + " due to " + answers[vmDataIndex].getDetails()); + return null; + } + return router; + } catch (StorageUnavailableException e) { + s_logger.error("Unable to start router " + vm.getDomainRouterId() + " because storage is unavailable."); + return null; + } catch (AgentUnavailableException e) { + s_logger.error("Unable to setup the router " + vm.getDomainRouterId() + " for vm " + vm.getId() + " - " + vm.getName() + " because agent is unavailable"); + return null; + } catch (OperationTimedoutException e) { + s_logger.error("Unable to setup the router " + vm.getDomainRouterId() + " for vm " + vm.getId() + " - " + vm.getName() + " because agent is too busy"); + return null; + } + } + + private VmDataCommand generateVmDataCommand(String routerPrivateIpAddress, String routerPublicIpAddress, + String vmPrivateIpAddress, String userData, String serviceOffering, String zoneName, + String guestIpAddress, String vmName, String vmInstanceName, long vmId) { + VmDataCommand cmd = new VmDataCommand(routerPrivateIpAddress, vmPrivateIpAddress); + + cmd.addVmData("userdata", "user-data", userData); + cmd.addVmData("metadata", "service-offering", serviceOffering); + cmd.addVmData("metadata", "availability-zone", zoneName); + cmd.addVmData("metadata", "local-ipv4", guestIpAddress); + cmd.addVmData("metadata", "local-hostname", vmName); + cmd.addVmData("metadata", "public-ipv4", routerPublicIpAddress); + cmd.addVmData("metadata", "public-hostname", routerPublicIpAddress); + cmd.addVmData("metadata", "instance-id", vmInstanceName); + cmd.addVmData("metadata", "vm-id", String.valueOf(vmId)); + + return cmd; + } + + public void releaseVirtualMachineFromGuestNetwork(UserVmVO vm) { + } + + @Override + public String createZoneVlan(DomainRouterVO router) { + String zoneVlan = _dcDao.allocateVnet(router.getDataCenterId(), router.getAccountId()); + final DataCenterVO dc = _dcDao.findById(router.getDataCenterId()); + router.setZoneVlan(zoneVlan); + router.setGuestZoneMacAddress(getRouterMacForZoneVlan(dc, zoneVlan)); + _routerDao.update(router.getId(), router); + final CreateZoneVlanCommand cmdCreateZoneVlan = new CreateZoneVlanCommand(router); + CreateZoneVlanAnswer answer = (CreateZoneVlanAnswer) _agentMgr.easySend(router.getHostId(), cmdCreateZoneVlan); + if(!answer.getResult()){ + s_logger.error("Unable to create zone vlan for router: "+router.getName()+ " zoneVlan: "+zoneVlan); + return null; + } + return zoneVlan; + } + + @Override + public List listPublicIpAddressesInVirtualNetwork(long accountId, long dcId, Boolean sourceNat) { + SearchBuilder ipAddressSB = _ipAddressDao.createSearchBuilder(); + ipAddressSB.and("accountId", ipAddressSB.entity().getAccountId(), SearchCriteria.Op.EQ); + ipAddressSB.and("dataCenterId", ipAddressSB.entity().getDataCenterId(), SearchCriteria.Op.EQ); + if (sourceNat != null) { + ipAddressSB.and("sourceNat", ipAddressSB.entity().isSourceNat(), SearchCriteria.Op.EQ); + } + + SearchBuilder virtualNetworkVlanSB = _vlanDao.createSearchBuilder(); + virtualNetworkVlanSB.and("vlanType", virtualNetworkVlanSB.entity().getVlanType(), SearchCriteria.Op.EQ); + ipAddressSB.join("virtualNetworkVlanSB", virtualNetworkVlanSB, ipAddressSB.entity().getVlanDbId(), virtualNetworkVlanSB.entity().getId(), JoinBuilder.JoinType.INNER); + + SearchCriteria ipAddressSC = ipAddressSB.create(); + ipAddressSC.setParameters("accountId", accountId); + ipAddressSC.setParameters("dataCenterId", dcId); + if (sourceNat != null) { + ipAddressSC.setParameters("sourceNat", sourceNat); + } + ipAddressSC.setJoinParameters("virtualNetworkVlanSB", "vlanType", VlanType.VirtualNetwork); + + return _ipAddressDao.search(ipAddressSC, null); + } + + @Override + public NetworkConfigurationVO setupNetworkConfiguration(AccountVO owner, NetworkOfferingVO offering, DeploymentPlan plan) { + return setupNetworkConfiguration(owner, offering, null, plan); + } + + @Override + public NetworkConfigurationVO setupNetworkConfiguration(AccountVO owner, NetworkOfferingVO offering, NetworkConfiguration predefined, DeploymentPlan plan) { + List configs = _networkProfileDao.listBy(owner.getId(), offering.getId(), plan.getDataCenterId()); + if (configs.size() > 0) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found existing network configuration for offering " + offering + ": " + configs.get(0)); + } + return configs.get(0); + } + + for (NetworkGuru guru : _networkGurus) { + NetworkConfiguration config = guru.design(offering, plan, predefined, owner); + if (config == null) { + continue; + } + + if (config.getId() != null) { + if (config instanceof NetworkConfigurationVO) { + return (NetworkConfigurationVO)config; + } else { + return _networkProfileDao.findById(config.getId()); + } + } + + NetworkConfigurationVO vo = new NetworkConfigurationVO(config, offering.getId(), plan.getDataCenterId(), guru.getName()); + return _networkProfileDao.persist(vo, owner.getId()); + } + + throw new CloudRuntimeException("Unable to convert network offering to network profile: " + offering.getId()); + } + + @Override + public List setupNetworkConfigurations(AccountVO owner, List offerings, DeploymentPlan plan) { + List profiles = new ArrayList(offerings.size()); + for (NetworkOfferingVO offering : offerings) { + profiles.add(setupNetworkConfiguration(owner, offering, plan)); + } + return profiles; + } + + @Override + public List getSystemAccountNetworkOfferings(String... offeringNames) { + List offerings = new ArrayList(offeringNames.length); + for (String offeringName : offeringNames) { + NetworkOfferingVO network = _systemNetworks.get(offeringName); + if (network == null) { + throw new CloudRuntimeException("Unable to find system network profile for " + offeringName); + } + offerings.add(network); + } + return offerings; + } + + public NetworkConfigurationVO createNetworkConfiguration(NetworkOfferingVO offering, DeploymentPlan plan, AccountVO owner) { + return null; + } + + + @Override @DB + public List allocate(VirtualMachineProfile vm, List> networks) throws InsufficientCapacityException { + List nicProfiles = new ArrayList(networks.size()); + + Transaction txn = Transaction.currentTxn(); + txn.start(); + + int deviceId = 0; + + boolean[] deviceIds = new boolean[networks.size()]; + Arrays.fill(deviceIds, false); + + List nics = new ArrayList(networks.size()); + NicVO defaultNic = null; + + for (Pair network : networks) { + NetworkConfigurationVO config = network.first(); + NetworkGuru concierge = _networkGurus.get(config.getGuruName()); + NicProfile requested = network.second(); + NicProfile profile = concierge.allocate(config, requested, vm); + if (profile == null) { + continue; + } + NicVO vo = new NicVO(concierge.getName(), vm.getId(), config.getId()); + vo.setMode(network.first().getMode()); + + while (deviceIds[deviceId] && deviceId < deviceIds.length) { + deviceId++; + } + + deviceId = applyProfileToNic(vo, profile, deviceId); + + vo = _nicDao.persist(vo); + + if (vo.isDefaultNic()) { + if (defaultNic != null) { + throw new IllegalArgumentException("You cannot specify two nics as default nics: nic 1 = " + defaultNic + "; nic 2 = " + vo); + } + defaultNic = vo; + } + + int devId = vo.getDeviceId(); + if (devId > deviceIds.length) { + throw new IllegalArgumentException("Device id for nic is too large: " + vo); + } + if (deviceIds[devId]) { + throw new IllegalArgumentException("Conflicting device id for two different nics: " + devId); + } + + deviceIds[devId] = true; + nics.add(vo); + nicProfiles.add(new NicProfile(vo, network.first(), vo.getBroadcastUri(), vo.getIsolationUri())); + } + + if (defaultNic == null && nics.size() > 2) { + throw new IllegalArgumentException("Default Nic was not set."); + } else if (nics.size() == 1) { + nics.get(0).setDefaultNic(true); + } + + txn.commit(); + + return nicProfiles; + } + + protected Integer applyProfileToNic(NicVO vo, NicProfile profile, Integer deviceId) { + if (profile.getDeviceId() != null) { + vo.setDeviceId(profile.getDeviceId()); + } else if (deviceId != null ) { + vo.setDeviceId(deviceId++); + } + + vo.setDefaultNic(profile.isDefaultNic()); + + if (profile.getIp4Address() != null) { + vo.setIp4Address(profile.getIp4Address()); + vo.setState(NicVO.State.Reserved); + } + + if (profile.getMacAddress() != null) { + vo.setMacAddress(profile.getMacAddress()); + } + + vo.setMode(profile.getMode()); + vo.setNetmask(profile.getNetmask()); + vo.setGateway(profile.getGateway()); + + return deviceId; + } + + protected NicTO toNicTO(NicVO nic, NicProfile profile, NetworkConfigurationVO config) { + NicTO to = new NicTO(); + to.setDeviceId(nic.getDeviceId()); + to.setBroadcastType(config.getBroadcastDomainType()); + to.setType(config.getTrafficType()); + to.setIp(nic.getIp4Address()); + to.setNetmask(nic.getNetmask()); + to.setMac(nic.getMacAddress()); + if (config.getDns() != null) { + String[] tokens = config.getDns().split(","); + to.setDns1(tokens[0]); + if (tokens.length > 2) { + to.setDns2(tokens[1]); + } + } + if (nic.getGateway() != null) { + to.setGateway(nic.getGateway()); + } else { + to.setGateway(config.getGateway()); + } + to.setDefaultNic(nic.isDefaultNic()); + to.setBroadcastUri(nic.getBroadcastUri()); + to.setIsolationuri(nic.getIsolationUri()); + if (profile != null) { + to.setDns1(profile.getDns1()); + to.setDns2(profile.getDns2()); + } + + return to; + } + + @Override + public NicTO[] prepare(VirtualMachineProfile vmProfile, DeployDestination dest) throws InsufficientAddressCapacityException, InsufficientVirtualNetworkCapcityException { + List nics = _nicDao.listBy(vmProfile.getId()); + NicTO[] nicTos = new NicTO[nics.size()]; + int i = 0; + for (NicVO nic : nics) { + NetworkConfigurationVO config = _networkProfileDao.findById(nic.getNetworkConfigurationId()); + NicProfile profile = null; + if (nic.getReservationStrategy() == ReservationStrategy.Start) { + NetworkGuru concierge = _networkGurus.get(config.getGuruName()); + nic.setState(Resource.State.Reserving); + _nicDao.update(nic.getId(), nic); + profile = toNicProfile(nic); + String reservationId = concierge.reserve(profile, config, vmProfile, dest); + nic.setIp4Address(profile.getIp4Address()); + nic.setIp6Address(profile.getIp6Address()); + nic.setMacAddress(profile.getMacAddress()); + nic.setIsolationUri(profile.getIsolationUri()); + nic.setBroadcastUri(profile.getBroadCastUri()); + nic.setReservationId(reservationId); + nic.setReserver(concierge.getName()); + nic.setState(Resource.State.Reserved); + nic.setNetmask(profile.getNetmask()); + nic.setGateway(profile.getGateway()); + nic.setAddressFormat(profile.getFormat()); + _nicDao.update(nic.getId(), nic); + for (NetworkElement element : _networkElements) { + if (!element.prepare(config, profile, vmProfile, null)) { + s_logger.warn("Unable to prepare " + nic + " for element " + element.getName()); + return null; + } + } + } + + nicTos[i++] = toNicTO(nic, profile, config); + + } + return nicTos; + } + + @Override + public void release(VirtualMachineProfile vmProfile) { + List nics = _nicDao.listBy(vmProfile.getId()); + for (NicVO nic : nics) { + NetworkConfigurationVO config = _networkProfileDao.findById(nic.getNetworkConfigurationId()); + if (nic.getReservationStrategy() == ReservationStrategy.Start) { + NetworkGuru concierge = _networkGurus.get(config.getGuruName()); + nic.setState(Resource.State.Releasing); + _nicDao.update(nic.getId(), nic); + concierge.release(nic.getReservationId()); + } + } + } + + NicProfile toNicProfile(NicVO nic) { + NetworkConfiguration config = _networkProfileDao.findById(nic.getNetworkConfigurationId()); + NicProfile profile = new NicProfile(nic, config, nic.getBroadcastUri(), nic.getIsolationUri()); + return profile; + } + + public void release(long vmId) { + List nics = _nicDao.listBy(vmId); + + for (NicVO nic : nics) { + nic.setState(Resource.State.Releasing); + _nicDao.update(nic.getId(), nic); + NetworkGuru concierge = _networkGurus.get(nic.getReserver()); + if (!concierge.release(nic.getReservationId())) { + s_logger.warn("Unable to release " + nic + " using " + concierge.getName()); + } + nic.setState(Resource.State.Allocated); + _nicDao.update(nic.getId(), nic); + } + } + + @Override + public void create(K vm) { + } + + @Override + public List getNics(K vm) { + return _nicDao.listBy(vm.getId()); + } + + protected class NetworkUsageTask implements Runnable { + + public NetworkUsageTask() { + } + + @Override + public void run() { + final List routers = _routerDao.listUpByHostId(null); + s_logger.debug("Found " + routers.size() + " running routers. "); + + for (DomainRouterVO router : routers) { + String privateIP = router.getPrivateIpAddress(); + if(privateIP != null){ + final NetworkUsageCommand usageCmd = new NetworkUsageCommand(privateIP, router.getName()); + final NetworkUsageAnswer answer = (NetworkUsageAnswer)_agentMgr.easySend(router.getHostId(), usageCmd); + if(answer != null){ + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + try { + if ((answer.getBytesReceived() == 0) && (answer.getBytesSent() == 0)) { + s_logger.debug("Recieved and Sent bytes are both 0. Not updating user_statistics"); + continue; + } + txn.start(); + UserStatisticsVO stats = _statsDao.lock(router.getAccountId(), router.getDataCenterId()); + if (stats == null) { + s_logger.warn("unable to find stats for account: " + router.getAccountId()); + continue; + } + if (stats.getCurrentBytesReceived() > answer.getBytesReceived()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Received # of bytes that's less than the last one. Assuming something went wrong and persisting it. Reported: " + answer.getBytesReceived() + " Stored: " + stats.getCurrentBytesReceived()); + } + stats.setNetBytesReceived(stats.getNetBytesReceived() + stats.getCurrentBytesReceived()); + } + stats.setCurrentBytesReceived(answer.getBytesReceived()); + if (stats.getCurrentBytesSent() > answer.getBytesSent()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Received # of bytes that's less than the last one. Assuming something went wrong and persisting it. Reported: " + answer.getBytesSent() + " Stored: " + stats.getCurrentBytesSent()); + } + stats.setNetBytesSent(stats.getNetBytesSent() + stats.getCurrentBytesSent()); + } + stats.setCurrentBytesSent(answer.getBytesSent()); + _statsDao.update(stats.getId(), stats); + txn.commit(); + } catch(Exception e) { + txn.rollback(); + s_logger.warn("Unable to update user statistics for account: " + router.getAccountId() + " Rx: " + answer.getBytesReceived() + "; Tx: " + answer.getBytesSent()); + } finally { + txn.close(); + } + } + } + } + } + } + } diff --git a/server/src/com/cloud/network/security/NetworkGroupListener.java b/server/src/com/cloud/network/security/NetworkGroupListener.java index b3a63aed351..e93f4e9792c 100644 --- a/server/src/com/cloud/network/security/NetworkGroupListener.java +++ b/server/src/com/cloud/network/security/NetworkGroupListener.java @@ -71,7 +71,7 @@ public class NetworkGroupListener implements Listener { @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { Set affectedVms = new HashSet(); int commandNum = 0; for (Answer ans: answers) { @@ -95,7 +95,7 @@ public class NetworkGroupListener implements Listener { } @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { boolean processed = false; for (Command cmd : commands) { if (cmd instanceof PingRoutingWithNwGroupsCommand) { diff --git a/server/src/com/cloud/network/security/NetworkGroupManagerImpl.java b/server/src/com/cloud/network/security/NetworkGroupManagerImpl.java index 0b5f6cf3952..8d946bab422 100644 --- a/server/src/com/cloud/network/security/NetworkGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/NetworkGroupManagerImpl.java @@ -38,9 +38,9 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; -import com.cloud.agent.api.Command; import com.cloud.agent.api.NetworkIngressRulesCmd; import com.cloud.agent.api.NetworkIngressRulesCmd.IpPortAndProto; +import com.cloud.agent.manager.Commands; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -662,9 +662,9 @@ public class NetworkGroupManagerImpl implements NetworkGroupManager { if (agentId != null ) { _rulesetLogDao.findByVmId(work.getInstanceId()); NetworkIngressRulesCmd cmd = generateRulesetCmd(vm.getInstanceName(), vm.getGuestIpAddress(), vm.getGuestMacAddress(), vm.getId(), generateRulesetSignature(rules), seqnum, rules); - Command[] cmds = new Command[]{cmd}; + Commands cmds = new Commands(cmd); try { - _agentMgr.send(agentId, cmds, false, _answerListener); + _agentMgr.send(agentId, cmds, _answerListener); } catch (AgentUnavailableException e) { s_logger.debug("Unable to send updates for vm: " + userVmId + "(agentid=" + agentId + ")"); _workDao.updateStep(work.getInstanceId(), seqnum, Step.Done); diff --git a/server/src/com/cloud/server/StatsCollector.java b/server/src/com/cloud/server/StatsCollector.java index 1e6de261268..5d991853b07 100644 --- a/server/src/com/cloud/server/StatsCollector.java +++ b/server/src/com/cloud/server/StatsCollector.java @@ -32,12 +32,13 @@ import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; import com.cloud.agent.api.Answer; -import com.cloud.agent.api.Command; import com.cloud.agent.api.GetFileStatsCommand; import com.cloud.agent.api.GetStorageStatsCommand; import com.cloud.agent.api.HostStatsEntry; import com.cloud.agent.api.VmStatsEntry; +import com.cloud.agent.manager.Commands; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; import com.cloud.exception.AgentUnavailableException; @@ -59,7 +60,6 @@ import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.concurrency.NamedThreadFactory; -import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.vm.UserVmManager; @@ -144,7 +144,8 @@ public class StatsCollector { } class HostCollector implements Runnable { - public void run() { + @Override + public void run() { try { s_logger.debug("HostStatsCollector is running..."); @@ -184,7 +185,8 @@ public class StatsCollector { } class VmStatsCollector implements Runnable { - public void run() { + @Override + public void run() { try { s_logger.debug("VmStatsCollector is running..."); @@ -252,7 +254,8 @@ public class StatsCollector { } class StorageCollector implements Runnable { - public void run() { + @Override + public void run() { try { SearchCriteria sc = _hostDao.createSearchCriteria(); sc.addAnd("status", SearchCriteria.Op.EQ, Status.Up.toString()); @@ -371,7 +374,8 @@ public class StatsCollector { } class VolumeCollector implements Runnable { - public void run() { + @Override + public void run() { try { List volumes = _volsDao.listAll(); Map> commandsByPool = new HashMap>(); @@ -393,16 +397,16 @@ public class StatsCollector { List commandsList = commandsByPool.get(poolId); long[] volumeIdArray = new long[commandsList.size()]; - Command[] commandsArray = new Command[commandsList.size()]; + Commands commands = new Commands(OnError.Continue); for (int i = 0; i < commandsList.size(); i++) { VolumeCommand vCommand = commandsList.get(i); volumeIdArray[i] = vCommand.volumeId; - commandsArray[i] = vCommand.command; + commands.addCommand(vCommand.command); } List poolhosts = _storagePoolHostDao.listByPoolId(poolId); for(StoragePoolHostVO poolhost : poolhosts) { - Answer[] answers = _agentMgr.send(poolhost.getHostId(), commandsArray, false); + Answer[] answers = _agentMgr.send(poolhost.getHostId(), commands); if (answers != null) { long totalBytes = 0L; for (int i = 0; i < answers.length; i++) { diff --git a/server/src/com/cloud/storage/LocalStoragePoolListener.java b/server/src/com/cloud/storage/LocalStoragePoolListener.java index 2c82edfe064..fecf47435a6 100644 --- a/server/src/com/cloud/storage/LocalStoragePoolListener.java +++ b/server/src/com/cloud/storage/LocalStoragePoolListener.java @@ -54,12 +54,12 @@ public class LocalStoragePoolListener implements Listener { } @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { return false; } @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { return false; } diff --git a/core/src/com/cloud/storage/StorageManager.java b/server/src/com/cloud/storage/StorageManager.java similarity index 99% rename from core/src/com/cloud/storage/StorageManager.java rename to server/src/com/cloud/storage/StorageManager.java index c38c439b66d..f12bdb44a36 100755 --- a/core/src/com/cloud/storage/StorageManager.java +++ b/server/src/com/cloud/storage/StorageManager.java @@ -25,6 +25,7 @@ import java.util.Map; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.agent.manager.Commands; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.deploy.DeployDestination; @@ -235,7 +236,7 @@ public interface StorageManager extends Manager { */ boolean volumeOnSharedStoragePool(VolumeVO volume); - Answer[] sendToPool(StoragePool pool, Command[] cmds, boolean stopOnError); + Answer[] sendToPool(StoragePool pool, Commands cmds); Answer sendToPool(StoragePool pool, Command cmd); diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 4ed91e9e416..60f89a98fd7 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -57,6 +57,7 @@ import com.cloud.agent.api.storage.DeleteTemplateCommand; import com.cloud.agent.api.storage.DestroyCommand; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.api.BaseCmd; import com.cloud.async.AsyncInstanceCreateStatus; @@ -427,13 +428,13 @@ public class StorageManagerImpl implements StorageManager { } @Override - public Answer[] sendToPool(StoragePool pool, Command[] cmds, boolean stopOnError) { + public Answer[] sendToPool(StoragePool pool, Commands cmds) { List poolHosts = _poolHostDao.listByHostStatus(pool.getId(), Status.Up); Collections.shuffle(poolHosts); for (StoragePoolHostVO poolHost: poolHosts) { try { - Answer[] answerRet = _agentMgr.send(poolHost.getHostId(), cmds, stopOnError); + Answer[] answerRet = _agentMgr.send(poolHost.getHostId(), cmds); return answerRet; } catch (AgentUnavailableException e) { @@ -450,8 +451,8 @@ public class StorageManagerImpl implements StorageManager { @Override public Answer sendToPool(StoragePool pool, Command cmd) { - Command[] cmds = new Command[]{cmd}; - Answer[] answers = sendToPool(pool, cmds, true); + Commands cmds = new Commands(cmd); + Answer[] answers = sendToPool(pool, cmds); if (answers == null) { return null; } diff --git a/server/src/com/cloud/storage/download/DownloadListener.java b/server/src/com/cloud/storage/download/DownloadListener.java index 0d83855bd32..0c42236fed2 100644 --- a/server/src/com/cloud/storage/download/DownloadListener.java +++ b/server/src/com/cloud/storage/download/DownloadListener.java @@ -208,7 +208,7 @@ public class DownloadListener implements Listener { @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { boolean processed = false; if(answers != null & answers.length > 0) { if(answers[0] instanceof DownloadAnswer) { @@ -258,7 +258,7 @@ public class DownloadListener implements Listener { } @Override - public boolean processCommand(long agentId, long seq, Command[] req) { + public boolean processCommands(long agentId, long seq, Command[] req) { return false; } diff --git a/server/src/com/cloud/storage/listener/StoragePoolMonitor.java b/server/src/com/cloud/storage/listener/StoragePoolMonitor.java index 394eed1599d..590695045d6 100755 --- a/server/src/com/cloud/storage/listener/StoragePoolMonitor.java +++ b/server/src/com/cloud/storage/listener/StoragePoolMonitor.java @@ -56,7 +56,7 @@ public class StoragePoolMonitor implements Listener { } @Override - public synchronized boolean processAnswer(long agentId, long seq, Answer[] resp) { + public synchronized boolean processAnswers(long agentId, long seq, Answer[] resp) { return true; } @@ -87,7 +87,7 @@ public class StoragePoolMonitor implements Listener { @Override - public boolean processCommand(long agentId, long seq, Command[] req) { + public boolean processCommands(long agentId, long seq, Command[] req) { return false; } diff --git a/server/src/com/cloud/storage/listener/StorageSyncListener.java b/server/src/com/cloud/storage/listener/StorageSyncListener.java index 8266720f84b..5d03ad35421 100755 --- a/server/src/com/cloud/storage/listener/StorageSyncListener.java +++ b/server/src/com/cloud/storage/listener/StorageSyncListener.java @@ -40,7 +40,7 @@ public class StorageSyncListener implements Listener { } @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { for (Answer answer : answers) { if (answer.getResult() == false) { s_logger.warn("Unable to execute sync command: " + answer.toString()); @@ -63,7 +63,7 @@ public class StorageSyncListener implements Listener { } @Override - public boolean processCommand(long agentId, long seq, Command[] request) { + public boolean processCommands(long agentId, long seq, Command[] request) { return false; } diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageListener.java b/server/src/com/cloud/storage/secondary/SecondaryStorageListener.java index 72677dbfe9c..f5aba0b66f6 100644 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageListener.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageListener.java @@ -46,7 +46,7 @@ public class SecondaryStorageListener implements Listener { } @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { boolean processed = false; if(answers != null) { for(int i = 0; i < answers.length; i++) { @@ -61,7 +61,7 @@ public class SecondaryStorageListener implements Listener { } @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { return false; } diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index 61992e63758..2387fe8226a 100644 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -1385,7 +1385,7 @@ public class SecondaryStorageManagerImpl implements SecondaryStorageVmManager, V _IpAllocator = it.nextElement(); } - boolean useLocalStorage = Boolean.parseBoolean((String)params.get(Config.SystemVMUseLocalStorage.key())); + boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); String networkRateStr = _configDao.getValue("network.throttling.rate"); String multicastRateStr = _configDao.getValue("multicast.throttling.rate"); _networkRate = ((networkRateStr == null) ? 200 : Integer.parseInt(networkRateStr)); diff --git a/server/src/com/cloud/storage/upload/UploadListener.java b/server/src/com/cloud/storage/upload/UploadListener.java index b956074df66..69140b6ce69 100755 --- a/server/src/com/cloud/storage/upload/UploadListener.java +++ b/server/src/com/cloud/storage/upload/UploadListener.java @@ -186,7 +186,7 @@ public class UploadListener implements Listener { } @Override - public boolean processAnswer(long agentId, long seq, Answer[] answers) { + public boolean processAnswers(long agentId, long seq, Answer[] answers) { boolean processed = false; if(answers != null & answers.length > 0) { if(answers[0] instanceof UploadAnswer) { @@ -205,7 +205,7 @@ public class UploadListener implements Listener { @Override - public boolean processCommand(long agentId, long seq, Command[] commands) { + public boolean processCommands(long agentId, long seq, Command[] commands) { return false; } diff --git a/server/src/com/cloud/vm/MauriceMoss.java b/server/src/com/cloud/vm/MauriceMoss.java index ae1019b76a8..7484c90a787 100644 --- a/server/src/com/cloud/vm/MauriceMoss.java +++ b/server/src/com/cloud/vm/MauriceMoss.java @@ -18,10 +18,8 @@ package com.cloud.vm; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import javax.ejb.Local; import javax.naming.ConfigurationException; @@ -29,19 +27,23 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; -import com.cloud.agent.api.Start2Answer; +import com.cloud.agent.AgentManager.OnError; +import com.cloud.agent.api.Answer; import com.cloud.agent.api.Start2Command; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.agent.manager.Commands; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.StorageUnavailableException; import com.cloud.network.NetworkConfigurationVO; @@ -209,7 +211,7 @@ public class MauriceMoss implements VmManager { } @Override - public T start(T vm, DeploymentPlan plan, VirtualMachineChecker checker) throws InsufficientCapacityException, ConcurrentOperationException { + public T start(T vm, DeploymentPlan plan, VirtualMachineGuru guru) throws InsufficientCapacityException, ConcurrentOperationException { if (s_logger.isDebugEnabled()) { s_logger.debug("Creating actual resources for VM " + vm); } @@ -230,23 +232,25 @@ public class MauriceMoss implements VmManager { throw new CloudRuntimeException("Guest OS is not set"); } VirtualMachineProfile vmProfile = new VirtualMachineProfile(vm, offering, guestOS.getDisplayName(), template.getHypervisorType()); - _vmDao.updateIf(vm, Event.StartRequested, null); + if (!_vmDao.updateIf(vm, Event.StartRequested, null)) { + throw new ConcurrentOperationException("Unable to start vm " + vm + " due to concurrent operations"); + } - Set avoids = new HashSet(); + ExcludeList avoids = new ExcludeList(); int retry = _retry; while (retry-- != 0) { // It's != so that it can match -1. DeployDestination dest = null; - for (DeploymentPlanner dispatcher : _planners) { - dest = dispatcher.plan(vmProfile, plan, avoids); + for (DeploymentPlanner planner : _planners) { + dest = planner.plan(vmProfile, plan, avoids); if (dest != null) { - avoids.add(dest); + avoids.addHost(dest.getHost().getId()); journal.record("Deployment found ", vmProfile, dest); break; } } if (dest == null) { - throw new CloudRuntimeException("Unable to create a deployment for " + vmProfile); + throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile); } vm.setDataCenterId(dest.getDataCenter().getId()); @@ -268,20 +272,20 @@ public class MauriceMoss implements VmManager { vmTO.setNics(nics); vmTO.setDisks(volumes); - if (checker != null) { - checker.finalizeDeployment(vmTO, vmProfile, dest); + Commands cmds = new Commands(OnError.Revert); + cmds.addCommand(new Start2Command(vmTO)); + if (guru != null) { + guru.finalizeDeployment(cmds, vmProfile, dest); } - - Start2Command cmd = new Start2Command(vmTO); try { - Start2Answer answer = (Start2Answer)_agentMgr.send(dest.getHost().getId(), cmd); - if (answer.getResult()) { + Answer[] answers = _agentMgr.send(dest.getHost().getId(), cmds); + if (answers[0].getResult()) { if (!_vmDao.updateIf(vm, Event.OperationSucceeded, dest.getHost().getId())) { throw new CloudRuntimeException("Unable to transition to a new state."); } return vm; } - s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + answer.getDetails()); + s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + answers[0].getDetails()); } catch (AgentUnavailableException e) { s_logger.debug("Unable to send the start command to host " + dest.getHost()); continue; diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 05593ca8d35..93b4d60392c 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -50,7 +50,6 @@ import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsCommand; -import com.cloud.agent.api.ManageSnapshotAnswer; import com.cloud.agent.api.ManageSnapshotCommand; import com.cloud.agent.api.MigrateCommand; import com.cloud.agent.api.PrepareForMigrationCommand; @@ -60,6 +59,7 @@ import com.cloud.agent.api.StartCommand; import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.VmStatsEntry; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; +import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.api.BaseCmd; import com.cloud.async.AsyncJobExecutor; @@ -105,7 +105,6 @@ import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.DetailsDao; import com.cloud.host.dao.HostDao; -import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.FirewallRuleVO; import com.cloud.network.IPAddressVO; @@ -128,8 +127,6 @@ import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOSVO; -import com.cloud.storage.Snapshot; -import com.cloud.storage.Snapshot.SnapshotType; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; @@ -161,7 +158,6 @@ import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; import com.cloud.user.dao.UserStatisticsDao; import com.cloud.uservm.UserVm; -import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.component.Adapters; @@ -1043,8 +1039,7 @@ public class UserVmManagerImpl implements UserVmManager { StopCommand cmd = new StopCommand(vm, vm.getInstanceName(), vm.getVnet()); try { - long seq = _agentMgr.send(vm.getHostId(), new Command[] {cmd}, true, - new VMOperationListener(executor, param, vm, 0)); + long seq = _agentMgr.send(vm.getHostId(), new Commands(cmd), new VMOperationListener(executor, param, vm, 0)); resultDescription = "Execute asynchronize stop VM command: sending command to agent, seq - " + seq; if(s_logger.isDebugEnabled()) s_logger.debug(resultDescription); @@ -1118,8 +1113,7 @@ public class UserVmManagerImpl implements UserVmManager { if (vm.getState() == State.Running && vm.getHostId() != null) { RebootCommand cmd = new RebootCommand(vm.getInstanceName()); try { - long seq = _agentMgr.send(vm.getHostId(), new Command[] {cmd}, true, - new VMOperationListener(executor, param, vm, 0)); + long seq = _agentMgr.send(vm.getHostId(), new Commands(cmd), new VMOperationListener(executor, param, vm, 0)); resultDescription = "Execute asynchronize Reboot VM command: sending command to agent, seq - " + seq; if(s_logger.isDebugEnabled()) s_logger.debug(resultDescription); @@ -1707,8 +1701,7 @@ public class UserVmManagerImpl implements UserVmManager { param.setChildEventId(childEventId); StopCommand cmd = new StopCommand(vm, vm.getInstanceName(), vm.getVnet()); try { - long seq = _agentMgr.send(vm.getHostId(), new Command[] {cmd}, true, - new VMOperationListener(executor, param, vm, 0)); + long seq = _agentMgr.send(vm.getHostId(), new Commands(cmd), new VMOperationListener(executor, param, vm, 0)); resultDescription = "Execute asynchronize destroy VM command: sending stop command to agent, seq - " + seq; if(s_logger.isDebugEnabled()) s_logger.debug(resultDescription); diff --git a/server/src/com/cloud/vm/VirtualMachineGuru.java b/server/src/com/cloud/vm/VirtualMachineGuru.java new file mode 100644 index 00000000000..0c1a68034a5 --- /dev/null +++ b/server/src/com/cloud/vm/VirtualMachineGuru.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2010 Cloud.com, 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.vm; + +import com.cloud.agent.manager.Commands; +import com.cloud.deploy.DeployDestination; + +/** + * A VirtualMachineGuru knows how to process a certain type of virtual machine. + * + */ +public interface VirtualMachineGuru { + /** + * finalize the virtual machine deployment. + * @param cmds commands that were created. + * @param profile virtual machine profile. + * @param dest destination to send the command. + * @return true if everything checks out. false if not and we should try again. + */ + boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest); + + + /** + * Check the deployment results. + * @param cmds commands and answers that were sent. + * @param profile virtual machine profile. + * @param dest destination it was sent to. + * @return true if deployment was fine; false if it didn't go well. + */ + boolean checkDeploymentResult(Commands cmds, VirtualMachineProfile profile, DeployDestination dest); +} diff --git a/server/src/com/cloud/vm/VmManager.java b/server/src/com/cloud/vm/VmManager.java index b46c9ac2a7c..0849ec3e859 100644 --- a/server/src/com/cloud/vm/VmManager.java +++ b/server/src/com/cloud/vm/VmManager.java @@ -62,7 +62,7 @@ public interface VmManager extends Manager { DeploymentPlan plan, AccountVO owner) throws InsufficientCapacityException, StorageUnavailableException; - T start(T vm, DeploymentPlan plan, VirtualMachineChecker checker) throws InsufficientCapacityException, StorageUnavailableException, ConcurrentOperationException; + T start(T vm, DeploymentPlan plan, VirtualMachineGuru checker) throws InsufficientCapacityException, StorageUnavailableException, ConcurrentOperationException; T stop(T vm) throws AgentUnavailableException, ConcurrentOperationException; diff --git a/server/src/com/cloud/vm/dao/NicDaoImpl.java b/server/src/com/cloud/vm/dao/NicDaoImpl.java index c6b1eda5ac8..15f7c63e83f 100644 --- a/server/src/com/cloud/vm/dao/NicDaoImpl.java +++ b/server/src/com/cloud/vm/dao/NicDaoImpl.java @@ -28,7 +28,7 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { public List listBy(long instanceId) { SearchCriteria sc = InstanceSearch.create(); sc.setParameters("instance", instanceId); - return listIncludingRemovedBy(sc); + return listBy(sc); } } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index eaaffefc8a8..0019f016315 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -436,7 +436,6 @@ CREATE TABLE `cloud`.`host` ( `ram` bigint unsigned, `resource` varchar(255) DEFAULT NULL COMMENT 'If it is a local resource, this is the class name', `version` varchar(40) NOT NULL, - `sequence` bigint unsigned NOT NULL DEFAULT 1, `parent` varchar(255) COMMENT 'parent path for the storage server', `total_size` bigint unsigned COMMENT 'TotalSize', `capabilities` varchar(255) COMMENT 'host capabilities in comma separated list',