diff --git a/api/src/com/cloud/network/ovs/OvsCreateTunnelAnswer.java b/api/src/com/cloud/network/ovs/OvsCreateTunnelAnswer.java new file mode 100644 index 00000000000..999228b262e --- /dev/null +++ b/api/src/com/cloud/network/ovs/OvsCreateTunnelAnswer.java @@ -0,0 +1,42 @@ +package com.cloud.network.ovs; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class OvsCreateTunnelAnswer extends Answer { + Long from; + Long to; + long account; + String inPortName; + + public OvsCreateTunnelAnswer(Command cmd, boolean success, String details) { + super(cmd, success, details); + OvsCreateTunnelCommand c = (OvsCreateTunnelCommand)cmd; + from = c.getFrom(); + to = c.getTo(); + account = c.getAccount(); + inPortName = "[]"; + } + + public OvsCreateTunnelAnswer(Command cmd, boolean success, String details, String inPortName) { + this(cmd, success, details); + this.inPortName = inPortName; + } + + + public Long getFrom() { + return from; + } + + public Long getTo() { + return to; + } + + public long getAccount() { + return account; + } + + public String getInPortName() { + return inPortName; + } +} diff --git a/api/src/com/cloud/network/ovs/OvsCreateTunnelCommand.java b/api/src/com/cloud/network/ovs/OvsCreateTunnelCommand.java new file mode 100644 index 00000000000..b31a27ce324 --- /dev/null +++ b/api/src/com/cloud/network/ovs/OvsCreateTunnelCommand.java @@ -0,0 +1,45 @@ +package com.cloud.network.ovs; + +import com.cloud.agent.api.Command; + +public class OvsCreateTunnelCommand extends Command { + String key; + String remoteIp; + Long from; + Long to; + long account; + + @Override + public boolean executeInSequence() { + return true; + } + + public OvsCreateTunnelCommand(String remoteIp, String key, Long from, Long to, long account) { + this.remoteIp = remoteIp; + this.key = key; + this.from = from; + this.to = to; + this.account = account; + } + + public String getKey() { + return key; + } + + public String getRemoteIp() { + return remoteIp; + } + + public Long getFrom() { + return from; + } + + public Long getTo() { + return to; + } + + public long getAccount() { + return account; + } + +} diff --git a/api/src/com/cloud/network/ovs/OvsDestroyTunnelCommand.java b/api/src/com/cloud/network/ovs/OvsDestroyTunnelCommand.java new file mode 100644 index 00000000000..65e12179ab0 --- /dev/null +++ b/api/src/com/cloud/network/ovs/OvsDestroyTunnelCommand.java @@ -0,0 +1,27 @@ +package com.cloud.network.ovs; + +import com.cloud.agent.api.Command; + +public class OvsDestroyTunnelCommand extends Command { + long account; + String inPortName; + + public OvsDestroyTunnelCommand(long account, String inPortName) { + this.account = account; + this.inPortName = inPortName; + } + + public long getAccount() { + return account; + } + + public String getInPortName() { + return inPortName; + } + + @Override + public boolean executeInSequence() { + return true; + } + +} diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index d1e6acbcc21..3eb63938020 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -162,7 +162,10 @@ import com.cloud.network.Networks.IsolationType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.ovs.OvsCreateGreTunnelAnswer; import com.cloud.network.ovs.OvsCreateGreTunnelCommand; +import com.cloud.network.ovs.OvsCreateTunnelAnswer; +import com.cloud.network.ovs.OvsCreateTunnelCommand; import com.cloud.network.ovs.OvsDeleteFlowCommand; +import com.cloud.network.ovs.OvsDestroyTunnelCommand; import com.cloud.network.ovs.OvsSetTagAndFlowAnswer; import com.cloud.network.ovs.OvsSetTagAndFlowCommand; import com.cloud.resource.ServerResource; @@ -440,6 +443,10 @@ public abstract class CitrixResourceBase implements ServerResource { return execute((CleanupNetworkRulesCmd)cmd); } else if (cmd instanceof NetworkRulesSystemVmCommand) { return execute((NetworkRulesSystemVmCommand)cmd); + } else if (cmd instanceof OvsCreateTunnelCommand) { + return execute((OvsCreateTunnelCommand)cmd); + } else if (cmd instanceof OvsDestroyTunnelCommand) { + return execute((OvsDestroyTunnelCommand)cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -544,6 +551,29 @@ public abstract class CitrixResourceBase implements ServerResource { return null; } + private synchronized Network createTunnelNetwork(Connection conn, long account) { + try { + String nwName = "OVSTunnel" + account; + Network nw = null; + Network.Record rec = new Network.Record(); + Set networks = Network.getByNameLabel(conn, nwName); + + if (networks.size() == 0) { + rec.nameDescription = "tunnel network for account " + account; + rec.nameLabel = nwName; + nw = Network.create(conn, rec); + } else { + nw = networks.iterator().next(); + } + + enableXenServerNetwork(conn, nw, nwName, "tunnel network for account " + account); + return nw; + } catch (Exception e) { + s_logger.warn("create tunnel network failed", e); + return null; + } + } + protected Network getNetwork(Connection conn, NicTO nic) throws XenAPIException, XmlRpcException { Pair network = getNativeNetworkForTraffic(conn, nic.getType()); if (nic.getBroadcastUri() != null && nic.getBroadcastUri().toString().contains("untagged")) { @@ -556,7 +586,13 @@ public abstract class CitrixResourceBase implements ServerResource { } else if (nic.getBroadcastType() == BroadcastDomainType.Native || nic.getBroadcastType() == BroadcastDomainType.LinkLocal) { return network.first(); } else if (nic.getBroadcastType() == BroadcastDomainType.Vswitch) { - return setupvSwitchNetwork(conn); + URI broadcastUri = nic.getBroadcastUri(); + if (broadcastUri.getHost().equalsIgnoreCase("vlan")) { + return setupvSwitchNetwork(conn); + } else { + long account = Long.parseLong(broadcastUri.getHost()); + return createTunnelNetwork(conn, account); + } } throw new CloudRuntimeException("Unable to support this type of network broadcast domain: " + nic.getBroadcastUri()); @@ -3971,6 +4007,51 @@ public abstract class CitrixResourceBase implements ServerResource { return Boolean.valueOf(callHostPlugin(conn, "vmops", "can_bridge_firewall", "host_uuid", _host.uuid)); } + private Answer execute(OvsDestroyTunnelCommand cmd) { + Connection conn = getConnection(); + try { + Network nw = createTunnelNetwork(conn, cmd.getAccount()); + if (nw == null) { + return new Answer(cmd, false, "No network found"); + } + + String bridge = nw.getBridge(conn); + String result = callHostPlugin(conn, "ovstunnel", "destroy_tunnel", "bridge", bridge, "in_port", cmd.getInPortName()); + if (result.equalsIgnoreCase("SUCCESS")) { + return new Answer(cmd, true, result); + } else { + return new Answer(cmd, false, result); + } + } catch (Exception e) { + s_logger.warn("caught execption when destroy ovs tunnel", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private OvsCreateTunnelAnswer execute(OvsCreateTunnelCommand cmd) { + Connection conn = getConnection(); + try { + Network nw = createTunnelNetwork(conn, cmd.getAccount()); + if (nw == null) { + return new OvsCreateTunnelAnswer(cmd, false, "Cannot create network"); + } + + String bridge = nw.getBridge(conn); + String result = callHostPlugin(conn, "ovstunnel", "create_tunnel", "bridge", bridge, "remote_ip", cmd.getRemoteIp(), "key", cmd.getKey(), "from", cmd.getFrom().toString(), "to", cmd + .getTo().toString()); + + String[] res = result.split(":"); + if (res.length == 2 && res[0].equalsIgnoreCase("SUCCESS")) { + return new OvsCreateTunnelAnswer(cmd, true, result, res[1]); + } else { + return new OvsCreateTunnelAnswer(cmd, false, result); + } + } catch (Exception e) { + s_logger.warn("caught execption when creating ovs tunnel", e); + return new OvsCreateTunnelAnswer(cmd, false, e.getMessage()); + } + } + private Answer execute(OvsDeleteFlowCommand cmd) { _isOvs = true; diff --git a/scripts/vm/hypervisor/xenserver/ovstunnel b/scripts/vm/hypervisor/xenserver/ovstunnel new file mode 100755 index 00000000000..22da51390c5 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/ovstunnel @@ -0,0 +1,262 @@ +#!/usr/bin/python +# +# A plugin for executing script needed by vmops cloud + +import os, sys, time +import XenAPIPlugin +sys.path.append("/opt/xensource/sm/") +import util +from util import CommandException +import hostvmstats +import socket +import stat +import base64 +import tempfile +from os.path import exists as _exists +from time import localtime as _localtime, asctime as _asctime + +vSwitchDBPidFile = "/var/run/openvswitch/ovsdb-server.pid" +vSwitchDBDaemonName = "ovsdb-server" +vSwitchPidFile = "/var/run/openvswitch/ovs-vswitchd.pid" +vsctlPath = "/usr/bin/ovs-vsctl" +ofctlPath = "/usr/bin/ovs-ofctl" +vSwitchDaemonName = "ovs-vswitchd" + +logFile = "/var/log/ovstunnel.log" +fLog = None + +def echo(fn): + def wrapped(*v, **k): + name = fn.__name__ + util.SMlog("#### VMOPS enter %s ####" % name ) + res = fn(*v, **k) + util.SMlog("#### VMOPS exit %s ####" % name ) + return res + return wrapped + +def open_log (): + global fLog + + try: + if fLog == None: + fLog = open (logFile, "a") + except IOError, e: + #print e + pass + +def pr (str): + global fLog + + if fLog != None: + str = "[%s]:" % _asctime (_localtime()) + str + "\n" + fLog.write (str) + +def close_log (): + global fLog + + if fLog != None: + fLog.close () + +def is_process_run (pidFile, name): + try: + fpid = open (pidFile, "r") + pid = fpid.readline () + fpid.close () + except IOError, e: + return -1 + + pid = pid[:-1] + ps = os.popen ("ps -ae") + for l in ps: + if pid in l and name in l: + ps.close () + return 0 + + ps.close () + return -2 + +def is_tool_exist (name): + if _exists (name): + return 0 + return -1 + + +def check_switch (): + global result + + ret = is_process_run (vSwitchDBPidFile, vSwitchDBDaemonName); + if ret < 0: + if ret == -1: return "NO_DB_PID_FILE" + if ret == -2: return "DB_NOT_RUN" + + ret = is_process_run (vSwitchPidFile, vSwitchDaemonName) + if ret < 0: + if ret == -1: return "NO_SWITCH_PID_FILE" + if ret == -2: return "SWITCH_NOT_RUN" + + if is_tool_exist (vsctlPath) < 0: + return "NO_VSCTL" + + if is_tool_exist (ofctlPath) < 0: + return "NO_OFCTL" + + return "SUCCESS" + +def do_cmd (cmds, lines=False): + cmd = "" + for i in cmds: + cmd += " " + cmd += i + + pr("do command '%s'" % cmd) + f = os.popen (cmd) + if lines == True: + res = f.readlines () + else: + res = f.readline () + res = res[:-1] + f.close () + + if lines == False: + pr("command output '%s'" % res) + return res + +######################## GRE creation utils ########################## +# UUID's format is 8-4-4-4-12 +def is_uuid (uuid): + list = uuid.split ("-") + + if len (list) != 5: + return -1 + + if len (list[0]) != 8 or len (list[1]) != 4 \ + or len (list[2]) != 4 or len (list[3]) != 4 \ + or len (list[4]) != 12: + return -1 + + return 0 + +def set_flood_flow(bridge, inport): + flow = "in_port=%s idle_timeout=0 hard_timeout=0 priority=10000 actions=flood" % inport + add_flow(bridge, flow) + +@echo +def create_tunnel (session, args): + bridge = args.pop("bridge") + remoteIP = args.pop("remote_ip") + greKey = args.pop("key") + srcHost = args.pop("from") + dstHost = args.pop("to") + + res = check_switch() + if res != "SUCCESS": + return res + + name = "%s-%s-%s-%s" % (bridge, srcHost, dstHost, greKey) + + wait = [vsctlPath, "--timeout=30 wait-until bridge %s -- get bridge %s name" % \ + (bridge, bridge)] + res = do_cmd(wait) + if bridge not in res: + pr("WARNIING:Can't find bridge %s for creating tunnel!" % bridge) + result = "COMMAND_FAILED_NO_BRIDGE" + return result + + createInterface = [vsctlPath, "create interface", "name=%s" % name, \ + 'type=gre options:remote_ip=%s options:key=%s' % (remoteIP, greKey)] + ifaceUUID = do_cmd (createInterface) + if is_uuid (ifaceUUID) < 0: + pr("create interface failed, %s is not UUID" % ifaceUUID) + result = "COMMAND_FAILED_CREATE_INTERFACE_FAILED" + return result + + createPort = [vsctlPath, "create port", "name=%s" % name, \ + "interfaces=[%s]" % ifaceUUID] + portUUID = do_cmd (createPort) + if is_uuid (portUUID) < 0: + pr("create port failed, %s is not UUID" % portUUID) + result = "COMMAND_FAILED_CREATE_PORT_FAILED" + return result + + addBridge = [vsctlPath, "add bridge %s" % bridge, "ports %s" % portUUID] + do_cmd (addBridge) + + wait = [vsctlPath, "--timeout=30 wait-until port %s -- get port %s name" % \ + (name, name)] + res = do_cmd(wait) + if name in res: + port = get_field_of_interface(name, "ofport"); + if port == "[]": + return "COMMAND_FAILED_PORT_IS_[]" + + noFlood = [ofctlPath, "mod-port %s %s noflood" % (bridge, \ + port)] + do_cmd(noFlood) + + set_flood_flow(bridge, port) + pr("create tunnel successful(bridge=%s, remote_ip=%s, key=%s, from=%s, to=%s" % \ + (bridge, remoteIP, greKey, srcHost, dstHost)) + result = "SUCCESS:%s" % name + else: + pr("create gre tunnel failed") + result = "COMMAND_FAILED_CREATE_TUNNEL_FAILED" + + return result +######################## End GRE creation utils ########################## + +def del_all_flows(bridge): + delFlow = [ofctlPath, "del-flows %s" % bridge] + do_cmd(delFlow) + + normalFlow = "priority=0 idle_timeout=0 hard_timeout=0 actions=normal" + add_flow(bridge, normalFlow) + +def del_flows(bridge, ofport): + delFlow = [ofctlPath, 'del-flows %s "in_port=%s"' % (bridge, ofport)] + do_cmd(delFlow) + +def del_port(bridge, port): + delPort = [vsctlPath, "del-port %s %s" % (bridge, port)] + do_cmd(delPort) + +@echo +def destroy_tunnel(session, args): + bridge = args.pop("bridge") + inPort = args.pop("in_port") + + # delete all gre ports on bridge + if inPort == "[]": + listPorts = [vsctlPath, "list-ports %s" % bridge] + res = do_cmd(listPorts, True) + for p in res: + if bridge in p: + del_port(bridge, p) + del_all_flows(bridge) + else: + ofport = get_field_of_interface(inPort, "ofport") + del_flows(bridge, ofport) + del_port(bridge, inPort) + + return "SUCCESS" + +def get_field_of_interface(nameOruuid, field): + listIface = [vsctlPath, "list interface", nameOruuid] + res = do_cmd(listIface, True) + + for i in res: + if field in i: + (x, r) = i.split(":") + return r.lstrip().rstrip() + return None + + +def add_flow(bridge, flow): + param = bridge + ' "%s"' % flow + addflow = ["ovs-ofctl add-flow", param] + do_cmd (addflow) + +if __name__ == "__main__": + open_log() + XenAPIPlugin.dispatch({"create_tunnel":create_tunnel, "destroy_tunnel":destroy_tunnel}) + close_log() + diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch index 8042e30d103..9bc7f0a9d69 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch @@ -13,6 +13,7 @@ NFSSR.py=/opt/xensource/sm nfs.py=/opt/xensource/sm vmops=..,0755,/etc/xapi.d/plugins ovsgre=..,0755,/etc/xapi.d/plugins +ovstunnel=..,0755,/etc/xapi.d/plugins vmopsSnapshot=..,0755,/etc/xapi.d/plugins hostvmstats.py=..,0755,/opt/xensource/sm systemvm.iso=../../../../../vms,0644,/opt/xensource/packages/iso diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 6227d22fdb4..f0f275d2624 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -59,7 +59,9 @@ public enum Config { //MulticastThrottlingRate("Network", ManagementServer.class, Integer.class, "multicast.throttling.rate", "10", "Default multicast rate in megabits per second allowed.", null), NetworkThrottlingRate("Network", ManagementServer.class, Integer.class, "network.throttling.rate", "200", "Default data transfer rate in megabits per second allowed.", null), GuestDomainSuffix("Network", AgentManager.class, String.class, "guest.domain.suffix", "cloud.internal", "Default domain name for vms inside virtualized networks fronted by router", null), - OvsNetwork("Network", ManagementServer.class, Boolean.class, "open.vswitch.network", "false", "enable/disable open vswitch network", null), + DirectNetworkNoDefaultRoute("Network", ManagementServer.class, Boolean.class, "direct.network.no.default.route", "false", "Direct Network Dhcp Server should not send a default route", "true/false"), + OvsNetwork("Network", ManagementServer.class, Boolean.class, "open.vswitch.vlan.network", "false", "enable/disable vlan remapping of open vswitch network", null), + OvsTunnelNetwork("Network", ManagementServer.class, Boolean.class, "open.vswitch.tunnel.network", "false", "enable/disable open vswitch tunnel network(no vlan)", null), //VPN RemoteAccessVpnPskLength("Network", AgentManager.class, Integer.class, "remote.access.vpn.psk.length", "24", "The length of the ipsec preshared key (minimum 8, maximum 256)", null), diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index 6ba6c5edf5c..c373dbdaa87 100644 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -73,7 +73,11 @@ import com.cloud.network.dao.RemoteAccessVpnDaoImpl; import com.cloud.network.dao.VpnUserDaoImpl; import com.cloud.network.lb.LoadBalancingRulesManagerImpl; import com.cloud.network.ovs.OvsNetworkManagerImpl; +import com.cloud.network.ovs.OvsTunnelManagerImpl; import com.cloud.network.ovs.dao.GreTunnelDaoImpl; +import com.cloud.network.ovs.dao.OvsTunnelAccountDao; +import com.cloud.network.ovs.dao.OvsTunnelAccountDaoImpl; +import com.cloud.network.ovs.dao.OvsTunnelDaoImpl; import com.cloud.network.ovs.dao.OvsWorkDaoImpl; import com.cloud.network.ovs.dao.VlanMappingDaoImpl; import com.cloud.network.ovs.dao.VlanMappingDirtyDaoImpl; @@ -251,6 +255,8 @@ public class DefaultComponentLibrary implements ComponentLibrary { addDao("OvsWorkDao", OvsWorkDaoImpl.class); addDao("VmFlowLogDao", VmFlowLogDaoImpl.class); addDao("GreTunnelDao", GreTunnelDaoImpl.class); + addDao("OvsTunnelDao", OvsTunnelDaoImpl.class); + addDao("OvsTunnelAccountDao", OvsTunnelAccountDaoImpl.class); } Map> _managers = new HashMap>(); @@ -306,6 +312,7 @@ public class DefaultComponentLibrary implements ComponentLibrary { addManager("RulesManager", RulesManagerImpl.class); addManager("RemoteAccessVpnManager", RemoteAccessVpnManagerImpl.class); addManager("OvsNetworkManager", OvsNetworkManagerImpl.class); + addManager("OvsTunnelManager", OvsTunnelManagerImpl.class); } protected List> addAdapterChain(Class interphace, List>> adapters) { diff --git a/server/src/com/cloud/network/ovs/OvsTunnelListener.java b/server/src/com/cloud/network/ovs/OvsTunnelListener.java new file mode 100644 index 00000000000..693de7b2a00 --- /dev/null +++ b/server/src/com/cloud/network/ovs/OvsTunnelListener.java @@ -0,0 +1,116 @@ +package com.cloud.network.ovs; + +import java.util.List; + +import javax.persistence.EntityExistsException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.Listener; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupCommand; +import com.cloud.exception.ConnectionException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.network.ovs.dao.GreTunnelVO; +import com.cloud.network.ovs.dao.OvsTunnelDao; +import com.cloud.network.ovs.dao.OvsTunnelVO; + +public class OvsTunnelListener implements Listener { + public static final Logger s_logger = Logger.getLogger(OvsListener.class.getName()); + HostDao _hostDao; + OvsTunnelDao _tunnelDao; + + public OvsTunnelListener(OvsTunnelDao tunnelDao, HostDao hostDao) { + this._hostDao = hostDao; + this._tunnelDao = tunnelDao; + } + + @Override + public boolean processAnswers(long agentId, long seq, Answer[] answers) { + + return true; + } + + @Override + public boolean processCommands(long agentId, long seq, Command[] commands) { + // TODO Auto-generated method stub + return true; + } + + @Override + public AgentControlAnswer processControlCommand(long agentId, + AgentControlCommand cmd) { + + return null; + } + + @Override + public void processConnect(HostVO host, StartupCommand cmd) + throws ConnectionException { + if (host.getType() != Host.Type.Routing) { + return; + } + + try { + List hosts = _hostDao.listByType(Host.Type.Routing); + for (HostVO h : hosts) { + if (h.getId() == host.getId()) { + continue; + } + + OvsTunnelVO t = _tunnelDao.getByFromAndTo(host.getId(), h.getId()); + if (t == null) { + t = new OvsTunnelVO(host.getId(), h.getId()); + try { + _tunnelDao.persist(t); + } catch (EntityExistsException e) { + s_logger.debug(String.format("Already has (from=%1$s, to=%2$s)", host.getId(), h.getId())); + } + } + + t = _tunnelDao.getByFromAndTo(h.getId(), host.getId()); + if (t == null) { + t = new OvsTunnelVO(h.getId(), host.getId()); + try { + _tunnelDao.persist(t); + } catch (EntityExistsException e) { + s_logger.debug(String.format("Already has (from=%1$s, to=%2$s)", h.getId(), host.getId())); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public boolean processDisconnect(long agentId, Status state) { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean isRecurring() { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getTimeout() { + // TODO Auto-generated method stub + return -1; + } + + @Override + public boolean processTimeout(long agentId, long seq) { + // TODO Auto-generated method stub + return true; + } + +} diff --git a/server/src/com/cloud/network/ovs/OvsTunnelManager.java b/server/src/com/cloud/network/ovs/OvsTunnelManager.java new file mode 100644 index 00000000000..e42d015ed8b --- /dev/null +++ b/server/src/com/cloud/network/ovs/OvsTunnelManager.java @@ -0,0 +1,19 @@ +package com.cloud.network.ovs; + +import com.cloud.agent.manager.Commands; +import com.cloud.deploy.DeployDestination; +import com.cloud.utils.component.Manager; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineProfile; + +public interface OvsTunnelManager extends Manager { + boolean isOvsTunnelEnabled(); + + public void UserVmCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile profile, DeployDestination dest); + + public void RouterCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile profile, DeployDestination dest); + + public void CheckAndDestroyTunnel(VMInstanceVO vm); +} diff --git a/server/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/server/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java new file mode 100644 index 00000000000..b2519269522 --- /dev/null +++ b/server/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -0,0 +1,288 @@ +package com.cloud.network.ovs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; +import javax.persistence.EntityExistsException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +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.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.network.ovs.dao.OvsTunnelAccountDao; +import com.cloud.network.ovs.dao.OvsTunnelAccountVO; +import com.cloud.network.ovs.dao.OvsTunnelDao; +import com.cloud.utils.Pair; +import com.cloud.utils.component.Inject; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.UserVmDao; + +@Local(value={OvsTunnelManager.class}) +public class OvsTunnelManagerImpl implements OvsTunnelManager { + public static final Logger s_logger = Logger.getLogger(OvsTunnelManagerImpl.class.getName()); + + String _name; + boolean _isEnabled; + ScheduledExecutorService _executorPool; + ScheduledExecutorService _cleanupExecutor; + OvsTunnelListener _listener; + + @Inject ConfigurationDao _configDao; + @Inject OvsTunnelDao _tunnelDao; + @Inject HostDao _hostDao; + @Inject UserVmDao _userVmDao; + @Inject DomainRouterDao _routerDao; + @Inject OvsTunnelAccountDao _tunnelAccountDao; + @Inject AgentManager _agentMgr; + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + _name = name; + _isEnabled = Boolean.parseBoolean(_configDao.getValue(Config.OvsTunnelNetwork.key())); + + if (_isEnabled) { + _executorPool = Executors.newScheduledThreadPool(10, new NamedThreadFactory("OVS")); + _cleanupExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("OVS-Cleanup")); + _listener = new OvsTunnelListener(_tunnelDao, _hostDao); + _agentMgr.registerForHostEvents(_listener, true, true, true); + } + + return true; + } + + protected int getGreKey(long from, long to, long account) { + OvsTunnelAccountVO ta = null; + int key; + + try { + key = _tunnelDao.askKey(from, to); + ta = new OvsTunnelAccountVO(from, to, key, account); + _tunnelAccountDao.persist(ta); + } catch (EntityExistsException e) { + ta = _tunnelAccountDao.getByFromToAccount(from, to, account); + if (ta == null) { + key = -1; + } else { + key = ta.getKey(); + } + } + + return key; + } + + private void handleCreateTunnelAnswer(Answer[] answers){ + OvsCreateTunnelAnswer r = (OvsCreateTunnelAnswer) answers[0]; + /* + String s = String.format( + "(hostIP:%1$s, remoteIP:%2$s, bridge:%3$s, greKey:%4$s)", + r.getHostIp(), r.getRemoteIp(), r.getBridge(), r.getKey()); + */ + String s = "hi"; + Long from = r.getFrom(); + Long to = r.getTo(); + long account = r.getAccount(); + OvsTunnelAccountVO ta = _tunnelAccountDao.getByFromToAccount(from, to, account); + if (ta == null) { + throw new CloudRuntimeException(String.format("Unable find tunnelAccount record(from=%1$s, to=%2$s, account=%3$s", from, to, account)); + } + + if (!r.getResult()) { + ta.setState("FAILED"); + s_logger.warn("Create GRE tunnel failed due to " + r.getDetails() + + s); + } else { + ta.setState("SUCCESS"); + ta.setPortName(r.getInPortName()); + s_logger.warn("Create GRE tunnel Success " + r.getDetails() + + s); + } + _tunnelAccountDao.update(ta.getId(), ta); + } + + @DB + protected void CheckAndCreateTunnel(VMInstanceVO instance, DeployDestination dest) { + if (!_isEnabled) { + return; + } + + if (instance.getType() != VirtualMachine.Type.User + && instance.getType() != VirtualMachine.Type.DomainRouter) { + return; + } + + long hostId = dest.getHost().getId(); + long accountId = instance.getAccountId(); + Listvms = _userVmDao.listByAccountId(accountId); + DomainRouterVO router = _routerDao.findBy(accountId, instance.getDataCenterId()); + Listins = new ArrayList(); + ins.addAll(vms); + ins.add(router); + List>toHosts = new ArrayList>(); + List>fromHosts = new ArrayList>(); + int key; + + for (VMInstanceVO v : ins) { + Long rh = v.getHostId(); + if (rh == null || rh.longValue() == hostId) { + continue; + } + + OvsTunnelAccountVO ta = _tunnelAccountDao.getByFromToAccount(hostId, rh.longValue(), accountId); + if (ta == null) { + key = getGreKey(hostId, rh.longValue(), accountId); + if (key == -1) { + s_logger.warn(String.format("Cannot get GRE key for from=%1$s to=%2$s accountId=%3$s, tunnel create failed", hostId, rh.longValue(), accountId)); + continue; + } + + Pair p = new Pair(rh, Integer.valueOf(key)); + if (!toHosts.contains(p)) { + toHosts.add(p); + } + } + + ta = _tunnelAccountDao.getByFromToAccount(rh.longValue(), hostId, accountId); + if (ta == null) { + key = getGreKey(rh.longValue(), hostId, accountId); + if (key == -1) { + s_logger.warn(String.format("Cannot get GRE key for from=%1$s to=%2$s accountId=%3$s, tunnel create failed", rh.longValue(), hostId, accountId)); + continue; + } + + Pair p = new Pair(rh, Integer.valueOf(key)); + if (!fromHosts.contains(p)) { + fromHosts.add(p); + } + } + } + + try { + String myIp = dest.getHost().getPrivateIpAddress(); + for (Pair i : toHosts) { + HostVO rHost = _hostDao.findById(i.first()); + Commands cmds = new Commands( + new OvsCreateTunnelCommand(rHost.getPrivateIpAddress(), i.second().toString(), Long.valueOf(hostId), i.first(), accountId)); + s_logger.debug("Ask host " + hostId + " to create gre tunnel to " + i.first()); + Answer[] answers = _agentMgr.send(hostId, cmds); + handleCreateTunnelAnswer(answers); + } + + for (Pair i : fromHosts) { + Commands cmd2s = new Commands( + new OvsCreateTunnelCommand(myIp, i.second().toString(), i.first(), Long.valueOf(hostId), accountId)); + s_logger.debug("Ask host " + i.first() + " to create gre tunnel to " + hostId); + Answer[] answers = _agentMgr.send(i.first(), cmd2s); + handleCreateTunnelAnswer(answers); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean isOvsTunnelEnabled() { + return _isEnabled; + } + + @Override + public void UserVmCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile profile, DeployDestination dest) { + CheckAndCreateTunnel(profile.getVirtualMachine(), dest); + } + + @Override + public void RouterCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile profile, DeployDestination dest) { + CheckAndCreateTunnel(profile.getVirtualMachine(), dest); + } + + private void handleDestroyTunnelAnswer(Answer ans, long from, long to, long account) { + String toStr = (to == 0 ? "all peers" : Long.toString(to)); + + if (ans.getResult()) { + if (to == 0) { + _tunnelAccountDao.removeByFromAccount(from, account); + } else { + _tunnelAccountDao.removeByFromToAccount(from, to, account); + } + + s_logger.debug(String.format("Destroy tunnel(account:%1$s, from:%2$s, to:%3$s) successful", account, from, toStr)); + } else { + s_logger.debug(String.format("Destroy tunnel(account:%1$s, from:%2$s, to:%3$s) failed", account, from, toStr)); + } + } + + @Override + public void CheckAndDestroyTunnel(VMInstanceVO vm) { + if (!_isEnabled) { + return; + } + + List userVms = _userVmDao.listByAccountIdAndHostId(vm.getAccountId(), vm.getHostId()); + if (vm.getType() == VirtualMachine.Type.User) { + if (userVms.size() > 1) { + return; + } + + DomainRouterVO router = _routerDao.findBy(vm.getAccountId(), vm.getDataCenterId()); + if (router.getHostId() == vm.getHostId()) { + return; + } + } else if (vm.getType() == VirtualMachine.Type.DomainRouter && userVms.size() != 0) { + return; + } + + try { + /* Now we are last one on host, destroy all tunnels of my account */ + Command cmd = new OvsDestroyTunnelCommand(vm.getAccountId(), "[]"); + Answer ans = _agentMgr.send(vm.getHostId(), cmd); + handleDestroyTunnelAnswer(ans, vm.getHostId(), 0, vm.getAccountId()); + + /* Then ask hosts have peer tunnel with me to destroy them */ + List peers = _tunnelAccountDao.listByToAccount(vm.getHostId(), vm.getAccountId()); + for (OvsTunnelAccountVO p : peers) { + cmd = new OvsDestroyTunnelCommand(p.getAccount(), p.getPortName()); + ans = _agentMgr.send(p.getFrom(), cmd); + handleDestroyTunnelAnswer(ans, p.getFrom(), p.getTo(), p.getAccount()); + } + } catch (Exception e) { + s_logger.warn(String.format("Destroy tunnel(account:%1$s, hostId:%2$s) failed", vm.getAccountId(), vm.getHostId()), e); + } + + } + +} diff --git a/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountDao.java b/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountDao.java new file mode 100644 index 00000000000..1de34693bcf --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountDao.java @@ -0,0 +1,13 @@ +package com.cloud.network.ovs.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; + +public interface OvsTunnelAccountDao extends + GenericDao { + OvsTunnelAccountVO getByFromToAccount(long from, long to, long account); + void removeByFromAccount(long from, long account); + void removeByFromToAccount(long from, long to, long account); + List listByToAccount(long to, long account); +} diff --git a/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountDaoImpl.java b/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountDaoImpl.java new file mode 100644 index 00000000000..0e3bd6a1b9d --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountDaoImpl.java @@ -0,0 +1,73 @@ +package com.cloud.network.ovs.dao; + +import java.util.List; + +import javax.ejb.Local; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; + +@Local(value = { OvsTunnelAccountDao.class }) +public class OvsTunnelAccountDaoImpl extends + GenericDaoBase implements OvsTunnelAccountDao { + + protected final SearchBuilder fromToAccountSearch; + protected final SearchBuilder fromAccountSearch; + protected final SearchBuilder toAccountSearch; + + public OvsTunnelAccountDaoImpl() { + fromToAccountSearch = createSearchBuilder(); + fromToAccountSearch.and("from", fromToAccountSearch.entity().getFrom(), Op.EQ); + fromToAccountSearch.and("to", fromToAccountSearch.entity().getTo(), Op.EQ); + fromToAccountSearch.and("account", fromToAccountSearch.entity().getAccount(), Op.EQ); + fromToAccountSearch.done(); + + fromAccountSearch = createSearchBuilder(); + fromAccountSearch.and("from", fromAccountSearch.entity().getFrom(), Op.EQ); + fromAccountSearch.and("account", fromAccountSearch.entity().getAccount(), Op.EQ); + fromAccountSearch.done(); + + toAccountSearch = createSearchBuilder(); + toAccountSearch.and("to", toAccountSearch.entity().getTo(), Op.EQ); + toAccountSearch.and("account", toAccountSearch.entity().getAccount(), Op.EQ); + toAccountSearch.done(); + } + + @Override + public OvsTunnelAccountVO getByFromToAccount(long from, long to, + long account) { + SearchCriteria sc = fromToAccountSearch.create(); + sc.setParameters("from", from); + sc.setParameters("to", to); + sc.setParameters("account", account); + return findOneBy(sc); + } + + @Override + public void removeByFromAccount(long from, long account) { + SearchCriteria sc = fromAccountSearch.create(); + sc.setParameters("from", from); + sc.setParameters("account", account); + remove(sc); + } + + @Override + public List listByToAccount(long to, long account) { + SearchCriteria sc = toAccountSearch.create(); + sc.setParameters("to", to); + sc.setParameters("account", account); + return listBy(sc); + } + + @Override + public void removeByFromToAccount(long from, long to, long account) { + SearchCriteria sc = fromToAccountSearch.create(); + sc.setParameters("from", from); + sc.setParameters("to", to); + sc.setParameters("account", account); + remove(sc); + } + +} diff --git a/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountVO.java b/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountVO.java new file mode 100644 index 00000000000..441863f6f68 --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsTunnelAccountVO.java @@ -0,0 +1,88 @@ +package com.cloud.network.ovs.dao; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name=("ovs_tunnel_account")) +public class OvsTunnelAccountVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "from") + private long from; + + @Column(name = "to") + private long to; + + @Column(name = "key") + private int key; + + @Column(name = "account") + private long account; + + @Column(name = "port_name") + private String portName; + + @Column(name = "state") + private String state; + + public OvsTunnelAccountVO() { + + } + + public OvsTunnelAccountVO(long from, long to, int key, long account) { + this.from = from; + this.to = to; + this.key = key; + this.account = account; + this.portName = "[]"; + this.state = "FAILED"; + } + + public void setKey(int key) { + this.key = key; + } + + public long getFrom() { + return from; + } + + public long getTo() { + return to; + } + + public int getKey() { + return key; + } + + public long getId() { + return id; + } + + public long getAccount() { + return account; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public void setPortName(String name) { + this.portName = name; + } + + public String getPortName() { + return portName; + } +} diff --git a/server/src/com/cloud/network/ovs/dao/OvsTunnelDao.java b/server/src/com/cloud/network/ovs/dao/OvsTunnelDao.java new file mode 100644 index 00000000000..3dc4ad9aa4f --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsTunnelDao.java @@ -0,0 +1,9 @@ +package com.cloud.network.ovs.dao; + +import com.cloud.utils.db.GenericDao; + +public interface OvsTunnelDao extends GenericDao { + OvsTunnelVO lockByFromAndTo(long from, long to); + OvsTunnelVO getByFromAndTo(long from, long to); + int askKey(long from, long to); +} diff --git a/server/src/com/cloud/network/ovs/dao/OvsTunnelDaoImpl.java b/server/src/com/cloud/network/ovs/dao/OvsTunnelDaoImpl.java new file mode 100644 index 00000000000..b0f0d3953e1 --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsTunnelDaoImpl.java @@ -0,0 +1,59 @@ +package com.cloud.network.ovs.dao; + +import javax.ejb.Local; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.SearchCriteria.Op; + +@Local(value = { OvsTunnelDao.class }) +public class OvsTunnelDaoImpl extends GenericDaoBase + implements OvsTunnelDao { + + protected final SearchBuilder fromToSearch; + + public OvsTunnelDaoImpl() { + fromToSearch = createSearchBuilder(); + fromToSearch.and("from", fromToSearch.entity().getFrom(), Op.EQ); + fromToSearch.and("to", fromToSearch.entity().getTo(), Op.EQ); + fromToSearch.done(); + } + + @Override + public OvsTunnelVO lockByFromAndTo(long from, long to) { + SearchCriteria sc = fromToSearch.create(); + sc.setParameters("from", from); + sc.setParameters("to", to); + return lockOneRandomRow(sc, true); + } + + @Override + @DB + public int askKey(long from, long to) { + int key = -1; + + final Transaction txn = Transaction.currentTxn(); + txn.start(); + OvsTunnelVO t = lockByFromAndTo(from, to); + if (t != null) { + key = t.getKey(); + t.setKey(key+1); + update(t.getId(), t); + } + + txn.commit(); + return key; + } + + @Override + public OvsTunnelVO getByFromAndTo(long from, long to) { + SearchCriteria sc = fromToSearch.create(); + sc.setParameters("from", from); + sc.setParameters("to", to); + return findOneBy(sc); + } + +} diff --git a/server/src/com/cloud/network/ovs/dao/OvsTunnelVO.java b/server/src/com/cloud/network/ovs/dao/OvsTunnelVO.java new file mode 100644 index 00000000000..1f5924f05d1 --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsTunnelVO.java @@ -0,0 +1,64 @@ +package com.cloud.network.ovs.dao; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name=("ovs_tunnel")) +public class OvsTunnelVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "from") + private long from; + + @Column(name = "to") + private long to; + + @Column(name = "key") + private int key; + + + public OvsTunnelVO() { + + } + + public OvsTunnelVO(long from, long to) { + this.from = from; + this.to = to; + this.key = 0; + } + + public OvsTunnelVO(long id, long from, long to) { + this.from = from; + this.to = to; + this.key = 0; + this.id = id; + } + + public void setKey(int key) { + this.key = key; + } + + public long getFrom() { + return from; + } + + public long getTo() { + return to; + } + + public int getKey() { + return key; + } + + public long getId() { + return id; + } +} diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index eeb1f6faaa6..721d0b75bee 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -118,6 +118,7 @@ import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.ovs.GreTunnelException; import com.cloud.network.ovs.OvsNetworkManager; +import com.cloud.network.ovs.OvsTunnelManager; import com.cloud.network.router.VirtualRouter.Role; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.PortForwardingRule; @@ -285,6 +286,8 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian VMInstanceDao _instanceDao; @Inject OvsNetworkManager _ovsNetworkMgr; + @Inject + OvsTunnelManager _ovsTunnelMgr; long _routerTemplateId = -1; int _routerRamSize; @@ -1103,6 +1106,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian try { _ovsNetworkMgr.RouterCheckAndCreateTunnel(cmds, profile, dest); _ovsNetworkMgr.applyDefaultFlowToRouter(cmds, profile, dest); + _ovsTunnelMgr.RouterCheckAndCreateTunnel(cmds, profile, dest); } catch (GreTunnelException e) { e.printStackTrace(); } @@ -1227,6 +1231,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian DomainRouterVO router = profile.getVirtualMachine(); _ovsNetworkMgr.handleVmStateTransition(router, State.Stopped); + _ovsTunnelMgr.CheckAndDestroyTunnel(router); } @Override diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 759c089ed72..230a3cc8701 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -125,6 +125,7 @@ import com.cloud.network.dao.NetworkDao; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.ovs.GreTunnelException; import com.cloud.network.ovs.OvsNetworkManager; +import com.cloud.network.ovs.OvsTunnelManager; import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.rules.RulesManager; import com.cloud.network.security.SecurityGroupManager; @@ -259,6 +260,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Inject SSHKeyPairDao _sshKeyPairDao; @Inject UserVmDetailsDao _vmDetailsDao; @Inject OvsNetworkManager _ovsNetworkMgr; + @Inject OvsTunnelManager _ovsTunnelMgr; private IpAddrAllocator _IpAllocator; ScheduledExecutorService _executor = null; @@ -2212,6 +2214,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager try { _ovsNetworkMgr.UserVmCheckAndCreateTunnel(cmds, profile, dest); _ovsNetworkMgr.applyDefaultFlowToUserVm(cmds, profile, dest); + _ovsTunnelMgr.UserVmCheckAndCreateTunnel(cmds, profile, dest); } catch (GreTunnelException e) { e.printStackTrace(); } @@ -2282,6 +2285,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager UserVmVO vm = profile.getVirtualMachine(); _networkGroupMgr.handleVmStateTransition(vm, State.Stopped); _ovsNetworkMgr.handleVmStateTransition(vm, State.Stopped); + _ovsTunnelMgr.CheckAndDestroyTunnel(vm); } public String generateRandomPassword() { diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 5f864d160d1..b22475d1636 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1376,6 +1376,25 @@ CREATE TABLE `cloud`.`ovs_tunnel_alloc`( PRIMARY KEY(`from`, `to`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `cloud`.`ovs_tunnel`( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, + `from` bigint unsigned COMMENT 'from host id', + `to` bigint unsigned COMMENT 'to host id', + `key` int unsigned default '0' COMMENT 'current gre key can be used', + PRIMARY KEY(`from`, `to`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`ovs_tunnel_account`( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, + `from` bigint unsigned COMMENT 'from host id', + `to` bigint unsigned COMMENT 'to host id', + `account` bigint unsigned COMMENT 'account', + `key` int unsigned COMMENT 'gre key', + `port_name` varchar(32) COMMENT 'in port on open vswitch', + `state` varchar(16) default 'FAILED' COMMENT 'result of tunnel creatation', + PRIMARY KEY(`from`, `to`, `account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + CREATE TABLE `cloud`.`ovs_vlan_mapping_dirty`( `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, `account_id` bigint unsigned COMMENT 'account id',