diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-02-05 20:10:57 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-02-05 23:38:04 +0900 |
commit | 332ed5f2d6edfb2b95802065bc5e3d08b4ea22e3 (patch) | |
tree | 77beb2fa2d9f5c7af1785903066bf58ec50ade3e | |
parent | 6e76328bbc6243a298cd93a9954cacd45a21e176 (diff) |
ryu/app/simple_vlan: simple VLAN app for OVS
Based on the following patch:
Subject: ryu/app/simple_vlan: simple VLAN spp
This application provides VLAN separation with ovs tag function.
Something similar openstack quantum openvswitch vlan function.
Example to run:
ryu-manager ryu/app/simple_vlan.py \
ryu/app/quantum_adapter.py \
ryu/app/rest.py \
ryu/app/rest_conf_switch.py \
ryu/app/rest_tunnel.py \
ryu/app/rest_quantum.py
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | ryu/app/simple_vlan.py | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/ryu/app/simple_vlan.py b/ryu/app/simple_vlan.py new file mode 100644 index 00000000..9c7e2454 --- /dev/null +++ b/ryu/app/simple_vlan.py @@ -0,0 +1,230 @@ +# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp> +# +# Licensed 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 gevent +import logging + +from ryu.app import (conf_switch_key, + rest_nw_id) +from ryu.base import app_manager +from ryu.controller import (conf_switch, + dpset, + handler, + network, + tunnels) +import ryu.exception as ryu_exc +from ryu.lib import dpid as dpid_lib +from ryu.lib.ovs import bridge +from ryu.ofproto import nx_match + + +LOG = logging.getLogger(__name__) + + +def _is_reserved_port(dp, port_no): + return port_no > dp.ofproto.OFPP_MAX + + +class SimpleVLAN(app_manager.RyuApp): + _CONTEXTS = { + 'conf_switch': conf_switch.ConfSwitchSet, + 'dpset': dpset.DPSet, + 'network': network.Network, + 'tunnels': tunnels.Tunnels, + } + + _PRIORITY_CATCHALL = 1 + _PRIORITY_NORMAL = 2 + + _COOKIE_CATCHALL = 1 + _COOKIE_NORMAL = 2 + + def __init__(self, *args, **kwargs): + super(SimpleVLAN, self).__init__(*args, **kwargs) + self.conf_sw = kwargs['conf_switch'] + self.dpset = kwargs['dpset'] + self.nw = kwargs['network'] + self.tunnels = kwargs['tunnels'] + + def _port_flow_add(self, dp, port_no): + LOG.debug('ovs_port_update dpid %s port_no %s', + dpid_lib.dpid_to_str(dp.id), port_no) + rule = nx_match.ClsRule() + rule.set_in_port(port_no) + ofproto = dp.ofproto + actions = [dp.ofproto_parser.OFPActionOutput(ofproto.OFPP_NORMAL)] + dp.send_flow_mod(rule=rule, cookie=self._COOKIE_NORMAL, + command=ofproto.OFPFC_ADD, + idle_timeout=0, hard_timeout=0, + priority=self._PRIORITY_NORMAL, actions=actions) + + def _port_flow_del(self, dp, port_no): + LOG.debug('_port_flow_del dp %s port_no %d', + dpid_lib.dpid_to_str(dp.id), port_no) + rule = nx_match.ClsRule() + rule.set_in_port(port_no) + dp.send_flow_del(rule=rule, cookie=self._COOKIE_NORMAL) + + def _queue_port_flow_add(self, dp, port_no): + self._port_flow_add(dp, port_no) + + def _queue_port_flow_del(self, dp, port_no): + self._port_flow_del(dp, port_no) + + @handler.set_ev_cls(dpset.EventDP) + def dp_handler(self, ev): + if not ev.enter: + return + + dp = ev.dp + rule = nx_match.ClsRule() + ofproto = dp.ofproto + dp.send_flow_mod(rule=rule, + cookie=self._COOKIE_CATCHALL, + command=ofproto.OFPFC_ADD, + idle_timeout=0, hard_timeout=0, + priority=self._PRIORITY_CATCHALL, + actions=[]) + for port in ev.ports: + self._port_add(dp, port.port_no) + + # There is no ordering between those events + # port creation: PortAdd event + # network_id assignment: NetworkPort event + # tunnel_key assignment: TunnelKeyAdd event + # ovsdb_addr: EventConfSwitchSet + # So on each events, check all necessary parameters are setup + def _port_setup(self, dp, port_no, tunnel_key): + if _is_reserved_port(dp, port_no): + return + + dpid = dp.id + try: + port = self.dpset.get_port(dpid, port_no) + except ryu_exc.PortNotFound: + LOG.debug('port not found') + return + + try: + ovsdb_addr = self.conf_sw.get_key(dpid, conf_switch_key.OVSDB_ADDR) + except KeyError: + LOG.debug('ovsdb_addr not found') + return + + self._port_flow_add(dp, port_no) + + LOG.debug('ovs_port_update dpid %s port_no %s', dpid, port_no) + # ovs-vsctl --db=ovsdb_addr --timeout=2 + # set Port port.name tag=tunnel_key + ovs_br = bridge.OVSBridge(dpid, ovsdb_addr, 2) + # ofp_phy_port::name is zero-padded + port_name = port.name.rstrip('\x00') + try: + ovs_br.set_db_attribute("Port", port_name, "tag", tunnel_key) + except gevent.Timeout: + LOG.error('timout') + return + + return True + + def _port_setup_netid(self, dpid, port_no, network_id): + LOG.debug('_port_setup_netid %s %s %s', + dpid_lib.dpid_to_str(dpid), port_no, network_id) + dp = self.dpset.get(dpid) + if dp is None: + LOG.debug('dp not found') + return + if _is_reserved_port(dp, port_no): + return + + if network_id == rest_nw_id.NW_ID_EXTERNAL: + LOG.debug('external interface') + self._queue_port_flow_add(dp, port_no) + return True + + try: + tunnel_key = self.tunnels.get_key(network_id) + except tunnels.TunnelKeyNotFound: + LOG.debug('tunnel key not found') + return + + return self._port_setup(dp, port_no, tunnel_key) + + def _port_add(self, dp, port_no): + if _is_reserved_port(dp, port_no): + return + + dpid = dp.id + try: + network_id = self.nw.get_network(dpid, port_no) + except ryu_exc.PortUnknown: + LOG.debug('port_unknown') + self._queue_port_flow_del(dp, port_no) + return + + if not self._port_setup_netid(dpid, port_no, network_id): + LOG.debug('_port_setup_netid failed') + self._queue_port_flow_del(dp, port_no) + + @handler.set_ev_cls(dpset.EventPortAdd) + def port_add_handler(self, ev): + LOG.debug('port_add %s', ev) + self._port_add(ev.dp, ev.port.port_no) + + @handler.set_ev_cls(dpset.EventPortDelete) + def port_del_handler(self, ev): + LOG.debug('port_del %s', ev) + dp = ev.dp + port_no = ev.port.port_no + if _is_reserved_port(dp, port_no): + return + self._queue_port_flow_del(dp, port_no) + + @handler.set_ev_cls(network.EventNetworkPort) + def network_port_handler(self, ev): + LOG.debug('network_port %s', ev) + if not ev.add_del: + return + self._port_setup_netid(ev.dpid, ev.port_no, ev.network_id) + + @handler.set_ev_cls(tunnels.EventTunnelKeyAdd) + def tunnel_key_add_handler(self, ev): + LOG.debug('tunnel_add %s', ev) + tunnel_key = ev.tunnel_key + for (dpid, port_no) in self.nw.list_ports(ev.network_id): + dp = self.dpset.get(dpid) + if dp is None: + continue + self._port_setup(dp, port_no, tunnel_key) + + @handler.set_ev_cls(conf_switch.EventConfSwitchSet) + def conf_switch_set_handler(self, ev): + LOG.debug('conf_switch_set %s', ev) + if ev.key != conf_switch_key.OVSDB_ADDR: + return + + dpid = ev.dpid + try: + ports = self.dpset.get_ports(dpid) + except KeyError: + return + for port in ports: + port_no = port.port_no + try: + network_id = self.nw.get_network(dpid, port_no) + except ryu_exc.PortUnknown: + continue + self._port_setup_netid(dpid, port_no, network_id) |