open vswitch - Implement gre tunnel based network that doesn't use vlan

it allows cross zone communication and no 4096 limitation introduced by vlan
This commit is contained in:
Frank 2011-01-21 17:23:38 -08:00
parent 0dc959c23f
commit 276e37115b
20 changed files with 1226 additions and 2 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<Network> 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, String> 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) {
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;

View File

@ -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()

View File

@ -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

View File

@ -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),

View File

@ -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<String, ComponentInfo<Manager>> _managers = new HashMap<String, ComponentInfo<Manager>>();
@ -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 <T> List<ComponentInfo<Adapter>> addAdapterChain(Class<T> interphace, List<Pair<String, Class<? extends T>>> adapters) {

View File

@ -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<HostVO> 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;
}
}

View File

@ -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<UserVmVO> profile, DeployDestination dest);
public void RouterCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile<DomainRouterVO> profile, DeployDestination dest);
public void CheckAndDestroyTunnel(VMInstanceVO vm);
}

View File

@ -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<String, Object> 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();
List<UserVmVO>vms = _userVmDao.listByAccountId(accountId);
DomainRouterVO router = _routerDao.findBy(accountId, instance.getDataCenterId());
List<VMInstanceVO>ins = new ArrayList<VMInstanceVO>();
ins.addAll(vms);
ins.add(router);
List<Pair<Long, Integer>>toHosts = new ArrayList<Pair<Long, Integer>>();
List<Pair<Long, Integer>>fromHosts = new ArrayList<Pair<Long, Integer>>();
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<Long, Integer> p = new Pair<Long, Integer>(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<Long, Integer> p = new Pair<Long, Integer>(rh, Integer.valueOf(key));
if (!fromHosts.contains(p)) {
fromHosts.add(p);
}
}
}
try {
String myIp = dest.getHost().getPrivateIpAddress();
for (Pair<Long, Integer> 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<Long, Integer> 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<UserVmVO> profile, DeployDestination dest) {
CheckAndCreateTunnel(profile.getVirtualMachine(), dest);
}
@Override
public void RouterCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile<DomainRouterVO> 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<UserVmVO> 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<OvsTunnelAccountVO> 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);
}
}
}

View File

@ -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, Long> {
OvsTunnelAccountVO getByFromToAccount(long from, long to, long account);
void removeByFromAccount(long from, long account);
void removeByFromToAccount(long from, long to, long account);
List<OvsTunnelAccountVO> listByToAccount(long to, long account);
}

View File

@ -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<OvsTunnelAccountVO, Long> implements OvsTunnelAccountDao {
protected final SearchBuilder<OvsTunnelAccountVO> fromToAccountSearch;
protected final SearchBuilder<OvsTunnelAccountVO> fromAccountSearch;
protected final SearchBuilder<OvsTunnelAccountVO> 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<OvsTunnelAccountVO> 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<OvsTunnelAccountVO> sc = fromAccountSearch.create();
sc.setParameters("from", from);
sc.setParameters("account", account);
remove(sc);
}
@Override
public List<OvsTunnelAccountVO> listByToAccount(long to, long account) {
SearchCriteria<OvsTunnelAccountVO> 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<OvsTunnelAccountVO> sc = fromToAccountSearch.create();
sc.setParameters("from", from);
sc.setParameters("to", to);
sc.setParameters("account", account);
remove(sc);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,9 @@
package com.cloud.network.ovs.dao;
import com.cloud.utils.db.GenericDao;
public interface OvsTunnelDao extends GenericDao<OvsTunnelVO, Long> {
OvsTunnelVO lockByFromAndTo(long from, long to);
OvsTunnelVO getByFromAndTo(long from, long to);
int askKey(long from, long to);
}

View File

@ -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<OvsTunnelVO, Long>
implements OvsTunnelDao {
protected final SearchBuilder<OvsTunnelVO> 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<OvsTunnelVO> 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<OvsTunnelVO> sc = fromToSearch.create();
sc.setParameters("from", from);
sc.setParameters("to", to);
return findOneBy(sc);
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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() {

View File

@ -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',