Implement PVLAN on Xen Start/stop vm/dhcp server are done. Not done with VM migration.
A new command(PvlanSetupCommand) is sent for setting up PVLAN for vms. Currently it's focus on OVS implementation. Need to be more abstruct and add vSwitch part. Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/b64039ba Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/b64039ba Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/b64039ba Branch: refs/heads/ui-vpc-redesign Commit: b64039bafd194be78f0a873d4b2431d01931febf Parents: 9c9e2ec Author: Sheng Yang <sheng.y...@citrix.com> Authored: Wed May 1 13:23:08 2013 -0700 Committer: Sheng Yang <sheng.y...@citrix.com> Committed: Wed May 1 13:23:08 2013 -0700 ---------------------------------------------------------------------- api/src/com/cloud/agent/api/PvlanSetupCommand.java | 130 +++++++++++ .../xen/resource/CitrixResourceBase.java | 46 ++++- scripts/vm/hypervisor/xenserver/ovs-pvlan | 168 +++++++++++++++ scripts/vm/hypervisor/xenserver/xenserver60/patch | 6 +- scripts/vm/network/ovs-pvlan-cleanup.sh | 23 ++ scripts/vm/network/ovs-pvlan-dhcp-host.sh | 104 +++++++++ scripts/vm/network/ovs-pvlan-vm-in-dhcp-host.sh | 88 ++++++++ scripts/vm/network/ovs-pvlan-vm.sh | 90 ++++++++ .../network/element/VirtualRouterElement.java | 11 + .../router/VirtualNetworkApplianceManager.java | 4 + .../router/VirtualNetworkApplianceManagerImpl.java | 87 +++++++- server/src/com/cloud/vm/UserVmManagerImpl.java | 45 ++++ .../vpc/MockVpcVirtualNetworkApplianceManager.java | 7 + 13 files changed, 804 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/api/src/com/cloud/agent/api/PvlanSetupCommand.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/agent/api/PvlanSetupCommand.java b/api/src/com/cloud/agent/api/PvlanSetupCommand.java new file mode 100644 index 0000000..22a828a --- /dev/null +++ b/api/src/com/cloud/agent/api/PvlanSetupCommand.java @@ -0,0 +1,130 @@ +// 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. +package com.cloud.agent.api; + +import java.net.URI; + +import com.cloud.utils.net.NetUtils; + +public class PvlanSetupCommand extends Command { + public enum Type { + DHCP, + VM, + VM_IN_DHCP_HOST + } + private String op; + private String bridge; + private String primary; + private String isolated; + private String vmMac; + private String dhcpMac; + private String dhcpIp; + private boolean strict; + private Type type; + + protected PvlanSetupCommand() {} + + protected PvlanSetupCommand(Type type, String op, String bridge, URI uri) + { + this.type = type; + this.op = op; + this.bridge = bridge; + this.primary = NetUtils.getPrimaryPvlanFromUri(uri); + this.isolated = NetUtils.getIsolatedPvlanFromUri(uri); + this.strict = true; + } + + static public PvlanSetupCommand createDhcpSetup(String op, String bridge, URI uri, String dhcpMac, String dhcpIp) + { + PvlanSetupCommand cmd = new PvlanSetupCommand(Type.DHCP, op, bridge, uri); + cmd.setDhcpMac(dhcpMac); + cmd.setDhcpIp(dhcpIp); + return cmd; + } + + static public PvlanSetupCommand createVmSetup(String op, String bridge, URI uri, String vmMac) + { + PvlanSetupCommand cmd = new PvlanSetupCommand(Type.VM, op, bridge, uri); + cmd.setVmMac(vmMac); + return cmd; + } + + static public PvlanSetupCommand createVmInDhcpHostSetup(String op, String bridge, URI uri, String dhcpMac, String vmMac) + { + PvlanSetupCommand cmd = new PvlanSetupCommand(Type.VM_IN_DHCP_HOST, op, bridge, uri); + cmd.setDhcpMac(dhcpMac); + cmd.setVmMac(vmMac); + return cmd; + } + + @Override + public boolean executeInSequence() { + return true; + } + + public String getOp() { + return op; + } + + public String getBridge() { + return bridge; + } + + public String getPrimary() { + return primary; + } + + public String getIsolated() { + return isolated; + } + + public String getVmMac() { + return vmMac; + } + + protected void setVmMac(String vmMac) { + this.vmMac = vmMac; + } + + public String getDhcpMac() { + return dhcpMac; + } + + protected void setDhcpMac(String dhcpMac) { + this.dhcpMac = dhcpMac; + } + + public String getDhcpIp() { + return dhcpIp; + } + + protected void setDhcpIp(String dhcpIp) { + this.dhcpIp = dhcpIp; + } + + public Type getType() { + return type; + } + + public boolean isStrict() { + return strict; + } + + public void setStrict(boolean strict) { + this.strict = strict; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 176dc00..34b590e 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -604,6 +604,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((NetworkRulesVmSecondaryIpCommand)cmd); } else if (clazz == ScaleVmCommand.class) { return execute((ScaleVmCommand) cmd); + } else if (clazz == PvlanSetupCommand.class) { + return execute((PvlanSetupCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -1054,7 +1056,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe vifr = vif.getRecord(conn); s_logger.debug("Created a vif " + vifr.uuid + " on " + nic.getDeviceId()); } - + return vif; } @@ -1465,6 +1467,48 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } } + + private Answer execute(PvlanSetupCommand cmd) { + Connection conn = getConnection(); + + String primaryPvlan = cmd.getPrimary(); + String isolatedPvlan = cmd.getIsolated(); + String op = cmd.getOp(); + String bridge = cmd.getBridge(); + String result = null; + String dhcpMac = cmd.getDhcpMac(); + String dhcpIp = cmd.getDhcpIp(); + String vmMac = cmd.getVmMac(); + if (cmd.getType() == PvlanSetupCommand.Type.DHCP) { + result = callHostPlugin(conn, "ovs-pvlan", "setup-pvlan-dhcp", "op", op, "bridge", bridge, + "primary-pvlan", primaryPvlan, "isolated-pvlan", isolatedPvlan, "dhcp-ip", dhcpIp, "dhcp-mac", dhcpMac); + if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { + s_logger.warn("Failed to program pvlan for dhcp server with mac " + dhcpMac); + return new Answer(cmd, false, result); + } else { + s_logger.info("Programmed pvlan for dhcp server with mac " + dhcpMac); + } + } else if (cmd.getType() == PvlanSetupCommand.Type.VM) { + result = callHostPlugin(conn, "ovs-pvlan", "setup-pvlan-vm-alone", "op", op, "bridge", bridge, + "primary-pvlan", primaryPvlan, "isolated-pvlan", isolatedPvlan, "vm-mac", vmMac); + if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { + s_logger.warn("Failed to program pvlan for vm with mac " + vmMac); + return new Answer(cmd, false, result); + } else { + s_logger.info("Programmed pvlan for vm with mac " + vmMac); + } + } else if (cmd.getType() == PvlanSetupCommand.Type.VM_IN_DHCP_HOST) { + result = callHostPlugin(conn, "ovs-pvlan", "setup-pvlan-vm-dhcp", "op", op, "bridge", bridge, + "primary-pvlan", primaryPvlan, "isolated-pvlan", isolatedPvlan, "vm-mac", vmMac, "dhcp-mac", dhcpMac); + if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) { + s_logger.warn("Failed to program pvlan for vm in dhcp host with mac " + vmMac); + return new Answer(cmd, false, result); + } else { + s_logger.info("Programmed pvlan for vm in dhcp host with mac " + vmMac); + } + } + return new Answer(cmd, true, result); + } @Override public StartAnswer execute(StartCommand cmd) { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/scripts/vm/hypervisor/xenserver/ovs-pvlan ---------------------------------------------------------------------- diff --git a/scripts/vm/hypervisor/xenserver/ovs-pvlan b/scripts/vm/hypervisor/xenserver/ovs-pvlan new file mode 100755 index 0000000..2c1e3af --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/ovs-pvlan @@ -0,0 +1,168 @@ +#!/usr/bin/python +# 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. + + +import cloudstack_pluginlib as lib +import logging +import os +import sys +import subprocess +import time +import XenAPIPlugin + +sys.path.append("/opt/xensource/sm/") +import util + +from time import localtime as _localtime, asctime as _asctime + +xePath = "/opt/xensource/bin/xe" +lib.setup_logging("/var/log/ovs-pvlan.log") +dhcpSetupPath = "/opt/xensource/bin/ovs-pvlan-dhcp-host.sh" +vmAloneSetupPath = "/opt/xensource/bin/ovs-pvlan-vm.sh" +vmDhcpSetupPath = "/opt/xensource/bin/ovs-pvlan-vm-in-dhcp-host.sh" +pvlanCleanupPath = "/opt/xensource/bin/ovs-pvlan-cleanup.sh" + +def echo(fn): + def wrapped(*v, **k): + name = fn.__name__ + util.SMlog("#### VMOPS enter %s ####" % name) + res = fn(*v, **k) + util.SMlog("#### VMOPS exit %s ####" % name) + return res + return wrapped + + +@echo +def setup_pvlan_dhcp(session, args): + op = args.pop("op") + bridge = args.pop("bridge") + primary = args.pop("primary-pvlan") + isolated = args.pop("isolated-pvlan") + dhcp_ip = args.pop("dhcp-ip"); + dhcp_mac = args.pop("dhcp-mac"); + + res = lib.check_switch() + if res != "SUCCESS": + return "FAILURE:%s" % res + + if op == "add": + logging.debug("About to setup dhcp vm on the switch:%s" % bridge) + res = lib.do_cmd([dhcpSetupPath, "-A", "-b", bridge, "-p", primary, + "-i", isolated, "-d", dhcp_ip, "-m", dhcp_mac]) + if res: + result = "FAILURE:%s" % res + return result; + logging.debug("Setup dhcp vm on switch program done") + elif op == "delete": + logging.debug("About to remove dhcp the switch:%s" % bridge) + res = lib.do_cmd([dhcpSetupPath, "-D", "-b", bridge, "-p", primary, + "-i", isolated, "-d", dhcp_ip, "-m", dhcp_mac]) + if res: + result = "FAILURE:%s" % res + return result; + logging.debug("Remove DHCP on switch program done") + + result = "true" + logging.debug("Setup_pvlan_dhcp completed with result:%s" % result) + return result + +@echo +def setup_pvlan_vm_alone(session, args): + op = args.pop("op") + bridge = args.pop("bridge") + isolated = args.pop("isolated-pvlan") + vm_mac = args.pop("vm-mac") + trunk_port = 1 + + res = lib.check_switch() + if res != "SUCCESS": + return "FAILURE:%s" % res + + if op == "add": + logging.debug("About to setup vm alone on the switch:%s" % bridge) + res = lib.do_cmd([vmAloneSetupPath, "-A", "-b", bridge, "-i", isolated, "-v", vm_mac]) + if res: + result = "FAILURE:%s" % res + return result; + logging.debug("Setup vm alone on switch program done") + elif op == "delete": + logging.debug("About to remove vm alone on the switch:%s" % bridge) + res = lib.do_cmd([vmAloneSetupPath, "-D", "-b", bridge, "-i", isolated, "-v", vm_mac]) + if res: + result = "FAILURE:%s" % res + return result; + logging.debug("Remove vm alone on switch program done") + + result = "true" + logging.debug("Setup_pvlan_vm_alone completed with result:%s" % result) + return result + +@echo +def setup_pvlan_vm_dhcp(session, args): + op = args.pop("op") + bridge = args.pop("bridge") + isolated = args.pop("isolated-pvlan") + vm_mac = args.pop("vm-mac") + dhcp_mac = args.pop("dhcp-mac"); + trunk_port = 1 + + res = lib.check_switch() + if res != "SUCCESS": + return "FAILURE:%s" % res + + if op == "add": + logging.debug("About to setup vm dhcp on the switch:%s" % bridge) + res = lib.do_cmd([vmDhcpSetupPath, "-A", "-b", bridge, "-i", isolated, + "-v", vm_mac, "-m", dhcp_mac]) + if res: + result = "FAILURE:%s" % res + return result; + logging.debug("Setup vm dhcp on switch program done") + elif op == "delete": + logging.debug("About to remove vm dhcp on the switch:%s" % bridge) + res = lib.do_cmd([vmDhcpSetupPath, "-D", "-b", bridge, "-i", isolated, + "-v", vm_mac, "-m", dhcp_mac]) + if res: + result = "FAILURE:%s" % res + return result; + logging.debug("Remove vm dhcp on switch program done") + + result = "true" + logging.debug("Setup_pvlan_vm_dhcp completed with result:%s" % result) + return result + +@echo +def cleanup(session, args): + res = lib.check_switch() + if res != "SUCCESS": + return "FAILURE:%s" % res + + res = lib.do_cmd([pvlanCleanUpPath]) + if res: + result = "FAILURE:%s" % res + return result; + + result = "true" + logging.debug("Setup_pvlan_vm_dhcp completed with result:%s" % result) + return result + +if __name__ == "__main__": + XenAPIPlugin.dispatch({"setup-pvlan-dhcp": setup_pvlan_dhcp, + "setup-pvlan-vm-alone": setup_pvlan_vm_alone, + "setup-pvlan-vm-dhcp": setup_pvlan_vm_dhcp, + "cleanup":cleanup}) http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/scripts/vm/hypervisor/xenserver/xenserver60/patch ---------------------------------------------------------------------- diff --git a/scripts/vm/hypervisor/xenserver/xenserver60/patch b/scripts/vm/hypervisor/xenserver/xenserver60/patch index c9125f4..c767f1a 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver60/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver60/patch @@ -67,4 +67,8 @@ bumpUpPriority.sh=../../../../network/domr/,0755,/opt/xensource/bin swift=..,0755,/opt/xensource/bin swiftxen=..,0755,/etc/xapi.d/plugins s3xen=..,0755,/etc/xapi.d/plugins - +ovs-pvlan=..,0755,/etc/xapi.d/plugins +ovs-pvlan-dhcp-host.sh=../../../network,0755,/opt/xensource/bin +ovs-pvlan-vm-in-dhcp-host.sh=../../../network,0755,/opt/xensource/bin +ovs-pvlan-vm.sh=../../../network,0755,/opt/xensource/bin +ovs-pvlan-cleanup.sh=../../../network,0755,/opt/xensource/bin http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/scripts/vm/network/ovs-pvlan-cleanup.sh ---------------------------------------------------------------------- diff --git a/scripts/vm/network/ovs-pvlan-cleanup.sh b/scripts/vm/network/ovs-pvlan-cleanup.sh new file mode 100755 index 0000000..7493bed --- /dev/null +++ b/scripts/vm/network/ovs-pvlan-cleanup.sh @@ -0,0 +1,23 @@ +#!/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 + +ovs-ofctl del-flows xenbr0 +ovs-ofctl add-flow xenbr0 priority=0,actions=NORMAL + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/scripts/vm/network/ovs-pvlan-dhcp-host.sh ---------------------------------------------------------------------- diff --git a/scripts/vm/network/ovs-pvlan-dhcp-host.sh b/scripts/vm/network/ovs-pvlan-dhcp-host.sh new file mode 100755 index 0000000..e12fbce --- /dev/null +++ b/scripts/vm/network/ovs-pvlan-dhcp-host.sh @@ -0,0 +1,104 @@ +#!/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. + +usage() { + printf "Usage: %s: (-A|-D) -b <bridge/switch> -p <primary vlan> -i <secondary isolated vlan> -d <DHCP server IP> -m <DHCP server MAC> -v <VM MAC> -h \n" $(basename $0) >&2 + exit 2 +} + +br= +pri_vlan= +sec_iso_vlan= +dhcp_ip= +dhcp_mac= +vm_mac= +op= + +while getopts 'ADb:p:i:d:m:v:h' OPTION +do + case $OPTION in + A) op="add" + ;; + D) op="del" + ;; + b) br="$OPTARG" + ;; + p) pri_vlan="$OPTARG" + ;; + i) sec_iso_vlan="$OPTARG" + ;; + d) dhcp_ip="$OPTARG" + ;; + m) dhcp_mac="$OPTARG" + ;; + v) vm_mac="$OPTARG" + ;; + h) usage + exit 1 + ;; + esac +done + +if [ -z "$op" ] +then + echo Missing operation pararmeter! + exit 1 +fi + +if [ -z "$br" ] +then + echo Missing parameter bridge! + exit 1 +fi + +if [ -z "$pri_vlan" ] +then + echo Missing parameter primary vlan! + exit 1 +fi + +if [ -z "$sec_iso_vlan" ] +then + echo Missing parameter secondary isolate vlan! + exit 1 +fi + +if [ -z "$dhcp_ip" ] +then + echo Missing parameter DHCP IP! + exit 1 +fi + +if [ -z "$dhcp_mac" ] +then + echo Missing parameter DHCP MAC! + exit 1 +fi + +if [ "$op" == "add" ] +then + ovs-ofctl add-flow $br priority=200,arp,dl_vlan=$sec_iso_vlan,nw_dst=$dhcp_ip,actions=mod_vlan_vid:$pri_vlan,NORMAL + ovs-ofctl add-flow $br priority=180,arp,nw_dst=$dhcp_ip,actions=NORMAL + ovs-ofctl add-flow $br priority=150,dl_vlan=$sec_iso_vlan,dl_dst=$dhcp_mac,actions=mod_vlan_vid:$pri_vlan,NORMAL + ovs-ofctl add-flow $br priority=100,udp,dl_vlan=$sec_iso_vlan,nw_dst=255.255.255.255,tp_dst=67,actions=mod_vlan_vid:$pri_vlan,NORMAL +else + ovs-ofctl del-flows --strict $br priority=200,arp,dl_vlan=$sec_iso_vlan,nw_dst=$dhcp_ip + ovs-ofctl del-flows --strict $br priority=180,arp,nw_dst=$dhcp_ip + ovs-ofctl del-flows --strict $br priority=150,dl_vlan=$sec_iso_vlan,dl_dst=$dhcp_mac + ovs-ofctl del-flows --strict $br priority=100,udp,dl_vlan=$sec_iso_vlan,nw_dst=255.255.255.255,tp_dst=67 +fi http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/scripts/vm/network/ovs-pvlan-vm-in-dhcp-host.sh ---------------------------------------------------------------------- diff --git a/scripts/vm/network/ovs-pvlan-vm-in-dhcp-host.sh b/scripts/vm/network/ovs-pvlan-vm-in-dhcp-host.sh new file mode 100755 index 0000000..de37882 --- /dev/null +++ b/scripts/vm/network/ovs-pvlan-vm-in-dhcp-host.sh @@ -0,0 +1,88 @@ +#!/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. + +usage() { + printf "Usage: %s: (-A|-D) -b <bridge/switch> -p <primary vlan> -i <secondary isolated vlan> -d <DHCP server IP> -m <DHCP server MAC> -v <VM MAC> -h \n" $(basename $0) >&2 + exit 2 +} + +br= +pri_vlan= +sec_iso_vlan= +dhcp_ip= +dhcp_mac= +vm_mac= +op= + +while getopts 'ADb:p:i:d:m:v:h' OPTION +do + case $OPTION in + A) op="add" + ;; + D) op="del" + ;; + b) br="$OPTARG" + ;; + p) pri_vlan="$OPTARG" + ;; + i) sec_iso_vlan="$OPTARG" + ;; + d) dhcp_ip="$OPTARG" + ;; + m) dhcp_mac="$OPTARG" + ;; + v) vm_mac="$OPTARG" + ;; + h) usage + exit 1 + ;; + esac +done + +if [ -z "$op" ] +then + echo Missing operation 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 "$dhcp_mac" ] +then + echo Missing parameter DHCP MAC! + exit 1 +fi + +if [ "$op" == "add" ] +then + ovs-ofctl add-flow $br priority=120,dl_src=$vm_mac,dl_dst=$dhcp_mac,actions=NORMAL + ovs-ofctl add-flow $br priority=80,udp,dl_src=$vm_mac,nw_dst=255.255.255.255,tp_dst=67,actions=NORMAL +else + ovs-ofctl del-flows --strict $br priority=120,dl_src=$vm_mac,dl_dst=$dhcp_mac + ovs-ofctl del-flows --strict $br priority=80,udp,dl_src=$vm_mac,nw_dst=255.255.255.255,tp_dst=67 +fi http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/scripts/vm/network/ovs-pvlan-vm.sh ---------------------------------------------------------------------- diff --git a/scripts/vm/network/ovs-pvlan-vm.sh b/scripts/vm/network/ovs-pvlan-vm.sh new file mode 100755 index 0000000..8ac20df --- /dev/null +++ b/scripts/vm/network/ovs-pvlan-vm.sh @@ -0,0 +1,90 @@ +#!/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. + +usage() { + printf "Usage: %s: (-A|-D) -b <bridge/switch> -p <primary vlan> -i <secondary isolated vlan> -d <DHCP server IP> -m <DHCP server MAC> -v <VM MAC> -h \n" $(basename $0) >&2 + exit 2 +} + +br= +pri_vlan= +sec_iso_vlan= +dhcp_ip= +dhcp_mac= +vm_mac= +op= + +while getopts 'ADb:p:i:d:m:v:h' OPTION +do + case $OPTION in + A) op="add" + ;; + D) op="del" + ;; + b) br="$OPTARG" + ;; + p) pri_vlan="$OPTARG" + ;; + i) sec_iso_vlan="$OPTARG" + ;; + d) dhcp_ip="$OPTARG" + ;; + m) dhcp_mac="$OPTARG" + ;; + v) vm_mac="$OPTARG" + ;; + h) usage + exit 1 + ;; + esac +done + +if [ -z "$op" ] +then + echo Missing operation 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 "$sec_iso_vlan" ] +then + echo Missing parameter secondary isolate vlan! + exit 1 +fi + +trunk_port=1 + +if [ "$op" == "add" ] +then + ovs-ofctl add-flow $br priority=50,dl_src=$vm_mac,actions=mod_vlan_vid:$sec_iso_vlan,output:$trunk_port +else + # it would delete any rule related to this vm, not only the rule added above + ovs-ofctl del-flows $br dl_src=$vm_mac +fi + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/server/src/com/cloud/network/element/VirtualRouterElement.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index f601f4f..d9c4356 100755 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -31,6 +31,7 @@ import org.apache.cloudstack.api.command.admin.router.CreateVirtualRouterElement import org.apache.cloudstack.api.command.admin.router.ListVirtualRouterElementsCmd; import org.apache.log4j.Logger; +import com.cloud.agent.api.PvlanSetupCommand; import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; @@ -48,6 +49,7 @@ import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.NetworkModel; +import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.network.PublicIpAddress; @@ -214,6 +216,15 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl DataCenter.class, network.getDataCenterId()); } + // Setup PVlan for vm if necessary + if (network.getTrafficType() == TrafficType.Guest && network.getBroadcastDomainType() == BroadcastDomainType.Pvlan) { + assert routers.size() == 1; + DomainRouterVO router = routers.get(0); + if (router.getHostId() == dest.getHost().getId()) { + _routerMgr.setupVmWithDhcpHostForPvlan(true, router, nic); + } + } + return true; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java index f49ab79..4dfd78c 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -19,6 +19,7 @@ package com.cloud.network.router; import java.util.List; import java.util.Map; +import com.cloud.agent.api.PvlanSetupCommand; import com.cloud.deploy.DeployDestination; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -35,6 +36,7 @@ import com.cloud.user.User; import com.cloud.uservm.UserVm; import com.cloud.utils.component.Manager; import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachineProfile; @@ -103,4 +105,6 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA boolean applyUserData(Network config, NicProfile nic, VirtualMachineProfile<UserVm> vm, DeployDestination dest, List<DomainRouterVO> routers) throws ResourceUnavailableException; + + void setupVmWithDhcpHostForPvlan(boolean add, DomainRouterVO router, NicProfile profile) throws ResourceUnavailableException; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 6620e0a..bb31e1c 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -62,6 +62,7 @@ import com.cloud.agent.api.GetDomRVersionCmd; import com.cloud.agent.api.ModifySshKeysCommand; import com.cloud.agent.api.NetworkUsageAnswer; import com.cloud.agent.api.NetworkUsageCommand; +import com.cloud.agent.api.PvlanSetupCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.check.CheckSshAnswer; @@ -2210,6 +2211,72 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V return dhcpRange; } + private boolean setupDhcpForPvlanOnHost(boolean add, DomainRouterVO router, Nic routerNic) { + if (!routerNic.getBroadcastUri().getScheme().equals("pvlan")) { + return false; + } + setupDhcpForPvlan(add, router, routerNic); + Long hostId = router.getHostId(); + List<UserVmVO> vms = _userVmDao.listByHostId(hostId); + for (UserVmVO vm : vms) { + if (vm.getState() != State.Running) { + continue; + } + List<NicVO> nics = _nicDao.listByVmId(vm.getId()); + for (NicVO nic : nics) { + if (nic.getNetworkId() == routerNic.getNetworkId()) { + try { + Network network = _networkDao.findById(routerNic.getNetworkId()); + NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), + null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network)); + setupVmWithDhcpHostForPvlan(add, router, profile); + } catch (ResourceUnavailableException e) { + s_logger.warn("Fail to program pvlan on nic " + nic.getMacAddress(), e); + return false; + } + } + } + } + return true; + } + + private boolean setupDhcpForPvlan(boolean add, DomainRouterVO router, Nic nic) { + if (!nic.getBroadcastUri().getScheme().equals("pvlan")) { + return false; + } + String op = "add"; + if (!add) { + op = "delete"; + } + PvlanSetupCommand cmd = PvlanSetupCommand.createDhcpSetup(op, "xenbr0", nic.getBroadcastUri(), nic.getMacAddress(), nic.getIp4Address()); + Commands cmds = new Commands(cmd); + // In fact we send command to the host of router, we're not programming router but the host + try { + sendCommandsToRouter(router, cmds); + } catch (AgentUnavailableException e) { + s_logger.warn("Agent Unavailable ", e); + return false; + } + return true; + } + + @Override + public void setupVmWithDhcpHostForPvlan(boolean add, DomainRouterVO router, NicProfile profile) throws ResourceUnavailableException + { + if (!profile.getBroadCastUri().getScheme().equals("pvlan")) { + return; + } + String op = "add"; + if (!add) { + op = "delete"; + } + NicVO routerNic = _nicDao.findByInstanceIdAndNetworkId(profile.getNetworkId(), router.getId()); + PvlanSetupCommand cmd = PvlanSetupCommand.createVmInDhcpHostSetup(op, "xenbr0", profile.getBroadCastUri(), routerNic.getMacAddress(), profile.getMacAddress()); + Commands cmds = new Commands(cmd); + // In fact we send command to the host of router, we're not programming router but the host + sendCommandsToRouter(router, cmds); + } + @Override public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile<DomainRouterVO> profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException { @@ -2505,11 +2572,18 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V List<Network> guestNetworks = new ArrayList<Network>(); List<? extends Nic> routerNics = _nicDao.listByVmId(profile.getId()); - for (Nic routerNic : routerNics) { - Network network = _networkModel.getNetwork(routerNic.getNetworkId()); + for (Nic nic : routerNics) { + Network network = _networkModel.getNetwork(nic.getNetworkId()); if (network.getTrafficType() == TrafficType.Guest) { guestNetworks.add(network); - } + if (nic.getBroadcastUri().getScheme().equals("pvlan")) { + result = setupDhcpForPvlanOnHost(true, router, nic); + } + } + } + + if (!result) { + return result; } answer = cmds.getAnswer("getDomRVersion"); @@ -2537,6 +2611,13 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V VMInstanceVO vm = profile.getVirtualMachine(); DomainRouterVO domR = _routerDao.findById(vm.getId()); processStopOrRebootAnswer(domR, answer); + List<? extends Nic> routerNics = _nicDao.listByVmId(profile.getId()); + for (Nic nic : routerNics) { + Network network = _networkModel.getNetwork(nic.getNetworkId()); + if (network.getTrafficType() == TrafficType.Guest && nic.getBroadcastUri().getScheme().equals("pvlan")) { + setupDhcpForPvlanOnHost(false, domR, nic); + } + } } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/server/src/com/cloud/vm/UserVmManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 8f2d103..7a9fc47 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -69,6 +69,7 @@ import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.PlugNicAnswer; import com.cloud.agent.api.PlugNicCommand; +import com.cloud.agent.api.PvlanSetupCommand; import com.cloud.agent.api.StartAnswer; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.UnPlugNicAnswer; @@ -2751,6 +2752,34 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use return true; } + private boolean setupVmForPvlan(boolean add, Long hostId, NicVO nic) { + if (!nic.getBroadcastUri().getScheme().equals("pvlan")) { + return false; + } + String op = "add"; + if (!add) { + // "delete" would remove all the rules(if using ovs) related to this vm + op = "delete"; + } + PvlanSetupCommand cmd = PvlanSetupCommand.createVmSetup(op, "xenbr0", nic.getBroadcastUri(), nic.getMacAddress()); + Answer answer = null; + try { + answer = _agentMgr.send(hostId, cmd); + } catch (OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + return false; + } catch (AgentUnavailableException e) { + s_logger.warn("Agent Unavailable ", e); + return false; + } + + boolean result = true; + if (answer == null || !answer.getResult()) { + result = false; + } + return result; + } + @Override public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile<UserVmVO> profile, DeployDestination dest, @@ -2812,6 +2841,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use originalIp = nic.getIp4Address(); guestNic = nic; guestNetwork = network; + if (nic.getBroadcastUri().getScheme().equals("pvlan")) { + if (!setupVmForPvlan(true, hostId, nic)) { + return false; + } + } } } boolean ipChanged = false; @@ -2942,6 +2976,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use + " stop due to exception ", ex); } } + + VMInstanceVO vm = profile.getVirtualMachine(); + List<NicVO> nics = _nicDao.listByVmId(vm.getId()); + for (NicVO nic : nics) { + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if (network.getTrafficType() == TrafficType.Guest) { + if (nic.getBroadcastUri().getScheme().equals("pvlan")) { + setupVmForPvlan(false, vm.getHostId(), nic); + } + } + } } public String generateRandomPassword() { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b64039ba/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java ---------------------------------------------------------------------- diff --git a/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java b/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java index ef5478b..5278b33 100644 --- a/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java +++ b/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java @@ -402,4 +402,11 @@ VpcVirtualNetworkApplianceService { return null; } + @Override + public void setupVmWithDhcpHostForPvlan(boolean add, + DomainRouterVO router, NicProfile nic) throws ResourceUnavailableException { + // TODO Auto-generated method stub + + } + }