diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index e8394961b2b..8f9adcf02dc 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -48,6 +48,7 @@ public interface Network extends ControlledEntity { public static final Service StaticNat = new Service("StaticNat", Capability.ElasticIp); public static final Service PortForwarding = new Service("PortForwarding"); public static final Service SecurityGroup = new Service("SecurityGroup"); + public static final Service Connectivity = new Service("Connectivity"); private String name; private Capability[] caps; @@ -109,6 +110,7 @@ public interface Network extends ControlledEntity { public static final Provider ExternalGateWay = new Provider("ExternalGateWay", true); public static final Provider ElasticLoadBalancerVm = new Provider("ElasticLoadBalancerVm", false); public static final Provider SecurityGroupProvider = new Provider("SecurityGroupProvider", false); + public static final Provider OvsConnectivityProvider = new Provider("OvsConnectivityProvider", false); public static final Provider None = new Provider("None", false); private String name; diff --git a/api/src/com/cloud/network/ovs/OvsCreateTunnelAnswer.java b/api/src/com/cloud/network/ovs/OvsCreateTunnelAnswer.java index b9115a79fc2..6413330eb57 100644 --- a/api/src/com/cloud/network/ovs/OvsCreateTunnelAnswer.java +++ b/api/src/com/cloud/network/ovs/OvsCreateTunnelAnswer.java @@ -10,71 +10,73 @@ // limitations under the License. // // Automatically generated by addcopyright.py at 04/03/2012 -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; - - //for debug info - String fromIp; - String toIp; - String key; - String bridge; - - public OvsCreateTunnelAnswer(Command cmd, boolean success, String details, String bridge) { - super(cmd, success, details); - OvsCreateTunnelCommand c = (OvsCreateTunnelCommand)cmd; - from = c.getFrom(); - to = c.getTo(); - account = c.getAccount(); - inPortName = "[]"; - fromIp = c.getFromIp(); - toIp = c.getRemoteIp(); - key = c.getKey(); - this.bridge = bridge; - } - - public OvsCreateTunnelAnswer(Command cmd, boolean success, String details, String inPortName, String bridge) { - this(cmd, success, details, bridge); - this.inPortName = inPortName; - } - - - public Long getFrom() { - return from; - } - - public Long getTo() { - return to; - } - - public long getAccount() { - return account; - } - - public String getInPortName() { - return inPortName; - } - - public String getFromIp() { - return fromIp; - } - - public String getToIp() { - return toIp; - } - - public String getKey() { - return key; - } - - public String getBridge() { - return bridge; - } -} +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 networkId; + String inPortName; + + //for debug info + String fromIp; + String toIp; + String key; + String bridge; + + public OvsCreateTunnelAnswer(Command cmd, boolean success, String details, String bridge) { + super(cmd, success, details); + OvsCreateTunnelCommand c = (OvsCreateTunnelCommand)cmd; + from = c.getFrom(); + to = c.getTo(); + networkId = c.getNetworkId(); + inPortName = "[]"; + fromIp = c.getFromIp(); + toIp = c.getRemoteIp(); + key = c.getKey(); + this.bridge = bridge; + } + + public OvsCreateTunnelAnswer(Command cmd, boolean success, String details, String inPortName, String bridge) { + this(cmd, success, details, bridge); + this.inPortName = inPortName; + } + + + public Long getFrom() { + return from; + } + + public Long getTo() { + return to; + } + + public long getNetworkId() { + return networkId; + } + + public String getInPortName() { + return inPortName; + } + + public String getFromIp() { + return fromIp; + } + + public String getToIp() { + return toIp; + } + + public String getKey() { + return key; + } + + public String getBridge() { + return bridge; + } +} + +>>>>>>> 9f321ff... Open vSwitch tunnel manager diff --git a/api/src/com/cloud/network/ovs/OvsCreateTunnelCommand.java b/api/src/com/cloud/network/ovs/OvsCreateTunnelCommand.java index 71ef0c3366e..454723dfc34 100644 --- a/api/src/com/cloud/network/ovs/OvsCreateTunnelCommand.java +++ b/api/src/com/cloud/network/ovs/OvsCreateTunnelCommand.java @@ -19,7 +19,7 @@ public class OvsCreateTunnelCommand extends Command { String remoteIp; Long from; Long to; - long account; + long networkId; // for debug info String fromIp; @@ -29,12 +29,12 @@ public class OvsCreateTunnelCommand extends Command { return true; } - public OvsCreateTunnelCommand(String remoteIp, String key, Long from, Long to, long account, String fromIp) { + public OvsCreateTunnelCommand(String remoteIp, String key, Long from, Long to, long networkId, String fromIp) { this.remoteIp = remoteIp; this.key = key; this.from = from; this.to = to; - this.account = account; + this.networkId = networkId; this.fromIp = fromIp; } @@ -54,8 +54,8 @@ public class OvsCreateTunnelCommand extends Command { return to; } - public long getAccount() { - return account; + public long getNetworkId() { + return networkId; } public String getFromIp() { diff --git a/api/src/com/cloud/network/ovs/OvsDestroyTunnelCommand.java b/api/src/com/cloud/network/ovs/OvsDestroyTunnelCommand.java index 4ca71f642af..53fc4ceeb44 100644 --- a/api/src/com/cloud/network/ovs/OvsDestroyTunnelCommand.java +++ b/api/src/com/cloud/network/ovs/OvsDestroyTunnelCommand.java @@ -15,16 +15,16 @@ package com.cloud.network.ovs; import com.cloud.agent.api.Command; public class OvsDestroyTunnelCommand extends Command { - long account; + long networkId; String inPortName; - public OvsDestroyTunnelCommand(long account, String inPortName) { - this.account = account; + public OvsDestroyTunnelCommand(long networkId, String inPortName) { + this.networkId = networkId; this.inPortName = inPortName; } - public long getAccount() { - return account; + public long getNetworkId() { + return networkId; } public String getInPortName() { diff --git a/api/src/com/cloud/offering/NetworkOffering.java b/api/src/com/cloud/offering/NetworkOffering.java index 2f5314efa46..195bc0d6926 100644 --- a/api/src/com/cloud/offering/NetworkOffering.java +++ b/api/src/com/cloud/offering/NetworkOffering.java @@ -38,6 +38,7 @@ public interface NetworkOffering { public final static String DefaultSharedNetworkOfferingWithSGService = "DefaultSharedNetworkOfferingWithSGService"; public final static String DefaultIsolatedNetworkOfferingWithSourceNatService = "DefaultIsolatedNetworkOfferingWithSourceNatService"; + public final static String OvsIsolatedNetworkOfferingWithSourceNatService = "OvsIsolatedNetworkOfferingWithSourceNatService"; public final static String DefaultSharedNetworkOffering = "DefaultSharedNetworkOffering"; public final static String DefaultIsolatedNetworkOffering = "DefaultIsolatedNetworkOffering"; public final static String DefaultSharedEIPandELBNetworkOffering = "DefaultSharedNetscalerEIPandELBNetworkOffering"; diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java old mode 100755 new mode 100644 index 7976a7bae53..54891979177 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -177,6 +177,7 @@ 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.OvsDestroyBridgeCommand; import com.cloud.network.ovs.OvsDestroyTunnelCommand; import com.cloud.network.ovs.OvsSetTagAndFlowAnswer; import com.cloud.network.ovs.OvsSetTagAndFlowCommand; @@ -484,6 +485,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((NetworkRulesSystemVmCommand)cmd); } else if (clazz == OvsCreateTunnelCommand.class) { return execute((OvsCreateTunnelCommand)cmd); + } else if (clazz == OvsDestroyBridgeCommand.class) { + return execute((OvsDestroyBridgeCommand)cmd); } else if (clazz == OvsDestroyTunnelCommand.class) { return execute((OvsDestroyTunnelCommand)cmd); } else if (clazz == UpdateHostPasswordCommand.class) { @@ -545,21 +548,33 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe VIF dom0vif = null; Pair vm = getControlDomain(conn); VM dom0 = vm.first(); - - s_logger.debug("Create a vif on dom0 for " + networkDesc); - VIF.Record vifr = new VIF.Record(); - vifr.VM = dom0; - vifr.device = getLowestAvailableVIFDeviceNum(conn, dom0); - if (vifr.device == null) { - s_logger.debug("Failed to create " + networkDesc + ", no vif available"); - return; + // Create a VIF unless there's not already another VIF + Set dom0Vifs = dom0.getVIFs(conn); + for (VIF vif:dom0Vifs) { + vif.getRecord(conn); + if (vif.getNetwork(conn).getUuid(conn) == nw.getUuid(conn)) { + dom0vif = vif; + s_logger.debug("### A dom0 VIF has already been found - No need to create one"); + } } - Map config = new HashMap(); - config.put("nameLabel", vifNameLabel); - vifr.otherConfig = config; - vifr.MAC = "FE:FF:FF:FF:FF:FF"; - vifr.network = nw; - dom0vif = VIF.create(conn, vifr); + if (dom0vif == null) { + s_logger.debug("Create a vif on dom0 for " + networkDesc); + VIF.Record vifr = new VIF.Record(); + vifr.VM = dom0; + vifr.device = getLowestAvailableVIFDeviceNum(conn, dom0); + if (vifr.device == null) { + s_logger.debug("Failed to create " + networkDesc + ", no vif available"); + return; + } + Map config = new HashMap(); + config.put("nameLabel", vifNameLabel); + vifr.otherConfig = config; + vifr.MAC = "FE:FF:FF:FF:FF:FF"; + vifr.network = nw; + + dom0vif = VIF.create(conn, vifr); + } + // At this stage we surely have a VIF dom0vif.plug(conn); dom0vif.unplug(conn); synchronized(_tmpDom0Vif) { @@ -595,30 +610,115 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return null; } - private synchronized Network createTunnelNetwork(Connection conn, long account) { + /** + * This method just creates a XenServer network following the tunnel network naming convention + */ + private synchronized Network createTunnelNetwork(Connection conn, long networkId) { try { - String nwName = "OVSTunnel" + account; + String nwName = "OVSTunnel" + networkId; 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.nameDescription = "tunnel network id# " + networkId; rec.nameLabel = nwName; + //Initialize the ovs-host-setup to avoid error when doing get-param in plugin + Map otherConfig = new HashMap(); + otherConfig.put("ovs-host-setup", ""); + rec.otherConfig = otherConfig; nw = Network.create(conn, rec); } else { nw = networks.iterator().next(); } - - enableXenServerNetwork(conn, nw, nwName, "tunnel network for account " + account); + enableXenServerNetwork(conn, nw, nwName, "tunnel network for account " + networkId); + s_logger.debug("### Xen Server network for tunnels created:" + nwName); return nw; } catch (Exception e) { - s_logger.warn("create tunnel network failed", e); + s_logger.warn("createTunnelNetwork failed", e); return null; } } - + /** + * This method creates a XenServer network and configures it for being used as a L2-in-L3 tunneled network + */ + private synchronized Network createAndConfigureTunnelNetwork(Connection conn, long networkId, long hostId) { + try { + Network nw = createTunnelNetwork(conn, networkId); + //Invoke plugin to setup the bridge which will be used by this network + String bridge = nw.getBridge(conn); + Map nwOtherConfig = nw.getOtherConfig(conn); + String configuredHosts = nwOtherConfig.get("ovs-host-setup"); + boolean configured = false; + if (configuredHosts!=null) { + String hostIdsStr[] = configuredHosts.split(","); + for (String hostIdStr:hostIdsStr) { + if (hostIdStr.equals(((Long)hostId).toString())) { + configured = true; + break; + } + } + } + if (!configured) { + String result = callHostPlugin(conn, "ovstunnel", "setup_ovs_bridge", "bridge", bridge, + "key", String.valueOf(networkId), + "xs_nw_uuid", nw.getUuid(conn), + "cs_host_id", ((Long)hostId).toString()); + //Note down the fact that the ovs bridge has been setup + String[] res = result.split(":"); + if (res.length != 2 || !res[0].equalsIgnoreCase("SUCCESS")) { + //TODO: Should make this error not fatal? + throw new CloudRuntimeException("Unable to pre-configure OVS bridge " + bridge + " for network ID:" + networkId + + " - " + res); + } + } + return nw; + } catch (Exception e) { + s_logger.warn("createandConfigureTunnelNetwork failed", e); + return null; + } + } + + private synchronized Network findTunnelNetwork(Connection conn, long networkId) { + try { + String nwName = "OVSTunnel" + networkId; + Network nw = null; + Set networks = Network.getByNameLabel(conn, nwName); + if (networks.size() == 0) { + String errorMessage = "Unable to find OVS bridge for network:" + networkId; + s_logger.error(errorMessage); + throw new CloudRuntimeException(errorMessage); + } else { + nw = networks.iterator().next(); + } + s_logger.debug("### Xen Server network for tunnels found:" + nwName); + return nw; + } catch (Exception e) { + s_logger.warn("findTunnelNetwork failed:", e); + return null; + } + } + + private synchronized void destroyTunnelNetwork(Connection conn, long networkId) { + try { + Network nw = findTunnelNetwork(conn, networkId); + String bridge = nw.getBridge(conn); + String result = callHostPlugin(conn, "ovstunnel", "destroy_ovs_bridge", "bridge", bridge); + String[] res = result.split(":"); + if (res.length != 2 || !res[0].equalsIgnoreCase("SUCCESS")) { + //TODO: Should make this error not fatal? + //Can Concurrent VM shutdown/migration/reboot events can cause this method + //to be executed on a bridge which has already been removed? + throw new CloudRuntimeException("Unable to remove OVS bridge " + bridge + ":" + res); + } + return; + } catch (Exception e) { + s_logger.warn("destroyTunnelNetwork failed:", e); + return; + } + } + protected Network getNetwork(Connection conn, NicTO nic) throws XenAPIException, XmlRpcException { String name = nic.getName(); XsLocalNetwork network = getNativeNetworkForTraffic(conn, nic.getType(), name); @@ -642,8 +742,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe _isOvs = true; return setupvSwitchNetwork(conn); } else { - long account = Long.parseLong(nic.getBroadcastUri().getHost()); - return createTunnelNetwork(conn, account); + long networkId = Long.parseLong(nic.getBroadcastUri().getHost()); + return createTunnelNetwork(conn, networkId); } } else if (nic.getBroadcastType() == BroadcastDomainType.Storage) { URI broadcastUri = nic.getBroadcastUri(); @@ -1107,6 +1207,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe startVM(conn, host, vm, vmName); if (_isOvs) { + // TODO(Salvatore-orlando): First option is to do per-NIC rules here for (NicTO nic : vmSpec.getNics()) { if (nic.getBroadcastType() == Networks.BroadcastDomainType.Vswitch) { HashMap args = parseDefaultOvsRuleComamnd(nic.getBroadcastUri().toString().substring(Networks.BroadcastDomainType.Vswitch.scheme().length() + "://".length())); @@ -4697,16 +4798,28 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return Boolean.valueOf(callHostPlugin(conn, "vmops", "can_bridge_firewall", "host_uuid", _host.uuid, "instance", _instance)); } + private Answer execute(OvsDestroyBridgeCommand cmd) { + Connection conn = getConnection(); + s_logger.debug("### About to destroy OVS bridge"); + destroyTunnelNetwork(conn, cmd.getNetworkId()); + s_logger.debug("### Bridge destroyed"); + return new Answer(cmd, true, null); + } + private Answer execute(OvsDestroyTunnelCommand cmd) { Connection conn = getConnection(); + s_logger.debug("### About to destroy tunnel network"); try { - Network nw = createTunnelNetwork(conn, cmd.getAccount()); + Network nw = findTunnelNetwork(conn, cmd.getNetworkId()); if (nw == null) { + s_logger.warn("### Unable to find tunnel network"); return new Answer(cmd, false, "No network found"); } String bridge = nw.getBridge(conn); + s_logger.debug("### About to remove tunnel named:" + cmd.getInPortName()); String result = callHostPlugin(conn, "ovstunnel", "destroy_tunnel", "bridge", bridge, "in_port", cmd.getInPortName()); + s_logger.debug("### Tunnel removed"); if (result.equalsIgnoreCase("SUCCESS")) { return new Answer(cmd, true, result); } else { @@ -4728,15 +4841,18 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Connection conn = getConnection(); String bridge = "unknown"; try { - Network nw = createTunnelNetwork(conn, cmd.getAccount()); + s_logger.debug("### About to create tunnel network"); + Network nw = createAndConfigureTunnelNetwork(conn, cmd.getNetworkId(), cmd.getFrom()); if (nw == null) { + s_logger.debug("### SOMETHING WENT WRONG DURING NETWORK SETUP"); return new OvsCreateTunnelAnswer(cmd, false, "Cannot create network", bridge); } - + bridge = nw.getBridge(conn); + s_logger.debug("### The bridge is:" + bridge); String result = callHostPlugin(conn, "ovstunnel", "create_tunnel", "bridge", bridge, "remote_ip", cmd.getRemoteIp(), "key", cmd.getKey(), "from", cmd.getFrom().toString(), "to", cmd .getTo().toString()); - + s_logger.debug("### Result from create tunnel operation:" + result); String[] res = result.split(":"); if (res.length == 2 && res[0].equalsIgnoreCase("SUCCESS")) { return new OvsCreateTunnelAnswer(cmd, true, result, res[1], bridge); @@ -4744,7 +4860,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return new OvsCreateTunnelAnswer(cmd, false, result, bridge); } } catch (Exception e) { - s_logger.warn("caught execption when creating ovs tunnel", e); + s_logger.debug("### SOMETHING WENT WRONG DURING TUNNEL SETUP"); + s_logger.warn("caught execption when creating ovs tunnel", e); return new OvsCreateTunnelAnswer(cmd, false, e.getMessage(), bridge); } } @@ -5128,12 +5245,14 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe VDI vdi = null; try { SR poolSr = getStorageRepository(conn, pool); - + s_logger.debug("### STORAGE CREATE - Template URL:" + cmd.getTemplateUrl()); if (cmd.getTemplateUrl() != null) { VDI tmpltvdi = null; - + tmpltvdi = getVDIbyUuid(conn, cmd.getTemplateUrl()); + s_logger.debug("### STORAGE CREATE - ABOUT TO CLONE VDI:" + tmpltvdi.getUuid(conn)); vdi = tmpltvdi.createClone(conn, new HashMap()); + s_logger.debug("### STORAGE CREATE - CLoned VDI:" + vdi.getUuid(conn)); vdi.setNameLabel(conn, dskch.getName()); } else { VDI.Record vdir = new VDI.Record(); @@ -5142,7 +5261,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe vdir.type = Types.VdiType.USER; vdir.virtualSize = dskch.getSize(); + s_logger.debug("### STORAGE CREATE - Creating VDI with size:" + dskch.getSize()); vdi = VDI.create(conn, vdir); + s_logger.debug("### STORAGE CREATE - VDI created:" + vdi.getUuid(conn)); } VDI.Record vdir; diff --git a/scripts/.pydevproject b/scripts/.pydevproject index f8c0075c765..f35abdeab17 100644 --- a/scripts/.pydevproject +++ b/scripts/.pydevproject @@ -4,4 +4,7 @@ Default python 2.6 + +/scripts + diff --git a/scripts/vm/hypervisor/xenserver/ovstunnel b/scripts/vm/hypervisor/xenserver/ovstunnel old mode 100644 new mode 100755 index 3dfaf20b76a..6e0ff1f8c76 --- a/scripts/vm/hypervisor/xenserver/ovstunnel +++ b/scripts/vm/hypervisor/xenserver/ovstunnel @@ -3,28 +3,94 @@ # # A plugin for executing script needed by vmops cloud +import cloudstack_pluginlib as lib +import logging import os, sys, time +import subprocess 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" +xePath= "/opt/xensource/bin/xe" +lib.setup_logging("/var/log/ovstunnel.log") + +def find_vifs_v5(xs_nw_uuid): + logging.debug("Fetching vifs on networks - for XS version 5.x") + vif_uuids_cmd = [xePath, 'network-list', 'uuid=%s' %xs_nw_uuid, + 'params=VIF-uuids', '--minimal'] + vif_uuids_str = lib.do_cmd(vif_uuids_cmd) + vif_uuids = vif_uuids_str.split(';') + vifs = [] + for vif_uuid in vif_uuids: + vif_uuid = vif_uuid.strip() + is_attached_cmd = [xePath, 'vif-list', 'uuid=%s' %vif_uuid, + 'params=currently-attached', '--minimal'] + is_attached = lib.do_cmd(is_attached_cmd) + # Consider only attached VIFs + if is_attached == 'false': + continue + vm_uuid_cmd = [xePath, 'vif-list', 'uuid=%s' %vif_uuid, + 'params=vm-uuid', '--minimal'] + vm_uuid = lib.do_cmd(vm_uuid_cmd) + dom_id_cmd = [xePath, 'vm-list', 'uuid=%s' %vm_uuid, + 'params=dom-id', '--minimal'] + dom_id = lib.do_cmd(dom_id_cmd) + device_cmd = [xePath, 'vif-list', 'uuid=%s' %vif_uuid, + 'params=device', '--minimal'] + device = lib.do_cmd(device_cmd) + vifs.append("vif%s.%s" % (dom_id, device)) + logging.debug("Vifs on network:%s" %vifs) + vif_ofports = [] + for vif in vifs: + vif_ofport_cmd=[lib.VSCTL_PATH, 'get', 'interface', vif, 'ofport'] + vif_ofport = lib.do_cmd(vif_ofport_cmd).strip() + if vif_ofport.endswith('\n'): + vif_ofport = vif_ofport[:-1] + vif_ofports.append(vif_ofport.strip()) + return vif_ofports + + +def find_vifs_v6(xs_nw_uuid): + logging.debug("Fetching vifs on networks - for XS version 6.x") + cmd_vif_ofports = [lib.VSCTL_PATH, "--", "--columns=ofport", + "find", "interface", + "external_ids:xs-network-uuid=%s" % xs_nw_uuid, + "type!=gre"] + vif_ofports_str = lib.do_cmd(cmd_vif_ofports) + vif_ofports = [] + for line in vif_ofports_str.split('\n'): + elements = line.split(':') + if len(elements)==2: + # ensure no trailing \n is returned + if elements[1].endswith('\n'): + elements[1] = elements[1][:-1] + vif_ofports.append(elements[1].strip()) + return vif_ofports + +vif_ofport_list_handlers = { + '5': find_vifs_v5, + '6': find_vifs_v6} + + +def block_ipv6_v5(bridge): + lib.add_flow(bridge, priority=65000, dl_type='0x86dd', actions='drop') + + +def block_ipv6_v6(bridge): + lib.add_flow(bridge, priority=65000, proto='ipv6', actions='drop') + + +block_ipv6_handlers = { + '5': block_ipv6_v5, + '6': block_ipv6_v6} + + +class PluginError(Exception): + """Base Exception class for all plugin errors.""" + def __init__(self, *args): + Exception.__init__(self, *args) -logFile = "/var/log/ovstunnel.log" -fLog = None def echo(fn): def wrapped(*v, **k): @@ -35,229 +101,201 @@ def echo(fn): 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): +def setup_ovs_bridge(session, args): bridge = args.pop("bridge") - remoteIP = args.pop("remote_ip") - greKey = args.pop("key") - srcHost = args.pop("from") - dstHost = args.pop("to") + key = args.pop("key") + xs_nw_uuid = args.pop("xs_nw_uuid") + cs_host_id = args.pop("cs_host_id") + + res = lib.check_switch() + if res != "SUCCESS": + return "FAILURE:%s" %res - res = check_switch() + logging.debug("About to manually create the bridge:%s" %bridge) + # create a bridge with the same name as the xapi network + # also associate gre key in other config attribute + res = lib.do_cmd([lib.VSCTL_PATH, "--", "--may-exist", "add-br", bridge, + "--", "set", "bridge", bridge, + "other_config:gre_key=%s" % key]) + logging.debug("Bridge has been manually created:%s" %res) + # TODO: Make sure xs-network-uuid is set into external_ids + lib.do_cmd([lib.VSCTL_PATH, "set", "Bridge", bridge, + "external_ids:xs-network-uuid=%s" % xs_nw_uuid]) + # Non empty result means something went wrong + if res: + result = "FAILURE:%s" %res + else: + # Verify the bridge actually exists, with the gre_key properly set + res = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", + bridge, "other_config:gre_key"]) + if key in res: + result = "SUCCESS:%s" %bridge + else: + result = "FAILURE:%s" %res + # Finally note in the xenapi network object that the network has + # been configured + xs_nw_uuid = lib.do_cmd([lib.XE_PATH, "network-list", + "bridge=%s" % bridge, "--minimal"]) + lib.do_cmd([lib.XE_PATH,"network-param-set", "uuid=%s" % xs_nw_uuid, + "other-config:is-ovs-tun-network=True"]) + conf_hosts = lib.do_cmd([lib.XE_PATH,"network-param-get", + "uuid=%s" % xs_nw_uuid, + "param-name=other-config", + "param-key=ovs-host-setup", "--minimal"]) + conf_hosts = conf_hosts + ",%s" %cs_host_id + lib.do_cmd([lib.XE_PATH,"network-param-set", "uuid=%s" % xs_nw_uuid, + "other-config:ovs-host-setup=%s" %conf_hosts]) + + # BLOCK IPv6 - Flow spec changes with ovs version + host_list_cmd = [lib.XE_PATH, 'host-list', '--minimal'] + host_list_str = lib.do_cmd(host_list_cmd) + host_uuid = host_list_str.split(',')[0].strip() + version_cmd = [lib.XE_PATH, 'host-param-get', 'uuid=%s' % host_uuid, + 'param-name=software-version', + 'param-key=product_version'] + version = lib.do_cmd(version_cmd) + block_ipv6_handlers[version](bridge) + lib.add_flow(bridge, priority=65000, proto='ipv6', actions='drop') + logging.debug("Setup_ovs_bridge completed with result:%s" %result) + return result + + +@echo +def destroy_ovs_bridge(session, args): + bridge = args.pop("bridge") + res = lib.check_switch() + # TODO: Must complete this routine 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 + res = lib.do_cmd([lib.VSCTL_PATH, "del-br", bridge]) + logging.debug("Bridge has been manually removed:%s" %res) + if res: + result = "FAILURE:%s" %res else: - pr("create gre tunnel failed") - result = "COMMAND_FAILED_CREATE_TUNNEL_FAILED" - + # Note that the bridge has been removed on xapi network object + xs_nw_uuid = lib.do_cmd([xePath, "network-list", + "bridge=%s" % bridge, "--minimal"]) + lib.do_cmd([xePath,"network-param-set", "uuid=%s" % xs_nw_uuid, + "other-config:ovs-setup=False"]) + result = "SUCCESS:%s" %bridge + + logging.debug("Destroy_ovs_bridge completed with result:%s" %result) return result -######################## End GRE creation utils ########################## + + +@echo +def create_tunnel(session, args): + bridge = args.pop("bridge") + remote_ip = args.pop("remote_ip") + gre_key = args.pop("key") + src_host = args.pop("from") + dst_host = args.pop("to") + + logging.debug("Entering create_tunnel") + + res = lib.check_switch() + if res != "SUCCESS": + logging.debug("Openvswitch running: NO") + return "FAILURE:%s" %res + + # We need to keep the name below 14 characters + # src and target are enough - consider a fixed length hash + name = "t%s-%s-%s" % (gre_key, src_host, dst_host) + + # Verify the xapi bridge to be created + # NOTE: Timeout should not be necessary anymore + wait = [lib.VSCTL_PATH, "--timeout=30", "wait-until", "bridge", bridge, "--", + "get", "bridge", bridge, "name"] + res = lib.do_cmd(wait) + if bridge not in res: + logging.debug("WARNING:Can't find bridge %s for creating " + + "tunnel!" % bridge) + return "FAILURE:NO_BRIDGE" + logging.debug("bridge %s for creating tunnel - VERIFIED" % bridge) + tunnel_setup = False + drop_flow_setup = False + try: + # Create a port and configure the tunnel interface for it + add_tunnel = [lib.VSCTL_PATH, "add-port", bridge, name, "--", "set", "interface", + name, "type=gre", "options:key=%s" % gre_key, + "options:remote_ip=%s" % remote_ip] + lib.do_cmd(add_tunnel) + tunnel_setup = True + # verify port + verify_port = [lib.VSCTL_PATH, "get", "port", name, "interfaces"] + res = lib.do_cmd(verify_port) + # Expecting python-style list as output + iface_list = [] + if len(res) > 2: + iface_list = res.strip()[1:-1].split(',') + if len(iface_list) != 1: + logging.debug("WARNING: Unexpected output while verifying " + + "port %s on bridge %s" %(name, bridge)) + return "FAILURE:VERIFY_PORT_FAILED" + + # verify interface + iface_uuid = iface_list[0] + verify_interface_key = [lib.VSCTL_PATH, "get", "interface", + iface_uuid, "options:key"] + verify_interface_ip = [lib.VSCTL_PATH, "get", "interface", + iface_uuid, "options:remote_ip"] + + key_validation = lib.do_cmd(verify_interface_key) + ip_validation = lib.do_cmd(verify_interface_ip) + + if not gre_key in key_validation or not remote_ip in ip_validation: + logging.debug("WARNING: Unexpected output while verifying " + + "interface %s on bridge %s" %(name, bridge)) + return "FAILURE:VERIFY_INTERFACE_FAILED" + logging.debug("Tunnel interface validated:%s" %verify_interface_ip) + cmd_tun_ofport = [lib.VSCTL_PATH, "get", "interface", + iface_uuid, "ofport"] + tun_ofport = lib.do_cmd(cmd_tun_ofport) + # Ensure no trailing LF + if tun_ofport.endswith('\n'): + tun_ofport = tun_ofport[:-1] + # add flow entryies for dropping broadcast coming in from gre tunnel + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, + dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') + lib.add_flow(bridge, priority=1000, in_port=tun_ofport, + nw_dst='224.0.0.0/24', actions='drop') + drop_flow_setup = True + logging.debug("Broadcast drop rules added") + return "SUCCESS:%s" % name + except: + logging.debug("An unexpected error occured. Rolling back") + if tunnel_setup: + logging.debug("Deleting GRE interface") + # Destroy GRE port and interface + lib.del_port(bridge, name) + if drop_flow_setup: + # Delete flows + logging.debug("Deleting flow entries from GRE interface") + lib.del_flows(bridge, in_port=tun_ofport) + raise -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) - + iface_name = args.pop("in_port") + logging.debug("Destroying tunnel at port %s for bridge %s" + % (iface_name, bridge)) + ofport = get_field_of_interface(iface_name, "ofport") + lib.del_flows(bridge, in_port=ofport) + lib.del_port(bridge, iface_name) 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) +def get_field_of_interface(iface_name, field): + get_iface_cmd = [lib.VSCTL_PATH, "get","interface", iface_name, field] + res = lib.do_cmd(get_iface_cmd) + return res if __name__ == "__main__": - open_log() - XenAPIPlugin.dispatch({"create_tunnel":create_tunnel, "destroy_tunnel":destroy_tunnel}) - close_log() - + XenAPIPlugin.dispatch({"create_tunnel":create_tunnel, + "destroy_tunnel":destroy_tunnel, + "setup_ovs_bridge": setup_ovs_bridge, + "destroy_ovs_bridge": destroy_ovs_bridge}) diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops index 11792b1b028..248108bbd85 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -441,7 +441,7 @@ def can_bridge_firewall(session, args): os.makedirs('/var/run/cloud') if not os.path.exists('/var/cache/cloud'): os.makedirs('/var/cache/cloud') - get_ipset_keyword() + #get_ipset_keyword() cleanup_rules_for_dead_vms(session) cleanup_rules(session, args) diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch index a3c1f91e319..bca65cbdfc5 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch @@ -11,6 +11,10 @@ # If [source path] does not start with '/' or '~', then it is relative path to the location of the patch file. NFSSR.py=/opt/xensource/sm vmops=..,0755,/etc/xapi.d/plugins +xen-ovs-vif-flows.rules=..,0644,/etc/udev/rules.d +ovs-vif-flows.py=.,0755,/etc/xapi.d/plugins +cloudstack_plugins.conf=..,0644,/etc/xensource +cloudstack_pluginlib.py=..,0755,/etc/xapi.d/plugins ovsgre=..,0755,/etc/xapi.d/plugins ovstunnel=..,0755,/etc/xapi.d/plugins vmopsSnapshot=..,0755,/etc/xapi.d/plugins diff --git a/scripts/vm/hypervisor/xenserver/xenserver60/patch b/scripts/vm/hypervisor/xenserver/xenserver60/patch index 344b4d3d571..b75596b3adb 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver60/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver60/patch @@ -11,6 +11,10 @@ # If [source path] does not start with '/' or '~', then it is relative path to the location of the patch file. NFSSR.py=/opt/xensource/sm vmops=..,0755,/etc/xapi.d/plugins +xen-ovs-vif-flows.rules=..,0644,/etc/udev/rules.d +ovs-vif-flows.py=.,0755,/etc/xapi.d/plugins +cloudstack_plugins.conf=..,0644,/etc/xensource +cloudstack_pluginlib.py=..,0755,/etc/xapi.d/plugins ovsgre=..,0755,/etc/xapi.d/plugins ovstunnel=..,0755,/etc/xapi.d/plugins vmopsSnapshot=..,0755,/etc/xapi.d/plugins diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index f3a196f648b..414b67c66d3 100755 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -106,7 +106,7 @@ import com.cloud.network.lb.dao.ElasticLbVmMapDaoImpl; 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.OvsTunnelAccountDaoImpl; +import com.cloud.network.ovs.dao.OvsTunnelNetworkDaoImpl; import com.cloud.network.ovs.dao.OvsTunnelDaoImpl; import com.cloud.network.ovs.dao.OvsWorkDaoImpl; import com.cloud.network.ovs.dao.VlanMappingDaoImpl; @@ -298,7 +298,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com addDao("VmFlowLogDao", VmFlowLogDaoImpl.class); addDao("GreTunnelDao", GreTunnelDaoImpl.class); addDao("OvsTunnelDao", OvsTunnelDaoImpl.class); - addDao("OvsTunnelAccountDao", OvsTunnelAccountDaoImpl.class); + addDao("OvsTunnelAccountDao", OvsTunnelNetworkDaoImpl.class); addDao("StoragePoolWorkDao", StoragePoolWorkDaoImpl.class); addDao("HostTagsDao", HostTagsDaoImpl.class); addDao("NetworkDomainDao", NetworkDomainDaoImpl.class); diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 6395a4feb7f..c53985a95ee 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -127,7 +127,8 @@ public interface NetworkManager extends NetworkService { void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException; - void release(VirtualMachineProfile vmProfile, boolean forced); + void release(VirtualMachineProfile vmProfile, boolean forced) throws + ConcurrentOperationException, ResourceUnavailableException; void cleanupNics(VirtualMachineProfile vm); @@ -204,6 +205,8 @@ public interface NetworkManager extends NetworkService { String getIpOfNetworkElementInVirtualNetwork(long accountId, long dataCenterId); List listNetworksForAccount(long accountId, long zoneId, Network.GuestType type); + + List listAllNetworksInAllZonesByType(Network.GuestType type); IPAddressVO markIpAsUnavailable(long addrId); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 7e8edd7cdd0..74c77747ecf 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -1908,7 +1908,8 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } @Override - public void release(VirtualMachineProfile vmProfile, boolean forced) { + public void release(VirtualMachineProfile vmProfile, boolean forced) throws + ConcurrentOperationException, ResourceUnavailableException { List nics = _nicDao.listByVmId(vmProfile.getId()); for (NicVO nic : nics) { NetworkVO network = _networksDao.findById(nic.getNetworkId()); @@ -1928,6 +1929,16 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag _nicDao.update(nic.getId(), nic); } } + // Perform release on network elements + for (NetworkElement element : _networkElements) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Asking " + element.getName() + " to release " + nic); + } + //NOTE: Context appear to never be used in release method + //implementations. Consider removing it from interface Element + element.release(network, profile, vmProfile, null); + } + } else { nic.setState(Nic.State.Allocated); updateNic(nic, network.getId(), -1); @@ -3838,6 +3849,17 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag return accountNetworks; } + @Override + public List listAllNetworksInAllZonesByType(Network.GuestType type) { + List networks = new ArrayList(); + for (NetworkVO network: _networksDao.listAll()) { + if (!isNetworkSystem(network)) { + networks.add(network); + } + } + return networks; + } + @DB @Override public IPAddressVO markIpAsUnavailable(long addrId) { diff --git a/server/src/com/cloud/network/element/OvsElement.java b/server/src/com/cloud/network/element/OvsElement.java index 58c137b80cb..3fa130e793d 100644 --- a/server/src/com/cloud/network/element/OvsElement.java +++ b/server/src/com/cloud/network/element/OvsElement.java @@ -66,7 +66,8 @@ public class OvsElement extends AdapterBase implements NetworkElement { DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - return true; + //Consider actually implementing the network here + return true; } @Override @@ -83,12 +84,16 @@ public class OvsElement extends AdapterBase implements NetworkElement { return true; } + //NOTE (Salvatore Orlando): + //The code for ovs Vlan Manager has not been touched. Only the OVS tunnel + //manager has been restored. Ideally, the code for OVS Vlan Manager should go. _ovsVlanMgr.VmCheckAndCreateTunnel(vm, dest); String command = _ovsVlanMgr.applyDefaultFlow(vm.getVirtualMachine(), dest); if (command != null) { nic.setBroadcastUri(BroadcastDomainType.Vswitch.toUri(command)); } - _ovsTunnelMgr.VmCheckAndCreateTunnel(vm, dest); + _ovsTunnelMgr.VmCheckAndCreateTunnel(vm, network, dest); + //_ovsTunnelMgr.applyDefaultFlow(vm.getVirtualMachine(), dest); return true; } @@ -106,7 +111,7 @@ public class OvsElement extends AdapterBase implements NetworkElement { return true; } - _ovsTunnelMgr.CheckAndDestroyTunnel(vm.getVirtualMachine()); + _ovsTunnelMgr.CheckAndDestroyTunnel(vm.getVirtualMachine(), network); return true; } @@ -118,7 +123,7 @@ public class OvsElement extends AdapterBase implements NetworkElement { @Override public boolean isReady(PhysicalNetworkServiceProvider provider) { - return true; + return true; } @Override diff --git a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java index 6ec1df810af..6b2882a7614 100644 --- a/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java +++ b/server/src/com/cloud/network/guru/ExternalGuestNetworkGuru.java @@ -64,14 +64,11 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru { @Inject DataCenterDao _zoneDao; @Inject - ConfigurationDao _configDao; - @Inject PortForwardingRulesDao _pfRulesDao; @Inject OvsNetworkManager _ovsNetworkMgr; @Inject OvsTunnelManager _tunnelMgr; - @Inject PhysicalNetworkDao _physicalNetworkDao; @Override public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { @@ -167,29 +164,6 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru { return implemented; } - public int getVlanOffset(long physicalNetworkId, int vlanTag) { - PhysicalNetworkVO pNetwork = _physicalNetworkDao.findById(physicalNetworkId); - if (pNetwork == null) { - throw new CloudRuntimeException("Could not find the physical Network " + physicalNetworkId + "."); - } - - if (pNetwork.getVnet() == null) { - throw new CloudRuntimeException("Could not find vlan range for physical Network " + physicalNetworkId + "."); - } - String vlanRange[] = pNetwork.getVnet().split("-"); - int lowestVlanTag = Integer.valueOf(vlanRange[0]); - return vlanTag - lowestVlanTag; - } - - public int getGloballyConfiguredCidrSize() { - try { - String globalVlanBits = _configDao.getValue(Config.GuestVlanBits.key()); - return 8 + Integer.parseInt(globalVlanBits); - } catch (Exception e) { - throw new CloudRuntimeException("Failed to read the globally configured VLAN bits size."); - } - } - @Override public NicProfile allocate(Network config, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException { diff --git a/server/src/com/cloud/network/guru/GuestNetworkGuru.java b/server/src/com/cloud/network/guru/GuestNetworkGuru.java index ae528238ca1..f9977102b1f 100755 --- a/server/src/com/cloud/network/guru/GuestNetworkGuru.java +++ b/server/src/com/cloud/network/guru/GuestNetworkGuru.java @@ -21,6 +21,8 @@ import javax.ejb.Local; import org.apache.log4j.Logger; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.dao.DataCenterDao; @@ -40,12 +42,14 @@ import com.cloud.network.Network.State; import com.cloud.network.NetworkManager; import com.cloud.network.NetworkProfile; import com.cloud.network.NetworkVO; +import com.cloud.network.PhysicalNetworkVO; import com.cloud.network.Networks.AddressFormat; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.Mode; import com.cloud.network.Networks.TrafficType; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; import com.cloud.user.UserContext; @@ -75,9 +79,13 @@ public class GuestNetworkGuru extends AdapterBase implements NetworkGuru { @Inject protected NicDao _nicDao; @Inject + ConfigurationDao _configDao; + @Inject protected NetworkDao _networkDao; @Inject IPAddressDao _ipAddressDao; + @Inject + protected PhysicalNetworkDao _physicalNetworkDao; Random _rand = new Random(System.currentTimeMillis()); private static final TrafficType[] _trafficTypes = {TrafficType.Guest}; @@ -267,6 +275,43 @@ public class GuestNetworkGuru extends AdapterBase implements NetworkGuru { return new Ip4Address(ip); } + public int getVlanOffset(long physicalNetworkId, int vlanTag) { + PhysicalNetworkVO pNetwork = _physicalNetworkDao.findById(physicalNetworkId); + if (pNetwork == null) { + throw new CloudRuntimeException("Could not find the physical Network " + physicalNetworkId + "."); + } + + if (pNetwork.getVnet() == null) { + throw new CloudRuntimeException("Could not find vlan range for physical Network " + physicalNetworkId + "."); + } + String vlanRange[] = pNetwork.getVnet().split("-"); + int lowestVlanTag = Integer.valueOf(vlanRange[0]); + return vlanTag - lowestVlanTag; + } + + public int getGloballyConfiguredCidrSize() { + try { + String globalVlanBits = _configDao.getValue(Config.GuestVlanBits.key()); + return 8 + Integer.parseInt(globalVlanBits); + } catch (Exception e) { + throw new CloudRuntimeException("Failed to read the globally configured VLAN bits size."); + } + } + + protected void allocateVnet(Network network, NetworkVO implemented, long dcId, + long physicalNetworkId, String reservationId) throws InsufficientVirtualNetworkCapcityException { + if (network.getBroadcastUri() == null) { + String vnet = _dcDao.allocateVnet(dcId, physicalNetworkId, network.getAccountId(), reservationId); + if (vnet == null) { + throw new InsufficientVirtualNetworkCapcityException("Unable to allocate vnet as a part of network " + network + " implement ", DataCenter.class, dcId); + } + implemented.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vnet)); + EventUtils.saveEvent(UserContext.current().getCallerUserId(), network.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: "+vnet+ " Network Id: "+network.getId(), 0); + } else { + implemented.setBroadcastUri(network.getBroadcastUri()); + } + } + @Override public Network implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapcityException { assert (network.getState() == State.Implementing) : "Why are we implementing " + network; @@ -279,16 +324,7 @@ public class GuestNetworkGuru extends AdapterBase implements NetworkGuru { NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(), network.getBroadcastDomainType(), network.getNetworkOfferingId(), State.Allocated, network.getDataCenterId(), physicalNetworkId); - if (network.getBroadcastUri() == null) { - String vnet = _dcDao.allocateVnet(dcId, physicalNetworkId, network.getAccountId(), context.getReservationId()); - if (vnet == null) { - throw new InsufficientVirtualNetworkCapcityException("Unable to allocate vnet as a part of network " + network + " implement ", DataCenter.class, dcId); - } - implemented.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vnet)); - EventUtils.saveEvent(UserContext.current().getCallerUserId(), network.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: "+vnet+ " Network Id: "+network.getId(), 0); - } else { - implemented.setBroadcastUri(network.getBroadcastUri()); - } + allocateVnet(network, implemented, dcId, physicalNetworkId, context.getReservationId()); if (network.getGateway() != null) { implemented.setGateway(network.getGateway()); diff --git a/server/src/com/cloud/network/guru/OvsGuestNetworkGuru.java b/server/src/com/cloud/network/guru/OvsGuestNetworkGuru.java index a373ea20d8b..222b79af37a 100644 --- a/server/src/com/cloud/network/guru/OvsGuestNetworkGuru.java +++ b/server/src/com/cloud/network/guru/OvsGuestNetworkGuru.java @@ -14,8 +14,11 @@ package com.cloud.network.guru; import javax.ejb.Local; +import org.apache.log4j.Logger; + import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; +import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientVirtualNetworkCapcityException; import com.cloud.network.Network; import com.cloud.network.NetworkManager; @@ -25,12 +28,18 @@ import com.cloud.network.ovs.OvsTunnelManager; import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; import com.cloud.utils.component.Inject; +import com.cloud.vm.Nic.ReservationStrategy; +import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Network.State; @Local(value=NetworkGuru.class) public class OvsGuestNetworkGuru extends GuestNetworkGuru { + private static final Logger s_logger = Logger.getLogger(OvsGuestNetworkGuru.class); + @Inject OvsNetworkManager _ovsNetworkMgr; @Inject NetworkManager _externalNetworkManager; @Inject OvsTunnelManager _ovsTunnelMgr; @@ -52,23 +61,29 @@ public class OvsGuestNetworkGuru extends GuestNetworkGuru { return config; } + protected void allocateVnet(Network network, NetworkVO implemented, long dcId, + long physicalNetworkId, String reservationId) throws InsufficientVirtualNetworkCapcityException { + // Overriden as an empty method to avoid Vnet allocation when OVS is the network manager + } + @Override - public Network implement(Network config, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapcityException { + public Network implement(Network config, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapcityException { assert (config.getState() == State.Implementing) : "Why are we implementing " + config; if (!_ovsNetworkMgr.isOvsNetworkEnabled()&& !_ovsTunnelMgr.isOvsTunnelEnabled()) { return null; } - + s_logger.debug("### Implementing network:" + config.getId() + " in OVS Guest Network Guru"); + // The above call will NOT reserve a Vnet NetworkVO implemented = (NetworkVO)super.implement(config, offering, dest, context); - String uri = null; if (_ovsNetworkMgr.isOvsNetworkEnabled()) { uri = "vlan"; } else if (_ovsTunnelMgr.isOvsTunnelEnabled()) { - uri = Long.toString(config.getAccountId()); + uri = Long.toString(config.getId()); } - + s_logger.debug("### URI value:" + uri); implemented.setBroadcastUri(BroadcastDomainType.Vswitch.toUri(uri)); + s_logger.debug("### Broadcast URI is:" + implemented.getBroadcastUri().toString()); return implemented; } diff --git a/server/src/com/cloud/network/ovs/OvsTunnelListener.java b/server/src/com/cloud/network/ovs/OvsTunnelListener.java index 0785c77c168..c93ce679d9d 100755 --- a/server/src/com/cloud/network/ovs/OvsTunnelListener.java +++ b/server/src/com/cloud/network/ovs/OvsTunnelListener.java @@ -29,6 +29,9 @@ 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.Network.GuestType; +import com.cloud.network.NetworkManager; +import com.cloud.network.NetworkVO; import com.cloud.network.ovs.dao.GreTunnelVO; import com.cloud.network.ovs.dao.OvsTunnelDao; import com.cloud.network.ovs.dao.OvsTunnelVO; @@ -40,12 +43,14 @@ public class OvsTunnelListener implements Listener { HostDao _hostDao; OvsTunnelDao _tunnelDao; ResourceManager _resourceMgr; + NetworkManager _networkMgr; public OvsTunnelListener(OvsTunnelDao tunnelDao, HostDao hostDao) { this._hostDao = hostDao; this._tunnelDao = tunnelDao; ComponentLocator locator = ComponentLocator.getLocator("management-server"); _resourceMgr = locator.getManager(ResourceManager.class); + _networkMgr = locator.getManager(NetworkManager.class); } @Override @@ -74,36 +79,38 @@ public class OvsTunnelListener implements Listener { return; } - try { - List hosts = _resourceMgr.listAllHostsInAllZonesByType(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(); - } + //try { + //List hosts = _resourceMgr.listAllHostsInAllZonesByType(Host.Type.Routing); + // NOTE: Trying to semplfy things by removing tunnel pre-allocation + //List networks = _networkMgr.listAllNetworksInAllZonesByType(GuestType.Isolated); + //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 diff --git a/server/src/com/cloud/network/ovs/OvsTunnelManager.java b/server/src/com/cloud/network/ovs/OvsTunnelManager.java index 3a6163672a4..f31a2b08953 100644 --- a/server/src/com/cloud/network/ovs/OvsTunnelManager.java +++ b/server/src/com/cloud/network/ovs/OvsTunnelManager.java @@ -12,19 +12,18 @@ // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.network.ovs; -import com.cloud.agent.manager.Commands; import com.cloud.deploy.DeployDestination; +import com.cloud.network.Network; 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.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; public interface OvsTunnelManager extends Manager { boolean isOvsTunnelEnabled(); - public void VmCheckAndCreateTunnel(VirtualMachineProfile vm, DeployDestination dest); + public void VmCheckAndCreateTunnel(VirtualMachineProfile vm, Network nw, DeployDestination dest); - public void CheckAndDestroyTunnel(VirtualMachine vm); + public void CheckAndDestroyTunnel(VirtualMachine vm, Network nw); + + public void applyDefaultFlow(VirtualMachine instance, DeployDestination dest); } diff --git a/server/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/server/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index b4799da9347..b9f177c9336 100644 --- a/server/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/server/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -33,8 +33,9 @@ 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.Network; +import com.cloud.network.ovs.dao.OvsTunnelNetworkDao; +import com.cloud.network.ovs.dao.OvsTunnelNetworkVO; import com.cloud.network.ovs.dao.OvsTunnelDao; import com.cloud.utils.Pair; import com.cloud.utils.component.Inject; @@ -42,11 +43,14 @@ 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.NicVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; @Local(value={OvsTunnelManager.class}) @@ -57,14 +61,13 @@ public class OvsTunnelManagerImpl implements OvsTunnelManager { boolean _isEnabled; ScheduledExecutorService _executorPool; ScheduledExecutorService _cleanupExecutor; - OvsTunnelListener _listener; @Inject ConfigurationDao _configDao; - @Inject OvsTunnelDao _tunnelDao; + @Inject NicDao _nicDao; @Inject HostDao _hostDao; @Inject UserVmDao _userVmDao; @Inject DomainRouterDao _routerDao; - @Inject OvsTunnelAccountDao _tunnelAccountDao; + @Inject OvsTunnelNetworkDao _tunnelNetworkDao; @Inject AgentManager _agentMgr; @Override @@ -76,37 +79,32 @@ public class OvsTunnelManagerImpl implements OvsTunnelManager { 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; + protected OvsTunnelNetworkVO createTunnelRecord(long from, long to, long networkId) { + OvsTunnelNetworkVO ta = null; try { - key = _tunnelDao.askKey(from, to); - ta = new OvsTunnelAccountVO(from, to, key, account); - OvsTunnelAccountVO lock = _tunnelAccountDao.acquireInLockTable(Long.valueOf(1)); + // Use the network id as a Key + // FIXME: networkid is long, key must be integer + // This is a horrible cast, and it must be removed and replace with something better + // before committing into master + ta = new OvsTunnelNetworkVO(from, to, (int)networkId, networkId); + OvsTunnelNetworkVO lock = _tunnelNetworkDao.acquireInLockTable(Long.valueOf(1)); if (lock == null) { s_logger.warn("Cannot lock table ovs_tunnel_account"); - return -1; + return null; } - _tunnelAccountDao.persist(ta); - _tunnelAccountDao.releaseFromLockTable(lock.getId()); + _tunnelNetworkDao.persist(ta); + _tunnelNetworkDao.releaseFromLockTable(lock.getId()); } catch (EntityExistsException e) { - ta = _tunnelAccountDao.getByFromToAccount(from, to, account); - if (ta == null) { - key = -1; - } else { - key = ta.getKey(); - } + s_logger.debug("A record for the tunnel from " + from + " to " + to + " already exists"); } - return key; + return ta; } private void handleCreateTunnelAnswer(Answer[] answers){ @@ -116,12 +114,12 @@ public class OvsTunnelManagerImpl implements OvsTunnelManager { r.getFromIp(), r.getToIp(), r.getBridge(), r.getKey(), r.getInPortName()); Long from = r.getFrom(); Long to = r.getTo(); - long account = r.getAccount(); - OvsTunnelAccountVO ta = _tunnelAccountDao.getByFromToAccount(from, to, account); + long networkId = r.getNetworkId(); + OvsTunnelNetworkVO ta = _tunnelNetworkDao.getByFromToNetwork(from, to, networkId); if (ta == null) { - throw new CloudRuntimeException(String.format("Unable find tunnelAccount record(from=%1$s, to=%2$s, account=%3$s", from, to, account)); + throw new CloudRuntimeException(String.format("Unable find tunnelAccount record(from=%1$s, to=%2$s, account=%3$s", from, to, networkId)); } - + s_logger.debug("Result:" + r.getResult()); if (!r.getResult()) { ta.setState("FAILED"); s_logger.warn("Create GRE tunnel failed due to " + r.getDetails() + s); @@ -130,24 +128,28 @@ public class OvsTunnelManagerImpl implements OvsTunnelManager { ta.setPortName(r.getInPortName()); s_logger.warn("Create GRE tunnel " + r.getDetails() + s); } - _tunnelAccountDao.update(ta.getId(), ta); + _tunnelNetworkDao.update(ta.getId(), ta); } @DB - protected void CheckAndCreateTunnel(VirtualMachine instance, DeployDestination dest) { + protected void CheckAndCreateTunnel(VirtualMachine instance, Network nw, DeployDestination dest) { if (!_isEnabled) { return; } + s_logger.debug("Creating tunnels with OVS tunnel manager"); if (instance.getType() != VirtualMachine.Type.User && instance.getType() != VirtualMachine.Type.DomainRouter) { + s_logger.debug("Will not work if you're not an instance or a virtual router"); return; } long hostId = dest.getHost().getId(); - long accountId = instance.getAccountId(); - Listvms = _userVmDao.listByAccountId(accountId); - List routers = _routerDao.findBy(accountId, instance.getDataCenterIdToDeployIn()); + // Find active (i.e.: not shut off) VMs with a NIC on the target network + List vms = _userVmDao.listByNetworkIdAndStates(nw.getId(), State.Running, State.Starting, + State.Stopping, State.Unknown, State.Migrating); + // Find routers for the network + List routers = _routerDao.findByNetwork(nw.getId()); Listins = new ArrayList(); if (vms != null) { ins.addAll(vms); @@ -155,62 +157,61 @@ public class OvsTunnelManagerImpl implements OvsTunnelManager { if (routers.size() != 0) { ins.addAll(routers); } - List>toHosts = new ArrayList>(); - List>fromHosts = new ArrayList>(); - int key; + s_logger.debug("### Virtual Machines:" + vms.size()); + s_logger.debug("### Virtual Routers:" + routers.size()); + List toHostIds = new ArrayList(); + List fromHostIds = new ArrayList(); 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); + //FIXME: Still using 'TunnelAccount name' - but should actually be tunnelNetwork or something like that + OvsTunnelNetworkVO ta = _tunnelNetworkDao.getByFromToNetwork(hostId, rh.longValue(), nw.getId()); + // Try and create the tunnel even if a previous attempt failed + if (ta == null || ta.getState().equals("FAILED")) { + s_logger.debug("Attempting to create tunnel from:" + hostId + " to:" + rh.longValue()); + if (ta == null) { + this.createTunnelRecord(hostId, rh.longValue(), nw.getId()); + } + if (!toHostIds.contains(rh)) { + toHostIds.add(rh); } } - 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); + ta = _tunnelNetworkDao.getByFromToNetwork(rh.longValue(), hostId, nw.getId()); + // Try and create the tunnel even if a previous attempt failed + if (ta == null || ta.getState().equals("FAILED")) { + s_logger.debug("Attempting to create tunnel from:" + rh.longValue() + " to:" + hostId); + if (ta == null) { + this.createTunnelRecord(rh.longValue(), hostId, nw.getId()); + } + if (!fromHostIds.contains(rh)) { + fromHostIds.add(rh); } } } try { String myIp = dest.getHost().getPrivateIpAddress(); - for (Pair i : toHosts) { - HostVO rHost = _hostDao.findById(i.first()); + for (Long i : toHostIds) { + HostVO rHost = _hostDao.findById(i); Commands cmds = new Commands( - new OvsCreateTunnelCommand(rHost.getPrivateIpAddress(), i.second().toString(), Long.valueOf(hostId), i.first(), accountId, myIp)); - s_logger.debug("Ask host " + hostId + " to create gre tunnel to " + i.first()); + new OvsCreateTunnelCommand(rHost.getPrivateIpAddress(), String.valueOf(nw.getId()), + Long.valueOf(hostId), i, nw.getId(), myIp)); + s_logger.debug("Ask host " + hostId + " to create gre tunnel to " + i); Answer[] answers = _agentMgr.send(hostId, cmds); handleCreateTunnelAnswer(answers); } - for (Pair i : fromHosts) { - HostVO rHost = _hostDao.findById(i.first()); + for (Long i : fromHostIds) { + HostVO rHost = _hostDao.findById(i); Commands cmd2s = new Commands( - new OvsCreateTunnelCommand(myIp, i.second().toString(), i.first(), Long.valueOf(hostId), accountId, rHost.getPrivateIpAddress())); - s_logger.debug("Ask host " + i.first() + " to create gre tunnel to " + hostId); - Answer[] answers = _agentMgr.send(i.first(), cmd2s); + new OvsCreateTunnelCommand(myIp, String.valueOf(nw.getId()), + i, Long.valueOf(hostId), nw.getId(), rHost.getPrivateIpAddress())); + s_logger.debug("Ask host " + i + " to create gre tunnel to " + hostId); + Answer[] answers = _agentMgr.send(i, cmd2s); handleCreateTunnelAnswer(answers); } } catch (Exception e) { @@ -239,67 +240,91 @@ public class OvsTunnelManagerImpl implements OvsTunnelManager { } @Override - public void VmCheckAndCreateTunnel(VirtualMachineProfile vm, DeployDestination dest) { - CheckAndCreateTunnel(vm.getVirtualMachine(), dest); + public void VmCheckAndCreateTunnel(VirtualMachineProfile vm, Network nw, DeployDestination dest) { + CheckAndCreateTunnel(vm.getVirtualMachine(), nw, dest); } - private void handleDestroyTunnelAnswer(Answer ans, long from, long to, long account) { - String toStr = (to == 0 ? "all peers" : Long.toString(to)); + private void handleDestroyTunnelAnswer(Answer ans, long from, long to, long network_id) { if (ans.getResult()) { - OvsTunnelAccountVO lock = _tunnelAccountDao.acquireInLockTable(Long.valueOf(1)); + OvsTunnelNetworkVO lock = _tunnelNetworkDao.acquireInLockTable(Long.valueOf(1)); if (lock == null) { - s_logger.warn(String.format("failed to lock ovs_tunnel_account, remove record of tunnel(from=%1$s, to=%2$s account=%3$s) failed", from, to, account)); + s_logger.warn(String.format("failed to lock ovs_tunnel_account, remove record of " + + "tunnel(from=%1$s, to=%2$s account=%3$s) failed", + from, to, network_id)); return; } - if (to == 0) { - _tunnelAccountDao.removeByFromAccount(from, account); - } else { - _tunnelAccountDao.removeByFromToAccount(from, to, account); - } - _tunnelAccountDao.releaseFromLockTable(lock.getId()); + _tunnelNetworkDao.removeByFromToNetwork(from, to, network_id); + _tunnelNetworkDao.releaseFromLockTable(lock.getId()); - s_logger.debug(String.format("Destroy tunnel(account:%1$s, from:%2$s, to:%3$s) successful", account, from, toStr)); + s_logger.debug(String.format("Destroy tunnel(account:%1$s, from:%2$s, to:%3$s) successful", + network_id, from, to)); } else { - s_logger.debug(String.format("Destroy tunnel(account:%1$s, from:%2$s, to:%3$s) failed", account, from, toStr)); + s_logger.debug(String.format("Destroy tunnel(account:%1$s, from:%2$s, to:%3$s) failed", + network_id, from, to)); } } - + + private void handleDestroyBridgeAnswer(Answer ans, long host_id, long network_id) { + + if (ans.getResult()) { + OvsTunnelNetworkVO lock = _tunnelNetworkDao.acquireInLockTable(Long.valueOf(1)); + if (lock == null) { + s_logger.warn("failed to lock ovs_tunnel_network, remove record"); + return; + } + + _tunnelNetworkDao.removeByFromNetwork(host_id, network_id); + _tunnelNetworkDao.releaseFromLockTable(lock.getId()); + + s_logger.debug(String.format("Destroy bridge for network %1$s successful", network_id)); + } else { + s_logger.debug(String.format("Destroy bridge for network %1$s failed", network_id)); + } + } + @Override - public void CheckAndDestroyTunnel(VirtualMachine vm) { - if (!_isEnabled) { + public void CheckAndDestroyTunnel(VirtualMachine vm, Network nw) { + s_logger.debug("### DESTROYING YOUR TUNNELS!"); + if (!_isEnabled) { return; } List userVms = _userVmDao.listByAccountIdAndHostId(vm.getAccountId(), vm.getHostId()); + s_logger.debug("### User Vms:" + userVms.size()); if (vm.getType() == VirtualMachine.Type.User) { if (userVms.size() > 1) { return; } - List routers = _routerDao.findBy(vm.getAccountId(), vm.getDataCenterIdToDeployIn()); + List routers = _routerDao.findByNetwork(nw.getId()); for (DomainRouterVO router : routers) { if (router.getHostId() == vm.getHostId()) { - return; + s_logger.debug("### Not destroying because on same host as router"); + return; } } } else if (vm.getType() == VirtualMachine.Type.DomainRouter && userVms.size() != 0) { return; } - + s_logger.debug("### I WILL DESTROYYY"); try { - /* Now we are last one on host, destroy all tunnels of my account */ - Command cmd = new OvsDestroyTunnelCommand(vm.getAccountId(), "[]"); + /* Now we are last one on host, destroy the bridge with all + * the tunnels for this network */ + Command cmd = new OvsDestroyBridgeCommand(nw.getId()); + s_logger.debug("### Destroying bridge for network " + nw.getId() + " on host:" + vm.getHostId()); Answer ans = _agentMgr.send(vm.getHostId(), cmd); - handleDestroyTunnelAnswer(ans, vm.getHostId(), 0, vm.getAccountId()); + handleDestroyBridgeAnswer(ans, vm.getHostId(), nw.getId()); /* 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()); + List peers = _tunnelNetworkDao.listByToNetwork(vm.getHostId(), nw.getId()); + for (OvsTunnelNetworkVO p : peers) { + cmd = new OvsDestroyTunnelCommand(p.getNetworkId(), p.getPortName()); + s_logger.debug("### Destroying tunnel to " + vm.getHostId() + + " from " + p.getFrom()); ans = _agentMgr.send(p.getFrom(), cmd); - handleDestroyTunnelAnswer(ans, p.getFrom(), p.getTo(), p.getAccount()); + handleDestroyTunnelAnswer(ans, p.getFrom(), p.getTo(), p.getNetworkId()); } } catch (Exception e) { s_logger.warn(String.format("Destroy tunnel(account:%1$s, hostId:%2$s) failed", vm.getAccountId(), vm.getHostId()), e); @@ -307,4 +332,21 @@ public class OvsTunnelManagerImpl implements OvsTunnelManager { } + @Override + public void applyDefaultFlow(VirtualMachine instance, + DeployDestination dest) { + if (!_isEnabled) { + return; + } + + VirtualMachine.Type vmType = instance.getType(); + if (vmType != VirtualMachine.Type.User + && vmType != VirtualMachine.Type.DomainRouter) { + return; + } + + s_logger.debug("### Applying rules for allowing broadcast traffic"); + + } + } diff --git a/server/src/com/cloud/offerings/dao/NetworkOfferingServiceMapDaoImpl.java b/server/src/com/cloud/offerings/dao/NetworkOfferingServiceMapDaoImpl.java index 1379461477f..28eed904e43 100644 --- a/server/src/com/cloud/offerings/dao/NetworkOfferingServiceMapDaoImpl.java +++ b/server/src/com/cloud/offerings/dao/NetworkOfferingServiceMapDaoImpl.java @@ -22,6 +22,7 @@ import com.cloud.network.NetworkServiceMapVO; import com.cloud.network.Network.Service; import com.cloud.network.Network.Provider; import com.cloud.offerings.NetworkOfferingServiceMapVO; +import com.cloud.offerings.NetworkOfferingVO; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -31,7 +32,8 @@ import com.cloud.utils.db.SearchCriteria.Func; @Local(value=NetworkOfferingServiceMapDao.class) @DB(txn=false) public class NetworkOfferingServiceMapDaoImpl extends GenericDaoBase implements NetworkOfferingServiceMapDao { - final SearchBuilder AllFieldsSearch; + + final SearchBuilder AllFieldsSearch; final SearchBuilder MultipleServicesSearch; final GenericSearchBuilder ProvidersSearch; final GenericSearchBuilder ServicesSearch; @@ -133,7 +135,18 @@ public class NetworkOfferingServiceMapDaoImpl extends GenericDaoBase listServicesForNetworkOffering(long networkOfferingId) { SearchCriteria sc = ServicesSearch.create();; sc.setParameters("networkOfferingId", networkOfferingId); - return customSearch(sc, null); } + + @Override + public NetworkOfferingServiceMapVO persist( + NetworkOfferingServiceMapVO entity) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("networkOfferingId", entity.getNetworkOfferingId()); + sc.setParameters("service", entity.getService()); + sc.setParameters("provider", entity.getProvider()); + NetworkOfferingServiceMapVO mappingInDb = findOneBy(sc); + return mappingInDb!=null? mappingInDb : super.persist(entity); + } + } diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 7a686f78986..fbabf335109 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -94,6 +94,7 @@ import com.cloud.utils.PropertiesUtil; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.DB; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; @@ -889,6 +890,9 @@ public class ConfigurationServerImpl implements ConfigurationServer { defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.PortForwarding, Provider.VirtualRouter); defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Vpn, Provider.VirtualRouter); + Map ovsIsolatedSourceNatEnabledNetworkOfferingProviders = + new HashMap(defaultIsolatedSourceNatEnabledNetworkOfferingProviders); + ovsIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Connectivity, Provider.OvsConnectivityProvider); Map netscalerServiceProviders = new HashMap(); netscalerServiceProviders.put(Service.Dhcp, Provider.VirtualRouter); netscalerServiceProviders.put(Service.Dns, Provider.VirtualRouter); @@ -903,18 +907,18 @@ public class ConfigurationServerImpl implements ConfigurationServer { txn.start(); // Offering #1 - NetworkOfferingVO deafultSharedSGNetworkOffering = new NetworkOfferingVO( + NetworkOfferingVO defaultSharedSGNetworkOffering = new NetworkOfferingVO( NetworkOffering.DefaultSharedNetworkOfferingWithSGService, "Offering for Shared Security group enabled networks", TrafficType.Guest, false, true, null, null, true, Availability.Optional, null, Network.GuestType.Shared, true, true); - deafultSharedSGNetworkOffering.setState(NetworkOffering.State.Enabled); - deafultSharedSGNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(deafultSharedSGNetworkOffering); - + defaultSharedSGNetworkOffering.setState(NetworkOffering.State.Enabled); + defaultSharedSGNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultSharedSGNetworkOffering); + for (Service service : defaultSharedSGNetworkOfferingProviders.keySet()) { - NetworkOfferingServiceMapVO offService = new NetworkOfferingServiceMapVO(deafultSharedSGNetworkOffering.getId(), service, defaultSharedSGNetworkOfferingProviders.get(service)); + NetworkOfferingServiceMapVO offService = new NetworkOfferingServiceMapVO(defaultSharedSGNetworkOffering.getId(), service, defaultSharedSGNetworkOfferingProviders.get(service)); _ntwkOfferingServiceMapDao.persist(offService); s_logger.trace("Added service for the network offering: " + offService); } @@ -986,7 +990,25 @@ public class ConfigurationServerImpl implements ConfigurationServer { _ntwkOfferingServiceMapDao.persist(offService); s_logger.trace("Added service for the network offering: " + offService); } + + // Offering #6 - OVS + s_logger.debug("Adding network offering for OVS networks"); + NetworkOfferingVO ovsIsolatedSourceNatEnabledNetworkOffering = new NetworkOfferingVO( + NetworkOffering.OvsIsolatedNetworkOfferingWithSourceNatService, + "Offering for OVS-based Isolated networks with Source Nat service enabled", + TrafficType.Guest, + false, false, null, null, true, Availability.Optional, + null, Network.GuestType.Isolated, true, false); + ovsIsolatedSourceNatEnabledNetworkOffering.setState(NetworkOffering.State.Enabled); + ovsIsolatedSourceNatEnabledNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(ovsIsolatedSourceNatEnabledNetworkOffering); + + for (Service service : ovsIsolatedSourceNatEnabledNetworkOfferingProviders.keySet()) { + NetworkOfferingServiceMapVO offService = new NetworkOfferingServiceMapVO(ovsIsolatedSourceNatEnabledNetworkOffering.getId(), service, ovsIsolatedSourceNatEnabledNetworkOfferingProviders.get(service)); + _ntwkOfferingServiceMapDao.persist(offService); + s_logger.debug("Added service for the OVS network offering: " + offService); + } + txn.commit(); } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 6eb98d89558..d34b8ffbd82 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3097,7 +3097,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager if (storageId != null) { sc.setJoinParameters("volumeSearch", "poolId", storageId); } - + s_logger.debug("THE WHERE CLAUSE IS:" + sc.getWhereClause()); return _vmDao.search(sc, searchFilter); } diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 8c40dfa2770..6e2961ea1d9 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -385,7 +385,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); - + s_logger.debug("Cleaning up NICS"); _networkMgr.cleanupNics(profile); // Clean up volumes based on the vm's instance id _storageMgr.cleanupVolumes(vm.getId()); @@ -949,7 +949,13 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } } - _networkMgr.release(profile, force); + try { + _networkMgr.release(profile, force); + s_logger.debug("Successfully released network resources for the vm " + vm); + } catch (Exception e) { + s_logger.warn("Unable to release some network resources.", e); + } + _storageMgr.release(profile); s_logger.debug("Successfully cleanued up resources for the vm " + vm + " in " + state + " state"); return true; diff --git a/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java b/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java index bc136ed81e1..1f28c3ad547 100755 --- a/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java +++ b/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java @@ -134,6 +134,7 @@ public class DomainRouterDaoImpl extends GenericDaoBase im return listBy(sc); } + @Override public List listBy(long accountId) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 5f8f4938b5f..cfd42223df4 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1725,18 +1725,18 @@ CREATE TABLE `cloud`.`ovs_tunnel`( PRIMARY KEY(`from`, `to`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE TABLE `cloud`.`ovs_tunnel_account`( +CREATE TABLE `cloud`.`ovs_tunnel_network`( `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', + `network_id` bigint unsigned COMMENT 'network identifier', `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; -INSERT INTO `cloud`.`ovs_tunnel_account` (`from`, `to`, `account`, `key`, `port_name`, `state`) VALUES (0, 0, 0, 0, 'lock', 'SUCCESS'); +INSERT INTO `cloud`.`ovs_tunnel_network` (`from`, `to`, `network_id`, `key`, `port_name`, `state`) VALUES (0, 0, 0, 0, 'lock', 'SUCCESS'); CREATE TABLE `cloud`.`ovs_vlan_mapping_dirty`( `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT,