From fcda7d15db2e2eae97474a42c20ef66ad61b7b66 Mon Sep 17 00:00:00 2001 From: davidjumani Date: Fri, 24 Apr 2020 16:49:33 +0530 Subject: [PATCH 1/8] Adding l2 pvlan for kvm --- .../cloud/agent/api/PvlanSetupCommand.java | 6 + .../orchestration/NetworkOrchestrator.java | 2 +- .../resource/LibvirtComputingResource.java | 2 +- .../LibvirtPvlanSetupCommandWrapper.java | 3 +- .../LibvirtComputingResourceTest.java | 2 +- scripts/vm/network/ovs-pvlan-kvm-vm.sh | 284 ++++++++++++++++++ .../main/java/com/cloud/utils/UriUtils.java | 2 +- .../java/com/cloud/utils/net/NetUtils.java | 43 +++ .../com/cloud/utils/net/NetUtilsTest.java | 12 +- 9 files changed, 350 insertions(+), 6 deletions(-) create mode 100644 scripts/vm/network/ovs-pvlan-kvm-vm.sh diff --git a/api/src/main/java/com/cloud/agent/api/PvlanSetupCommand.java b/api/src/main/java/com/cloud/agent/api/PvlanSetupCommand.java index 512d1bcbe4af..dc571f0d52c5 100644 --- a/api/src/main/java/com/cloud/agent/api/PvlanSetupCommand.java +++ b/api/src/main/java/com/cloud/agent/api/PvlanSetupCommand.java @@ -34,6 +34,7 @@ public enum Type { private String dhcpIp; private Type type; private String networkTag; + private String pvlanType; protected PvlanSetupCommand() { } @@ -43,6 +44,7 @@ protected PvlanSetupCommand(Type type, String op, URI uri, String networkTag) { this.op = op; this.primary = NetUtils.getPrimaryPvlanFromUri(uri); this.isolated = NetUtils.getIsolatedPvlanFromUri(uri); + this.pvlanType = NetUtils.getPvlanTypeFromUri(uri); this.networkTag = networkTag; } @@ -116,4 +118,8 @@ public void setDhcpName(String dhcpName) { public String getNetworkTag() { return networkTag; } + + public String getPvlanType() { + return pvlanType; + } } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index e16bde62b3ff..577a28e333ac 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2508,7 +2508,7 @@ public Network doInTransaction(final TransactionStatus status) { if (vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { throw new InvalidParameterValueException("Cannot support pvlan with untagged primary vlan!"); } - URI uri = NetUtils.generateUriForPvlan(vlanIdFinal, isolatedPvlan); + URI uri = NetUtils.generateUriForPvlan(vlanIdFinal, isolatedPvlan, isolatedPvlanType.toString()); if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString(), isolatedPvlanType).size() > 0) { throw new InvalidParameterValueException("Network with primary vlan " + vlanIdFinal + " and secondary vlan " + isolatedPvlan + " type " + isolatedPvlanType + diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 79958ef8ea43..3542e3cf36ba 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -803,7 +803,7 @@ public boolean configure(final String name, final Map params) th throw new ConfigurationException("Unable to find the ovs-pvlan-dhcp-host.sh"); } - _ovsPvlanVmPath = Script.findScript(networkScriptsDir, "ovs-pvlan-vm.sh"); + _ovsPvlanVmPath = Script.findScript(networkScriptsDir, "ovs-pvlan-kvm-vm.sh"); if (_ovsPvlanVmPath == null) { throw new ConfigurationException("Unable to find the ovs-pvlan-vm.sh"); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPvlanSetupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPvlanSetupCommandWrapper.java index 3e01dc425993..0d4b49ec0f65 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPvlanSetupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPvlanSetupCommandWrapper.java @@ -43,6 +43,7 @@ public final class LibvirtPvlanSetupCommandWrapper extends CommandWrapper -p -s -v -h \n" $(basename $0) >&2 + exit 2 +} + +br= +pri_vlan= +sec_vlan= +vm_mac= +op= +type= + +while getopts 'ADPCIb:p:s:i:d:m:v:n:h' OPTION +do + case $OPTION in + A) op="add" + ;; + D) op="del" + ;; + P) type="P" + ;; + I) type="I" + ;; + C) type="C" + ;; + b) br="$OPTARG" + ;; + p) pri_vlan="$OPTARG" + ;; + s) sec_vlan="$OPTARG" + ;; + v) vm_mac="$OPTARG" + ;; + h) usage + exit 1 + ;; + esac +done + +if [ -z "$op" ] +then + echo Missing operation pararmeter! + exit 1 +fi + +if [ -z "$type" ] +then + echo Missing pvlan type pararmeter! + exit 1 +fi + +if [ -z "$br" ] +then + echo Missing parameter bridge! + exit 1 +fi + +if [ -z "$vm_mac" ] +then + echo Missing parameter VM MAC! + exit 1 +fi + +if [ -z "$pri_vlan" ] +then + echo Missing parameter primary vlan! + exit 1 +fi + +if [ -z "$sec_vlan" ] +then + echo Missing parameter secondary vlan! + exit 1 +fi + +find_port() { + mac=`echo "$1" | sed -e 's/:/\\\:/g'` + port=`ovs-vsctl --column ofport find interface external_ids:attached-mac="$mac" | tr -d ' ' | cut -d ':' -f 2` + echo $port +} + +find_port_group() { + ovs-ofctl -O OpenFlow13 dump-groups $br | grep group_id=$1, | sed -e 's/.*actions=//g' -e 's/resubmit(,3)//g' -e 's/output://g' -e 's/^,//g' -e 's/,$//g' -e 's/,,/,/g' -e 's/ //g' +} + +# try to find the physical link to outside, only supports eth and em prefix now +trunk_port=`ovs-ofctl show $br | egrep "\((eth|em)[0-9]" | cut -d '(' -f 1|tr -d ' '` +vm_port=$(find_port $vm_mac) + +# craft the vlan headers. Adding 4096 as in hex, it must be of the form 0x1XXX +pri_vlan_header=$((4096 + $pri_vlan)) +sec_vlan_header=$((4096 + $sec_vlan)) + +# Get the groups for broadcast. Ensure we end the group id with ',' so that we wont accidentally match groupid 111 with 1110. +# We're using the header value for the pri vlan port group, as anything from a promiscuous device has to go to every device in the vlan. +# Since we're creating a separate group for just the promiscuous devices, adding 4096 so that it'll be unique. Hence we're restricted to 4096 vlans! +# Not a big deal because if you have vxlan, why do you even need pvlan!! +pri_vlan_ports=$(find_port_group $pri_vlan_header) +# Be smart! If it's an isolated pvlan, why do we need a group for those ports ? +if [ "$type" != "I" ] +then + sec_vlan_ports=$(find_port_group $sec_vlan) +fi + +add_to_ports() { + if [ -z "$1" ] + then + # To ensure that we don't get trailing commas + echo "$2" + else + # Dont add it if it already exists + echo "$1" | grep -w -q "$2" && echo "$1" && return + echo "$2,$1" + fi +} + +del_from_ports() { + # Delete when only, begining, middle and end of string + echo "$1" | sed -e "s/^$2$//g" -e "s/^$2,//g" -e "s/,$2$//g" -e "s/,$2,/,/g" +} + +mod_group() { + # Ensure that we don't delete the prom port group, because if we do, the rules that have it go away! + if [ "$1" == "$pri_vlan" ] + then + if [ -z "$2" ] + then + ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=indirect,bucket=resubmit\(,3\) + else + ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=indirect,bucket=resubmit\(,3\),$2 + fi + return + fi + if [ -z "$2" ] + then + ovs-ofctl -O OpenFlow13 del-groups $br group_id=$1 + else + ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=indirect,bucket=$2 + fi +} + +# Allow the neccessary protocols and QinQ +ovs-vsctl set bridge $br protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13 +ovs-vsctl set Open_vSwitch . other_config:vlan-limit=2 + +# So that we're friendly to non pvlan devices +ovs-ofctl add-flow $br priority=0,actions=NORMAL + +if [ "$op" == "add" ] +then + # From our pri vlan + ovs-ofctl add-flow $br table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac,actions=strip_vlan,resubmit\(,1\) + # From promiscuous + ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac,actions=strip_vlan,output:$vm_port + # From others in our own community + if [ "$type" == "C" ] + then + ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=$vm_mac,actions=strip_vlan,output:$vm_port + fi + # If we're promiscuous, accept anything because it's passed the first header + if [ "$type" == "P" ] + then + ovs-ofctl add-flow $br table=1,priority=70,dl_dst=$vm_mac,actions=strip_vlan,output:$vm_port + fi + # Security101 + ovs-ofctl add-flow $br table=1,priority=0,actions=drop + + # If the dest isn't on our switch send it out + ovs-ofctl add-flow $br table=0,priority=60,dl_vlan=$pri_vlan,dl_src=$vm_mac,actions=output:$trunk_port + # QinQ the packet. Outter header is the primary vlan and inner is the secondary + ovs-ofctl add-flow -O OpenFlow13 $br table=0,priority=50,vlan_tci=0x0000,dl_src=$vm_mac,actions=push_vlan:0x8100,set_field:$sec_vlan_header-\>vlan_vid,push_vlan:0x8100,set_field:$pri_vlan_header-\>vlan_vid,resubmit:$trunk_port + + # Broadcasats + # Create the respective groups + # To output anything that comes from a promiscuous device + pri_vlan_ports=$(add_to_ports "$pri_vlan_ports" "$vm_port") + mod_group $pri_vlan_header $pri_vlan_ports + if [ "$type" != "I" ] + then + # To output anything that comes from the same sec vlan + sec_vlan_ports=$(add_to_ports "$sec_vlan_ports" "$vm_port") + mod_group $sec_vlan $sec_vlan_ports + fi + # Group id 9999 for braodcasts sent by a vm on this switch. One action is to to output it to the trunk port, the other to process it ourselves + # Chose 9999 since the vlan range goes upto 4096, so it's more than double of that + ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=9999,type=all,bucket=action:output:$trunk_port,bucket=action:strip_vlan,resubmit\(,1\) + + # From a device on the same switch + ovs-ofctl add-flow $br table=0,priority=80,dl_vlan=$pri_vlan,dl_src=$vm_mac,dl_dst=ff:ff:ff:ff:ff:ff,actions=group:9999 + # From our pri vlan + ovs-ofctl add-flow $br table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,resubmit\(,1\) + # From a promiscuous device + ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$pri_vlan_header + if [ "$type" == "C" ] + then + # Ensure we have the promiscuous port group because if we don't, it'll fail to create the following rule + prom_ports=$(find_port_group $pri_vlan) + mod_group $pri_vlan $prom_ports + # Since it's from a community, gotta braodcast it to all community and promiscuous ports + ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$sec_vlan,group:$pri_vlan + fi + # I'm promiscuous, accept anything because it's passed the first header + if [ "$type" == "P" ] + then + ovs-ofctl add-flow $br table=1,priority=60,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$pri_vlan + fi +else + # Delete whatever we've added that's vm specific + ovs-ofctl del-flows $br --strict table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac + + # Need to ge the vmport from the rules as it's already been removed from the switch + vm_port=`ovs-ofctl dump-flows $br | grep "priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac" | tr ':' '\n' | tail -n 1` + ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac + if [ "$type" == "C" ] + then + ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=$vm_mac + fi + if [ "$type" == "P" ] + then + ovs-ofctl del-flows $br --strict table=1,priority=70,dl_dst=$vm_mac + fi + ovs-ofctl del-flows $br --strict table=0,priority=60,dl_vlan=$pri_vlan,dl_src=$vm_mac + ovs-ofctl del-flows $br --strict table=0,priority=50,vlan_tci=0x0000,dl_src=$vm_mac + # For some ovs versions + ovs-ofctl del-flows $br --strict table=0,priority=50,vlan_tci=0x0000/0x1fff,dl_src=$vm_mac + + # Remove the port from the groups + pri_vlan_ports=$(del_from_ports "$pri_vlan_ports" "$vm_port") + mod_group $pri_vlan_header $pri_vlan_ports + if [ "$type" != "I" ] + then + # To output anything that comes from the same sec vlan + sec_vlan_ports=$(del_from_ports "$sec_vlan_ports" "$vm_port") + mod_group $sec_vlan $sec_vlan_ports + fi + + # Remove vm specific rules + ovs-ofctl del-flows $br --strict table=0,priority=80,dl_vlan=$pri_vlan,dl_src=$vm_mac,dl_dst=ff:ff:ff:ff:ff:ff + + # If no more vms exist on this host, clear up all the rules + result=`ovs-vsctl find port tag=$pri_vlan` + if [ -z "$result" ] + then + ovs-ofctl del-flows $br --strict table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff + ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff + ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=ff:ff:ff:ff:ff:ff + ovs-ofctl del-flows $br --strict table=1,priority=60,dl_dst=ff:ff:ff:ff:ff:ff + ovs-ofctl -O OpenFlow13 del-groups $br group_id=$pri_vlan + fi + + # Remove the remaining rules / groups if there's no vm with pvlan on this host + result=`ovs-ofctl dump-flows $br | grep -e "actions=group:9999$"` + if [ -z "$result" ] + then + ovs-ofctl del-flows $br --strict table=1,priority=0 + ovs-ofctl -O OpenFlow13 del-groups $br group_id=9999 + fi +fi diff --git a/utils/src/main/java/com/cloud/utils/UriUtils.java b/utils/src/main/java/com/cloud/utils/UriUtils.java index 5b0b06e9a00d..d2022c169f96 100644 --- a/utils/src/main/java/com/cloud/utils/UriUtils.java +++ b/utils/src/main/java/com/cloud/utils/UriUtils.java @@ -630,7 +630,7 @@ public static List expandPvlanUri(String pvlanRange) { if (Strings.isNullOrEmpty(pvlanRange)) { return expandedVlans; } - String[] parts = pvlanRange.split("-i"); + String[] parts = pvlanRange.split("-\\w"); expandedVlans.add(Integer.parseInt(parts[0])); expandedVlans.add(Integer.parseInt(parts[1])); return expandedVlans; diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java b/utils/src/main/java/com/cloud/utils/net/NetUtils.java index 0fb055f55319..c60eac1a8d69 100644 --- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java +++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java @@ -1471,6 +1471,24 @@ public static URI generateUriForPvlan(final String primaryVlan, final String iso return URI.create("pvlan://" + primaryVlan + "-i" + isolatedPvlan); } + public static URI generateUriForPvlan(final String primaryVlan, final String isolatedPvlan, final String isolatedPvlanType) { + // Defaulting to isolated for backward compatibility + if (isolatedPvlan.length() < 1) { + return generateUriForPvlan(primaryVlan, isolatedPvlan); + } + char type = isolatedPvlanType.charAt(0); + switch(type) { + case 'c': + case 'C': + return URI.create("pvlan://" + primaryVlan + "-c" + isolatedPvlan); + case 'p': + case 'P': + return URI.create("pvlan://" + primaryVlan + "-p" + primaryVlan); + default : + return generateUriForPvlan(primaryVlan, isolatedPvlan); + } + } + public static String getPrimaryPvlanFromUri(final URI uri) { final String[] vlans = uri.getHost().split("-"); if (vlans.length < 1) { @@ -1488,6 +1506,31 @@ public static String getIsolatedPvlanFromUri(final URI uri) { if (vlan.startsWith("i")) { return vlan.replace("i", " ").trim(); } + if (vlan.startsWith("p")) { + return vlan.replace("p", " ").trim(); + } + if (vlan.startsWith("c")) { + return vlan.replace("c", " ").trim(); + } + } + return null; + } + + public static String getPvlanTypeFromUri(final URI uri) { + final String[] vlans = uri.getHost().split("-"); + if (vlans.length < 2) { + return null; + } + for (final String vlan : vlans) { + if (vlan.startsWith("i")) { + return "I"; + } + if (vlan.startsWith("p")) { + return "P"; + } + if (vlan.startsWith("c")) { + return "C"; + } } return null; } diff --git a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java index 0ac1032bab9d..1eff484a2785 100644 --- a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java +++ b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java @@ -249,10 +249,20 @@ public void testStandardizeIp6Address() { } @Test - public void testGenerateUriForPvlan() { + public void testGenerateUriForIsolatedPvlan() { assertEquals("pvlan://123-i456", NetUtils.generateUriForPvlan("123", "456").toString()); } + @Test + public void testGenerateUriForCommunityPvlan() { + assertEquals("pvlan://123-c456", NetUtils.generateUriForPvlan("123", "456", "Community").toString()); + } + + @Test + public void testGenerateUriForPromiscuousPvlan() { + assertEquals("pvlan://123-p123", NetUtils.generateUriForPvlan("123", "123", "promiscuous").toString()); + } + @Test public void testGetPrimaryPvlanFromUri() { assertEquals("123", NetUtils.getPrimaryPvlanFromUri(NetUtils.generateUriForPvlan("123", "456"))); From 506645b072cd3dbe0268c0a2b647278593920b6c Mon Sep 17 00:00:00 2001 From: davidjumani Date: Wed, 29 Apr 2020 12:36:33 +0530 Subject: [PATCH 2/8] Fixing shared network pvlan --- .../com/cloud/network/dao/NetworkDaoImpl.java | 4 +- .../resource/LibvirtComputingResource.java | 6 +- .../LibvirtPvlanSetupCommandWrapper.java | 78 ++++------ .../LibvirtComputingResourceTest.java | 75 +--------- scripts/vm/network/ovs-pvlan-kvm-dhcp-host.sh | 137 ++++++++++++++++++ scripts/vm/network/ovs-pvlan-kvm-vm.sh | 115 +++++++-------- test/integration/smoke/test_network.py | 14 +- 7 files changed, 240 insertions(+), 189 deletions(-) create mode 100755 scripts/vm/network/ovs-pvlan-kvm-dhcp-host.sh diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java index eeee3d12c65a..beddad9b88f2 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java @@ -740,6 +740,7 @@ public List listByAccountIdNetworkName(final long accountId, final St * - The requested exact PVLAN pair exists * - The requested secondary VLAN ID is secondary VLAN ID of an existing PVLAN pair * - The requested secondary VLAN ID is primary VLAN ID of an existing PVLAN pair + * - The requested primary VLAN ID is secondary VLAN ID of an existing PVLAN pair */ protected boolean isNetworkOverlappingRequestedPvlan(Integer existingPrimaryVlan, Integer existingSecondaryVlan, Network.PVlanType existingPvlanType, Integer requestedPrimaryVlan, Integer requestedSecondaryVlan, Network.PVlanType requestedPvlanType) { @@ -749,6 +750,7 @@ protected boolean isNetworkOverlappingRequestedPvlan(Integer existingPrimaryVlan } boolean exactMatch = existingPrimaryVlan.equals(requestedPrimaryVlan) && existingSecondaryVlan.equals(requestedSecondaryVlan); boolean secondaryVlanUsed = requestedPvlanType != Network.PVlanType.Promiscuous && requestedSecondaryVlan.equals(existingPrimaryVlan) || requestedSecondaryVlan.equals(existingSecondaryVlan); + boolean primaryVlanUsed = existingPvlanType != Network.PVlanType.Promiscuous && requestedPrimaryVlan.equals(existingSecondaryVlan); boolean isolatedMax = false; boolean promiscuousMax = false; if (requestedPvlanType == Network.PVlanType.Isolated && existingPrimaryVlan.equals(requestedPrimaryVlan) && existingPvlanType.equals(Network.PVlanType.Isolated)) { @@ -756,7 +758,7 @@ protected boolean isNetworkOverlappingRequestedPvlan(Integer existingPrimaryVlan } else if (requestedPvlanType == Network.PVlanType.Promiscuous && existingPrimaryVlan.equals(requestedPrimaryVlan) && existingPvlanType == Network.PVlanType.Promiscuous) { promiscuousMax = true; } - return exactMatch || secondaryVlanUsed || isolatedMax || promiscuousMax; + return exactMatch || secondaryVlanUsed || primaryVlanUsed || isolatedMax || promiscuousMax; } protected Network.PVlanType getNetworkPvlanType(long networkId, List existingPvlan) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 3542e3cf36ba..5bcc679eca1d 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -798,14 +798,14 @@ public boolean configure(final String name, final Map params) th throw new ConfigurationException("Unable to find the router_proxy.sh"); } - _ovsPvlanDhcpHostPath = Script.findScript(networkScriptsDir, "ovs-pvlan-dhcp-host.sh"); + _ovsPvlanDhcpHostPath = Script.findScript(networkScriptsDir, "ovs-pvlan-kvm-dhcp-host.sh"); if (_ovsPvlanDhcpHostPath == null) { - throw new ConfigurationException("Unable to find the ovs-pvlan-dhcp-host.sh"); + throw new ConfigurationException("Unable to find the ovs-pvlan-kvm-dhcp-host.sh"); } _ovsPvlanVmPath = Script.findScript(networkScriptsDir, "ovs-pvlan-kvm-vm.sh"); if (_ovsPvlanVmPath == null) { - throw new ConfigurationException("Unable to find the ovs-pvlan-vm.sh"); + throw new ConfigurationException("Unable to find the ovs-pvlan-kvm-vm.sh"); } String value = (String)params.get("developer"); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPvlanSetupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPvlanSetupCommandWrapper.java index 0d4b49ec0f65..23722a535e40 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPvlanSetupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPvlanSetupCommandWrapper.java @@ -19,22 +19,17 @@ package com.cloud.hypervisor.kvm.resource.wrapper; -import java.util.List; - import org.apache.log4j.Logger; import org.joda.time.Duration; -import org.libvirt.Connect; -import org.libvirt.LibvirtException; import com.cloud.agent.api.Answer; import com.cloud.agent.api.PvlanSetupCommand; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; -import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.utils.script.Script; -@ResourceWrapper(handles = PvlanSetupCommand.class) +@ResourceWrapper(handles = PvlanSetupCommand.class) public final class LibvirtPvlanSetupCommandWrapper extends CommandWrapper { private static final Logger s_logger = Logger.getLogger(LibvirtPvlanSetupCommandWrapper.class); @@ -45,65 +40,48 @@ public Answer execute(final PvlanSetupCommand command, final LibvirtComputingRes final String isolatedPvlan = command.getIsolated(); final String pvlanType = "-" + command.getPvlanType(); final String op = command.getOp(); - final String dhcpName = command.getDhcpName(); final String dhcpMac = command.getDhcpMac(); - final String vmMac = command.getVmMac(); + final String vmMac = command.getVmMac() == null ? dhcpMac : command.getVmMac(); final String dhcpIp = command.getDhcpIp(); - boolean add = true; String opr = "-A"; if (op.equals("delete")) { opr = "-D"; - add = false; } String result = null; - try { - final String guestBridgeName = libvirtComputingResource.getGuestBridgeName(); - final Duration timeout = libvirtComputingResource.getTimeout(); - - if (command.getType() == PvlanSetupCommand.Type.DHCP) { - final String ovsPvlanDhcpHostPath = libvirtComputingResource.getOvsPvlanDhcpHostPath(); - final Script script = new Script(ovsPvlanDhcpHostPath, timeout, s_logger); - - if (add) { - final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper(); - final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(dhcpName); - - final List ifaces = libvirtComputingResource.getInterfaces(conn, dhcpName); - final InterfaceDef guestNic = ifaces.get(0); - script.add(opr, "-b", guestBridgeName, "-p", primaryPvlan, "-i", isolatedPvlan, "-n", dhcpName, "-d", dhcpIp, "-m", dhcpMac, "-I", - guestNic.getDevName()); - } else { - script.add(opr, "-b", guestBridgeName, "-p", primaryPvlan, "-i", isolatedPvlan, "-n", dhcpName, "-d", dhcpIp, "-m", dhcpMac); - } - result = script.execute(); + final String guestBridgeName = libvirtComputingResource.getGuestBridgeName(); + final Duration timeout = libvirtComputingResource.getTimeout(); - if (result != null) { - s_logger.warn("Failed to program pvlan for dhcp server with mac " + dhcpMac); - return new Answer(command, false, result); - } else { - s_logger.info("Programmed pvlan for dhcp server with mac " + dhcpMac); - } - } else if (command.getType() == PvlanSetupCommand.Type.VM) { - final String ovsPvlanVmPath = libvirtComputingResource.getOvsPvlanVmPath(); + if (command.getType() == PvlanSetupCommand.Type.DHCP) { + final String ovsPvlanDhcpHostPath = libvirtComputingResource.getOvsPvlanDhcpHostPath(); + final Script script = new Script(ovsPvlanDhcpHostPath, timeout, s_logger); - final Script script = new Script(ovsPvlanVmPath, timeout, s_logger); - script.add(opr, pvlanType, "-b", guestBridgeName, "-p", primaryPvlan, "-s", isolatedPvlan, "-v", vmMac); - result = script.execute(); + script.add(opr, pvlanType, "-b", guestBridgeName, "-p", primaryPvlan, "-s", isolatedPvlan, "-m", dhcpMac, + "-d", dhcpIp); + result = script.execute(); - if (result != null) { - s_logger.warn("Failed to program pvlan for vm with mac " + vmMac); - return new Answer(command, false, result); - } else { - s_logger.info("Programmed pvlan for vm with mac " + vmMac); - } + if (result != null) { + s_logger.warn("Failed to program pvlan for dhcp server with mac " + dhcpMac); + } else { + s_logger.info("Programmed pvlan for dhcp server with mac " + dhcpMac); } - } catch (final LibvirtException e) { - s_logger.error("Error whislt executing OVS Setup command! ==> " + e.getMessage()); - return new Answer(command, false, e.getMessage()); } + + // We run this even for DHCP servers since they're all vms after all + final String ovsPvlanVmPath = libvirtComputingResource.getOvsPvlanVmPath(); + final Script script = new Script(ovsPvlanVmPath, timeout, s_logger); + script.add(opr, pvlanType, "-b", guestBridgeName, "-p", primaryPvlan, "-s", isolatedPvlan, "-m", vmMac); + result = script.execute(); + + if (result != null) { + s_logger.warn("Failed to program pvlan for vm with mac " + vmMac); + return new Answer(command, false, result); + } else { + s_logger.info("Programmed pvlan for vm with mac " + vmMac); + } + return new Answer(command, true, result); } } \ No newline at end of file diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 4debb3ad2dd2..bd651f4c02c2 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -4337,7 +4337,7 @@ public void testCopyVolumeCommandPrimaryNotFound() { @Test public void testPvlanSetupCommandDhcpAdd() { final String op = "add"; - final URI uri = URI.create("http://localhost"); + final URI uri = URI.create("pvlan://200-p200"); final String networkTag = "/105"; final String dhcpName = "dhcp"; final String dhcpMac = "00:00:00:00"; @@ -4345,40 +4345,18 @@ public void testPvlanSetupCommandDhcpAdd() { final PvlanSetupCommand command = PvlanSetupCommand.createDhcpSetup(op, uri, networkTag, dhcpName, dhcpMac, dhcpIp); - final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class); - final Connect conn = Mockito.mock(Connect.class); - final String guestBridgeName = "br0"; when(libvirtComputingResource.getGuestBridgeName()).thenReturn(guestBridgeName); - when(libvirtComputingResource.getTimeout()).thenReturn(Duration.ZERO); + final String ovsPvlanDhcpHostPath = "/pvlan"; when(libvirtComputingResource.getOvsPvlanDhcpHostPath()).thenReturn(ovsPvlanDhcpHostPath); - when(libvirtComputingResource.getLibvirtUtilitiesHelper()).thenReturn(libvirtUtilitiesHelper); - - final List ifaces = new ArrayList(); - final InterfaceDef nic = Mockito.mock(InterfaceDef.class); - ifaces.add(nic); - - try { - when(libvirtUtilitiesHelper.getConnectionByVmName(dhcpName)).thenReturn(conn); - when(libvirtComputingResource.getInterfaces(conn, dhcpName)).thenReturn(ifaces); - } catch (final LibvirtException e) { - fail(e.getMessage()); - } final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); final Answer answer = wrapper.execute(command, libvirtComputingResource); assertFalse(answer.getResult()); - - verify(libvirtComputingResource, times(1)).getLibvirtUtilitiesHelper(); - try { - verify(libvirtUtilitiesHelper, times(1)).getConnectionByVmName(dhcpName); - } catch (final LibvirtException e) { - fail(e.getMessage()); - } } @Test @@ -4404,52 +4382,10 @@ public void testPvlanSetupCommandVm() { assertFalse(answer.getResult()); } - @SuppressWarnings("unchecked") - @Test - public void testPvlanSetupCommandDhcpException() { - final String op = "add"; - final URI uri = URI.create("http://localhost"); - final String networkTag = "/105"; - final String dhcpName = "dhcp"; - final String dhcpMac = "00:00:00:00"; - final String dhcpIp = "127.0.0.1"; - - final PvlanSetupCommand command = PvlanSetupCommand.createDhcpSetup(op, uri, networkTag, dhcpName, dhcpMac, dhcpIp); - - final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class); - - final String guestBridgeName = "br0"; - when(libvirtComputingResource.getGuestBridgeName()).thenReturn(guestBridgeName); - - when(libvirtComputingResource.getTimeout()).thenReturn(Duration.ZERO); - final String ovsPvlanDhcpHostPath = "/pvlan"; - when(libvirtComputingResource.getOvsPvlanDhcpHostPath()).thenReturn(ovsPvlanDhcpHostPath); - when(libvirtComputingResource.getLibvirtUtilitiesHelper()).thenReturn(libvirtUtilitiesHelper); - - try { - when(libvirtUtilitiesHelper.getConnectionByVmName(dhcpName)).thenThrow(LibvirtException.class); - } catch (final LibvirtException e) { - fail(e.getMessage()); - } - - final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); - assertNotNull(wrapper); - - final Answer answer = wrapper.execute(command, libvirtComputingResource); - assertFalse(answer.getResult()); - - verify(libvirtComputingResource, times(1)).getLibvirtUtilitiesHelper(); - try { - verify(libvirtUtilitiesHelper, times(1)).getConnectionByVmName(dhcpName); - } catch (final LibvirtException e) { - fail(e.getMessage()); - } - } - @Test public void testPvlanSetupCommandDhcpDelete() { final String op = "delete"; - final URI uri = URI.create("http://localhost"); + final URI uri = URI.create("pvlan://200-p200"); final String networkTag = "/105"; final String dhcpName = "dhcp"; final String dhcpMac = "00:00:00:00"; @@ -4457,15 +4393,12 @@ public void testPvlanSetupCommandDhcpDelete() { final PvlanSetupCommand command = PvlanSetupCommand.createDhcpSetup(op, uri, networkTag, dhcpName, dhcpMac, dhcpIp); - final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class); - final String guestBridgeName = "br0"; when(libvirtComputingResource.getGuestBridgeName()).thenReturn(guestBridgeName); - when(libvirtComputingResource.getTimeout()).thenReturn(Duration.ZERO); + final String ovsPvlanDhcpHostPath = "/pvlan"; when(libvirtComputingResource.getOvsPvlanDhcpHostPath()).thenReturn(ovsPvlanDhcpHostPath); - when(libvirtComputingResource.getLibvirtUtilitiesHelper()).thenReturn(libvirtUtilitiesHelper); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); diff --git a/scripts/vm/network/ovs-pvlan-kvm-dhcp-host.sh b/scripts/vm/network/ovs-pvlan-kvm-dhcp-host.sh new file mode 100755 index 000000000000..46eceb960264 --- /dev/null +++ b/scripts/vm/network/ovs-pvlan-kvm-dhcp-host.sh @@ -0,0 +1,137 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +#!/bin/bash + +# We're trying to do the impossible here by allowing pvlan on kvm / xen. As only God can do the impossible, and we've got too much ego to +# admit that we can't, we're just hacking our way around it. +# We're pretty much crafting two vlan headers, one with the primary vlan and the other with the secondary and with a few fancy rules +# it managed to work. But take note that the'res no checking over here for secondary vlan overlap. That has to be handled while +# creating the pvlan!! + +exec 2>&1 + +usage() { + printf "Usage: %s: (-A|-D) (-P/I/C) -b -p -s -m -d -h \n" $(basename $0) >&2 + exit 2 +} + +br= +pri_vlan= +sec_vlan= +vm_mac= +dhcp_ip= +op= +type= + +while getopts 'ADPICb:p:s:m:d:h' OPTION +do + case $OPTION in + A) op="add" + ;; + D) op="del" + ;; + P) type="P" + ;; + I) type="I" + ;; + C) type="C" + ;; + b) br="$OPTARG" + ;; + p) pri_vlan="$OPTARG" + ;; + s) sec_vlan="$OPTARG" + ;; + m) vm_mac="$OPTARG" + ;; + d) dhcp_ip="$OPTARG" + ;; + h) usage + exit 1 + ;; + esac +done + +if [ -z "$op" ] +then + echo Missing operation pararmeter! + exit 1 +fi + +if [ -z "$type" ] +then + echo Missing pvlan type pararmeter! + exit 1 +fi + +if [ -z "$br" ] +then + echo Missing parameter bridge! + exit 1 +fi + +if [ -z "$vm_mac" ] +then + echo Missing parameter VM MAC! + exit 1 +fi + +if [ -z "$pri_vlan" ] +then + echo Missing parameter primary vlan! + exit 1 +fi + +if [ -z "$sec_vlan" ] +then + echo Missing parameter secondary vlan! + exit 1 +fi + +if [ -z "$dhcp_ip" ] +then + echo Missing parameter DHCP IP! + exit 1 +fi + +find_port() { + mac=`echo "$1" | sed -e 's/:/\\\:/g'` + port=`ovs-vsctl --column ofport find interface external_ids:attached-mac="$mac" | tr -d ' ' | cut -d ':' -f 2` + echo $port +} + +ovs-vsctl set bridge $br protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13 +ovs-vsctl set Open_vSwitch . other_config:vlan-limit=2 + +if [ "$op" == "add" ] +then + dhcp_port=$(find_port $vm_mac) + + ovs-ofctl add-flow $br table=0,priority=200,arp,dl_vlan=$pri_vlan,nw_dst=$dhcp_ip,actions=strip_vlan,resubmit\(,1\) + ovs-ofctl add-flow $br table=1,priority=200,arp,dl_vlan=$sec_vlan,nw_dst=$dhcp_ip,actions=strip_vlan,output:$dhcp_port + + ovs-ofctl add-flow $br table=0,priority=100,udp,dl_vlan=$pri_vlan,nw_dst=255.255.255.255,tp_dst=67,actions=strip_vlan,resubmit\(,1\) + ovs-ofctl add-flow $br table=1,priority=100,udp,dl_vlan=$sec_vlan,nw_dst=255.255.255.255,tp_dst=67,actions=strip_vlan,output:$dhcp_port +else + ovs-ofctl del-flows --strict $br table=0,priority=200,arp,dl_vlan=$pri_vlan,nw_dst=$dhcp_ip + ovs-ofctl del-flows --strict $br table=1,priority=200,arp,dl_vlan=$sec_vlan,nw_dst=$dhcp_ip + + ovs-ofctl del-flows --strict $br table=0,priority=100,udp,dl_vlan=$pri_vlan,nw_dst=255.255.255.255,tp_dst=67 + ovs-ofctl del-flows --strict $br table=1,priority=100,udp,dl_vlan=$sec_vlan,nw_dst=255.255.255.255,tp_dst=67 +fi diff --git a/scripts/vm/network/ovs-pvlan-kvm-vm.sh b/scripts/vm/network/ovs-pvlan-kvm-vm.sh index b74d23bd4bf0..13a06148d114 100644 --- a/scripts/vm/network/ovs-pvlan-kvm-vm.sh +++ b/scripts/vm/network/ovs-pvlan-kvm-vm.sh @@ -24,8 +24,10 @@ # it managed to work. But take note that the'res no checking over here for secondary vlan overlap. That has to be handled while # creating the pvlan!! +exec 2>&1 + usage() { - printf "Usage: %s: (-A|-D) (-P/I/C) -b -p -s -v -h \n" $(basename $0) >&2 + printf "Usage: %s: (-A|-D) (-P/I/C) -b -p -s -m -h \n" $(basename $0) >&2 exit 2 } @@ -36,7 +38,7 @@ vm_mac= op= type= -while getopts 'ADPCIb:p:s:i:d:m:v:n:h' OPTION +while getopts 'ADPICb:p:s:m:h' OPTION do case $OPTION in A) op="add" @@ -55,7 +57,7 @@ do ;; s) sec_vlan="$OPTARG" ;; - v) vm_mac="$OPTARG" + m) vm_mac="$OPTARG" ;; h) usage exit 1 @@ -106,7 +108,7 @@ find_port() { } find_port_group() { - ovs-ofctl -O OpenFlow13 dump-groups $br | grep group_id=$1, | sed -e 's/.*actions=//g' -e 's/resubmit(,3)//g' -e 's/output://g' -e 's/^,//g' -e 's/,$//g' -e 's/,,/,/g' -e 's/ //g' + ovs-ofctl -O OpenFlow13 dump-groups $br | grep group_id=$1, | sed -e 's/.*type=all,//g' -e 's/bucket=actions=//g' -e 's/resubmit(,1)//g' -e 's/strip_vlan,//g' -e 's/pop_vlan,//g' -e 's/output://g' -e 's/^,//g' -e 's/,$//g' -e 's/,,/,/g' -e 's/ //g' } # try to find the physical link to outside, only supports eth and em prefix now @@ -122,11 +124,7 @@ sec_vlan_header=$((4096 + $sec_vlan)) # Since we're creating a separate group for just the promiscuous devices, adding 4096 so that it'll be unique. Hence we're restricted to 4096 vlans! # Not a big deal because if you have vxlan, why do you even need pvlan!! pri_vlan_ports=$(find_port_group $pri_vlan_header) -# Be smart! If it's an isolated pvlan, why do we need a group for those ports ? -if [ "$type" != "I" ] -then - sec_vlan_ports=$(find_port_group $sec_vlan) -fi +sec_vlan_ports=$(find_port_group $sec_vlan) add_to_ports() { if [ -z "$1" ] @@ -147,13 +145,15 @@ del_from_ports() { mod_group() { # Ensure that we don't delete the prom port group, because if we do, the rules that have it go away! + actions=`echo "$2" | sed -e 's/,/,bucket=actions=/g'` if [ "$1" == "$pri_vlan" ] then + actions=`echo "$2" | sed -e 's/,/,bucket=actions=strip_vlan,output:/g'` if [ -z "$2" ] then - ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=indirect,bucket=resubmit\(,3\) + ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=all,bucket=resubmit\(,1\) else - ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=indirect,bucket=resubmit\(,3\),$2 + ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=all,bucket=resubmit\(,1\),bucket=actions=strip_vlan,output:$actions fi return fi @@ -161,7 +161,7 @@ mod_group() { then ovs-ofctl -O OpenFlow13 del-groups $br group_id=$1 else - ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=indirect,bucket=$2 + ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=$1,type=all,bucket=actions=$actions fi } @@ -175,19 +175,26 @@ ovs-ofctl add-flow $br priority=0,actions=NORMAL if [ "$op" == "add" ] then # From our pri vlan - ovs-ofctl add-flow $br table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac,actions=strip_vlan,resubmit\(,1\) - # From promiscuous + if [ "$type" == "P" ] + then + ovs-ofctl add-flow $br table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac,actions=strip_vlan,strip_vlan,output:$vm_port + else + ovs-ofctl add-flow $br table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac,actions=strip_vlan,resubmit\(,1\) + fi + + # Accept from promiscuous ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac,actions=strip_vlan,output:$vm_port # From others in our own community if [ "$type" == "C" ] then ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=$vm_mac,actions=strip_vlan,output:$vm_port fi - # If we're promiscuous, accept anything because it's passed the first header - if [ "$type" == "P" ] + # Allow only dhcp to isolated vm + if [ "$type" == "I" ] then - ovs-ofctl add-flow $br table=1,priority=70,dl_dst=$vm_mac,actions=strip_vlan,output:$vm_port + ovs-ofctl add-flow $br table=1,priority=70,udp,dl_vlan=$sec_vlan,dl_dst=$vm_mac,tp_src=67,actions=strip_vlan,output:$vm_port fi + # Security101 ovs-ofctl add-flow $br table=1,priority=0,actions=drop @@ -196,40 +203,39 @@ then # QinQ the packet. Outter header is the primary vlan and inner is the secondary ovs-ofctl add-flow -O OpenFlow13 $br table=0,priority=50,vlan_tci=0x0000,dl_src=$vm_mac,actions=push_vlan:0x8100,set_field:$sec_vlan_header-\>vlan_vid,push_vlan:0x8100,set_field:$pri_vlan_header-\>vlan_vid,resubmit:$trunk_port - # Broadcasats + # BROADCASTS # Create the respective groups - # To output anything that comes from a promiscuous device - pri_vlan_ports=$(add_to_ports "$pri_vlan_ports" "$vm_port") - mod_group $pri_vlan_header $pri_vlan_ports - if [ "$type" != "I" ] + # pri_vlan_ports are the list of ports of all iso & comm dev for a give pvlan + if [ "$type" != "P" ] then - # To output anything that comes from the same sec vlan - sec_vlan_ports=$(add_to_ports "$sec_vlan_ports" "$vm_port") - mod_group $sec_vlan $sec_vlan_ports + pri_vlan_ports=$(add_to_ports "$pri_vlan_ports" "$vm_port") + mod_group $pri_vlan_header $pri_vlan_ports fi - # Group id 9999 for braodcasts sent by a vm on this switch. One action is to to output it to the trunk port, the other to process it ourselves - # Chose 9999 since the vlan range goes upto 4096, so it's more than double of that - ovs-ofctl -O OpenFlow13 mod-group --may-create $br group_id=9999,type=all,bucket=action:output:$trunk_port,bucket=action:strip_vlan,resubmit\(,1\) - - # From a device on the same switch - ovs-ofctl add-flow $br table=0,priority=80,dl_vlan=$pri_vlan,dl_src=$vm_mac,dl_dst=ff:ff:ff:ff:ff:ff,actions=group:9999 - # From our pri vlan - ovs-ofctl add-flow $br table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,resubmit\(,1\) - # From a promiscuous device + # sec_vlan_ports are the list of ports for a given secondary pvlan + sec_vlan_ports=$(add_to_ports "$sec_vlan_ports" "$vm_port") + mod_group $sec_vlan $sec_vlan_ports + + # Ensure we have the promiscuous port group because if we don't, it'll fail to create the following rule + prom_ports=$(find_port_group $pri_vlan) + mod_group $pri_vlan $prom_ports + + # From a device on this switch. Pass it to the trunk port and process it ourselves for other devices on the switch. + ovs-ofctl add-flow $br table=0,priority=300,dl_vlan=$pri_vlan,dl_src=$vm_mac,dl_dst=ff:ff:ff:ff:ff:ff,actions=output:$trunk_port,strip_vlan,group:$pri_vlan + # Got a packet from the trunk port from out pri vlan, pass it to pri_vlan_group which sends the packet out to the promiscuous devices as well as passes it onto table 1 + ovs-ofctl add-flow $br table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$pri_vlan + # From a promiscuous device, so send it to all community and isolated devices on this switch. Passed to all promiscuous devices in the prior step ^^ ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$pri_vlan_header + # Since it's from a community, gotta braodcast it to all community devices if [ "$type" == "C" ] then - # Ensure we have the promiscuous port group because if we don't, it'll fail to create the following rule - prom_ports=$(find_port_group $pri_vlan) - mod_group $pri_vlan $prom_ports - # Since it's from a community, gotta braodcast it to all community and promiscuous ports - ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$sec_vlan,group:$pri_vlan + ovs-ofctl add-flow $br table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$sec_vlan fi - # I'm promiscuous, accept anything because it's passed the first header - if [ "$type" == "P" ] + # Allow only dhcp form isolated router to isolated vm + if [ "$type" == "I" ] then - ovs-ofctl add-flow $br table=1,priority=60,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$pri_vlan + ovs-ofctl add-flow $br table=1,priority=70,udp,dl_vlan=$sec_vlan,tp_src=67,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$sec_vlan fi + else # Delete whatever we've added that's vm specific ovs-ofctl del-flows $br --strict table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac @@ -241,10 +247,11 @@ else then ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=$vm_mac fi - if [ "$type" == "P" ] + if [ "$type" == "I" ] then - ovs-ofctl del-flows $br --strict table=1,priority=70,dl_dst=$vm_mac + ovs-ofctl del-flows $br --strict table=1,priority=70,udp,dl_vlan=$sec_vlan,dl_dst=$vm_mac,tp_src=67 fi + ovs-ofctl del-flows $br --strict table=0,priority=60,dl_vlan=$pri_vlan,dl_src=$vm_mac ovs-ofctl del-flows $br --strict table=0,priority=50,vlan_tci=0x0000,dl_src=$vm_mac # For some ovs versions @@ -253,15 +260,11 @@ else # Remove the port from the groups pri_vlan_ports=$(del_from_ports "$pri_vlan_ports" "$vm_port") mod_group $pri_vlan_header $pri_vlan_ports - if [ "$type" != "I" ] - then - # To output anything that comes from the same sec vlan - sec_vlan_ports=$(del_from_ports "$sec_vlan_ports" "$vm_port") - mod_group $sec_vlan $sec_vlan_ports - fi + sec_vlan_ports=$(del_from_ports "$sec_vlan_ports" "$vm_port") + mod_group $sec_vlan $sec_vlan_ports # Remove vm specific rules - ovs-ofctl del-flows $br --strict table=0,priority=80,dl_vlan=$pri_vlan,dl_src=$vm_mac,dl_dst=ff:ff:ff:ff:ff:ff + ovs-ofctl del-flows $br --strict table=0,priority=300,dl_vlan=$pri_vlan,dl_src=$vm_mac,dl_dst=ff:ff:ff:ff:ff:ff # If no more vms exist on this host, clear up all the rules result=`ovs-vsctl find port tag=$pri_vlan` @@ -269,16 +272,6 @@ else then ovs-ofctl del-flows $br --strict table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff - ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$sec_vlan,dl_dst=ff:ff:ff:ff:ff:ff - ovs-ofctl del-flows $br --strict table=1,priority=60,dl_dst=ff:ff:ff:ff:ff:ff ovs-ofctl -O OpenFlow13 del-groups $br group_id=$pri_vlan fi - - # Remove the remaining rules / groups if there's no vm with pvlan on this host - result=`ovs-ofctl dump-flows $br | grep -e "actions=group:9999$"` - if [ -z "$result" ] - then - ovs-ofctl del-flows $br --strict table=1,priority=0 - ovs-ofctl -O OpenFlow13 del-groups $br group_id=9999 - fi fi diff --git a/test/integration/smoke/test_network.py b/test/integration/smoke/test_network.py index b6d1bae6b160..b45fa81b86d3 100644 --- a/test/integration/smoke/test_network.py +++ b/test/integration/smoke/test_network.py @@ -1562,6 +1562,7 @@ def setUpClass(cls): # Supported hypervisor = Vmware using dvSwitches for guest traffic isVmware = False isDvSwitch = False + isKVM = False if cls.hypervisor.lower() in ["vmware"]: isVmware = True clusters = Cluster.list(cls.apiclient, zoneid=cls.zone.id, hypervisor=cls.hypervisor) @@ -1571,8 +1572,10 @@ def setUpClass(cls): isDvSwitch = True break - supported = isVmware and isDvSwitch - cls.vmwareHypervisorDvSwitchesForGuestTrafficNotPresent = not supported + isKVM = cls.hypervisor.lower() in ["kvm"] + + supported = isVmware and isDvSwitch or isKVM + cls.unsupportedHardware = not supported cls._cleanup = [] @@ -1730,7 +1733,7 @@ def enable_l2_nic(self, vm): return vm_ip, eth_device @attr(tags=["advanced", "advancedns", "smoke", "pvlan"], required_hardware="true") - @skipTestIf("vmwareHypervisorDvSwitchesForGuestTrafficNotPresent") + @skipTestIf("unsupportedHardware") def test_l2_network_pvlan_connectivity(self): try: vm_community1_one = self.deploy_vm_multiple_nics("vmcommunity1one", self.l2_pvlan_community1) @@ -1788,6 +1791,7 @@ def test_l2_network_pvlan_connectivity(self): # Isolated PVLAN checks same_isolated = self.is_vm_l2_isolated_from_dest(vm_isolated1, vm_isolated1_eth, vm_isolated2_ip) isolated_to_community_isolated = self.is_vm_l2_isolated_from_dest(vm_isolated1, vm_isolated1_eth, vm_community1_one_ip) + isolated_to_promiscuous_isolated = self.is_vm_l2_isolated_from_dest(vm_isolated1, vm_isolated1_eth, vm_promiscuous1_ip) self.assertTrue( same_isolated, @@ -1797,6 +1801,10 @@ def test_l2_network_pvlan_connectivity(self): isolated_to_community_isolated, "VMs on isolated PVLANs must be isolated on layer 2 to Vms on community PVLAN" ) + self.assertFalse( + isolated_to_promiscuous_isolated, + "VMs on isolated PVLANs must not be isolated on layer 2 to Vms on promiscuous PVLAN", + ) # Promiscuous PVLAN checks same_promiscuous = self.is_vm_l2_isolated_from_dest(vm_promiscuous1, vm_promiscuous1_eth, vm_promiscuous2_ip) From 910d9f7f991bf9e202c4fac3f3aab6547e02ec47 Mon Sep 17 00:00:00 2001 From: davidjumani Date: Wed, 27 May 2020 16:27:00 +0530 Subject: [PATCH 3/8] Fixing smoke tests to run only if kvm and ovs --- test/integration/smoke/test_network.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/test/integration/smoke/test_network.py b/test/integration/smoke/test_network.py index b45fa81b86d3..99d96d7bdef1 100644 --- a/test/integration/smoke/test_network.py +++ b/test/integration/smoke/test_network.py @@ -1562,7 +1562,6 @@ def setUpClass(cls): # Supported hypervisor = Vmware using dvSwitches for guest traffic isVmware = False isDvSwitch = False - isKVM = False if cls.hypervisor.lower() in ["vmware"]: isVmware = True clusters = Cluster.list(cls.apiclient, zoneid=cls.zone.id, hypervisor=cls.hypervisor) @@ -1572,9 +1571,21 @@ def setUpClass(cls): isDvSwitch = True break + # Supported hypervisor = KVM using OVS isKVM = cls.hypervisor.lower() in ["kvm"] - - supported = isVmware and isDvSwitch or isKVM + isOVSEnabled = False + hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][0].__dict__ + if isKVM : + # Test only if all the hosts use OVS + grepCmd = 'grep "network.bridge.type=openvswitch" /etc/cloudstack/agent/agent.properties' + hosts = list_hosts(cls.apiclient, type='Routing', hypervisor='kvm') + if len(hosts) > 0 : + isOVSEnabled = True + for host in hosts : + isOVSEnabled = isOVSEnabled and len(SshClient(host.ipaddress, port=22, user=hostConfig["username"], + passwd=hostConfig["password"]).execute(grepCmd)) != 0 + + supported = isVmware and isDvSwitch or isKVM and isOVSEnabled cls.unsupportedHardware = not supported cls._cleanup = [] From 2cc200d07f9451f07e46e1d70fd2ca3a9981d2f0 Mon Sep 17 00:00:00 2001 From: davidjumani Date: Fri, 19 Jun 2020 13:23:33 +0530 Subject: [PATCH 4/8] Fixing vlan allowed to be created with existing pvlan id --- .../orchestration/NetworkOrchestrator.java | 6 +++ .../com/cloud/network/dao/NetworkDao.java | 2 + .../com/cloud/network/dao/NetworkDaoImpl.java | 37 +++++++++++++++++++ .../com/cloud/network/dao/NetworkDaoTest.java | 10 +++++ .../com/cloud/vpc/dao/MockNetworkDaoImpl.java | 5 +++ 5 files changed, 60 insertions(+) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 577a28e333ac..4deccd2a7d75 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2498,6 +2498,12 @@ public Network doInTransaction(final TransactionStatus status) { } else { uri = BroadcastDomainType.fromString(vlanIdFinal); } + + if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString()).size() > 0) { + throw new InvalidParameterValueException("Network with vlan " + vlanIdFinal + + " already exists or overlaps with other network pvlans in zone " + zoneId); + } + userNetwork.setBroadcastUri(uri); if (!vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { userNetwork.setBroadcastDomainType(BroadcastDomainType.Vlan); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java index a84e4d575f4d..c82e4e745999 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java @@ -126,4 +126,6 @@ public interface NetworkDao extends GenericDao, StateDao listByAccountIdNetworkName(long accountId, String name); List listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri, Network.PVlanType pVlanType); + + List listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java index beddad9b88f2..2578a14fc70a 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java @@ -761,6 +761,11 @@ protected boolean isNetworkOverlappingRequestedPvlan(Integer existingPrimaryVlan return exactMatch || secondaryVlanUsed || primaryVlanUsed || isolatedMax || promiscuousMax; } + // True when a VLAN ID overlaps with an existing PVLAN primary or secondary ID + protected boolean isNetworkOverlappingRequestedPvlan(Integer existingPrimaryVlan, Integer existingSecondaryVlan, Integer requestedVlan) { + return requestedVlan.equals(existingPrimaryVlan) || requestedVlan.equals(existingSecondaryVlan); + } + protected Network.PVlanType getNetworkPvlanType(long networkId, List existingPvlan) { Network.PVlanType existingPvlanType = null; NetworkDetailVO pvlanTypeDetail = networkDetailsDao.findDetail(networkId, ApiConstants.ISOLATED_PVLAN_TYPE); @@ -772,6 +777,38 @@ protected Network.PVlanType getNetworkPvlanType(long networkId, List ex return existingPvlanType; } + @Override + public List listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri) { + final URI searchUri = BroadcastDomainType.fromString(broadcastUri); + if (!searchUri.getScheme().equalsIgnoreCase("vlan")) { + throw new CloudRuntimeException("VLAN requested but URI is not in the expected format: " + searchUri.toString()); + } + final String searchRange = BroadcastDomainType.getValue(searchUri); + final List searchVlans = UriUtils.expandVlanUri(searchRange); + final List overlappingNetworks = new ArrayList<>(); + + final SearchCriteria sc = PhysicalNetworkSearch.create(); + sc.setParameters("physicalNetworkId", physicalNetworkId); + + for (final NetworkVO network : listBy(sc)) { + if (network.getBroadcastUri() == null || !network.getBroadcastUri().getScheme().equalsIgnoreCase("pvlan")) { + continue; + } + // Ensure existing and proposed VLAN don't overlap + final String networkVlanRange = BroadcastDomainType.getValue(network.getBroadcastUri()); + if (networkVlanRange == null || networkVlanRange.isEmpty()) { + continue; + } + List existingPvlan = UriUtils.expandPvlanUri(networkVlanRange); + if (isNetworkOverlappingRequestedPvlan(existingPvlan.get(0), existingPvlan.get(1), searchVlans.get(0))) { + overlappingNetworks.add(network); + break; + } + } + + return overlappingNetworks; + } + @Override public List listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri, Network.PVlanType pVlanType) { final URI searchUri = BroadcastDomainType.fromString(broadcastUri); diff --git a/server/src/test/java/com/cloud/network/dao/NetworkDaoTest.java b/server/src/test/java/com/cloud/network/dao/NetworkDaoTest.java index ca918691d09c..ec7da94f92f5 100644 --- a/server/src/test/java/com/cloud/network/dao/NetworkDaoTest.java +++ b/server/src/test/java/com/cloud/network/dao/NetworkDaoTest.java @@ -52,4 +52,14 @@ public void testNetworkOverlappingMultipleCommunityAllowed() { Assert.assertFalse(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingSecondaryVlan, Network.PVlanType.Community, existingPrimaryVlan, requestedVlan, Network.PVlanType.Community)); } + + public void testNetworkOverlappingVlanPvlanTrue() { + Assert.assertTrue(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingSecondaryVlan, existingPrimaryVlan)); + Assert.assertTrue(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingSecondaryVlan, existingSecondaryVlan)); + } + + public void testNetworkOverlappingVlanPvlanFalse() { + Assert.assertFalse(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingSecondaryVlan, requestedVlan)); + } + } diff --git a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java index 1a7c1f70090c..a20ac6f5b706 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java @@ -250,4 +250,9 @@ public List listByAccountIdNetworkName(final long accountId, final St public List listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri, Network.PVlanType pVlanType) { return null; } + + @Override + public List listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri) { + return null; + } } From bb5086beedad9e32a503c83bbde243b9445a3bc2 Mon Sep 17 00:00:00 2001 From: davidjumani Date: Mon, 17 Aug 2020 14:11:09 +0530 Subject: [PATCH 5/8] Adding pvlan rules when nic added or removed --- .../src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index f05cbbcb26de..8f722c92fdea 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -3756,6 +3756,7 @@ private NicProfile orchestrateAddVmToNetwork(final VirtualMachine vm, final Netw try { result = plugNic(network, nicTO, vmTO, context, dest); if (result) { + _userVmMgr.setupVmForPvlan(true, vm.getHostId(), nic); s_logger.debug("Nic is plugged successfully for vm " + vm + " in network " + network + ". Vm is a part of network now"); final long isDefault = nic.isDefaultNic() ? 1 : 0; // insert nic's Id into DB as resource_name @@ -3863,6 +3864,7 @@ private boolean orchestrateRemoveNicFromVm(final VirtualMachine vm, final Nic ni s_logger.debug("Un-plugging nic " + nic + " for vm " + vm + " from network " + network); final boolean result = unplugNic(network, nicTO, vmTO, context, dest); if (result) { + _userVmMgr.setupVmForPvlan(false, vm.getHostId(), nicProfile); s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network); final long isDefault = nic.isDefaultNic() ? 1 : 0; UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), From 5c022f1ff2a8c0bb88068c8b65ff9a7adbdf7687 Mon Sep 17 00:00:00 2001 From: davidjumani Date: Mon, 17 Aug 2020 14:12:53 +0530 Subject: [PATCH 6/8] Handle null --- agent/src/main/java/com/cloud/agent/Agent.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/agent/src/main/java/com/cloud/agent/Agent.java b/agent/src/main/java/com/cloud/agent/Agent.java index 930f38d2aa08..1f2f4859384e 100644 --- a/agent/src/main/java/com/cloud/agent/Agent.java +++ b/agent/src/main/java/com/cloud/agent/Agent.java @@ -811,7 +811,10 @@ public void processResponse(final Response response, final Link link) { public void processReadyCommand(final Command cmd) { final ReadyCommand ready = (ReadyCommand)cmd; // Set human readable sizes; - NumbersUtil.enableHumanReadableSizes = ready.getEnableHumanReadableSizes(); + Boolean humanReadable = ready.getEnableHumanReadableSizes(); + if (humanReadable != null){ + NumbersUtil.enableHumanReadableSizes = humanReadable; + } s_logger.info("Processing agent ready command, agent id = " + ready.getHostId()); if (ready.getHostId() != null) { From 2c6144884d0ea06d608013384df5b9603aad1ef2 Mon Sep 17 00:00:00 2001 From: davidjumani Date: Mon, 17 Aug 2020 18:30:25 +0530 Subject: [PATCH 7/8] Migrating pvlan ovs rules for L2 --- .../orchestration/NetworkOrchestrator.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 4deccd2a7d75..2c6dbbe55b86 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -16,7 +16,6 @@ // under the License. package org.apache.cloudstack.engine.orchestration; - import java.net.URI; import java.util.ArrayList; import java.util.Arrays; @@ -215,6 +214,7 @@ import com.cloud.vm.NicVO; import com.cloud.vm.ReservationContext; import com.cloud.vm.ReservationContextImpl; +import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -299,6 +299,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra RemoteAccessVpnDao _remoteAccessVpnDao; @Inject VpcVirtualNetworkApplianceService _routerService; + @Inject + UserVmManager _userVmMgr; List networkGurus; @@ -1792,6 +1794,11 @@ public void prepareNicForMigration(final VirtualMachineProfile vm, final DeployD s_logger.error("NetworkGuru " + guru + " prepareForMigration failed."); // XXX: Transaction error } } + + if (network.getGuestType() == Network.GuestType.L2 && vm.getType() == VirtualMachine.Type.User) { + _userVmMgr.setupVmForPvlan(false, vm.getVirtualMachine().getHostId(), profile); + } + final List providersToImplement = getNetworkProviders(network.getId()); for (final NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { @@ -1912,6 +1919,11 @@ public void commitNicForMigration(final VirtualMachineProfile src, final Virtual if (guru instanceof NetworkMigrationResponder) { ((NetworkMigrationResponder)guru).commitMigration(nicSrc, network, src, src_context, dst_context); } + + if (network.getGuestType() == Network.GuestType.L2 && src.getType() == VirtualMachine.Type.User) { + _userVmMgr.setupVmForPvlan(true, src.getVirtualMachine().getHostId(), nicSrc); + } + final List providersToImplement = getNetworkProviders(network.getId()); for (final NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { @@ -1943,6 +1955,11 @@ public void rollbackNicForMigration(final VirtualMachineProfile src, final Virtu if (guru instanceof NetworkMigrationResponder) { ((NetworkMigrationResponder)guru).rollbackMigration(nicDst, network, dst, src_context, dst_context); } + + if (network.getGuestType() == Network.GuestType.L2 && src.getType() == VirtualMachine.Type.User) { + _userVmMgr.setupVmForPvlan(true, dst.getVirtualMachine().getHostId(), nicDst); + } + final List providersToImplement = getNetworkProviders(network.getId()); for (final NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { From 8c41853c25799ff1b2f5e8b94b7c6e7f158fe4b9 Mon Sep 17 00:00:00 2001 From: davidjumani Date: Wed, 19 Aug 2020 21:17:32 +0530 Subject: [PATCH 8/8] Cleanup rules migration --- scripts/vm/network/ovs-pvlan-kvm-vm.sh | 27 +++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/scripts/vm/network/ovs-pvlan-kvm-vm.sh b/scripts/vm/network/ovs-pvlan-kvm-vm.sh index 13a06148d114..7b4e2740f34d 100644 --- a/scripts/vm/network/ovs-pvlan-kvm-vm.sh +++ b/scripts/vm/network/ovs-pvlan-kvm-vm.sh @@ -6,9 +6,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -165,6 +165,12 @@ mod_group() { fi } +cleanup_flows() { + ovs-ofctl del-flows $br --strict table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff + ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff + ovs-ofctl -O OpenFlow13 del-groups $br group_id=$pri_vlan +} + # Allow the neccessary protocols and QinQ ovs-vsctl set bridge $br protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13 ovs-vsctl set Open_vSwitch . other_config:vlan-limit=2 @@ -194,7 +200,7 @@ then then ovs-ofctl add-flow $br table=1,priority=70,udp,dl_vlan=$sec_vlan,dl_dst=$vm_mac,tp_src=67,actions=strip_vlan,output:$vm_port fi - + # Security101 ovs-ofctl add-flow $br table=1,priority=0,actions=drop @@ -235,7 +241,7 @@ then then ovs-ofctl add-flow $br table=1,priority=70,udp,dl_vlan=$sec_vlan,tp_src=67,dl_dst=ff:ff:ff:ff:ff:ff,actions=strip_vlan,group:$sec_vlan fi - + else # Delete whatever we've added that's vm specific ovs-ofctl del-flows $br --strict table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=$vm_mac @@ -266,12 +272,19 @@ else # Remove vm specific rules ovs-ofctl del-flows $br --strict table=0,priority=300,dl_vlan=$pri_vlan,dl_src=$vm_mac,dl_dst=ff:ff:ff:ff:ff:ff + # If the vm is going to be migrated but not yet removed. Remove the rules if it's the only vm in the vlan + res1=`ovs-vsctl --column _uuid find port tag=$pri_vlan | wc -l` + res2=`find_port $vm_mac | wc -l` + if [ "$res1" -eq 1 ] && [ "$res2" -eq 1 ] + then + cleanup_flows + fi + # If no more vms exist on this host, clear up all the rules result=`ovs-vsctl find port tag=$pri_vlan` if [ -z "$result" ] then - ovs-ofctl del-flows $br --strict table=0,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff - ovs-ofctl del-flows $br --strict table=1,priority=70,dl_vlan=$pri_vlan,dl_dst=ff:ff:ff:ff:ff:ff - ovs-ofctl -O OpenFlow13 del-groups $br group_id=$pri_vlan + cleanup_flows fi + fi