From c94b6acadf27972571e7315199472efa40e789ca Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 5 Jan 2011 18:05:44 -0800 Subject: [PATCH] bug 7722: open vswitch - works at VM start time, need fullsync() and flow clean in next --- .../xen/resource/CitrixResourceBase.java | 23 +- .../vm/hypervisor/xenserver/vlanRemapUtils.py | 55 +-- scripts/vm/hypervisor/xenserver/vmops | 59 +++- .../hypervisor/xenserver/xenserver56fp1/patch | 1 + .../DefaultComponentLibrary.java | 4 + .../com/cloud/network/NetworkManagerImpl.java | 1 + .../com/cloud/network/element/OvsElement.java | 11 +- .../cloud/network/ovs/OvsNetworkManager.java | 16 +- .../network/ovs/OvsNetworkManagerImpl.java | 316 ++++++++++++++++-- .../com/cloud/network/ovs/dao/OvsWorkDao.java | 23 ++ .../cloud/network/ovs/dao/OvsWorkDaoImpl.java | 187 +++++++++++ .../com/cloud/network/ovs/dao/OvsWorkVO.java | 123 +++++++ .../cloud/network/ovs/dao/VlanMappingDao.java | 1 + .../network/ovs/dao/VlanMappingDaoImpl.java | 7 + .../cloud/network/ovs/dao/VlanMappingVO.java | 17 + .../cloud/network/ovs/dao/VmFlowLogDao.java | 7 + .../network/ovs/dao/VmFlowLogDaoImpl.java | 27 ++ .../cloud/network/ovs/dao/VmFlowLogVO.java | 59 ++++ .../VirtualNetworkApplianceManagerImpl.java | 5 + .../src/com/cloud/vm/UserVmManagerImpl.java | 11 +- setup/db/create-schema.sql | 20 ++ 21 files changed, 890 insertions(+), 83 deletions(-) create mode 100644 server/src/com/cloud/network/ovs/dao/OvsWorkDao.java create mode 100644 server/src/com/cloud/network/ovs/dao/OvsWorkDaoImpl.java create mode 100644 server/src/com/cloud/network/ovs/dao/OvsWorkVO.java create mode 100644 server/src/com/cloud/network/ovs/dao/VmFlowLogDao.java create mode 100644 server/src/com/cloud/network/ovs/dao/VmFlowLogDaoImpl.java create mode 100644 server/src/com/cloud/network/ovs/dao/VmFlowLogVO.java diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 66bec2dcb60..d51aab03e12 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -3820,34 +3820,22 @@ public abstract class CitrixResourceBase implements ServerResource { return Boolean.valueOf(callHostPlugin(conn, "vmops", "can_bridge_firewall", "host_uuid", _host.uuid)); } - //TODO: it's better to move more stuff at host plugin side private Answer execute(OvsSetTagAndFlowCommand cmd) { Connection conn = getConnection(); try { - Set vms = VM.getByNameLabel(conn, cmd.getVmName()); - VM vm = vms.iterator().next(); - Set vifs = vm.getVIFs(conn); - String domId = vm.getDomid(conn).toString(); String nwName = Networks.BroadcastScheme.VSwitch.toString(); Network nw = getNetworkByName(conn, nwName); assert nw!= null : "Why there is no vswith network ???"; String bridge = nw.getBridge(conn); /*If VM is domainRouter, this will try to set flow and tag on its - * none guest network nic. don't worry, it will fail sciently at host + * none guest network nic. don't worry, it will fail silently at host * plugin side */ - for (VIF vif : vifs) { - String vifName = "vif" + domId + vif.getDevice(conn); - String result = callHostPlugin(conn, "vmops", "vlanRemapUtils", "op", "createFlow", "bridge", - bridge, "vifName", vifName, "mac", - vif.getMAC(conn), "remap", cmd.getVlans(), - "ip", "placeholder now"); - s_logger.debug("set flow for " + vifName + " on " + cmd.getVmName() + " " + result); - - } + String result = callHostPlugin(conn, "vmops", "ovs_set_tag_and_flow", "bridge", bridge, + "vmName", cmd.getVmName(), "vlans", cmd.getVlans()); + s_logger.debug("set flow for " + cmd.getVmName() + " " + result); - /* if (result.equalsIgnoreCase("SUCCESS")) { return new Answer(cmd, true, "Set flow for " + cmd.getVmName() + " success, vlans:" + cmd.getVlans()); @@ -3855,9 +3843,6 @@ public abstract class CitrixResourceBase implements ServerResource { return new Answer(cmd, false, "Set flow for " + cmd.getVmName() + " failed, vlans:" + cmd.getVlans()); } - */ - return new Answer(cmd, true, "Set flow for " + cmd.getVmName() - + " success, vlans:" + cmd.getVlans()); } catch (Exception e) { e.printStackTrace(); } diff --git a/scripts/vm/hypervisor/xenserver/vlanRemapUtils.py b/scripts/vm/hypervisor/xenserver/vlanRemapUtils.py index 60147055872..4be4b8d8a99 100644 --- a/scripts/vm/hypervisor/xenserver/vlanRemapUtils.py +++ b/scripts/vm/hypervisor/xenserver/vlanRemapUtils.py @@ -338,7 +338,7 @@ def formatARPFlow(bridge, inPort, vlan, ports): actions=strip_vlan,%s" % (inPort, vlan, outputs) return flow -def createFlow (bridge, vifName, mac, ip, vlan, remap): +def createFlow (bridge, vifName, mac, remap): inport = getGreOfPorts(bridge) if len(inport) == 0: log("WARNING: no inport found") @@ -357,7 +357,7 @@ def createFlow (bridge, vifName, mac, ip, vlan, remap): delFlow(mac) #set remap here, remap has format e.g. [1,22,200,13,16] - remap = strip(remap, "both") + remap = strip(remap) log("") log("Create flow for remap") noneGreOfPorts = getNoneGreOfPort(bridge) @@ -366,7 +366,7 @@ def createFlow (bridge, vifName, mac, ip, vlan, remap): log("WARNING: no none GRE ofports found, no ARP flow will be created") isARP = False - for j in remap.split(","): + for j in remap.split("/"): delARPFlow(j) for i in inport: flow = formatFlow(i, j, mac, output) @@ -383,8 +383,19 @@ def createFlow (bridge, vifName, mac, ip, vlan, remap): return 0 ######################## End Flow creation utils ########################## -def setTag(vifName, vlan): - log("") +def setTag(bridge, vifName, vlan): + # The startVM command is slow, we may wait for a while for it creates vif on + # open vswitch + log("Waiting for %s ..." % vifName) + waitPortCmd = [vsctlPath, "--timeout=10 wait-until port %s -- get port %s name" % \ + (vifName, vifName)] + doCmd (waitPortCmd) + log("%s is here" % vifName) + + if getVifPort(bridge, vifName) == None: + log("WARNING: %s is not on bridge %s" % (vifName, bridge)) + return 0 + log("Set tag") setTagCmd = [vsctlPath, "set port", vifName, "tag=%s"%vlan] doCmd (setTagCmd) @@ -398,20 +409,22 @@ def doCreateGRE(bridge, remoteIP, key): log("WARNING: create GRE tunnel on %s for %s success" % (bridge, \ remoteIP)) -def doCreateFlow (bridge, vifName, mac, ip, vlan, remap): - setTag (vifName, vlan) - if createFlow(bridge, vifName, mac, ip, vlan, remap) < 0: - log ("Create flow failed(bridge=%s, vifName=%s, mac=%s, ip=%s, vlan=%s,\ -remap=%s" % (bridge, vifName, mac, ip, vlan, remap)) +def doCreateFlow (bridge, vifName, mac, remap): + if createFlow(bridge, vifName, mac, remap) < 0: + log ("Create flow failed(bridge=%s, vifName=%s, mac=%s,\ +remap=%s" % (bridge, vifName, mac, remap)) else: - log ("Create flow success(bridge=%s, vifName=%s, mac=%s, ip=%s, vlan=%s,\ -remap=%s" % (bridge, vifName, mac, ip, vlan, remap)) + log ("Create flow success(bridge=%s, vifName=%s, mac=%s,\ +remap=%s" % (bridge, vifName, mac, remap)) + +def doSetTag (bridge, vifName, tag): + setTag(bridge, vifName, tag) def doDeleteFlow(bridge, vifName, mac, remap): delFlow(mac) log("Delete flows for %s" % mac) - remap = strip(remap, "both") + remap = strip(remap) # remove our port from arp flow inport = getGreOfPorts(bridge) @@ -423,7 +436,7 @@ def doDeleteFlow(bridge, vifName, mac, remap): noneGreOfPorts.remove(mine) log("Delete ARP flows for(vifname=%s, ofport=%s)" % (vifName, mine)) - for j in remap.split(","): + for j in remap.split("/"): delARPFlow(j) for i in inport: flow = formatARPFlow(bridge, i, j, noneGreOfPorts) @@ -457,14 +470,12 @@ if __name__ == "__main__": key = sys.argv[4] doCreateGRE(bridge, remoteIP, key) elif op == "createFlow": - checkArgNum(8) + checkArgNum(6) bridge = sys.argv[2] vifName = sys.argv[3] mac = sys.argv[4] - vlan = sys.argv[5] - remap = sys.argv[6] - ip = sys.argv[7] - doCreateFlow(bridge, vifName, mac, ip, vlan, remap) + remap = sys.argv[5] + doCreateFlow(bridge, vifName, mac, remap) elif op == "deleteFlow": checkArgNum(6) bridge = sys.argv[2] @@ -472,6 +483,12 @@ if __name__ == "__main__": mac = sys.argv[4] remap = sys.argv[5] doDeleteFlow(bridge, vifName, mac, remap) + elif op == "setTag": + checkArgNum(5) + bridge = sys.argv[2] + vifName = sys.argv[3] + tag = sys.argv[4] + doSetTag(bridge, vifName, tag) else: log("WARNING: get an unkown op %s" % op) result=errors["ERROR_OP"] diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops index a49fb2ef2c6..1c0071f8032 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -144,14 +144,16 @@ def vlanRemapUtils(session, args): cmd.insert(3, args.pop("bridge")) cmd.insert(4, args.pop("vifName")) cmd.insert(5, args.pop("mac")) - cmd.insert(6, args.pop("vlan")) cmd.insert(7, args.pop("remap")) - cmd.insert(8, args.pop("ip")) elif op == "deleteFlow": cmd.insert(3, args.pop("bridge")) cmd.insert(4, args.pop("vifName")) cmd.insert(5, args.pop("mac")) cmd.insert(6, args.pop("remap")) + elif op == "setTag": + cmd.insert(3, args.pop("bridge")) + cmd.insert(4, args.pop("vifName")) + cmd.insert(5, args.pop("tag")) try: txt = util.pread2(cmd) @@ -552,6 +554,47 @@ def default_ebtables_rules(vm_name, vif, vm_ip, vm_mac): util.SMlog("Failed to program default ebtables OUT rules") return 'false' +def ovs_set_tag_and_flow(session, args): + def get_vif_field(name, field): + return session.xenapi.VIF.get_record(name).get(field) + + bridge = args.pop('bridge') + vm_name = args.pop('vmName') + vlanStr = args.pop('vlans') + + try: + vm = session.xenapi.VM.get_by_name_label(vm_name) + if len(vm) != 1: + return 'false' + vm_rec = session.xenapi.VM.get_record(vm[0]) + vm_vifs = vm_rec.get('VIFs') + vifrs = [] + for vif in vm_vifs: + rec = (get_vif_field(vif, 'device'), get_vif_field(vif, 'MAC')) + vifrs.append(rec) + domid = vm_rec.get('domid') + except: + util.SMlog("### Failed to get domid or vif list for vm ##" + vm_name) + return 'false' + + if domid == '-1': + util.SMlog("### Failed to get domid for vm (-1): " + vm_name) + return 'false' + + if len(vifrs) == 0: + return 'SUCCESS' + + if vlanStr.startswith("/"): vlanStr = vlanStr[1:] + if vlanStr.endswith("/"): vlanStr = vlanStr[:-1] + vlans = vlanStr.split("/") + for vifr in vifrs: + vifName = "vif" + domid + "." + vifr[0] + mac = vifr[1] + vlanRemapUtils(session, {"op":"setTag", "vifName":vifName, "bridge":bridge, "tag":vlans[0]}) + vlanRemapUtils(session, {"op":"createFlow", "vifName":vifName, "bridge":bridge, "mac":mac, "remap":vlanStr}) + + return 'SUCCESS' + @echo def default_network_rules_systemvm(session, args): vm_name = args.pop('vmName') @@ -627,9 +670,9 @@ def default_network_rules(session, args): vifs = [vif] try: util.pread2(['ifconfig', tap]) - vifs.append(tap) + vifs.append(tap) except: - pass + pass delete_rules_for_vm_in_bridge_firewall_chain(vm_name) @@ -669,7 +712,7 @@ def default_network_rules(session, args): return 'false' for v in vifs: - default_ebtables_rules(vm_name, v, vm_ip, vm_mac) + default_ebtables_rules(vm_name, v, vm_ip, vm_mac) if write_rule_log_for_vm(vmName, vm_id, vm_ip, domid, '_initial_', '-1') == False: util.SMlog("Failed to log default network rules, ignoring") @@ -983,9 +1026,9 @@ def network_rules(session, args): vifs = [vif] try: util.pread2(['ifconfig', tap]) - vifs.append(tap) + vifs.append(tap) except: - pass + pass vm_name = '-'.join(vm_name.split('-')[:-1]) vmchain = vm_name @@ -1064,5 +1107,5 @@ def network_rules(session, args): if __name__ == "__main__": - XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "preparemigration": preparemigration, "setIptables": setIptables, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "networkUsage": networkUsage, "network_rules":network_rules, "can_bridge_firewall":can_bridge_firewall, "default_network_rules":default_network_rules, "destroy_network_rules_for_vm":destroy_network_rules_for_vm, "default_network_rules_systemvm":default_network_rules_systemvm, "get_rule_logs_for_vms":get_rule_logs_for_vms, "setLinkLocalIP":setLinkLocalIP, "lt2p_vpn":lt2p_vpn, "vlanRemapUtils":vlanRemapUtils,"cleanup_rules":cleanup_rules}) + XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "preparemigration": preparemigration, "setIptables": setIptables, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "networkUsage": networkUsage, "network_rules":network_rules, "can_bridge_firewall":can_bridge_firewall, "default_network_rules":default_network_rules, "destroy_network_rules_for_vm":destroy_network_rules_for_vm, "default_network_rules_systemvm":default_network_rules_systemvm, "get_rule_logs_for_vms":get_rule_logs_for_vms, "setLinkLocalIP":setLinkLocalIP, "lt2p_vpn":lt2p_vpn,"cleanup_rules":cleanup_rules,"vlanRemapUtils":vlanRemapUtils, "ovs_set_tag_and_flow":ovs_set_tag_and_flow}) diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch index 9c95ab4847f..67c20b97d3c 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch @@ -31,3 +31,4 @@ call_firewall.sh=../../../../network/domr/,0755,/opt/xensource/bin call_loadbalancer.sh=../../../../network/domr/,0755,/opt/xensource/bin l2tp_vpn.sh=../../../../network/domr/,0755,/opt/xensource/bin cloud-setup-bonding.sh=..,0755,/opt/xensource/bin +vlanRemapUtils.py=..,0755,/opt/xensource/bin diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index 717b5d17936..94536cdeb3d 100644 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -73,8 +73,10 @@ 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.dao.OvsWorkDaoImpl; import com.cloud.network.ovs.dao.VlanMappingDaoImpl; import com.cloud.network.ovs.dao.VlanMappingDirtyDaoImpl; +import com.cloud.network.ovs.dao.VmFlowLogDaoImpl; import com.cloud.network.router.VirtualNetworkApplianceManagerImpl; import com.cloud.network.rules.RulesManagerImpl; import com.cloud.network.rules.dao.PortForwardingRulesDaoImpl; @@ -241,6 +243,8 @@ public class DefaultComponentLibrary implements ComponentLibrary { addDao("ClusterDetailsDao", ClusterDetailsDaoImpl.class); addDao("VlanMappingDao", VlanMappingDaoImpl.class); addDao("VlanMappingDirtyDao", VlanMappingDirtyDaoImpl.class); + addDao("OvsWorkDao", OvsWorkDaoImpl.class); + addDao("VmFlowLogDao", VmFlowLogDaoImpl.class); } Map> _managers = new HashMap>(); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index ed071278b1c..5f1b0bbefb3 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -252,6 +252,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } @Override + @DB public PublicIp assignSourceNatIpAddress(Account owner, Network network, long callerId) throws ConcurrentOperationException, InsufficientAddressCapacityException { assert (network.getTrafficType() != null) : "You're asking for a source nat but your network can't participate in source nat. What do you have to say for yourself?"; diff --git a/server/src/com/cloud/network/element/OvsElement.java b/server/src/com/cloud/network/element/OvsElement.java index 2f036e53c0b..4d660b8723c 100644 --- a/server/src/com/cloud/network/element/OvsElement.java +++ b/server/src/com/cloud/network/element/OvsElement.java @@ -10,6 +10,7 @@ import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; +import com.cloud.network.Networks; import com.cloud.network.PublicIpAddress; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; @@ -55,7 +56,15 @@ public class OvsElement extends AdapterBase implements NetworkElement { DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - _ovsNetworkMgr.CheckAndUpdateDhcpFlow(network); + VirtualMachine instance = vm.getVirtualMachine(); + + if (instance.getType() == VirtualMachine.Type.DomainRouter) { + return true; + } + + if (network.getTrafficType() == Networks.TrafficType.Guest) { + _ovsNetworkMgr.CheckAndUpdateDhcpFlow(network); + } return true; } diff --git a/server/src/com/cloud/network/ovs/OvsNetworkManager.java b/server/src/com/cloud/network/ovs/OvsNetworkManager.java index 8ea881cdeef..41687e92e00 100644 --- a/server/src/com/cloud/network/ovs/OvsNetworkManager.java +++ b/server/src/com/cloud/network/ovs/OvsNetworkManager.java @@ -5,6 +5,7 @@ import com.cloud.deploy.DeployDestination; import com.cloud.network.Network; import com.cloud.uservm.UserVm; import com.cloud.utils.component.Manager; +import com.cloud.vm.DomainRouterVO; import com.cloud.vm.State; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachineProfile; @@ -16,12 +17,21 @@ public interface OvsNetworkManager extends Manager { public String getVlanMapping(long accountId); - public void CheckAndCreateTunnel(Commands cmds, + public void UserVmCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile profile, DeployDestination dest); - public void applyDefaultFlow(Commands cmds, + public void applyDefaultFlowToUserVm(Commands cmds, VirtualMachineProfile profile, DeployDestination dest); - + + public void applyDefaultFlowToRouter(Commands cmds, + VirtualMachineProfile profile, + DeployDestination dest); + public void CheckAndUpdateDhcpFlow(Network nw); + public void handleVmStateTransition(UserVm userVm, State vmState); + + public void RouterCheckAndCreateTunnel(Commands cmds, + VirtualMachineProfile profile, + DeployDestination dest); } diff --git a/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java b/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java index 28e8741b584..b7a12e6436c 100644 --- a/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java +++ b/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java @@ -1,8 +1,15 @@ package com.cloud.network.ovs; import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + import javax.ejb.Local; import javax.naming.ConfigurationException; @@ -13,6 +20,7 @@ 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.exception.AgentUnavailableException; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.network.Network; @@ -20,17 +28,27 @@ import com.cloud.network.NetworkVO; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.ovs.dao.OvsWorkDao; +import com.cloud.network.ovs.dao.OvsWorkVO; +import com.cloud.network.ovs.dao.OvsWorkVO.Step; import com.cloud.network.ovs.dao.VlanMappingDao; import com.cloud.network.ovs.dao.VlanMappingDirtyDao; import com.cloud.network.ovs.dao.VlanMappingVO; +import com.cloud.network.ovs.dao.VmFlowLogDao; +import com.cloud.network.ovs.dao.VmFlowLogVO; +import com.cloud.server.ManagementServer; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.NicVO; import com.cloud.vm.State; import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; @@ -49,61 +67,183 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { @Inject NetworkDao _networkDao; @Inject VlanMappingDirtyDao _vlanMappingDirtyDao; @Inject DomainRouterDao _routerDao; + @Inject OvsWorkDao _workDao; + @Inject VmFlowLogDao _flowLogDao; + @Inject UserVmDao _userVMDao; String _name; boolean _isEnabled; + ScheduledExecutorService _executorPool; + ScheduledExecutorService _cleanupExecutor; + + private long _serverId; + private final long _timeBetweenCleanups = 30; //seconds + + public class WorkerThread implements Runnable { + @Override + public void run() { + work(); + } + + WorkerThread() { + + } + } + + public class CleanupThread implements Runnable { + @Override + public void run() { + cleanupFinishedWork(); + cleanupUnfinishedWork(); + } + + CleanupThread() { + + } + } @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; _isEnabled = _configDao.getValue(Config.OvsNetwork.key()).equalsIgnoreCase("true") ? true : false; + _serverId = ((ManagementServer)ComponentLocator.getComponent(ManagementServer.Name)).getId(); + _executorPool = Executors.newScheduledThreadPool(10, new NamedThreadFactory("OVS")); + _cleanupExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("OVS-Cleanup")); return true; } @Override public boolean start() { - // TODO Auto-generated method stub + if (_isEnabled) { + _cleanupExecutor.scheduleAtFixedRate(new CleanupThread(), _timeBetweenCleanups, _timeBetweenCleanups, TimeUnit.SECONDS); + } return true; } @Override public boolean stop() { - // TODO Auto-generated method stub return true; } @Override public String getName() { - // TODO Auto-generated method stub return _name; } @Override public boolean isOvsNetworkEnabled() { - // TODO Auto-generated method stub return _isEnabled; } + public void cleanupFinishedWork() { + Date before = new Date(System.currentTimeMillis() - 24*3600*1000l); + int numDeleted = _workDao.deleteFinishedWork(before); + if (numDeleted > 0) { + s_logger.info("Ovs cleanup deleted " + numDeleted + " finished work items older than " + before.toString()); + } + + } + + + private void cleanupUnfinishedWork() { + Date before = new Date(System.currentTimeMillis() - 30*1000l); + List unfinished = _workDao.findUnfinishedWork(before); + if (unfinished.size() > 0) { + s_logger.info("Ovscleanup found " + unfinished.size() + " unfinished work items older than " + before.toString()); + Set affectedVms = new HashSet(); + for (OvsWorkVO work: unfinished) { + affectedVms.add(work.getInstanceId()); + } + + s_logger.info("Ovs cleanup re-schedule unfinished work"); + scheduleFlowUpdateToHosts(affectedVms, false, null); + } else { + s_logger.debug("Ovs cleanup found no unfinished work items older than " + before.toString()); + } + } + + //TODO: think about lock, how new VM start when we change rows + @DB + public void work() { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Checking the database"); + } + final OvsWorkVO work = _workDao.take(_serverId); + if (work == null) { + return; + } + Long userVmId = work.getInstanceId(); + UserVm vm = null; + Long seqnum = null; + s_logger.info("Ovs working on " + work.toString()); + final Transaction txn = Transaction.currentTxn(); + txn.start(); + try { + vm = _userVMDao.acquireInLockTable(work.getInstanceId()); + if (vm == null) { + s_logger.warn("Ovs unable to acquire lock on vm id=" + userVmId); + return ; + } + + String vlans = getVlanMapping(vm.getAccountId()); + Long agentId = null; + VmFlowLogVO log = _flowLogDao.findByVmId(userVmId); + if (log == null) { + s_logger.warn("Ovs cannot find log record for vm id=" + userVmId); + return; + } + seqnum = log.getLogsequence(); + + if (vm != null && vm.getState() == State.Running) { + agentId = vm.getHostId(); + if (agentId != null ) { + //TODO: set flow here + OvsSetTagAndFlowCommand cmd = new OvsSetTagAndFlowCommand(vm.getName(),vlans); + Commands cmds = new Commands(cmd); + try { + //_agentMgr.send(agentId, cmds, _answerListener); + _agentMgr.send(agentId, cmds, null); + //TODO: clean dirty in answerListener + } catch (AgentUnavailableException e) { + s_logger.debug("Unable to send updates for vm: " + userVmId + "(agentid=" + agentId + ")"); + _workDao.updateStep(work.getInstanceId(), seqnum, Step.Done); + } + } + } + } finally { + if (vm != null) { + _userVMDao.releaseFromLockTable(userVmId); + _workDao.updateStep(work.getId(), Step.Done); + } + txn.commit(); + } + + + } + + //TODO: think about lock @Override + @DB public long askVlanId(long accountId, long hostId) { assert _isEnabled : "Who call me ??? while OvsNetwokr is not enabled!!!"; final Transaction txn = Transaction.currentTxn(); txn.start(); - List mappings = _vlanMappingDao.listByAccountIdAndHostId(accountId, hostId); + VlanMappingVO currVlan = _vlanMappingDao.findByAccountIdAndHostId(accountId, hostId); long vlan = 0; - if (mappings.size() !=0) { - assert mappings.size() == 1 : "We should only have one vlan for an account on a host"; - txn.commit(); - vlan = mappings.get(0).getVlan(); + if (currVlan != null) { + vlan = currVlan.getVlan(); + currVlan.ref(); + _vlanMappingDao.update(currVlan.getId(), currVlan); s_logger.debug("Already has an Vlan " + vlan + " on host " + hostId - + " for account " + accountId + ", use it!"); + + " for account " + accountId + ", use it, reference count is " + currVlan.getRef()); + txn.commit(); return vlan; } - mappings = _vlanMappingDao.listByHostId(hostId); + Listmappings = _vlanMappingDao.listByHostId(hostId); if (mappings.size() > 0) { ArrayList vlans = new ArrayList(); for (VlanMappingVO vo : mappings) { @@ -129,8 +269,9 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { } @Override + @DB public String getVlanMapping(long accountId) { - assert _isEnabled : "Who call me ??? while OvsNetwokr is not enabled!!!"; + assert _isEnabled : "Who call me ??? while OvsNetwork is not enabled!!!"; final Transaction txn = Transaction.currentTxn(); txn.start(); @@ -151,22 +292,21 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { return buf.toString(); } - @Override - public void CheckAndCreateTunnel(Commands cmds, VirtualMachineProfile profile, + protected void CheckAndCreateTunnel(Commands cmds, VMInstanceVO instance, DeployDestination dest) { if (!_isEnabled) { return; } - UserVmVO userVm = profile.getVirtualMachine(); - if (userVm.getType() != VirtualMachine.Type.User) { + if (instance.getType() != VirtualMachine.Type.User + && instance.getType() != VirtualMachine.Type.DomainRouter) { return; } long hostId = dest.getHost().getId(); - long accountId = userVm.getAccountId(); + long accountId = instance.getAccountId(); List vms = _userVmDao.listByAccountIdAndHostId(accountId, hostId); - if (vms.size() != 0) { + if (vms.size() > 1 || (vms.size() == 1 && vms.get(0).getId() != instance.getId())) { s_logger.debug("Already has GRE tunnel for account " + accountId + " for host " + hostId); return; @@ -204,21 +344,19 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { return sub; } - @Override - public void applyDefaultFlow(Commands cmds, - VirtualMachineProfile profile, DeployDestination dest) { + protected void applyDefaultFlow(Commands cmds, + VMInstanceVO instance, DeployDestination dest) { if (!_isEnabled) { return; } - UserVmVO userVm = profile.getVirtualMachine(); - VirtualMachine.Type vmType = userVm.getType(); + VirtualMachine.Type vmType = instance.getType(); if (vmType != VirtualMachine.Type.User && vmType != VirtualMachine.Type.DomainRouter) { return; } - List nics = _nicDao.listBy(userVm.getId()); + List nics = _nicDao.listBy(instance.getId()); if (nics.size() == 0) return; @@ -237,7 +375,7 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { assert nic!=null : "Why there is no guest network nic???"; String vlans = parseVlanAndMapping(nic.getBroadcastUri().toASCIIString()); - cmds.addCommand(new OvsSetTagAndFlowCommand(userVm.getName(), vlans)); + cmds.addCommand(new OvsSetTagAndFlowCommand(instance.getName(), vlans)); } @Override @@ -267,12 +405,102 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { } } - protected void handleVmStarted(UserVm userVm) { + @DB + public void scheduleFlowUpdateToHosts(Set affectedVms, boolean updateSeqno, Long delayMs) { + if (!_isEnabled) { + return; + } + + if (affectedVms == null) { + return; + } + + if (delayMs == null) + delayMs = new Long(100l); + + for (Long vmId: affectedVms) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + VmFlowLogVO log = null; + OvsWorkVO work = null; + UserVm vm = null; + try { + vm = _userVMDao.acquireInLockTable(vmId); + if (vm == null) { + s_logger.warn("Ovs failed to acquire lock on vm id " + vmId); + continue; + } + log = _flowLogDao.findByVmId(vmId); + if (log == null) { + log = new VmFlowLogVO(vmId); + log = _flowLogDao.persist(log); + } + + if (log != null && updateSeqno){ + log.incrLogsequence(); + _flowLogDao.update(log.getId(), log); + } + + work = _workDao.findByVmIdStep(vmId, Step.Scheduled); + if (work == null) { + work = new OvsWorkVO(vmId, null, null, OvsWorkVO.Step.Scheduled, null); + work = _workDao.persist(work); + } + + work.setLogsequenceNumber(log.getLogsequence()); + _workDao.update(work.getId(), work); + } finally { + if (vm != null) { + _userVMDao.releaseFromLockTable(vmId); + } + } + txn.commit(); + + _executorPool.schedule(new WorkerThread(), delayMs, TimeUnit.MILLISECONDS); + + } + } + + protected Set getAffectedVms(UserVm userVm) { + long accountId = userVm.getAccountId(); + if (!_vlanMappingDirtyDao.isDirty(accountId)) { + return null; + } + + Set affectedVms = new HashSet(); + List vms = _userVmDao.listByAccountId(accountId); + for (UserVmVO vm : vms) { + affectedVms.add(new Long(vm.getId())); + } + return affectedVms; + } + + protected void handleVmStateChange(UserVm userVm) { + Set affectedVms = getAffectedVms(userVm); scheduleFlowUpdateToHosts(affectedVms, true, null); } - protected void handleVmStopped(UserVm userVm) { - scheduleFlowUpdateToHosts(affectedVms, true, null); + //TODO: think about lock + protected void checkAndRemove(UserVm userVm) { + long accountId = userVm.getAccountId(); + long hostId = userVm.getHostId(); + + final Transaction txn = Transaction.currentTxn(); + txn.start(); + VlanMappingVO vo = _vlanMappingDao.findByAccountIdAndHostId(accountId, hostId); + if (vo.unref() == 0) { + _vlanMappingDao.remove(vo.getId()); + s_logger.debug(userVm.getName() + " is the last one on host " + + hostId + " for account " + accountId + + ", remove vlan in ovs_host_vlan_alloc"); + _vlanMappingDirtyDao.markDirty(accountId); + } else { + _vlanMappingDao.update(vo.getId(), vo); + s_logger.debug(userVm.getName() + + " reduces reference count of (account,host) = (" + + accountId + "," + hostId + ") to " + vo.getRef()); + } + txn.commit(); } @Override @@ -291,14 +519,42 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { case Unknown: return; case Running: - handleVmStarted(userVm); + handleVmStateChange(userVm); break; case Stopping: case Stopped: - handleVmStopped(userVm); + checkAndRemove(userVm); + handleVmStateChange(userVm); break; } } + @Override + public void UserVmCheckAndCreateTunnel(Commands cmds, + VirtualMachineProfile profile, DeployDestination dest) { + CheckAndCreateTunnel(cmds, (VMInstanceVO)profile.getVirtualMachine(), dest); + } + + @Override + public void RouterCheckAndCreateTunnel(Commands cmds, + VirtualMachineProfile profile, + DeployDestination dest) { + CheckAndCreateTunnel(cmds, (VMInstanceVO)profile.getVirtualMachine(), dest); + } + + @Override + public void applyDefaultFlowToUserVm(Commands cmds, + VirtualMachineProfile profile, DeployDestination dest) { + applyDefaultFlow(cmds, profile.getVirtualMachine(), dest); + + } + + @Override + public void applyDefaultFlowToRouter(Commands cmds, + VirtualMachineProfile profile, + DeployDestination dest) { + applyDefaultFlow(cmds, profile.getVirtualMachine(), dest); + } + } diff --git a/server/src/com/cloud/network/ovs/dao/OvsWorkDao.java b/server/src/com/cloud/network/ovs/dao/OvsWorkDao.java new file mode 100644 index 00000000000..048de9d9c78 --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsWorkDao.java @@ -0,0 +1,23 @@ +package com.cloud.network.ovs.dao; + +import java.util.Date; +import java.util.List; + +import com.cloud.network.ovs.dao.OvsWorkVO.Step; +import com.cloud.utils.db.GenericDao; + +public interface OvsWorkDao extends GenericDao { + OvsWorkVO findByVmId(long vmId, boolean taken); + + OvsWorkVO findByVmIdStep(long vmId, Step step); + + OvsWorkVO take(long serverId); + + void updateStep(Long vmId, Long logSequenceNumber, Step done); + + void updateStep(Long workId, Step done); + + int deleteFinishedWork(Date timeBefore); + + List findUnfinishedWork(Date timeBefore); +} diff --git a/server/src/com/cloud/network/ovs/dao/OvsWorkDaoImpl.java b/server/src/com/cloud/network/ovs/dao/OvsWorkDaoImpl.java new file mode 100644 index 00000000000..663ff3f0b01 --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsWorkDaoImpl.java @@ -0,0 +1,187 @@ +package com.cloud.network.ovs.dao; + +import java.util.Date; +import java.util.List; +import javax.ejb.Local; +import com.cloud.ha.HaWorkVO; +import com.cloud.network.ovs.dao.OvsWorkVO.Step; +import com.cloud.utils.db.Filter; +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; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; + +@Local(value={OvsWorkDao.class}) +public class OvsWorkDaoImpl extends GenericDaoBase implements + OvsWorkDao { + private SearchBuilder VmIdTakenSearch; + private SearchBuilder VmIdSeqNumSearch; + private SearchBuilder VmIdUnTakenSearch; + private SearchBuilder UntakenWorkSearch; + private SearchBuilder VmIdStepSearch; + private SearchBuilder CleanupSearch; + + + protected OvsWorkDaoImpl() { + VmIdTakenSearch = createSearchBuilder(); + VmIdTakenSearch.and("vmId", VmIdTakenSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); + VmIdTakenSearch.and("taken", VmIdTakenSearch.entity().getDateTaken(), SearchCriteria.Op.NNULL); + + VmIdTakenSearch.done(); + + VmIdUnTakenSearch = createSearchBuilder(); + VmIdUnTakenSearch.and("vmId", VmIdUnTakenSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); + VmIdUnTakenSearch.and("taken", VmIdUnTakenSearch.entity().getDateTaken(), SearchCriteria.Op.NULL); + + VmIdUnTakenSearch.done(); + + UntakenWorkSearch = createSearchBuilder(); + UntakenWorkSearch.and("server", UntakenWorkSearch.entity().getServerId(), SearchCriteria.Op.NULL); + UntakenWorkSearch.and("taken", UntakenWorkSearch.entity().getDateTaken(), SearchCriteria.Op.NULL); + UntakenWorkSearch.and("step", UntakenWorkSearch.entity().getStep(), SearchCriteria.Op.EQ); + + UntakenWorkSearch.done(); + + VmIdSeqNumSearch = createSearchBuilder(); + VmIdSeqNumSearch.and("vmId", VmIdSeqNumSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); + VmIdSeqNumSearch.and("seqno", VmIdSeqNumSearch.entity().getLogsequenceNumber(), SearchCriteria.Op.EQ); + + VmIdSeqNumSearch.done(); + + VmIdStepSearch = createSearchBuilder(); + VmIdStepSearch.and("vmId", VmIdStepSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); + VmIdStepSearch.and("step", VmIdStepSearch.entity().getStep(), SearchCriteria.Op.EQ); + + VmIdStepSearch.done(); + + CleanupSearch = createSearchBuilder(); + CleanupSearch.and("taken", CleanupSearch.entity().getDateTaken(), Op.LTEQ); + CleanupSearch.and("step", CleanupSearch.entity().getStep(), SearchCriteria.Op.IN); + + CleanupSearch.done(); + + + } + + @Override + public OvsWorkVO findByVmId(long vmId, boolean taken) { + SearchCriteria sc = taken?VmIdTakenSearch.create():VmIdUnTakenSearch.create(); + sc.setParameters("vmId", vmId); + return findOneIncludingRemovedBy(sc); + } + + @Override + public OvsWorkVO take(long serverId) { + final Transaction txn = Transaction.currentTxn(); + try { + final SearchCriteria sc = UntakenWorkSearch.create(); + sc.setParameters("step", Step.Scheduled); + + final Filter filter = new Filter(OvsWorkVO.class, null, true, 0l, 1l);//FIXME: order desc by update time? + + txn.start(); + final List vos = lockRows(sc, filter, true); + if (vos.size() == 0) { + txn.commit(); + return null; + } + OvsWorkVO work = null; + for (OvsWorkVO w: vos) { + //ensure that there is no job in Processing state for the same VM + if ( findByVmIdStep(w.getInstanceId(), Step.Processing) == null) { + work = w; + break; + } + } + if (work == null) { + txn.commit(); + return null; + } + work.setServerId(serverId); + work.setDateTaken(new Date()); + work.setStep(OvsWorkVO.Step.Processing); + + update(work.getId(), work); + + txn.commit(); + + return work; + + } catch (final Throwable e) { + throw new CloudRuntimeException("Unable to execute take", e); + } + } + + @Override + public void updateStep(Long vmId, Long logSequenceNumber, Step step) { + final Transaction txn = Transaction.currentTxn(); + txn.start(); + SearchCriteria sc = VmIdSeqNumSearch.create(); + sc.setParameters("vmId", vmId); + sc.setParameters("seqno", logSequenceNumber); + + final Filter filter = new Filter(HaWorkVO.class, null, true, 0l, 1l); + + final List vos = lockRows(sc, filter, true); + if (vos.size() == 0) { + txn.commit(); + return; + } + OvsWorkVO work = vos.get(0); + work.setStep(step); + update(work.getId(), work); + + txn.commit(); + } + + @Override + public OvsWorkVO findByVmIdStep(long vmId, Step step) { + SearchCriteria sc = VmIdStepSearch.create(); + sc.setParameters("vmId", vmId); + sc.setParameters("step", step); + return findOneIncludingRemovedBy(sc); + } + + @Override + public void updateStep(Long workId, Step step) { + final Transaction txn = Transaction.currentTxn(); + txn.start(); + + OvsWorkVO work = lockRow(workId, true); + if (work == null) { + txn.commit(); + return; + } + work.setStep(step); + update(work.getId(), work); + + txn.commit(); + + } + + @Override + public int deleteFinishedWork(Date timeBefore) { + final SearchCriteria sc = CleanupSearch.create(); + sc.setParameters("taken", timeBefore); + sc.setParameters("step", Step.Done); + + return expunge(sc); + } + + @Override + public List findUnfinishedWork(Date timeBefore) { + final SearchCriteria sc = CleanupSearch.create(); + sc.setParameters("taken", timeBefore); + sc.setParameters("step", Step.Processing); + + List result = listIncludingRemovedBy(sc); + + OvsWorkVO work = createForUpdate(); + work.setStep(Step.Error); + update(work, sc); + + return result; + } +} diff --git a/server/src/com/cloud/network/ovs/dao/OvsWorkVO.java b/server/src/com/cloud/network/ovs/dao/OvsWorkVO.java new file mode 100644 index 00000000000..0a49f583c05 --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/OvsWorkVO.java @@ -0,0 +1,123 @@ +package com.cloud.network.ovs.dao; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import com.cloud.utils.db.GenericDao; + + +@Entity +@Table(name="ovs_work") +public class OvsWorkVO { + public enum Step { + Scheduled, + Processing, + Done, + Error + } + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id; + + @Column(name="instance_id", updatable=false, nullable=false) + private Long instanceId; // vm_instance id + + + @Column(name="mgmt_server_id", nullable=true) + private Long serverId; + + @Column(name=GenericDao.CREATED_COLUMN) + private Date created; + + + @Column(name="step", nullable = false) + @Enumerated(value=EnumType.STRING) + private Step step; + + @Column(name="taken", nullable=true) + @Temporal(value=TemporalType.TIMESTAMP) + private Date dateTaken; + + @Column(name="seq_no", nullable=true) + private Long logsequenceNumber = null; + + + protected OvsWorkVO() { + } + + public Long getId() { + return id; + } + + public Long getInstanceId() { + return instanceId; + } + + + public Long getServerId() { + return serverId; + } + + + public void setServerId(final Long serverId) { + this.serverId = serverId; + } + + public Date getCreated() { + return created; + } + + + + public OvsWorkVO(Long instanceId, Long serverId, Date created, + Step step, Date dateTaken) { + super(); + this.instanceId = instanceId; + this.serverId = serverId; + this.created = created; + this.step = step; + this.dateTaken = dateTaken; + } + + @Override + public String toString() { + return new StringBuilder("[Ovs-Work:id=").append(id).append(":vm=").append(instanceId).append("]").toString(); + } + + public Date getDateTaken() { + return dateTaken; + } + + public void setStep(Step step) { + this.step = step; + } + + public Step getStep() { + return step; + } + + public void setDateTaken(Date date) { + dateTaken = date; + } + + public Long getLogsequenceNumber() { + return logsequenceNumber; + } + + public void setLogsequenceNumber(Long logsequenceNumber) { + this.logsequenceNumber = logsequenceNumber; + } + +} diff --git a/server/src/com/cloud/network/ovs/dao/VlanMappingDao.java b/server/src/com/cloud/network/ovs/dao/VlanMappingDao.java index 713a9ca13c2..43a9926ec6e 100644 --- a/server/src/com/cloud/network/ovs/dao/VlanMappingDao.java +++ b/server/src/com/cloud/network/ovs/dao/VlanMappingDao.java @@ -8,4 +8,5 @@ public interface VlanMappingDao extends GenericDao { List listByAccountIdAndHostId(long accountId, long hostId); List listByHostId(long hostId); List listByAccountId(long accountId); + VlanMappingVO findByAccountIdAndHostId(long accountId, long hostId); } diff --git a/server/src/com/cloud/network/ovs/dao/VlanMappingDaoImpl.java b/server/src/com/cloud/network/ovs/dao/VlanMappingDaoImpl.java index be1c7bda360..ea801138125 100644 --- a/server/src/com/cloud/network/ovs/dao/VlanMappingDaoImpl.java +++ b/server/src/com/cloud/network/ovs/dao/VlanMappingDaoImpl.java @@ -47,4 +47,11 @@ public class VlanMappingDaoImpl extends GenericDaoBase return listBy(sc, null); } + + @Override + public VlanMappingVO findByAccountIdAndHostId(long accountId, long hostId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("account_id", accountId); + return findOneBy(sc); + } } diff --git a/server/src/com/cloud/network/ovs/dao/VlanMappingVO.java b/server/src/com/cloud/network/ovs/dao/VlanMappingVO.java index 37fafa55fda..f19fe57281d 100644 --- a/server/src/com/cloud/network/ovs/dao/VlanMappingVO.java +++ b/server/src/com/cloud/network/ovs/dao/VlanMappingVO.java @@ -23,11 +23,15 @@ public class VlanMappingVO { @Column(name = "vlan") private long vlan; + + @Column(name = "ref") + private int ref; public VlanMappingVO(long accountId, long hostId, long vlan) { this.hostId = hostId; this.accountId = accountId; this.vlan = vlan; + this.ref = 1; } public VlanMappingVO() { @@ -49,4 +53,17 @@ public class VlanMappingVO { public long getId() { return id; } + + public int getRef() { + return ref; + } + + public void ref() { + ref++; + } + + public int unref() { + ref--; + return ref; + } } diff --git a/server/src/com/cloud/network/ovs/dao/VmFlowLogDao.java b/server/src/com/cloud/network/ovs/dao/VmFlowLogDao.java new file mode 100644 index 00000000000..6742a6a9c94 --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/VmFlowLogDao.java @@ -0,0 +1,7 @@ +package com.cloud.network.ovs.dao; + +import com.cloud.utils.db.GenericDao; + +public interface VmFlowLogDao extends GenericDao { + VmFlowLogVO findByVmId(long vmId); +} diff --git a/server/src/com/cloud/network/ovs/dao/VmFlowLogDaoImpl.java b/server/src/com/cloud/network/ovs/dao/VmFlowLogDaoImpl.java new file mode 100644 index 00000000000..a842bc3bbbc --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/VmFlowLogDaoImpl.java @@ -0,0 +1,27 @@ +package com.cloud.network.ovs.dao; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import javax.ejb.Local; + +@Local(value={VmFlowLogDao.class}) +public class VmFlowLogDaoImpl extends GenericDaoBase + implements VmFlowLogDao { + private SearchBuilder VmIdSearch; + + @Override + public VmFlowLogVO findByVmId(long vmId) { + SearchCriteria sc = VmIdSearch.create(); + sc.setParameters("vmId", vmId); + return findOneIncludingRemovedBy(sc); + } + + protected VmFlowLogDaoImpl() { + VmIdSearch = createSearchBuilder(); + VmIdSearch.and("vmId", VmIdSearch.entity().getInstanceId(), + SearchCriteria.Op.EQ); + VmIdSearch.done(); + + } +} diff --git a/server/src/com/cloud/network/ovs/dao/VmFlowLogVO.java b/server/src/com/cloud/network/ovs/dao/VmFlowLogVO.java new file mode 100644 index 00000000000..df44d6c326d --- /dev/null +++ b/server/src/com/cloud/network/ovs/dao/VmFlowLogVO.java @@ -0,0 +1,59 @@ +package com.cloud.network.ovs.dao; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.utils.db.GenericDao; + +@Entity +@Table(name="ovs_vm_flow_log") +public class VmFlowLogVO { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id; + + @Column(name="instance_id", updatable=false, nullable=false) + private Long instanceId; // vm_instance id + + @Column(name=GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name="logsequence") + long logsequence; + + protected VmFlowLogVO() { + + } + + public VmFlowLogVO(Long instanceId) { + super(); + this.instanceId = instanceId; + } + + public Long getId() { + return id; + } + + public Long getInstanceId() { + return instanceId; + } + + public Date getCreated() { + return created; + } + + public long getLogsequence() { + return logsequence; + } + + public void incrLogsequence() { + logsequence++; + } +} diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 0f90aaa5344..87d729c1f60 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -129,6 +129,7 @@ import com.cloud.network.dao.VpnUserDao; import com.cloud.network.lb.LoadBalancingRule; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRulesManager; +import com.cloud.network.ovs.OvsNetworkManager; import com.cloud.network.router.VirtualRouter.Role; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.PortForwardingRule; @@ -294,6 +295,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian RemoteAccessVpnDao _vpnDao; @Inject VMInstanceDao _instanceDao; + OvsNetworkManager _ovsNetworkMgr; long _routerTemplateId = -1; int _routerRamSize; @@ -1250,6 +1252,9 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian @Override public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException{ NicProfile controlNic = (NicProfile) profile.getParameter("control.nic"); + _ovsNetworkMgr.RouterCheckAndCreateTunnel(cmds, profile, dest); + _ovsNetworkMgr.applyDefaultFlowToRouter(cmds, profile, dest); + cmds.addCommand("checkSsh", new CheckSshCommand(profile.getInstanceName(), controlNic.getIp4Address(), 3922, 5, 20)); DomainRouterVO router = profile.getVirtualMachine(); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 4974ecc933f..9403e768a1c 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -1133,7 +1133,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager public void completeStartCommand(UserVmVO vm) { _itMgr.stateTransitTo(vm, VirtualMachine.Event.AgentReportRunning, vm.getHostId()); _networkGroupMgr.handleVmStateTransition(vm, State.Running); - + _ovsNetworkMgr.handleVmStateTransition(vm, State.Running); } @Override @@ -1167,6 +1167,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager txn.commit(); _networkGroupMgr.handleVmStateTransition(vm, State.Stopped); + _ovsNetworkMgr.handleVmStateTransition(vm, State.Stopped); } catch (Throwable th) { s_logger.error("Error during stop: ", th); throw new CloudRuntimeException("Error during stop: ", th); @@ -2343,7 +2344,10 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager try { vm = _itMgr.start(vm, null, caller, owner, cmd.getHypervisor()); - } finally { + } catch (Exception e) { + e.printStackTrace(); + } + finally { updateVmStateForFailedVmCreation(vm.getId()); } vm.setPassword(password); @@ -2397,7 +2401,8 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } _vmDao.update(userVm.getId(), userVm); - _ovsNetworkMgr.CheckAndCreateTunnel(cmds, profile, dest); + _ovsNetworkMgr.UserVmCheckAndCreateTunnel(cmds, profile, dest); + _ovsNetworkMgr.applyDefaultFlowToUserVm(cmds, profile, dest); return true; } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index d2d1c68b56a..bd980f476a5 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1328,6 +1328,7 @@ CREATE TABLE `cloud`.`ovs_host_vlan_alloc`( `host_id` bigint unsigned COMMENT 'host id', `account_id` bigint unsigned COMMENT 'account id', `vlan` bigint unsigned COMMENT 'vlan id under account #account_id on host #host_id', + `ref` int unsigned NOT NULL DEFAULT 0 COMMENT 'reference count', PRIMARY KEY(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -1338,4 +1339,23 @@ CREATE TABLE `cloud`.`ovs_vlan_mapping_dirty`( PRIMARY KEY(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `cloud`.`ovs_vm_flow_log` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `instance_id` bigint unsigned NOT NULL COMMENT 'vm instance that needs flows to be synced.', + `created` datetime NOT NULL COMMENT 'time the entry was requested', + `logsequence` bigint unsigned COMMENT 'seq number to be sent to agent, uniquely identifies flow update', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`ovs_work` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `instance_id` bigint unsigned NOT NULL COMMENT 'vm instance that needs rules to be synced.', + `mgmt_server_id` bigint unsigned COMMENT 'management server that has taken up the work of doing rule sync', + `created` datetime NOT NULL COMMENT 'time the entry was requested', + `taken` datetime COMMENT 'time it was taken by the management server', + `step` varchar(32) NOT NULL COMMENT 'Step in the work', + `seq_no` bigint unsigned COMMENT 'seq number to be sent to agent, uniquely identifies ruleset update', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + SET foreign_key_checks = 1;