summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc/source/components.rst36
-rw-r--r--ryu/app/client.py286
-rw-r--r--ryu/app/gre_tunnel.py980
-rw-r--r--ryu/app/quantum_adapter.py446
-rw-r--r--ryu/app/rest.py270
-rw-r--r--ryu/app/rest_nw_id.py41
-rw-r--r--ryu/app/rest_quantum.py136
-rw-r--r--ryu/app/rest_tunnel.py218
-rw-r--r--ryu/app/simple_isolation.py351
-rw-r--r--ryu/app/simple_vlan.py229
-rw-r--r--ryu/app/tunnel_port_updater.py473
-rw-r--r--ryu/flags.py30
-rw-r--r--ryu/lib/quantum_ifaces.py131
13 files changed, 0 insertions, 3627 deletions
diff --git a/doc/source/components.rst b/doc/source/components.rst
index 3a5876c5..d2dbb7b2 100644
--- a/doc/source/components.rst
+++ b/doc/source/components.rst
@@ -94,42 +94,6 @@ ryu.app.simple_switch
---------------------
.. automodule:: ryu.app.simple_switch
-ryu.app.simple_isolation
-------------------------
-.. automodule:: ryu.app.simple_isolation
-
-ryu.app.simple_vlan
--------------------
-.. automodule:: ryu.app.simple_vlan
-
-ryu.app.gre_tunnel
-------------------
-.. automodule:: ryu.app.gre_tunnel
-
-ryu.app.tunnel_port_updater
----------------------------
-.. automodule:: ryu.app.tunnel_port_updater
-
-ryu.app.quantum_adapter
------------------------
-.. automodule:: ryu.app.quantum_adapter
-
-ryu.app.rest
-------------
-.. automodule:: ryu.app.rest
-
-ryu.app.rest_conf_switch
-------------------------
-.. automodule:: ryu.app.rest_conf_switch
-
-ryu.app.rest_quantum
---------------------
-.. automodule:: ryu.app.rest_quantum
-
-ryu.app.rest_tunnel
--------------------
-.. automodule:: ryu.app.rest_tunnel
-
ryu.topology
------------
.. automodule:: ryu.topology
diff --git a/ryu/app/client.py b/ryu/app/client.py
deleted file mode 100644
index 98752cee..00000000
--- a/ryu/app/client.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
-# Copyright (C) 2011, 2012 Isaku Yamahata <yamahata at valinux co 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.
-
-# This is a client library for Ryu REST API. (ryu.app.rest_quantum etc)
-# This module is *not* used by ryu-manager.
-# Imported and used by OpenStack Ryu plug-in and agent.
-
-from six.moves import http_client
-import json
-from six.moves import urllib_parse
-
-
-def ignore_http_not_found(func):
- """
- Ignore http not found(404) with Ryu client library.
- Ryu client raises httplib.HTTPException with an error in args[0]
- """
- try:
- func()
- except http_client.HTTPException as e:
- res = e.args[0]
- if res.status != http_client.NOT_FOUND:
- raise
-
-
-class RyuClientBase(object):
- def __init__(self, version, address):
- super(RyuClientBase, self).__init__()
- self.version = version
- res = urllib_parse.SplitResult('', address, '', '', '')
- self.host = res.hostname
- self.port = res.port
- self.url_prefix = '/' + self.version + '/'
-
- def _do_request(self, method, action, body=None):
- conn = http_client.HTTPConnection(self.host, self.port)
- url = self.url_prefix + action
- headers = {}
- if body is not None:
- body = json.dumps(body)
- headers['Content-Type'] = 'application/json'
- conn.request(method, url, body, headers)
- res = conn.getresponse()
- if res.status in (http_client.OK,
- http_client.CREATED,
- http_client.ACCEPTED,
- http_client.NO_CONTENT):
- return res
-
- raise http_client.HTTPException(
- res, 'code %d reason %s' % (res.status, res.reason),
- res.getheaders(), res.read())
-
- def _do_request_read(self, method, action):
- res = self._do_request(method, action)
- return res.read()
-
-
-class OFPClientV1_0(RyuClientBase):
- version = 'v1.0'
-
- # /networks/{network_id}/{dpid}_{port}/macs/{mac_address}
- path_networks = 'networks'
- path_network = path_networks + '/%s'
- path_port = path_network + '/%s_%s'
- path_macs = path_port + '/macs'
- path_mac = path_macs + '/%s'
-
- def __init__(self, address):
- super(OFPClientV1_0, self).__init__(OFPClientV1_0.version, address)
-
- def get_networks(self):
- return self._do_request_read('GET', self.path_networks)
-
- def create_network(self, network_id):
- self._do_request('POST', self.path_network % network_id)
-
- def update_network(self, network_id):
- self._do_request('PUT', self.path_network % network_id)
-
- def delete_network(self, network_id):
- self._do_request('DELETE', self.path_network % network_id)
-
- def get_ports(self, network_id):
- return self._do_request_read('GET', self.path_network % network_id)
-
- def create_port(self, network_id, dpid, port):
- self._do_request('POST', self.path_port % (network_id, dpid, port))
-
- def update_port(self, network_id, dpid, port):
- self._do_request('PUT', self.path_port % (network_id, dpid, port))
-
- def delete_port(self, network_id, dpid, port):
- self._do_request('DELETE', self.path_port % (network_id, dpid, port))
-
- def list_macs(self, network_id, dpid, port):
- return self._do_request_read('GET',
- self.path_macs % (network_id, dpid, port))
-
- def create_mac(self, network_id, dpid, port, mac_address):
- self._do_request('POST', self.path_mac % (network_id, dpid, port,
- mac_address))
-
- def update_mac(self, network_id, dpid, port, mac_address):
- self._do_request('PUT', self.path_mac % (network_id, dpid, port,
- mac_address))
-
-
-OFPClient = OFPClientV1_0
-
-
-class TunnelClientV1_0(RyuClientBase):
- version = 'v1.0'
-
- # /tunnels/networks/{network-id}/key/{tunnel_key}
- # /tunnels/switches/{dpid}/ports/{port-id}/{remote_dpip}
- path_tunnels = 'tunnels'
- path_key = path_tunnels + '/networks/%(network_id)s/key'
- path_tunnel_key = path_key + '/%(tunnel_key)s'
- path_ports = path_tunnels + '/switches/%(dpid)s/ports'
- path_port = path_ports + '/%(port_no)s'
- path_remote_dpid = path_port + '/%(remote_dpid)s'
-
- def __init__(self, address):
- super(TunnelClientV1_0, self).__init__(self.version, address)
-
- def get_tunnel_key(self, network_id):
- return self._do_request_read('GET', self.path_key % locals())
-
- def delete_tunnel_key(self, network_id):
- return self._do_request_read('DELETE', self.path_key % locals())
-
- def create_tunnel_key(self, network_id, tunnel_key):
- self._do_request('POST', self.path_tunnel_key % locals())
-
- def update_tunnel_key(self, network_id, tunnel_key):
- self._do_request('PUT', self.path_tunnel_key % locals())
-
- def list_ports(self, dpid):
- return self._do_request_read('GET', self.path_ports % locals())
-
- def delete_port(self, dpid, port_no):
- return self._do_request_read('DELETE', self.path_port % locals())
-
- def get_remote_dpid(self, dpid, port_no):
- return self._do_request_read('GET', self.path_port % locals())
-
- def create_remote_dpid(self, dpid, port_no, remote_dpid):
- self._do_request('POST', self.path_remote_dpid % locals())
-
- def update_remote_dpid(self, dpid, port_no, remote_dpid):
- self._do_request('PUT', self.path_remote_dpid % locals())
-
-
-TunnelClient = TunnelClientV1_0
-
-
-class SwitchConfClientV1_0(RyuClientBase):
- version = 'v1.0'
-
- # /conf/switches
- # /conf/switches/<dpid>
- # /conf/switches/<dpid>/<key>
- path_conf_switches = 'conf/switches'
- path_switch = path_conf_switches + '/%(dpid)s'
- path_key = path_switch + '/%(key)s'
-
- def __init__(self, address):
- super(SwitchConfClientV1_0, self).__init__(self.version, address)
-
- def list_switches(self):
- return self._do_request_read('GET', self.path_conf_switches)
-
- def delete_switch(self, dpid):
- self._do_request('DELETE', self.path_switch % locals())
-
- def list_keys(self, dpid):
- return self._do_request_read('GET', self.path_switch % locals())
-
- def set_key(self, dpid, key, value):
- self._do_request('PUT', self.path_key % locals(), value)
-
- def get_key(self, dpid, key):
- return self._do_request_read('GET', self.path_key % locals())
-
- def delete_key(self, dpid, key):
- self._do_request('DELETE', self.path_key % locals())
-
-
-SwitchConfClient = SwitchConfClientV1_0
-
-
-class QuantumIfaceClientV1_0(RyuClientBase):
- version = 'v1.0'
-
- # /quantum/ports
- # /quantum/ports/{iface_id}
- # /quantum/ports/{iface_id}/keys/
- # /quantum/ports/{iface_id}/keys/{key}/{value}
- path_quantum_ports = 'quantum/ports'
- path_iface_id = path_quantum_ports + '/%(iface_id)s'
- path_keys = path_iface_id + '/keys'
- path_key = path_keys + '/%(key)s'
- path_value = path_key + '/%(value)s'
-
- def __init__(self, address):
- super(QuantumIfaceClientV1_0, self).__init__(self.version, address)
-
- def list_ifaces(self):
- return self._do_request_read('GET', self.path_quantum_ports)
-
- def delete_iface(self, iface_id):
- self._do_request('DELETE', self.path_iface_id % locals())
-
- def list_keys(self, iface_id):
- return self._do_request_read('GET', self.path_keys % locals())
-
- def get_key(self, iface_id, key):
- return self._do_request_read('GET', self.path_key % locals())
-
- def create_key(self, iface_id, key, value):
- self._do_request('POST', self.path_value % locals())
-
- def update_key(self, iface_id, key, value):
- self._do_request('PUT', self.path_value % locals())
-
- # for convenience
- def get_network_id(self, iface_id):
- return self.get_key(iface_id, 'network_id')
-
- def create_network_id(self, iface_id, network_id):
- self.create_key(iface_id, 'network_id', network_id)
-
- def update_network_id(self, iface_id, network_id):
- self.update_key(iface_id, 'network_id', network_id)
-
-
-QuantumIfaceClient = QuantumIfaceClientV1_0
-NeutronIfaceClient = QuantumIfaceClient # project rename quantum -> neutron
-
-
-class TopologyClientV1_0(RyuClientBase):
- version = 'v1.0'
-
- # /topology/switches
- # /topology/switches/{dpid}
- # /topology/links
- # /topology/links/{dpid}
- _path_switches = 'topology/switches'
- _path_links = 'topology/links'
-
- def __init__(self, address):
- super(TopologyClientV1_0, self).__init__(self.version, address)
-
- # dpid: string representation (see ryu.lib.dpid)
- # if None, get all
- def list_switches(self, dpid=None):
- uri = self._path_switches
- if dpid:
- uri += '/%s' % (dpid)
-
- return self._do_request('GET', uri)
-
- # dpid: string representation (see ryu.lib.dpid)
- # if None, get all
- def list_links(self, dpid=None):
- uri = self._path_links
- if dpid:
- uri += '/%s' % (dpid)
- return self._do_request('GET', uri)
-
-
-TopologyClient = TopologyClientV1_0
diff --git a/ryu/app/gre_tunnel.py b/ryu/app/gre_tunnel.py
deleted file mode 100644
index aae910ac..00000000
--- a/ryu/app/gre_tunnel.py
+++ /dev/null
@@ -1,980 +0,0 @@
-# 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.
-
-# This module updates flow table for OpenStack integration.
-# Despite of the name, this module isn't GRE specific and
-# should work for VXLAN etc as well.
-
-"""
-Flow table updater for OpenStack integration. Despite of the name, this
-isn't GRE specific.
-"""
-
-import collections
-
-from ryu import exception as ryu_exc
-from ryu.app.rest_nw_id import (NW_ID_VPORT_GRE,
- RESERVED_NETWORK_IDS)
-from ryu.base import app_manager
-from ryu.controller import (dpset,
- event,
- handler,
- network,
- ofp_event,
- tunnels)
-from ryu.ofproto import nx_match
-from ryu.lib import dpid as dpid_lib
-from ryu.lib import mac
-
-
-def _is_reserved_port(ofproto, port_no):
- return port_no > ofproto.OFPP_MAX
-
-
-def _link_is_up(dpset_, dp, port_no):
- try:
- state = dpset_.get_port(dp.id, port_no).state
- return not (state & dp.ofproto.OFPPS_LINK_DOWN)
- except ryu_exc.PortNotFound:
- return False
-
-
-class PortSet(app_manager.RyuApp):
-
- # Those events are higher level events than events of network tenant,
- # tunnel ports as the race conditions are masked.
- # Add event is generated only when all necessary informations are gathered,
- # Del event is generated when any one of the informations are deleted.
- #
- # Example: ports for VMs
- # there is a race condition between ofp port add/del event and
- # register network_id for the port.
-
- class EventTunnelKeyDel(event.EventBase):
- def __init__(self, tunnel_key):
- super(PortSet.EventTunnelKeyDel, self).__init__()
- self.tunnel_key = tunnel_key
-
- class EventPortBase(event.EventBase):
- def __init__(self, dpid, port_no):
- super(PortSet.EventPortBase, self).__init__()
- self.dpid = dpid
- self.port_no = port_no
-
- class EventVMPort(EventPortBase):
- def __init__(self, network_id, tunnel_key,
- dpid, port_no, mac_address, add_del):
- super(PortSet.EventVMPort, self).__init__(dpid, port_no)
- self.network_id = network_id
- self.tunnel_key = tunnel_key
- self.mac_address = mac_address
- self.add_del = add_del
-
- def __str__(self):
- return ('EventVMPort<dpid %s port_no %d '
- 'network_id %s tunnel_key %s mac %s add_del %s>' %
- (dpid_lib.dpid_to_str(self.dpid), self.port_no,
- self.network_id, self.tunnel_key,
- mac.haddr_to_str(self.mac_address), self.add_del))
-
- class EventTunnelPort(EventPortBase):
- def __init__(self, dpid, port_no, remote_dpid, add_del):
- super(PortSet.EventTunnelPort, self).__init__(dpid, port_no)
- self.remote_dpid = remote_dpid
- self.add_del = add_del
-
- def __str__(self):
- return ('EventTunnelPort<dpid %s port_no %d remote_dpid %s '
- 'add_del %s>' %
- (dpid_lib.dpid_to_str(self.dpid), self.port_no,
- dpid_lib.dpid_to_str(self.remote_dpid), self.add_del))
-
- def __init__(self, **kwargs):
- super(PortSet, self).__init__()
- self.nw = kwargs['network']
- self.tunnels = kwargs['tunnels']
- self.dpset = kwargs['dpset']
- app_manager.register_app(self)
-
- def _check_link_state(self, dp, port_no, add_del):
- if add_del:
- # When adding port, the link should be UP.
- return _link_is_up(self.dpset, dp, port_no)
- else:
- # When deleting port, the link status isn't cared.
- return True
-
- # Tunnel port
- # of connecting: self.dpids by (dpid, port_no)
- # datapath: connected: EventDP event
- # port status: UP: port add/delete/modify event
- # remote dpid: self.tunnels by (dpid, port_no): tunnel port add/del even
- def _tunnel_port_handler(self, dpid, port_no, add_del):
- dp = self.dpset.get(dpid)
- if dp is None:
- return
- if not self._check_link_state(dp, port_no, add_del):
- return
- try:
- remote_dpid = self.tunnels.get_remote_dpid(dpid, port_no)
- except ryu_exc.PortNotFound:
- return
-
- self.send_event_to_observers(self.EventTunnelPort(dpid, port_no,
- remote_dpid, add_del))
-
- # VM port
- # of connection: self.dpids by (dpid, port_no)
- # datapath: connected: EventDP event
- # port status: UP: Port add/delete/modify event
- # network_id: self.nw by (dpid, port_no): network port add/del event
- # mac_address: self.nw by (dpid, port_no): mac address add/del event
- # tunnel key: from self.tunnels by network_id: tunnel key add/del event
- def _vm_port_handler(self, dpid, port_no,
- network_id, mac_address, add_del):
- if network_id in RESERVED_NETWORK_IDS:
- return
- if mac_address is None:
- return
- dp = self.dpset.get(dpid)
- if dp is None:
- return
- if _is_reserved_port(dp.ofproto, port_no):
- return
- if not self._check_link_state(dp, port_no, add_del):
- return
- try:
- tunnel_key = self.tunnels.get_key(network_id)
- except tunnels.TunnelKeyNotFound:
- return
-
- self.send_event_to_observers(self.EventVMPort(network_id, tunnel_key,
- dpid, port_no, mac_address, add_del))
-
- def _vm_port_mac_handler(self, dpid, port_no, network_id, add_del):
- if network_id == NW_ID_VPORT_GRE:
- self._tunnel_port_handler(dpid, port_no, add_del)
- return
-
- try:
- mac_address = self.nw.get_mac(dpid, port_no)
- except ryu_exc.PortNotFound:
- return
- self._vm_port_handler(dpid, port_no, network_id, mac_address,
- add_del)
-
- def _port_handler(self, dpid, port_no, add_del):
- """
- :type add_del: bool
- :param add_del: True for add, False for del
- """
- try:
- port = self.nw.get_port(dpid, port_no)
- except ryu_exc.PortNotFound:
- return
-
- if port.network_id is None:
- return
-
- if port.network_id == NW_ID_VPORT_GRE:
- self._tunnel_port_handler(dpid, port_no, add_del)
- return
-
- self._vm_port_handler(dpid, port_no, port.network_id,
- port.mac_address, add_del)
-
- def _tunnel_key_del(self, tunnel_key):
- self.send_event_to_observers(self.EventTunnelKeyDel(tunnel_key))
-
- # nw: network del
- # port add/del (vm/tunnel port)
- # mac address add/del(only vm port)
- # tunnels: tunnel key add/del
- # tunnel port add/del
- # dpset: eventdp
- # port add/delete/modify
-
- @handler.set_ev_cls(network.EventNetworkDel)
- def network_del_handler(self, ev):
- network_id = ev.network_id
- if network_id in RESERVED_NETWORK_IDS:
- return
- try:
- tunnel_key = self.tunnels.get_key(network_id)
- except tunnels.TunnelKeyNotFound:
- return
- self._tunnel_key_del(tunnel_key)
-
- @handler.set_ev_cls(network.EventNetworkPort)
- def network_port_handler(self, ev):
- self._vm_port_mac_handler(ev.dpid, ev.port_no, ev.network_id,
- ev.add_del)
-
- @handler.set_ev_cls(network.EventMacAddress)
- def network_mac_address_handler(self, ev):
- self._vm_port_handler(ev.dpid, ev.port_no, ev.network_id,
- ev.mac_address, ev.add_del)
-
- @handler.set_ev_cls(tunnels.EventTunnelKeyAdd)
- def tunnel_key_add_handler(self, ev):
- network_id = ev.network_id
- for (dpid, port_no) in self.nw.list_ports_noraise(network_id):
- self._vm_port_mac_handler(dpid, port_no, network_id, True)
-
- @handler.set_ev_cls(tunnels.EventTunnelKeyDel)
- def tunnel_key_del_handler(self, ev):
- network_id = ev.network_id
- for (dpid, port_no) in self.nw.list_ports_noraise(network_id):
- self._vm_port_mac_handler(dpid, port_no, network_id, False)
- if self.nw.has_network(network_id):
- self._tunnel_key_del(ev.tunnel_key)
-
- @handler.set_ev_cls(tunnels.EventTunnelPort)
- def tunnel_port_handler(self, ev):
- self._port_handler(ev.dpid, ev.port_no, ev.add_del)
-
- @handler.set_ev_cls(dpset.EventDP)
- def dp_handler(self, ev):
- self.send_event_to_observers(ev)
- enter_leave = ev.enter
- if not enter_leave:
- # TODO:XXX
- # What to do on datapath disconnection?
- self.logger.debug('dp disconnection ev:%s', ev)
-
- dpid = ev.dp.id
- ports = set(port.port_no for port in ev.ports)
- ports.update(port.port_no for port in self.nw.get_ports(dpid))
- for port_no in ports:
- self._port_handler(dpid, port_no, enter_leave)
-
- @handler.set_ev_cls(dpset.EventPortAdd)
- def port_add_handler(self, ev):
- self._port_handler(ev.dp.id, ev.port.port_no, True)
-
- @handler.set_ev_cls(dpset.EventPortDelete)
- def port_del_handler(self, ev):
- self._port_handler(ev.dp.id, ev.port.port_no, False)
-
- @handler.set_ev_cls(dpset.EventPortModify)
- def port_modify_handler(self, ev):
- # We don't know LINK status has been changed.
- # So VM/TUNNEL port event can be triggered many times.
- dp = ev.dp
- port = ev.port
- self._port_handler(dp.id, port.port_no,
- not (port.state & dp.ofproto.OFPPS_LINK_DOWN))
-
- @handler.set_ev_cls(ofp_event.EventOFPPacketIn)
- def packet_in_handler(self, ev):
- # for debug
- self.send_event_to_observers(ev)
-
-
-def cls_rule(in_port=None, tun_id=None, dl_src=None, dl_dst=None):
- """Convenience function to initialize nx_match.ClsRule()"""
- rule = nx_match.ClsRule()
- if in_port is not None:
- rule.set_in_port(in_port)
- if tun_id is not None:
- rule.set_tun_id(tun_id)
- if dl_src is not None:
- rule.set_dl_src(dl_src)
- if dl_dst is not None:
- rule.set_dl_dst(dl_dst)
- return rule
-
-
-class GRETunnel(app_manager.RyuApp):
- """
- app for L2/L3 with gre tunneling
-
- PORTS
- VM-port: the port which is connected to VM instance
- TUNNEL-port: the ovs GRE vport
-
- TABLES: multi tables is used
- SRC_TABLE:
- This table is firstly used to match packets.
- by in_port, determine which port the packet comes VM-port or
- TUNNEL-port.
- If the packet came from VM-port, set tunnel id based on which network
- the VM belongs to, and send the packet to the tunnel out table.
- If the packet came from TUNNEL-port and its tunnel id is known to this
- switch, send the packet to local out table. Otherwise drop it.
-
- TUNNEL_OUT_TABLE:
- This table looks at tunnel id and dl_dst, send the packet to tunnel
- ports if necessary. And then, sends the packet to LOCAL_OUT_TABLE.
- By matching the packet with tunnel_id and dl_dst, determine which
- tunnel port the packet is send to.
-
- LOCAL_OUT_TABLE:
- This table looks at tunnel id and dl_dst, send the packet to local
- VM ports if necessary. Otherwise drop the packet.
-
-
- The packet from vm port traverses as
- SRC_TABLE -> TUNNEL_OUT_TABLE -> LOCAL_OUT_TABLE
-
- The packet from tunnel port traverses as
- SRC_TABLE -> LOCAL_OUT_TABLE
-
-
- The packet from vm port:
- SRC_TABLE
- match action
- in_port(VM) & dl_src set_tunnel & goto TUNNEL_OUT_TABLE
- in_port(VM) drop (catch-all drop rule)
-
- in_port(TUNNEL) & tun_id goto LOCAL_OUT_TABLE
- in_port(TUNNEL) drop (catch-all drop rule)
-
- TUNNEL_OUT_TABLE
- match action
- tun_id & dl_dst out tunnel port & goto LOCAL_OUT_TABLE
- (unicast or broadcast)
- tun_id goto LOCAL_OUT_TABLE (catch-all rule)
-
- LOCAL_OUT_TABLE
- tun_id & dl_dst output(VM) (unicast or broadcast)
- tun_id drop (catch-all drop rule)
-
- NOTE:
- adding/deleting flow entries should be done carefully in certain order
- such that packet in event should not be triggered.
- """
- _CONTEXTS = {
- 'network': network.Network,
- 'dpset': dpset.DPSet,
- 'tunnels': tunnels.Tunnels,
- }
-
- DEFAULT_COOKIE = 0 # cookie isn't used. Just set 0
-
- # Tables
- SRC_TABLE = 0
- TUNNEL_OUT_TABLE = 1
- LOCAL_OUT_TABLE = 2
- FLOW_TABLES = [SRC_TABLE, TUNNEL_OUT_TABLE, LOCAL_OUT_TABLE]
-
- # Priorities. The only inequality is important.
- # '/ 2' is used just for easy looking instead of '- 1'.
- # 0x7ffff vs 0x4000
- TABLE_DEFAULT_PRPIRITY = 32768 # = ofproto.OFP_DEFAULT_PRIORITY
-
- # SRC_TABLE for VM-port
- SRC_PRI_MAC = TABLE_DEFAULT_PRPIRITY
- SRC_PRI_DROP = TABLE_DEFAULT_PRPIRITY / 2
- # SRC_TABLE for TUNNEL-port
- SRC_PRI_TUNNEL_PASS = TABLE_DEFAULT_PRPIRITY
- SRC_PRI_TUNNEL_DROP = TABLE_DEFAULT_PRPIRITY / 2
-
- # TUNNEL_OUT_TABLE
- TUNNEL_OUT_PRI_MAC = TABLE_DEFAULT_PRPIRITY
- TUNNEL_OUT_PRI_BROADCAST = TABLE_DEFAULT_PRPIRITY / 2
- TUNNEL_OUT_PRI_PASS = TABLE_DEFAULT_PRPIRITY / 4
- TUNNEL_OUT_PRI_DROP = TABLE_DEFAULT_PRPIRITY / 8
-
- # LOCAL_OUT_TABLE
- LOCAL_OUT_PRI_MAC = TABLE_DEFAULT_PRPIRITY
- LOCAL_OUT_PRI_BROADCAST = TABLE_DEFAULT_PRPIRITY / 2
- LOCAL_OUT_PRI_DROP = TABLE_DEFAULT_PRPIRITY / 4
-
- def __init__(self, *args, **kwargs):
- super(GRETunnel, self).__init__(*args, **kwargs)
- self.nw = kwargs['network']
- self.dpset = kwargs['dpset']
- self.tunnels = kwargs['tunnels']
-
- self.port_set = PortSet(**kwargs)
- map(lambda ev_cls: self.port_set.register_observer(ev_cls, self.name),
- [dpset.EventDP, PortSet.EventTunnelKeyDel, PortSet.EventVMPort,
- PortSet.EventTunnelPort, ofp_event.EventOFPPacketIn])
-
- def start(self):
- super(GRETunnel, self).start()
- self.port_set.start()
-
- def stop(self):
- app_mgr = app_manager.get_instance()
- app_mgr.uninstantiate(self.port_set)
- self.port_set = None
- super(GRETunnel, self).stop()
-
- # TODO: track active vm/tunnel ports
-
- @handler.set_ev_handler(dpset.EventDP)
- def dp_handler(self, ev):
- if not ev.enter:
- return
-
- # enable nicira extension
- # TODO:XXX error handling
- dp = ev.dp
- ofproto = dp.ofproto
-
- dp.send_nxt_set_flow_format(ofproto.NXFF_NXM)
- flow_mod_table_id = dp.ofproto_parser.NXTFlowModTableId(dp, 1)
- dp.send_msg(flow_mod_table_id)
- dp.send_barrier()
-
- # delete all flows in all tables
- # current controller.handlers takes care of only table = 0
- for table in self.FLOW_TABLES:
- rule = cls_rule()
- self.send_flow_del(dp, rule, table, ofproto.OFPFC_DELETE,
- None, None)
- dp.send_barrier()
-
- @staticmethod
- def _make_command(table, command):
- return table << 8 | command
-
- def send_flow_mod(self, dp, rule, table, command, priority, actions):
- command = self._make_command(table, command)
- dp.send_flow_mod(rule=rule, cookie=self.DEFAULT_COOKIE,
- command=command, idle_timeout=0,
- hard_timeout=0, priority=priority, actions=actions)
-
- def send_flow_del(self, dp, rule, table, command, priority, out_port):
- command = self._make_command(table, command)
- dp.send_flow_mod(rule=rule, cookie=self.DEFAULT_COOKIE,
- command=command, idle_timeout=0,
- hard_timeout=0, priority=priority, out_port=out_port)
-
- def _list_tunnel_port(self, dp, remote_dpids):
- dpid = dp.id
- tunnel_ports = []
- for other_dpid in remote_dpids:
- if other_dpid == dpid:
- continue
- other_dp = self.dpset.get(other_dpid)
- if other_dp is None:
- continue
- try:
- port_no = self.tunnels.get_port(dpid, other_dpid)
- except ryu_exc.PortNotFound:
- continue
- if not self._link_is_up(dp, port_no):
- continue
- tunnel_ports.append(port_no)
-
- return tunnel_ports
-
- def _link_is_up(self, dp, port_no):
- return _link_is_up(self.dpset, dp, port_no)
-
- def _port_is_active(self, network_id, dp, nw_port):
- return (nw_port.network_id == network_id and
- nw_port.mac_address is not None and
- self._link_is_up(dp, nw_port.port_no))
-
- def _tunnel_port_with_mac(self, remote_dp, dpid, network_id, port_no,
- mac_address):
- tunnel_ports = []
- ports = self.nw.get_ports_with_mac(network_id, mac_address).copy()
- ports.discard((dpid, port_no))
- assert len(ports) <= 1
- for port in ports:
- try:
- tunnel_port_no = self.tunnels.get_port(remote_dp.id, port.dpid)
- except ryu_exc.PortNotFound:
- pass
- else:
- if self._link_is_up(remote_dp, tunnel_port_no):
- tunnel_ports.append(tunnel_port_no)
-
- assert len(tunnel_ports) <= 1
- return tunnel_ports
-
- def _vm_port_add(self, ev):
- dpid = ev.dpid
- dp = self.dpset.get(dpid)
- assert dp is not None
- ofproto = dp.ofproto
- ofproto_parser = dp.ofproto_parser
- mac_address = ev.mac_address
- network_id = ev.network_id
- tunnel_key = ev.tunnel_key
- remote_dpids = self.nw.get_dpids(network_id)
- remote_dpids.remove(dpid)
-
- # LOCAL_OUT_TABLE: unicast
- # live-migration: there can be two ports with same mac_address
- ports = self.nw.get_ports(dpid, network_id, mac_address)
- assert ev.port_no in [port.port_no for port in ports]
- rule = cls_rule(tun_id=tunnel_key, dl_dst=mac_address)
- actions = [ofproto_parser.OFPActionOutput(port.port_no)
- for port in ports if self._link_is_up(dp, port.port_no)]
- self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE, ofproto.OFPFC_ADD,
- self.LOCAL_OUT_PRI_MAC, actions)
-
- # LOCAL_OUT_TABLE: broad cast
- rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
- actions = []
- for port in self.nw.get_ports(dpid):
- if not self._port_is_active(network_id, dp, port):
- continue
- actions.append(ofproto_parser.OFPActionOutput(port.port_no))
-
- first_instance = (len(actions) == 1)
- assert actions
- if first_instance:
- command = ofproto.OFPFC_ADD
- else:
- command = ofproto.OFPFC_MODIFY_STRICT
- self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE, command,
- self.LOCAL_OUT_PRI_BROADCAST, actions)
-
- # LOCAL_OUT_TABLE: multicast TODO:XXX
-
- # LOCAL_OUT_TABLE: catch-all drop
- if first_instance:
- rule = cls_rule(tun_id=tunnel_key)
- self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE,
- ofproto.OFPFC_ADD, self.LOCAL_OUT_PRI_DROP, [])
-
- # TUNNEL_OUT_TABLE: unicast
- mac_to_ports = collections.defaultdict(set)
- for remote_dpid in remote_dpids:
- remote_dp = self.dpset.get(remote_dpid)
- if remote_dp is None:
- continue
- try:
- tunnel_port_no = self.tunnels.get_port(dpid, remote_dpid)
- except ryu_exc.PortNotFound:
- continue
- if not self._link_is_up(dp, tunnel_port_no):
- continue
-
- for port in self.nw.get_ports(remote_dpid):
- if not self._port_is_active(network_id, remote_dp, port):
- continue
- # TUNNEL_OUT_TABLE: unicast
- # live-migration: there can be more than one tunnel-ports that
- # have a given mac address
- mac_to_ports[port.mac_address].add(tunnel_port_no)
-
- if first_instance:
- # SRC_TABLE: TUNNEL-port: resubmit to LOAL_OUT_TABLE
- rule = cls_rule(in_port=tunnel_port_no, tun_id=tunnel_key)
- resubmit_table = ofproto_parser.NXActionResubmitTable(
- in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
- actions = [resubmit_table]
- self.send_flow_mod(dp, rule, self.SRC_TABLE,
- ofproto.OFPFC_ADD, self.SRC_PRI_TUNNEL_PASS,
- actions)
-
- # TUNNEL_OUT_TABLE: unicast
- for remote_mac_address, tunnel_ports in mac_to_ports.items():
- rule = cls_rule(tun_id=tunnel_key, dl_dst=remote_mac_address)
- outputs = [ofproto_parser.OFPActionOutput(tunnel_port_no)
- for tunnel_port_no in tunnel_ports]
- resubmit_table = ofproto_parser.NXActionResubmitTable(
- in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
- actions = outputs + [resubmit_table]
- self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
- ofproto.OFPFC_ADD, self.TUNNEL_OUT_PRI_MAC,
- actions)
-
- if first_instance:
- # TUNNEL_OUT_TABLE: catch-all(resubmit to LOCAL_OUT_TABLE)
- rule = cls_rule(tun_id=tunnel_key)
- resubmit_table = ofproto_parser.NXActionResubmitTable(
- in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
- actions = [resubmit_table]
- self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
- ofproto.OFPFC_ADD,
- self.TUNNEL_OUT_PRI_PASS, actions)
-
- # TUNNEL_OUT_TABLE: broadcast
- rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
- actions = [ofproto_parser.OFPActionOutput(tunnel_port_no)
- for tunnel_port_no
- in self._list_tunnel_port(dp, remote_dpids)]
- resubmit_table = ofproto_parser.NXActionResubmitTable(
- in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
- actions.append(resubmit_table)
- self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
- ofproto.OFPFC_ADD,
- self.TUNNEL_OUT_PRI_BROADCAST, actions)
-
- # TUNNEL_OUT_TABLE: multicast TODO:XXX
-
- # SRC_TABLE: VM-port unicast
- dp.send_barrier()
- rule = cls_rule(in_port=ev.port_no, dl_src=mac_address)
- set_tunnel = ofproto_parser.NXActionSetTunnel(tunnel_key)
- resubmit_table = ofproto_parser.NXActionResubmitTable(
- in_port=ofproto.OFPP_IN_PORT, table=self.TUNNEL_OUT_TABLE)
- actions = [set_tunnel, resubmit_table]
- self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_ADD,
- self.SRC_PRI_MAC, actions)
-
- # SRC_TABLE: VM-port catch-call drop
- rule = cls_rule(in_port=ev.port_no)
- self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_ADD,
- self.SRC_PRI_DROP, [])
-
- # remote dp
- for remote_dpid in remote_dpids:
- remote_dp = self.dpset.get(remote_dpid)
- if remote_dp is None:
- continue
- try:
- tunnel_port_no = self.tunnels.get_port(remote_dpid, dpid)
- except ryu_exc.PortNotFound:
- continue
- if not self._link_is_up(remote_dp, tunnel_port_no):
- continue
-
- remote_ofproto = remote_dp.ofproto
- remote_ofproto_parser = remote_dp.ofproto_parser
-
- # TUNNEL_OUT_TABLE: unicast
- # live-migration: there can be another port that has
- # same mac address
- tunnel_ports = self._tunnel_port_with_mac(remote_dp, dpid,
- network_id, ev.port_no,
- mac_address)
- tunnel_ports.append(tunnel_port_no)
-
- rule = cls_rule(tun_id=ev.tunnel_key, dl_dst=mac_address)
- outputs = [remote_ofproto_parser.OFPActionOutput(port_no)
- for port_no in tunnel_ports]
- resubmit_table = remote_ofproto_parser.NXActionResubmitTable(
- in_port=remote_ofproto.OFPP_IN_PORT,
- table=self.LOCAL_OUT_TABLE)
- actions = outputs + [resubmit_table]
- self.send_flow_mod(remote_dp, rule, self.TUNNEL_OUT_TABLE,
- remote_ofproto.OFPFC_ADD,
- self.TUNNEL_OUT_PRI_MAC, actions)
-
- if not first_instance:
- continue
-
- # SRC_TABLE: TUNNEL-port
- rule = cls_rule(in_port=tunnel_port_no, tun_id=ev.tunnel_key)
- resubmit_table = remote_ofproto_parser.NXActionResubmitTable(
- in_port=remote_ofproto.OFPP_IN_PORT,
- table=self.LOCAL_OUT_TABLE)
- actions = [resubmit_table]
- self.send_flow_mod(remote_dp, rule, self.SRC_TABLE,
- remote_ofproto.OFPFC_ADD,
- self.SRC_PRI_TUNNEL_PASS, actions)
-
- # TUNNEL_OUT_TABLE: broadcast
- rule = cls_rule(tun_id=ev.tunnel_key, dl_dst=mac.BROADCAST)
- tunnel_ports = self._list_tunnel_port(remote_dp, remote_dpids)
- if tunnel_port_no not in tunnel_ports:
- tunnel_ports.append(tunnel_port_no)
- actions = [remote_ofproto_parser.OFPActionOutput(port_no)
- for port_no in tunnel_ports]
- if len(actions) == 1:
- command = remote_dp.ofproto.OFPFC_ADD
- else:
- command = remote_dp.ofproto.OFPFC_MODIFY_STRICT
- resubmit_table = remote_ofproto_parser.NXActionResubmitTable(
- in_port=remote_ofproto.OFPP_IN_PORT,
- table=self.LOCAL_OUT_TABLE)
- actions.append(resubmit_table)
- self.send_flow_mod(remote_dp, rule, self.TUNNEL_OUT_TABLE,
- command, self.TUNNEL_OUT_PRI_BROADCAST, actions)
-
- # TUNNEL_OUT_TABLE: multicast TODO:XXX
-
- def _vm_port_del(self, ev):
- dpid = ev.dpid
- dp = self.dpset.get(dpid)
- assert dp is not None
- ofproto = dp.ofproto
- ofproto_parser = dp.ofproto_parser
- mac_address = ev.mac_address
- network_id = ev.network_id
- tunnel_key = ev.tunnel_key
-
- local_ports = []
- for port in self.nw.get_ports(dpid):
- if port.port_no == ev.port_no:
- continue
- if not self._port_is_active(network_id, dp, port):
- continue
- local_ports.append(port.port_no)
-
- last_instance = not local_ports
-
- # SRC_TABLE: VM-port unicast and catch-call
- rule = cls_rule(in_port=ev.port_no)
- self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_DELETE,
- ofproto.OFP_DEFAULT_PRIORITY,
- []) # priority is ignored
-
- if last_instance:
- # SRC_TABLE: TUNNEL-port: all tunnel matching
- rule = cls_rule(tun_id=tunnel_key)
- self.send_flow_mod(dp, rule, self.SRC_TABLE,
- ofproto.OFPFC_DELETE,
- ofproto.OFP_DEFAULT_PRIORITY,
- []) # priority is ignored
-
- # TUNNEL_OUT_TABLE: (tun_id & dl_dst) and tun_id
- rule = cls_rule(tun_id=tunnel_key)
- self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
- ofproto.OFPFC_DELETE,
- ofproto.OFP_DEFAULT_PRIORITY,
- []) # priority is ignored
-
- # LOCAL_OUT: tun_id catch-all drop rule
- rule = cls_rule(tun_id=tunnel_key)
- self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE,
- ofproto.OFPFC_DELETE,
- ofproto.OFP_DEFAULT_PRIORITY,
- []) # priority is ignored
- else:
- # LOCAL_OUT_TABLE: unicast
- # live-migration: there can be two ports with same mac_address
- ports = self.nw.get_ports(dpid, network_id, mac_address)
- port_nos = [port.port_no for port in ports
- if (port.port_no != ev.port_no and
- self._link_is_up(dp, port.port_no))]
- rule = cls_rule(tun_id=tunnel_key, dl_dst=mac_address)
- if port_nos:
- assert len(ports) == 1
- actions = [ofproto_parser.OFPActionOutput(port_no)
- for port_no in port_nos]
- self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE,
- ofproto.OFPFC_MODIFY_STRICT,
- self.LOCAL_OUT_PRI_MAC, actions)
- else:
- self.send_flow_del(dp, rule, self.LOCAL_OUT_TABLE,
- ofproto.OFPFC_DELETE_STRICT,
- self.LOCAL_OUT_PRI_MAC, ev.port_no)
-
- # LOCAL_OUT_TABLE: broadcast
- rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
- actions = [ofproto_parser.OFPActionOutput(port_no)
- for port_no in local_ports]
- self.send_flow_mod(dp, rule, self.LOCAL_OUT_TABLE,
- ofproto.OFPFC_MODIFY_STRICT,
- self.LOCAL_OUT_PRI_BROADCAST, actions)
-
- # LOCAL_OUT_TABLE: multicast TODO:XXX
-
- # remote dp
- remote_dpids = self.nw.get_dpids(ev.network_id)
- if dpid in remote_dpids:
- remote_dpids.remove(dpid)
- for remote_dpid in remote_dpids:
- remote_dp = self.dpset.get(remote_dpid)
- if remote_dp is None:
- continue
- try:
- tunnel_port_no = self.tunnels.get_port(remote_dpid, dpid)
- except ryu_exc.PortNotFound:
- continue
- if not self._link_is_up(remote_dp, tunnel_port_no):
- continue
-
- remote_ofproto = remote_dp.ofproto
- remote_ofproto_parser = remote_dp.ofproto_parser
-
- if last_instance:
- # SRC_TABLE: TUNNEL-port
- rule = cls_rule(in_port=tunnel_port_no, tun_id=tunnel_key)
- self.send_flow_del(remote_dp, rule, self.SRC_TABLE,
- remote_ofproto.OFPFC_DELETE_STRICT,
- self.SRC_PRI_TUNNEL_PASS, None)
-
- # SRC_TABLE: TUNNEL-port catch-call drop rule
- rule = cls_rule(in_port=tunnel_port_no)
- self.send_flow_del(remote_dp, rule, self.SRC_TABLE,
- remote_ofproto.OFPFC_DELETE_STRICT,
- self.SRC_PRI_TUNNEL_DROP, None)
-
- # TUNNEL_OUT_TABLE: broadcast
- # tunnel_ports.remove(tunnel_port_no)
- rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
- tunnel_ports = self._list_tunnel_port(remote_dp,
- remote_dpids)
- assert tunnel_port_no not in tunnel_ports
- actions = [remote_ofproto_parser.OFPActionOutput(port_no)
- for port_no in tunnel_ports]
- if not actions:
- command = remote_dp.ofproto.OFPFC_DELETE_STRICT
- else:
- command = remote_dp.ofproto.OFPFC_MODIFY_STRICT
- resubmit_table = \
- remote_ofproto_parser.NXActionResubmitTable(
- in_port=remote_ofproto.OFPP_IN_PORT,
- table=self.LOCAL_OUT_TABLE)
- actions.append(resubmit_table)
- self.send_flow_mod(remote_dp, rule, self.TUNNEL_OUT_TABLE,
- command, self.TUNNEL_OUT_PRI_BROADCAST,
- actions)
-
- # TUNNEL_OUT_TABLE: unicast
- # live-migration: there can be more than one (dpid, port_no)
- # with a given mac address
- tunnel_ports = self._tunnel_port_with_mac(remote_dp, dpid,
- network_id, ev.port_no,
- mac_address)
- rule = cls_rule(tun_id=tunnel_key, dl_dst=mac_address)
- if tunnel_ports:
- outputs = [remote_ofproto_parser.OFPActionOutput(port_no)
- for port_no in tunnel_ports]
- resubmit_table = remote_ofproto_parser.NXActionResubmitTable(
- in_port=remote_ofproto.OFPP_IN_PORT,
- table=self.LOCAL_OUT_TABLE)
- actions = outputs + [resubmit_table]
- self.send_flow_mod(remote_dp, rule, self.TUNNEL_OUT_TABLE,
- remote_ofproto.OFPFC_ADD,
- self.TUNNEL_OUT_PRI_MAC, actions)
- else:
- self.send_flow_del(remote_dp, rule, self.TUNNEL_OUT_TABLE,
- remote_ofproto.OFPFC_DELETE_STRICT,
- self.TUNNEL_OUT_PRI_MAC, tunnel_port_no)
-
- # TODO:XXX multicast
-
- def _get_vm_ports(self, dpid):
- ports = collections.defaultdict(list)
- for port in self.nw.get_ports(dpid):
- if port.network_id in RESERVED_NETWORK_IDS:
- continue
- ports[port.network_id].append(port)
- return ports
-
- def _tunnel_port_add(self, ev):
- dpid = ev.dpid
- dp = self.dpset.get(dpid)
- ofproto = dp.ofproto
- ofproto_parser = dp.ofproto_parser
- remote_dpid = ev.remote_dpid
-
- local_ports = self._get_vm_ports(dpid)
- remote_ports = self._get_vm_ports(remote_dpid)
-
- # SRC_TABLE: TUNNEL-port catch-call drop rule
- # ingress flow from this tunnel port: remote -> tunnel port
- # drop if unknown tunnel_key
- rule = cls_rule(in_port=ev.port_no)
- self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_ADD,
- self.SRC_PRI_TUNNEL_DROP, [])
-
- # SRC_TABLE: TUNNEL-port: pass if known tunnel_key
- for network_id in local_ports:
- try:
- tunnel_key = self.tunnels.get_key(network_id)
- except tunnels.TunnelKeyNotFound:
- continue
- if network_id not in remote_ports:
- continue
-
- rule = cls_rule(in_port=ev.port_no, tun_id=tunnel_key)
- resubmit_table = ofproto_parser.NXActionResubmitTable(
- in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
- actions = [resubmit_table]
- self.send_flow_mod(dp, rule, self.SRC_TABLE, ofproto.OFPFC_ADD,
- self.SRC_PRI_TUNNEL_PASS, actions)
-
- # egress flow into this tunnel port: vm port -> tunnel port -> remote
- for network_id in local_ports:
- try:
- tunnel_key = self.tunnels.get_key(network_id)
- except tunnels.TunnelKeyNotFound:
- continue
- ports = remote_ports.get(network_id)
- if ports is None:
- continue
-
- # TUNNEL_OUT_TABLE: unicast
- for port in ports:
- if port.mac_address is None:
- continue
- rule = cls_rule(tun_id=tunnel_key, dl_dst=port.mac_address)
- output = ofproto_parser.OFPActionOutput(ev.port_no)
- resubmit_table = ofproto_parser.NXActionResubmitTable(
- in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
- actions = [output, resubmit_table]
- self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
- ofproto.OFPFC_ADD, self.TUNNEL_OUT_PRI_MAC,
- actions)
-
- # TUNNEL_OUT_TABLE: broadcast
- remote_dpids = self.nw.get_dpids(network_id)
- remote_dpids.remove(dpid)
-
- rule = cls_rule(tun_id=tunnel_key, dl_dst=mac.BROADCAST)
- tunnel_ports = self._list_tunnel_port(dp, remote_dpids)
- if ev.port_no not in tunnel_ports:
- tunnel_ports.append(ev.port_no)
- actions = [ofproto_parser.OFPActionOutput(port_no)
- for port_no in tunnel_ports]
- resubmit_table = ofproto_parser.NXActionResubmitTable(
- in_port=ofproto.OFPP_IN_PORT, table=self.LOCAL_OUT_TABLE)
- actions.append(resubmit_table)
- if len(tunnel_ports) == 1:
- command = ofproto.OFPFC_ADD
- else:
- command = ofproto.OFPFC_MODIFY_STRICT
- self.send_flow_mod(dp, rule, self.TUNNEL_OUT_TABLE,
- command, self.TUNNEL_OUT_PRI_BROADCAST, actions)
-
- # TUNNEL_OUT_TABLE: multicast TODO:XXX
-
- def _tunnel_port_del(self, ev):
- # almost nothing to do because all flow related to this tunnel port
- # should be handled by self._vm_port_del() as tunnel port deletion
- # follows vm port deletion.
- # the tunnel port is deleted if and only if no instance of same
- # tenants resides in both nodes of tunnel end points.
- self.logger.debug('tunnel_port_del %s', ev)
- dp = self.dpset.get(ev.dpid)
-
- # SRC_TABLE: TUNNEL-port catch-all drop rule
- rule = cls_rule(in_port=ev.port_no)
- self.send_flow_mod(dp, rule, self.SRC_TABLE,
- dp.ofproto.OFPFC_DELETE_STRICT,
- self.SRC_PRI_TUNNEL_DROP, [])
-
- @handler.set_ev_handler(PortSet.EventTunnelKeyDel)
- def tunnel_key_del_handler(self, ev):
- self.logger.debug('tunnel_key_del ev %s', ev)
-
- @handler.set_ev_handler(PortSet.EventVMPort)
- def vm_port_handler(self, ev):
- self.logger.debug('vm_port ev %s', ev)
- if ev.add_del:
- self._vm_port_add(ev)
- else:
- self._vm_port_del(ev)
-
- @handler.set_ev_handler(PortSet.EventTunnelPort)
- def tunnel_port_handler(self, ev):
- self.logger.debug('tunnel_port ev %s', ev)
- if ev.add_del:
- self._tunnel_port_add(ev)
- else:
- self._tunnel_port_del(ev)
-
- @handler.set_ev_handler(ofp_event.EventOFPPacketIn)
- def packet_in_handler(self, ev):
- # for debug
- msg = ev.msg
- self.logger.debug('packet in ev %s msg %s', ev, ev.msg)
- if msg.buffer_id != msg.datapath.ofproto.OFP_NO_BUFFER:
- msg.datapath.send_packet_out(msg.buffer_id, msg.in_port, [])
diff --git a/ryu/app/quantum_adapter.py b/ryu/app/quantum_adapter.py
deleted file mode 100644
index 67aa6522..00000000
--- a/ryu/app/quantum_adapter.py
+++ /dev/null
@@ -1,446 +0,0 @@
-# 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.
-
-"""
-Listen OpenFlow port status change notifications from switches. Consult
-ovsdb to retrieve the corresponding port uuid. Notify relevant parties,
-including quantum (via Ryu plug-in) and Ryu applications. (via Ryu Events)
-"""
-
-import traceback
-
-try:
- from neutronclient import client as q_client
- from neutronclient.common import exceptions as q_exc
- from neutronclient.common.exceptions import (NeutronClientException as
- client_exc)
- from neutronclient.v2_0 import client as q_clientv2
-except ImportError:
- from quantumclient import client as q_client
- from quantumclient.common import exceptions as q_exc
- from quantumclient.common.exceptions import (QuantumClientException as
- client_exc)
- from quantumclient.v2_0 import client as q_clientv2
-
-from ryu.app import conf_switch_key as cs_key
-from ryu.app import rest_nw_id
-from ryu.base import app_manager
-from ryu.controller import (conf_switch,
- dpset,
- handler,
- network)
-from ryu import exception as ryu_exc
-from ryu.lib import dpid as dpid_lib
-from ryu.lib import mac as mac_lib
-from ryu.lib import quantum_ifaces
-from ryu.lib.ovs import bridge
-from ryu.lib.quantum_ifaces import QuantumIfaces
-
-
-def _get_auth_token(CONF, logger):
- httpclient = q_client.HTTPClient(
- username=CONF.neutron_admin_username,
- tenant_name=CONF.neutron_admin_tenant_name,
- password=CONF.neutron_admin_password,
- auth_url=CONF.neutron_admin_auth_url,
- timeout=CONF.neutron_url_timeout,
- auth_strategy=CONF.neutron_auth_strategy)
- try:
- httpclient.authenticate()
- except (q_exc.Unauthorized, q_exc.Forbidden, q_exc.EndpointNotFound) as e:
- logger.error("authentication failure: %s", e)
- return None
- # logger.debug("_get_auth_token: token=%s", httpclient.auth_token)
- return httpclient.auth_token
-
-
-def _get_quantum_client(CONF, token):
- if token:
- my_client = q_clientv2.Client(
- endpoint_url=CONF.neutron_url,
- token=token, timeout=CONF.neutron_url_timeout)
- else:
- my_client = q_clientv2.Client(
- endpoint_url=CONF.neutron_url,
- auth_strategy=None, timeout=CONF.neutron_url_timeout)
- return my_client
-
-
-class OVSPort(object):
- PORT_ERROR = -1
- PORT_UNKNOWN = 0
- PORT_GATEWAY = 1
- PORT_VETH_GATEWAY = 2
- PORT_GUEST = 3
- PORT_TUNNEL = 4
-
- # extra-ids: 'attached-mac', 'iface-id', 'iface-status', 'vm-uuid'
- def __init__(self, ofport, port_name):
- super(OVSPort, self).__init__()
- self.ofport = ofport
- self.name = port_name
- self.type = None
- self.ext_ids = {}
- self.options = {}
-
- def update(self, port):
- self.__dict__.update((key, port[key]) for key
- in ['name', 'ofport', 'type']
- if key in port)
- if 'external_ids' in port:
- self.ext_ids = dict(port['external_ids'])
- if 'options' in port:
- self.options = dict(port['options'])
-
- def get_port_type(self):
- if not isinstance(self.ofport, int):
- return self.PORT_ERROR
- if self.type == 'internal' and 'iface-id' in self.ext_ids:
- return self.PORT_GATEWAY
- if self.type == '' and 'iface-id' in self.ext_ids:
- return self.PORT_VETH_GATEWAY
- if (self.type == 'gre' and 'local_ip' in self.options and
- 'remote_ip' in self.options):
- return self.PORT_TUNNEL
- if self.type == '' and 'vm-uuid' in self.ext_ids:
- return self.PORT_GUEST
- return self.PORT_UNKNOWN
-
- def __str__(self):
- return "type=%s ofport=%s name=%s, ext_ids=%s options=%s" % (
- self.type, self.ofport, self.name, self.ext_ids, self.options)
-
- def __eq__(self, other):
- return (other is not None and
- self.ofport == other.ofport and
- self.type == other.type and
- self.ext_ids == other.ext_ids and
- self.options == other.options)
-
-
-class OVSSwitch(object):
- def __init__(self, CONF, dpid, nw, ifaces, logger):
- # TODO: clean up
- self.CONF = CONF
- self.dpid = dpid
- self.network_api = nw
- self.ifaces = ifaces
- self.logger = logger
- self._q_api = None # lazy initialization
- self.ctrl_addr = self.CONF.neutron_controller_addr
- if not self.ctrl_addr:
- raise ValueError('option neutron_controler_addr must be speicfied')
-
- self.ovsdb_addr = None
- self.tunnel_ip = None
-
- self.ovs_bridge = None
- self.ports = {} # port_no -> OVSPort
-
- super(OVSSwitch, self).__init__()
-
- @property
- def q_api(self):
- if self._q_api is None:
- token = None
- if self.CONF.neutron_auth_strategy:
- token = _get_auth_token(self.CONF, self.logger)
- self._q_api = _get_quantum_client(self.CONF, token)
- return self._q_api
-
- def set_ovsdb_addr(self, dpid, ovsdb_addr):
- # easy check if the address format valid
- self.logger.debug('set_ovsdb_addr dpid %s ovsdb_addr %s',
- dpid_lib.dpid_to_str(dpid), ovsdb_addr)
- _proto, _host, _port = ovsdb_addr.split(':')
-
- old_address = self.ovsdb_addr
- if old_address == ovsdb_addr:
- return
- if ovsdb_addr is None:
- # TODO: clean up this ovs switch
- if self.ovs_bridge:
- self.ovs_bridge.del_controller()
- self.ovs_bridge = None
- return
- self.ovsdb_addr = ovsdb_addr
- if self.ovs_bridge is None:
- self.logger.debug('ovsdb: adding ports %s', self.ports)
- ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
- self.ovs_bridge = ovs_bridge
- ovs_bridge.init()
- # TODO: for multi-controller
- # not overwrite controllers, but append this controller
- ovs_bridge.set_controller([self.ctrl_addr])
- for port in self.ports.values():
- self.logger.debug('adding port %s', port)
- self.update_port(port.ofport, port.name, True)
-
- def _update_external_port(self, port, add=True):
- if add:
- self.network_api.update_port(rest_nw_id.NW_ID_EXTERNAL,
- self.dpid, port.ofport)
- else:
- self.network_api.remove_port(rest_nw_id.NW_ID_EXTERNAL,
- self.dpid, port.ofport)
-
- def _update_vif_port(self, port, add=True):
- # When ovs port is updated, the corresponding network id may or
- # may not exist because the order between the notification of
- # ovs port deletion via OVSDB protocol and the notification
- # network id/port deletion via REST from quantum plugin
- # isn't deterministic.
- self.logger.debug("_update_vif_port: %s %s", port, add)
- iface_id = port.ext_ids.get('iface-id')
- if iface_id is None:
- return
- try:
- network_id = self.ifaces.get_key(iface_id,
- QuantumIfaces.KEY_NETWORK_ID)
- except KeyError:
- return
-
- if not add:
- try:
- self.network_api.remove_port(network_id,
- self.dpid, port.ofport)
- except (network.NetworkNotFound, ryu_exc.PortNotFound) as e:
- self.logger.debug('remove_port %s', traceback.format_exc())
- ports = self.ifaces.get_key(iface_id, QuantumIfaces.KEY_PORTS)
- other_ovs_ports = None
- for p in ports:
- dpid = p.get(QuantumIfaces.SUBKEY_DATAPATH_ID)
- if dpid is None:
- continue
- if dpid != self.dpid:
- continue
-
- other_ovs_ports = self.ifaces.del_key(iface_id,
- QuantumIfaces.KEY_PORTS,
- p)
- if other_ovs_ports:
- # When live-migration, one of the two OVS ports is deleted.
- return
-
- port_data = {
- 'status': 'DOWN'
- }
- body = {'port': port_data}
- # self.logger.debug("port-body = %s", body)
-
- try:
- self.q_api.update_port(port.ext_ids['iface-id'], body)
- except (q_exc.ConnectionFailed, client_exc) as e:
- self.logger.error("quantum update port failed: %s", e)
- # TODO: When authentication failure occurred,
- # it should get auth token again
- return
-
- # update {network, port, mac}
- try:
- self.network_api.update_network(network_id)
- self.network_api.update_port(network_id, self.dpid, port.ofport)
- mac = port.ext_ids.get('attached-mac')
- if mac:
- self.network_api.update_mac(network_id, self.dpid, port.ofport,
- mac_lib.haddr_to_bin(mac))
- except (network.NetworkNotFound, ryu_exc.PortNotFound) as e:
- self.logger.debug('update network/port/mac %s',
- traceback.format_exc())
-
- def update_port(self, port_no, port_name, add):
- self.logger.debug('update_port port_no %d %s %s', port_no, port_name,
- add)
- assert port_name is not None
- old_port = self.ports.get(port_no)
- if not add:
- new_port = None
- self.ports.pop(port_no, None)
- else:
- new_port = OVSPort(port_no, port_name)
- if self.ovs_bridge:
- port_cfg = self.ovs_bridge.get_quantum_ports(port_name)
- if port_cfg:
- if 'ofport' not in port_cfg or not port_cfg['ofport']:
- port_cfg['ofport'] = port_no
- elif port_cfg['ofport'] != port_no:
- self.logger.warn('inconsistent port_no: %d port_cfg '
- '%s', port_no, port_cfg)
- return
- if port_cfg['name'] != port_name:
- self.logger.warn('inconsistent port_name: %s '
- 'port_cfg %s', port_name, port_cfg)
- return
- new_port.update(port_cfg)
-
- self.ports[port_no] = new_port
- iface_id = new_port.ext_ids.get('iface-id')
- if iface_id:
- p = {QuantumIfaces.SUBKEY_DATAPATH_ID: self.dpid,
- QuantumIfaces.SUBKEY_OFPORT: port_no,
- QuantumIfaces.SUBKEY_NAME: port_name}
- self.ifaces.update_key(iface_id, QuantumIfaces.KEY_PORTS, p)
-
- if old_port == new_port:
- return
-
- if not new_port:
- port_type = old_port.get_port_type()
- if port_type == OVSPort.PORT_ERROR:
- return
- elif port_type == OVSPort.PORT_UNKNOWN:
- # self.logger.info("delete external port: %s", old_port)
- self._update_external_port(old_port, add=False)
- else:
- # self.logger.info("delete port: %s", old_port)
- if port_type != OVSPort.PORT_TUNNEL:
- self._update_vif_port(old_port, add=False)
- return
-
- if new_port.ofport == -1:
- return
- if not old_port or old_port.ofport == -1:
- port_type = new_port.get_port_type()
- if port_type == OVSPort.PORT_ERROR:
- return
- elif port_type == OVSPort.PORT_UNKNOWN:
- # self.logger.info("create external port: %s", new_port)
- self._update_external_port(new_port)
- else:
- # self.logger.info("create port: %s", new_port)
- if port_type != OVSPort.PORT_TUNNEL:
- self._update_vif_port(new_port)
- return
- if new_port.get_port_type() in (OVSPort.PORT_GUEST,
- OVSPort.PORT_GATEWAY,
- OVSPort.PORT_VETH_GATEWAY):
- # self.logger.info("update port: %s", new_port)
- self._update_vif_port(new_port)
-
-
-class QuantumAdapter(app_manager.RyuApp):
- _CONTEXTS = {
- 'conf_switch': conf_switch.ConfSwitchSet,
- 'network': network.Network,
- 'quantum_ifaces': quantum_ifaces.QuantumIfaces,
- }
-
- def __init__(self, *_args, **kwargs):
- super(QuantumAdapter, self).__init__()
-
- self.cs = kwargs['conf_switch']
- self.nw = kwargs['network']
- self.ifaces = kwargs['quantum_ifaces']
- self.dps = {}
-
- for network_id in rest_nw_id.RESERVED_NETWORK_IDS:
- if network_id == rest_nw_id.NW_ID_UNKNOWN:
- continue
- self.nw.update_network(network_id)
-
- def _get_ovs_switch(self, dpid, create=True):
- ovs_switch = self.dps.get(dpid)
- if not ovs_switch:
- if create:
- ovs_switch = OVSSwitch(self.CONF, dpid, self.nw, self.ifaces,
- self.logger)
- self.dps[dpid] = ovs_switch
- else:
- self.logger.debug('ovs switch %s is already known', dpid)
- return ovs_switch
-
- def _port_handler(self, dpid, port_no, port_name, add):
- ovs_switch = self._get_ovs_switch(dpid)
- if ovs_switch:
- ovs_switch.update_port(port_no, port_name, add)
- else:
- self.logger.warn('unknown ovs switch %s %s %s %s\n',
- dpid, port_no, port_name, add)
-
- @handler.set_ev_cls(dpset.EventDP)
- def dp_handler(self, ev):
- dpid = ev.dp.id
- ovs_switch = self._get_ovs_switch(dpid)
- if not ovs_switch:
- return
-
- if ev.enter:
- for port in ev.ports:
- ovs_switch.update_port(port.port_no, port.name, True)
- else:
- # When dp leaving, we don't delete ports because OF connection
- # can be disconnected for some reason.
- # TODO: configuration needed to tell that this dp is really
- # removed.
- self.dps.pop(dpid, None)
-
- @handler.set_ev_cls(dpset.EventPortAdd)
- def port_add_handler(self, ev):
- port = ev.port
- name = port.name.rstrip('\0')
- self._port_handler(ev.dp.id, port.port_no, name, True)
-
- @handler.set_ev_cls(dpset.EventPortDelete)
- def port_del_handler(self, ev):
- port = ev.port
- name = port.name.rstrip('\0')
- self._port_handler(ev.dp.id, port.port_no, name, False)
-
- def _conf_switch_set_ovsdb_addr(self, dpid, value):
- ovs_switch = self._get_ovs_switch(dpid)
- ovs_switch.set_ovsdb_addr(dpid, value)
-
- def _conf_switch_del_ovsdb_addr(self, dpid):
- ovs_switch = self._get_ovs_switch(dpid, False)
- if ovs_switch:
- ovs_switch.set_ovsdb_addr(dpid, None)
-
- @handler.set_ev_cls(conf_switch.EventConfSwitchSet)
- def conf_switch_set_handler(self, ev):
- self.logger.debug("conf_switch set: %s", ev)
- if ev.key == cs_key.OVSDB_ADDR:
- self._conf_switch_set_ovsdb_addr(ev.dpid, ev.value)
- else:
- self.logger.debug("unknown event: %s", ev)
-
- @handler.set_ev_cls(conf_switch.EventConfSwitchDel)
- def conf_switch_del_handler(self, ev):
- self.logger.debug("conf_switch del: %s", ev)
- if ev.key == cs_key.OVSDB_ADDR:
- self._conf_switch_del_ovsdb_addr(ev.dpid)
- else:
- self.logger.debug("unknown event: %s", ev)
-
- @handler.set_ev_cls(quantum_ifaces.EventQuantumIfaceSet)
- def quantum_iface_set_handler(self, ev):
- if ev.key != quantum_ifaces.QuantumIfaces.KEY_NETWORK_ID:
- # self.logger.debug("unknown key %s", ev.key)
- return
- iface_id = ev.iface_id
- try:
- ports = self.ifaces.get_key(iface_id, QuantumIfaces.KEY_PORTS)
- except KeyError:
- return
- for p in ports:
- try:
- dpid = p[QuantumIfaces.SUBKEY_DATAPATH_ID]
- ofport = p[QuantumIfaces.SUBKEY_OFPORT]
- port_name = p[QuantumIfaces.SUBKEY_NAME]
- except KeyError:
- continue
- ovs_switch = self._get_ovs_switch(dpid, False)
- if ovs_switch:
- ovs_switch.update_port(ofport, port_name, True)
diff --git a/ryu/app/rest.py b/ryu/app/rest.py
deleted file mode 100644
index fdd19cac..00000000
--- a/ryu/app/rest.py
+++ /dev/null
@@ -1,270 +0,0 @@
-# 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.
-
-"""
-This module provides a basic set of REST API.
-
-- Network registration
-- End-point port management
- - OpenFlow port number
- - MAC address (for anti-spoofing)
-
-Used by OpenStack Ryu plug-in.
-"""
-
-import json
-from webob import Response
-
-from ryu.app import wsgi as app_wsgi
-from ryu.app.wsgi import ControllerBase, WSGIApplication
-from ryu.base import app_manager
-from ryu.controller import network
-from ryu.exception import NetworkNotFound, NetworkAlreadyExist
-from ryu.exception import PortNotFound, PortAlreadyExist
-from ryu.lib import dpid as dpid_lib
-from ryu.lib import mac as mac_lib
-
-# TODO:XXX
-# define db interface and store those information into db
-
-# REST API
-
-# get the list of networks
-# GET /v1.0/networks/
-#
-# register a new network.
-# Fail if the network is already registered.
-# POST /v1.0/networks/{network-id}
-#
-# update a new network.
-# Success as nop even if the network is already registered.
-#
-# PUT /v1.0/networks/{network-id}
-#
-# remove a network
-# DELETE /v1.0/networks/{network-id}
-#
-# get the list of sets of dpid and port
-# GET /v1.0/networks/{network-id}/
-#
-# register a new set of dpid and port
-# Fail if the port is already registered.
-# POST /v1.0/networks/{network-id}/{dpid}_{port-id}
-#
-# update a new set of dpid and port
-# Success as nop even if same port already registered
-# PUT /v1.0/networks/{network-id}/{dpid}_{port-id}
-#
-# remove a set of dpid and port
-# DELETE /v1.0/networks/{network-id}/{dpid}_{port-id}
-#
-# get the list of mac addresses of dpid and port
-# GET /v1.0/networks/{network-id}/{dpid}_{port-id}/macs/
-#
-# register a new mac address for dpid and port
-# Fail if mac address is already registered or the mac address is used
-# for other ports of the same network-id
-# POST /v1.0/networks/{network-id}/{dpid}_{port-id}/macs/{mac}
-#
-# update a new mac address for dpid and port
-# Success as nop even if same mac address is already registered.
-# For now, changing mac address is not allows as it fails.
-# PUT /v1.0/networks/{network-id}/{dpid}_{port-id}/macs/{mac}
-#
-# For now DELETE /v1.0/networks/{network-id}/{dpid}_{port-id}/macs/{mac}
-# is not supported. mac address is released when port is deleted.
-#
-
-
-class NetworkController(ControllerBase):
- def __init__(self, req, link, data, **config):
- super(NetworkController, self).__init__(req, link, data, **config)
- self.nw = data
-
- def create(self, req, network_id, **_kwargs):
- try:
- self.nw.create_network(network_id)
- except NetworkAlreadyExist:
- return Response(status=409)
- else:
- return Response(status=200)
-
- def update(self, req, network_id, **_kwargs):
- self.nw.update_network(network_id)
- return Response(status=200)
-
- def lists(self, req, **_kwargs):
- body = json.dumps(self.nw.list_networks())
- return Response(content_type='application/json', body=body)
-
- def delete(self, req, network_id, **_kwargs):
- try:
- self.nw.remove_network(network_id)
- except NetworkNotFound:
- return Response(status=404)
-
- return Response(status=200)
-
-
-class PortController(ControllerBase):
- def __init__(self, req, link, data, **config):
- super(PortController, self).__init__(req, link, data, **config)
- self.nw = data
-
- def create(self, req, network_id, dpid, port_id, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- try:
- self.nw.create_port(network_id, dpid, port_id)
- except NetworkNotFound:
- return Response(status=404)
- except PortAlreadyExist:
- return Response(status=409)
-
- return Response(status=200)
-
- def update(self, req, network_id, dpid, port_id, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- try:
- self.nw.update_port(network_id, dpid, port_id)
- except NetworkNotFound:
- return Response(status=404)
-
- return Response(status=200)
-
- def lists(self, req, network_id, **_kwargs):
- try:
- body = json.dumps(self.nw.list_ports(network_id))
- except NetworkNotFound:
- return Response(status=404)
-
- return Response(content_type='application/json', body=body)
-
- def delete(self, req, network_id, dpid, port_id, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- try:
- self.nw.remove_port(network_id, dpid, port_id)
- except (NetworkNotFound, PortNotFound):
- return Response(status=404)
-
- return Response(status=200)
-
-
-class MacController(ControllerBase):
- def __init__(self, req, link, data, **config):
- super(MacController, self).__init__(req, link, data, **config)
- self.nw = data
-
- def create(self, _req, network_id, dpid, port_id, mac_addr, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- mac_addr = mac_lib.haddr_to_bin(mac_addr)
- try:
- self.nw.create_mac(network_id, dpid, port_id, mac_addr)
- except PortNotFound:
- return Response(status=404)
- except network.MacAddressAlreadyExist:
- return Response(status=409)
-
- return Response(status=200)
-
- def update(self, _req, network_id, dpid, port_id, mac_addr, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- mac_addr = mac_lib.haddr_to_bin(mac_addr)
- try:
- self.nw.update_mac(network_id, dpid, port_id, mac_addr)
- except PortNotFound:
- return Response(status=404)
-
- return Response(status=200)
-
- def lists(self, _req, network_id, dpid, port_id, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- try:
- body = json.dumps([mac_lib.haddr_to_str(mac_addr) for mac_addr in
- self.nw.list_mac(dpid, port_id)])
- except PortNotFound:
- return Response(status=404)
-
- return Response(content_type='application/json', body=body)
-
-
-class RestAPI(app_manager.RyuApp):
- _CONTEXTS = {
- 'network': network.Network,
- 'wsgi': WSGIApplication
- }
-
- def __init__(self, *args, **kwargs):
- super(RestAPI, self).__init__(*args, **kwargs)
- self.nw = kwargs['network']
- wsgi = kwargs['wsgi']
- mapper = wsgi.mapper
-
- wsgi.registory['NetworkController'] = self.nw
- route_name = 'networks'
- uri = '/v1.0/networks'
- mapper.connect(route_name, uri,
- controller=NetworkController, action='lists',
- conditions=dict(method=['GET', 'HEAD']))
-
- uri += '/{network_id}'
- s = mapper.submapper(controller=NetworkController)
- s.connect(route_name, uri, action='create',
- conditions=dict(method=['POST']))
- s.connect(route_name, uri, action='update',
- conditions=dict(method=['PUT']))
- s.connect(route_name, uri, action='delete',
- conditions=dict(method=['DELETE']))
-
- wsgi.registory['PortController'] = self.nw
- route_name = 'ports'
- mapper.connect(route_name, uri,
- controller=PortController, action='lists',
- conditions=dict(method=['GET']))
-
- uri += '/{dpid}_{port_id}'
- requirements = {'dpid': dpid_lib.DPID_PATTERN,
- 'port_id': app_wsgi.DIGIT_PATTERN}
- s = mapper.submapper(controller=PortController,
- requirements=requirements)
- s.connect(route_name, uri, action='create',
- conditions=dict(method=['POST']))
- s.connect(route_name, uri, action='update',
- conditions=dict(method=['PUT']))
- s.connect(route_name, uri, action='delete',
- conditions=dict(method=['DELETE']))
-
- wsgi.registory['MacController'] = self.nw
- route_name = 'macs'
- uri += '/macs'
- mapper.connect(route_name, uri,
- controller=MacController, action='lists',
- conditions=dict(method=['GET']),
- requirements=requirements)
-
- uri += '/{mac_addr}'
- requirements['mac_addr'] = mac_lib.HADDR_PATTERN
- s = mapper.submapper(controller=MacController,
- requirements=requirements)
- s.connect(route_name, uri, action='create',
- conditions=dict(method=['POST']))
- s.connect(route_name, uri, action='update',
- conditions=dict(method=['PUT']))
diff --git a/ryu/app/rest_nw_id.py b/ryu/app/rest_nw_id.py
deleted file mode 100644
index c31fd7b9..00000000
--- a/ryu/app/rest_nw_id.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
-# Copyright (C) 2011, 2012 Isaku Yamahata <yamahata at valinux co 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.
-
-
-NW_ID_EXTERNAL = '__NW_ID_EXTERNAL__'
-NW_ID_RESERVED = '__NW_ID_RESERVED__'
-NW_ID_VPORT_GRE = '__NW_ID_VPORT_GRE__'
-NW_ID_UNKNOWN = '__NW_ID_UNKNOWN__'
-
-RESERVED_NETWORK_IDS = (
- NW_ID_EXTERNAL,
- NW_ID_RESERVED,
- NW_ID_VPORT_GRE,
- NW_ID_UNKNOWN,
-)
-
-# tunnel type
-_TUNNEL_TYPE_TO_NETWORK_ID = {
- 'gre': NW_ID_VPORT_GRE,
-}
-
-
-def tunnel_type_to_network_id(tunnel_type):
- return _TUNNEL_TYPE_TO_NETWORK_ID[tunnel_type.lower()]
-
-# PORT_TYPE_VM = 'guestvm'
-# PORT_TYPE_GW = 'gateway'
-# PORT_TYPE_EXTERNAL = 'external'
diff --git a/ryu/app/rest_quantum.py b/ryu/app/rest_quantum.py
deleted file mode 100644
index 5bf14800..00000000
--- a/ryu/app/rest_quantum.py
+++ /dev/null
@@ -1,136 +0,0 @@
-# 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.
-
-"""
-This module provides a set of REST API dedicated to OpenStack Ryu plug-in.
-
-- Interface (uuid in ovsdb) registration
-- Maintain interface association to a network
-
-Used by OpenStack Ryu plug-in.
-"""
-
-import json
-from webob import Response
-
-from ryu.base import app_manager
-from ryu.app.wsgi import (ControllerBase,
- WSGIApplication)
-from ryu.lib import quantum_ifaces
-
-# REST API for openstack quantum
-# get the list of iface-ids
-# GET /v1.0/quantum/ports/
-#
-# register the iface_id
-# POST /v1.0/quantum/ports/{iface_id}
-#
-# unregister iface_id
-# DELETE /v1.0/quantum/ports/{iface_id}
-#
-# associate network_id with iface_id
-# GET /v1.0/quantum/ports/{iface_id}/network_id
-#
-# associate network_id with iface_id
-# POST /v1.0/quantum/ports/{iface_id}/network_id/{network_id}
-#
-# update network_id with iface_id
-# PUT /v1.0/quantum/ports/{iface_id}/network_id/{network_id}
-
-
-class QuantumController(ControllerBase):
- def __init__(self, req, link, data, **config):
- super(QuantumController, self).__init__(req, link, data, **config)
- self.ifaces = data
-
- def list_ifaces(self, _req, **_kwargs):
- body = json.dumps(list(self.ifaces.keys()))
- return Response(content_type='application/json', body=body)
-
- def delete_iface(self, _req, iface_id, **_kwargs):
- self.ifaces.unregister(iface_id)
- return Response(status=200)
-
- def list_keys(self, _req, iface_id, **_kwargs):
- try:
- keys = self.ifaces.list_keys(iface_id)
- except KeyError:
- return Response(status=404)
- body = json.dumps(keys)
- return Response(content_type='application/json', body=body)
-
- def get_key(self, _req, iface_id, key, **_kwargs):
- try:
- value = self.ifaces.get_key(iface_id, key)
- except KeyError:
- return Response(status=404)
- body = json.dumps(value)
- return Response(content_type='application/json', body=body)
-
- def create_value(self, _req, iface_id, key, value, **_kwargs):
- try:
- self.ifaces.set_key(iface_id, key, value)
- except ValueError:
- return Response(status=404)
- return Response(status=200)
-
- def update_value(self, _req, iface_id, key, value, **_kwargs):
- try:
- self.ifaces.update_key(iface_id, key, value)
- except ValueError:
- return Response(status=404)
- return Response(status=200)
-
-
-class QuantumIfaceAPI(app_manager.RyuApp):
- _CONTEXTS = {
- 'quantum_ifaces': quantum_ifaces.QuantumIfaces,
- 'wsgi': WSGIApplication,
- }
-
- def __init__(self, *args, **kwargs):
- super(QuantumIfaceAPI, self).__init__(*args, **kwargs)
- self.ifaces = kwargs['quantum_ifaces']
- wsgi = kwargs['wsgi']
- mapper = wsgi.mapper
-
- controller = QuantumController
- wsgi.registory[controller.__name__] = self.ifaces
- route_name = 'quantum_ifaces'
- uri = '/v1.0/quantum'
-
- ports_uri = uri + '/ports'
- s = mapper.submapper(controller=controller)
- s.connect(route_name, ports_uri, action='list_ifaces',
- conditions=dict(method=['GET', 'HEAD']))
-
- iface_uri = ports_uri + '/{iface_id}'
- s.connect(route_name, iface_uri, action='delete_iface',
- conditions=dict(method=['DELETE']))
-
- keys_uri = iface_uri + '/keys'
- s.connect(route_name, keys_uri, action='list_keys',
- conditions=dict(method=['GET', 'HEAD']))
-
- key_uri = keys_uri + '/{key}'
- s.connect(route_name, key_uri, action='get_key',
- conditions=dict(method=['GET', 'HEAD']))
-
- value_uri = keys_uri + '/{key}/{value}'
- s.connect(route_name, value_uri, action='create_value',
- conditions=dict(method=['POST']))
- s.connect(route_name, value_uri, action='update_value',
- conditions=dict(method=['PUT']))
diff --git a/ryu/app/rest_tunnel.py b/ryu/app/rest_tunnel.py
deleted file mode 100644
index 74f5e461..00000000
--- a/ryu/app/rest_tunnel.py
+++ /dev/null
@@ -1,218 +0,0 @@
-# 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.
-
-"""
-Provide a set of REST API for tunnel key management. Used by OpenStack
-Ryu plug-in.
-
-- Tunnel key registration for a network
-- Manage switches and their ports which are used to establish a tunnel
-"""
-
-import json
-from webob import Response
-
-from ryu.app import wsgi as app_wsgi
-from ryu.app.wsgi import ControllerBase, WSGIApplication
-from ryu.base import app_manager
-from ryu.controller import network
-from ryu.controller import tunnels
-import ryu.exception as ryu_exc
-from ryu.lib import dpid as dpid_lib
-
-
-# REST API for tunneling
-#
-# register tunnel key of this network
-# Fail if the key is already registered
-# POST /v1.0/tunnels/networks/{network-id}/key/{tunnel_key}
-#
-# register tunnel key of this network
-# Success as nop even if the same key is already registered
-# PUT /v1.0/tunnels/networks/{network-id}/key/{tunnel_key}
-#
-# return allocated tunnel key of this network
-# GET /v1.0/tunnels/networks/{network-id}/key
-#
-# get the ports of dpid that are used for tunneling
-# GET /v1.0/tunnels/switches/{dpid}/ports
-#
-# get the dpid of the other end of tunnel
-# GET /v1.0/tunnels/switches/{dpid}/ports/{port-id}/
-#
-# register the dpid of the other end of tunnel
-# Fail if the dpid is already registered
-# POST /v1.0/tunnels/switches/{dpid}/ports/{port-id}/{remote_dpid}
-#
-# register the dpid of the other end of tunnel
-# Success as nop even if the dpid is already registered
-# PUT /v1.0/tunnels/switches/{dpid}/ports/{port-id}/{remote_dpid}
-
-
-class TunnelKeyController(ControllerBase):
- def __init__(self, req, link, data, **config):
- super(TunnelKeyController, self).__init__(req, link, data, **config)
- self.tunnels = data
-
- def create(self, _req, network_id, tunnel_key, **_kwargs):
- tunnel_key = int(tunnel_key)
- try:
- self.tunnels.register_key(network_id, tunnel_key)
- except (ryu_exc.NetworkAlreadyExist, tunnels.TunnelKeyAlreadyExist):
- return Response(status=409)
-
- return Response(status=200)
-
- def update(self, _req, network_id, tunnel_key, **_kwargs):
- tunnel_key = int(tunnel_key)
- try:
- self.tunnels.update_key(network_id, tunnel_key)
- except (ryu_exc.NetworkAlreadyExist, tunnels.TunnelKeyAlreadyExist):
- return Response(status=409)
-
- return Response(status=200)
-
- def lists(self, _req, network_id, **_kwargs):
- try:
- tunnel_key = self.tunnels.get_key(network_id)
- except tunnels.TunnelKeyNotFound:
- return Response(status=404)
- body = json.dumps(tunnel_key)
-
- return Response(content_type='application/json', body=body)
-
- def delete(self, _req, network_id, **_kwargs):
- try:
- self.tunnels.delete_key(network_id)
- except (ryu_exc.NetworkNotFound, tunnels.TunnelKeyNotFound):
- return Response(status=404)
-
- return Response(status=200)
-
-
-class TunnelPortController(ControllerBase):
- def __init__(self, req, link, data, **config):
- super(TunnelPortController, self).__init__(req, link, data, **config)
- self.tunnels = data
-
- def create(self, _req, dpid, port_id, remote_dpid, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- remote_dpid = dpid_lib.str_to_dpid(remote_dpid)
- try:
- self.tunnels.register_port(dpid, port_id, remote_dpid)
- except ryu_exc.PortAlreadyExist:
- return Response(status=409)
-
- return Response(status=200)
-
- def update(self, _req, dpid, port_id, remote_dpid, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- remote_dpid = dpid_lib.str_to_dpid(remote_dpid)
- try:
- self.tunnels.update_port(dpid, port_id, remote_dpid)
- except tunnels.RemoteDPIDAlreadyExist:
- return Response(status=409)
-
- return Response(status=200)
-
- def lists(self, _req, dpid, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- ports = self.tunnels.list_ports(dpid)
- body = json.dumps(ports)
-
- return Response(content_type='application/json', body=body)
-
- def get(self, _req, dpid, port_id, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- try:
- remote_dpid = self.tunnels.get_remote_dpid(dpid, port_id)
- except ryu_exc.PortNotFound:
- return Response(status=404)
- body = json.dumps(dpid_lib.dpid_to_str(remote_dpid))
-
- return Response(content_type='application/json', body=body)
-
- def delete(self, _req, dpid, port_id, **_kwargs):
- dpid = dpid_lib.str_to_dpid(dpid)
- port_id = int(port_id)
- try:
- self.tunnels.delete_port(dpid, port_id)
- except ryu_exc.PortNotFound:
- return Response(status=404)
-
- return Response(status=200)
-
-
-class TunnelAPI(app_manager.RyuApp):
- _CONTEXTS = {
- 'network': network.Network,
- 'tunnels': tunnels.Tunnels,
- 'wsgi': WSGIApplication
- }
-
- def __init__(self, *_args, **kwargs):
- super(TunnelAPI, self).__init__()
- self.nw = kwargs['network']
- self.tunnels = kwargs['tunnels']
- wsgi = kwargs['wsgi']
- mapper = wsgi.mapper
-
- controller = TunnelKeyController
- wsgi.registory[controller.__name__] = self.tunnels
- route_name = 'tunnel_key'
- uri = '/v1.0/tunnels'
- key_uri = uri + '/networks/{network_id}/key'
- s = mapper.submapper(controller=controller)
- s.connect(route_name, key_uri, action='lists',
- conditions=dict(method=['GET', 'HEAD']))
- s.connect(route_name, key_uri, action='delete',
- conditions=dict(method=['DELETE']))
-
- key_uri += '/{tunnel_key}'
- requirements = {route_name: app_wsgi.DIGIT_PATTERN}
- s = mapper.submapper(controller=controller, requirements=requirements)
- s.connect(route_name, key_uri, action='create',
- conditions=dict(method=['POST']))
- s.connect(route_name, key_uri, action='update',
- conditions=dict(method=['PUT']))
-
- controller = TunnelPortController
- wsgi.registory[controller.__name__] = self.tunnels
- route_name = 'tunnel_port'
- sw_uri = uri + '/switches/{dpid}/ports'
- requirements = {'dpid': dpid_lib.DPID_PATTERN}
- mapper.connect(route_name, sw_uri, controller=controller,
- action='lists', conditions=dict(method=['GET', 'HEAD']),
- requirements=requirements)
-
- sw_uri += '/{port_id}'
- requirements['port_id'] = app_wsgi.DIGIT_PATTERN
- s = mapper.submapper(controller=controller, requirements=requirements)
- mapper.connect(route_name, sw_uri, action='get',
- conditions=dict(method=['GET', 'HEAD']))
- mapper.connect(route_name, sw_uri, action='delete',
- conditions=dict(method=['DELETE']))
-
- sw_uri += '/{remote_dpid}'
- requirements['remote_dpid'] = dpid_lib.DPID_PATTERN
- s = mapper.submapper(controller=controller, requirements=requirements)
- mapper.connect(route_name, sw_uri, action='create',
- conditions=dict(method=['POST']))
- mapper.connect(route_name, sw_uri, action='update',
- conditions=dict(method=['PUT']))
diff --git a/ryu/app/simple_isolation.py b/ryu/app/simple_isolation.py
deleted file mode 100644
index 40c490ef..00000000
--- a/ryu/app/simple_isolation.py
+++ /dev/null
@@ -1,351 +0,0 @@
-# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
-# Copyright (C) 2011, 2012 Isaku Yamahata <yamahata at valinux co 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.
-
-"""
-MAC address based isolation logic.
-"""
-
-import logging
-import struct
-
-from ryu.app.rest_nw_id import NW_ID_UNKNOWN, NW_ID_EXTERNAL
-from ryu.base import app_manager
-from ryu.exception import MacAddressDuplicated
-from ryu.exception import PortUnknown
-from ryu.controller import dpset
-from ryu.controller import mac_to_network
-from ryu.controller import mac_to_port
-from ryu.controller import network
-from ryu.controller import ofp_event
-from ryu.controller.handler import MAIN_DISPATCHER
-from ryu.controller.handler import CONFIG_DISPATCHER
-from ryu.controller.handler import set_ev_cls
-from ryu.ofproto import nx_match
-from ryu.lib.mac import haddr_to_str
-from ryu.lib import mac
-
-
-class SimpleIsolation(app_manager.RyuApp):
- _CONTEXTS = {
- 'network': network.Network,
- 'dpset': dpset.DPSet,
- }
-
- def __init__(self, *args, **kwargs):
- super(SimpleIsolation, self).__init__(*args, **kwargs)
- self.nw = kwargs['network']
- self.dpset = kwargs['dpset']
- self.mac2port = mac_to_port.MacToPortTable()
- self.mac2net = mac_to_network.MacToNetwork(self.nw)
-
- @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
- def switch_features_handler(self, ev):
- msg = ev.msg
- datapath = msg.datapath
-
- datapath.send_delete_all_flows()
- datapath.send_barrier()
-
- self.mac2port.dpid_add(ev.msg.datapath_id)
- self.nw.add_datapath(ev.msg)
-
- @staticmethod
- def _modflow_and_send_packet(msg, src, dst, actions):
- datapath = msg.datapath
- ofproto = datapath.ofproto
-
- #
- # install flow and then send packet
- #
- rule = nx_match.ClsRule()
- rule.set_in_port(msg.in_port)
- rule.set_dl_dst(dst)
- rule.set_dl_src(src)
- datapath.send_flow_mod(
- rule=rule, cookie=0, command=datapath.ofproto.OFPFC_ADD,
- idle_timeout=0, hard_timeout=0,
- priority=ofproto.OFP_DEFAULT_PRIORITY,
- buffer_id=ofproto.OFP_NO_BUFFER, out_port=ofproto.OFPP_NONE,
- flags=ofproto.OFPFF_SEND_FLOW_REM, actions=actions)
-
- datapath.send_packet_out(msg.buffer_id, msg.in_port, actions)
-
- def _forward_to_nw_id(self, msg, src, dst, nw_id, out_port):
- assert out_port is not None
- datapath = msg.datapath
-
- if not self.nw.same_network(datapath.id, nw_id, out_port,
- NW_ID_EXTERNAL):
- self.logger.debug('packet is blocked src %s dst %s '
- 'from %d to %d on datapath %d',
- haddr_to_str(src), haddr_to_str(dst),
- msg.in_port, out_port, datapath.id)
- return
-
- self.logger.debug("learned dpid %s in_port %d out_port "
- "%d src %s dst %s",
- datapath.id, msg.in_port, out_port,
- haddr_to_str(src), haddr_to_str(dst))
- actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]
- self._modflow_and_send_packet(msg, src, dst, actions)
-
- def _flood_to_nw_id(self, msg, src, dst, nw_id):
- datapath = msg.datapath
- actions = []
- self.logger.debug("dpid %s in_port %d src %s dst %s ports %s",
- datapath.id, msg.in_port,
- haddr_to_str(src), haddr_to_str(dst),
- list(self.nw.dpids.get(datapath.id, {}).items()))
- for port_no in self.nw.filter_ports(datapath.id, msg.in_port,
- nw_id, NW_ID_EXTERNAL):
- self.logger.debug("port_no %s", port_no)
- actions.append(datapath.ofproto_parser.OFPActionOutput(port_no))
- self._modflow_and_send_packet(msg, src, dst, actions)
-
- def _learned_mac_or_flood_to_nw_id(self, msg, src, dst,
- dst_nw_id, out_port):
- if out_port is not None:
- self._forward_to_nw_id(msg, src, dst, dst_nw_id, out_port)
- else:
- self._flood_to_nw_id(msg, src, dst, dst_nw_id)
-
- def _modflow_and_drop_packet(self, msg, src, dst):
- self._modflow_and_send_packet(msg, src, dst, [])
-
- def _drop_packet(self, msg):
- datapath = msg.datapath
- datapath.send_packet_out(msg.buffer_id, msg.in_port, [])
-
- @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
- def packet_in_handler(self, ev):
- # self.logger.debug('packet in ev %s msg %s', ev, ev.msg)
- msg = ev.msg
- datapath = msg.datapath
- ofproto = datapath.ofproto
-
- dst, src, _eth_type = struct.unpack_from('!6s6sH', buffer(msg.data), 0)
-
- try:
- port_nw_id = self.nw.get_network(datapath.id, msg.in_port)
- except PortUnknown:
- port_nw_id = NW_ID_UNKNOWN
-
- if port_nw_id != NW_ID_UNKNOWN:
- # Here it is assumed that the
- # (port <-> network id)/(mac <-> network id) relationship
- # is stable once the port is created. The port will be destroyed
- # before assigning new network id to the given port.
- # This is correct nova-network/nova-compute.
- try:
- # allow external -> known nw id change
- self.mac2net.add_mac(src, port_nw_id, NW_ID_EXTERNAL)
- except MacAddressDuplicated:
- self.logger.warn('mac address %s is already in use.'
- ' So (dpid %s, port %s) can not use it',
- haddr_to_str(src), datapath.id, msg.in_port)
- #
- # should we install drop action pro-actively for future?
- #
- self._drop_packet(msg)
- return
-
- old_port = self.mac2port.port_add(datapath.id, msg.in_port, src)
- if old_port is not None and old_port != msg.in_port:
- # We really overwrite already learned mac address.
- # So discard already installed stale flow entry which conflicts
- # new port.
- rule = nx_match.ClsRule()
- rule.set_dl_dst(src)
- datapath.send_flow_mod(rule=rule,
- cookie=0,
- command=ofproto.OFPFC_DELETE,
- idle_timeout=0,
- hard_timeout=0,
- priority=ofproto.OFP_DEFAULT_PRIORITY,
- out_port=old_port)
-
- # to make sure the old flow entries are purged.
- datapath.send_barrier()
-
- src_nw_id = self.mac2net.get_network(src, NW_ID_UNKNOWN)
- dst_nw_id = self.mac2net.get_network(dst, NW_ID_UNKNOWN)
-
- # we handle multicast packet as same as broadcast
- broadcast = (dst == mac.BROADCAST) or mac.is_multicast(dst)
- out_port = self.mac2port.port_get(datapath.id, dst)
-
- #
- # there are several combinations:
- # in_port: known nw_id, external, unknown nw,
- # src mac: known nw_id, external, unknown nw,
- # dst mac: known nw_id, external, unknown nw, and broadcast/multicast
- # where known nw_id: is quantum network id
- # external: means that these ports are connected to outside
- # unknown nw: means that we don't know this port is bounded to
- # specific nw_id or external
- # broadcast: the destination mac address is broadcast address
- # (or multicast address)
- #
- # Can the following logic be refined/shortened?
- #
-
- # When NW_ID_UNKNOWN is found, registering ports might be delayed.
- # So just drop only this packet and not install flow entry.
- # It is expected that when next packet arrives, the port is registers
- # with some network id
-
- if port_nw_id != NW_ID_EXTERNAL and port_nw_id != NW_ID_UNKNOWN:
- if broadcast:
- # flood to all ports of external or src_nw_id
- self._flood_to_nw_id(msg, src, dst, src_nw_id)
- elif src_nw_id == NW_ID_EXTERNAL:
- self._modflow_and_drop_packet(msg, src, dst)
- return
- elif src_nw_id == NW_ID_UNKNOWN:
- self._drop_packet(msg)
- return
- else:
- # src_nw_id != NW_ID_EXTERNAL and src_nw_id != NW_ID_UNKNOWN:
- #
- # try learned mac check if the port is net_id
- # or
- # flood to all ports of external or src_nw_id
- self._learned_mac_or_flood_to_nw_id(msg, src, dst,
- src_nw_id, out_port)
-
- elif port_nw_id == NW_ID_EXTERNAL:
- if src_nw_id != NW_ID_EXTERNAL and src_nw_id != NW_ID_UNKNOWN:
- if broadcast:
- # flood to all ports of external or src_nw_id
- self._flood_to_nw_id(msg, src, dst, src_nw_id)
- elif (dst_nw_id != NW_ID_EXTERNAL and
- dst_nw_id != NW_ID_UNKNOWN):
- if src_nw_id == dst_nw_id:
- # try learned mac
- # check if the port is external or same net_id
- # or
- # flood to all ports of external or src_nw_id
- self._learned_mac_or_flood_to_nw_id(msg, src, dst,
- src_nw_id,
- out_port)
- else:
- # should not occur?
- self.logger.debug("should this case happen?")
- self._drop_packet(msg)
- elif dst_nw_id == NW_ID_EXTERNAL:
- # try learned mac
- # or
- # flood to all ports of external or src_nw_id
- self._learned_mac_or_flood_to_nw_id(msg, src, dst,
- src_nw_id, out_port)
- else:
- assert dst_nw_id == NW_ID_UNKNOWN
- self.logger.debug("Unknown dst_nw_id")
- self._drop_packet(msg)
- elif src_nw_id == NW_ID_EXTERNAL:
- self._modflow_and_drop_packet(msg, src, dst)
- else:
- # should not occur?
- assert src_nw_id == NW_ID_UNKNOWN
- self._drop_packet(msg)
- else:
- # drop packets
- assert port_nw_id == NW_ID_UNKNOWN
- self._drop_packet(msg)
- # self.logger.debug("Unknown port_nw_id")
-
- def _port_add(self, ev):
- #
- # delete flows entries that matches with
- # dl_dst == broadcast/multicast
- # and dl_src = network id if network id of this port is known
- # to send broadcast packet to this newly added port.
- #
- # Openflow v1.0 doesn't support masked match of dl_dst,
- # so delete all flow entries. It's inefficient, though.
- #
- msg = ev.msg
- datapath = msg.datapath
-
- datapath.send_delete_all_flows()
- datapath.send_barrier()
- self.nw.port_added(datapath, msg.desc.port_no)
-
- def _port_del(self, ev):
- # free mac addresses associated to this VM port,
- # and delete related flow entries for later reuse of mac address
-
- dps_needs_barrier = set()
-
- msg = ev.msg
- datapath = msg.datapath
- datapath_id = datapath.id
- port_no = msg.desc.port_no
-
- rule = nx_match.ClsRule()
- rule.set_in_port(port_no)
- datapath.send_flow_del(rule=rule, cookie=0)
-
- rule = nx_match.ClsRule()
- datapath.send_flow_del(rule=rule, cookie=0, out_port=port_no)
- dps_needs_barrier.add(datapath)
-
- try:
- port_nw_id = self.nw.get_network(datapath_id, port_no)
- except PortUnknown:
- # race condition between rest api delete port
- # and openflow port deletion ofp_event
- pass
- else:
- if port_nw_id in (NW_ID_UNKNOWN, NW_ID_EXTERNAL):
- datapath.send_barrier()
- return
-
- for mac_ in self.mac2port.mac_list(datapath_id, port_no):
- for (_dpid, dp) in self.dpset.get_all():
- if self.mac2port.port_get(dp.id, mac_) is None:
- continue
-
- rule = nx_match.ClsRule()
- rule.set_dl_src(mac_)
- dp.send_flow_del(rule=rule, cookie=0)
-
- rule = nx_match.ClsRule()
- rule.set_dl_dst(mac_)
- dp.send_flow_del(rule=rule, cookie=0)
- dps_needs_barrier.add(dp)
-
- self.mac2port.mac_del(dp.id, mac_)
-
- self.mac2net.del_mac(mac_)
-
- self.nw.port_deleted(datapath.id, port_no)
-
- for dp in dps_needs_barrier:
- dp.send_barrier()
-
- @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
- def port_status_handler(self, ev):
- msg = ev.msg
- reason = msg.reason
- ofproto = msg.datapath.ofproto
-
- if reason == ofproto.OFPPR_ADD:
- self._port_add(ev)
- elif reason == ofproto.OFPPR_DELETE:
- self._port_del(ev)
- else:
- assert reason == ofproto.OFPPR_MODIFY
diff --git a/ryu/app/simple_vlan.py b/ryu/app/simple_vlan.py
deleted file mode 100644
index ea0e76fe..00000000
--- a/ryu/app/simple_vlan.py
+++ /dev/null
@@ -1,229 +0,0 @@
-# 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.
-
-"""
-VLAN based isolation logic.
-"""
-
-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 import hub
-from ryu.lib.ovs import bridge
-from ryu.ofproto import nx_match
-
-
-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):
- self.logger.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):
- self.logger.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:
- self.logger.debug('port not found')
- return
-
- try:
- ovsdb_addr = self.conf_sw.get_key(dpid, conf_switch_key.OVSDB_ADDR)
- except KeyError:
- self.logger.debug('ovsdb_addr not found')
- return
-
- self._port_flow_add(dp, port_no)
-
- self.logger.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(self.CONF, 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 hub.Timeout:
- self.logger.error('timeout')
- return
-
- return True
-
- def _port_setup_netid(self, dpid, port_no, network_id):
- self.logger.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:
- self.logger.debug('dp not found')
- return
- if _is_reserved_port(dp, port_no):
- return
-
- if network_id == rest_nw_id.NW_ID_EXTERNAL:
- self.logger.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:
- self.logger.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:
- self.logger.debug('port_unknown')
- self._queue_port_flow_del(dp, port_no)
- return
-
- if not self._port_setup_netid(dpid, port_no, network_id):
- self.logger.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):
- self.logger.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):
- self.logger.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):
- self.logger.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):
- self.logger.debug('tunnel_add %s', ev)
- tunnel_key = ev.tunnel_key
- for (dpid, port_no) in self.nw.list_ports_noraise(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):
- self.logger.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)
diff --git a/ryu/app/tunnel_port_updater.py b/ryu/app/tunnel_port_updater.py
deleted file mode 100644
index 6e25c8f8..00000000
--- a/ryu/app/tunnel_port_updater.py
+++ /dev/null
@@ -1,473 +0,0 @@
-# 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.
-
-"""
-This module updates OVS tunnel ports for OpenStack integration.
-"""
-
-import collections
-from ryu import cfg
-import logging
-import netaddr
-
-from ryu import exception as ryu_exc
-from ryu.app import conf_switch_key as cs_key
-from ryu.app import rest_nw_id
-from ryu.base import app_manager
-from ryu.controller import (conf_switch,
- handler,
- network,
- tunnels)
-from ryu.lib import dpid as dpid_lib
-from ryu.lib import hub
-from ryu.lib.ovs import bridge as ovs_bridge
-
-
-_TUNNEL_TYPE_TO_NW_ID = {
- 'gre': rest_nw_id.NW_ID_VPORT_GRE,
-}
-
-
-class NetworkAPI(object):
- """Internal adopter class for RestAPI"""
- def __init__(self, network_):
- super(NetworkAPI, self).__init__()
- self.nw = network_
-
- def update_network(self, network_id):
- self.nw.update_network(network_id)
-
- def create_port(self, network_id, dpid, port_id):
- self.nw.create_port(network_id, dpid, port_id)
-
- def update_port(self, network_id, dpid, port_id):
- self.nw.update_port(network_id, dpid, port_id)
-
- def delete_port(self, network_id, dpid, port_id):
- try:
- self.nw.remove_port(network_id, dpid, port_id)
- except (ryu_exc.NetworkNotFound, ryu_exc.PortNotFound):
- pass
-
-
-class TunnelAPI(object):
- """Internal adopter class for RestTunnelAPI"""
- def __init__(self, tunnels_):
- super(TunnelAPI, self).__init__()
- self.tunnels = tunnels_
-
- def update_remote_dpid(self, dpid, port_id, remote_dpid):
- self.tunnels.update_port(dpid, port_id, remote_dpid)
-
- def create_remote_dpid(self, dpid, port_id, remote_dpid):
- self.tunnels.register_port(dpid, port_id, remote_dpid)
-
- def delete_port(self, dpid, port_id):
- try:
- self.tunnels.delete_port(dpid, port_id)
- except ryu_exc.PortNotFound:
- pass
-
-
-class TunnelPort(object):
- def __init__(self, dpid, port_no, local_ip, remote_ip, remote_dpid=None):
- super(TunnelPort, self).__init__()
- self.dpid = dpid
- self.port_no = port_no
- self.local_ip = local_ip
- self.remote_ip = remote_ip
- self.remote_dpid = remote_dpid
-
- def __eq__(self, other):
- return (self.dpid == other.dpid and
- self.port_no == other.port_no and
- self.local_ip == other.local_ip and
- self.remote_ip == other.remote_ip and
- self.remote_dpid == other.remote_dpid)
-
-
-class TunnelDP(object):
- def __init__(self, CONF, dpid, ovsdb_addr, tunnel_ip, tunnel_type,
- conf_switch_, network_api, tunnel_api, logger):
- super(TunnelDP, self).__init__()
- self.dpid = dpid
- self.network_api = network_api
- self.tunnel_api = tunnel_api
- self.logger = logger
-
- self.ovs_bridge = ovs_bridge.OVSBridge(CONF, dpid, ovsdb_addr)
-
- self.tunnel_ip = tunnel_ip
- self.tunnel_type = tunnel_type
- self.tunnel_nw_id = _TUNNEL_TYPE_TO_NW_ID[tunnel_type]
- self.tunnels = {} # port number -> TunnelPort
-
- self.conf_switch = conf_switch_
- self.inited = False
-
- self.req_q = hub.Queue()
- self.thr = hub.spawn(self._serve_loop)
-
- def _init(self):
- self.ovs_bridge.init()
- for tp in self.ovs_bridge.get_tunnel_ports(self.tunnel_type):
- if tp.local_ip != self.tunnel_ip:
- self.logger.warn('unknown tunnel port %s', tp)
- continue
-
- remote_dpid = self.conf_switch.find_dpid(cs_key.OVS_TUNNEL_ADDR,
- tp.remote_ip)
- self.tunnels[tp.ofport] = TunnelPort(self.dpid, tp.ofport,
- self.tunnel_ip, tp.remote_ip,
- remote_dpid)
- if remote_dpid:
- self._api_update(tp.ofport, remote_dpid)
-
- self.conf_switch = None
- self.inited = True
-
- def _api_update(self, port_no, remote_dpid):
- self.network_api.update_port(self.tunnel_nw_id, self.dpid, port_no)
- self.tunnel_api.update_remote_dpid(self.dpid, port_no, remote_dpid)
-
- def _api_delete(self, port_no):
- self.network_api.delete_port(self.tunnel_nw_id, self.dpid, port_no)
- self.tunnel_api.delete_port(self.dpid, port_no)
-
- def _update_remote(self, remote_dpid, remote_ip):
- if self.dpid == remote_dpid:
- if self.tunnel_ip == remote_ip:
- return
-
- # tunnel ip address is changed.
- self.logger.warn('local ip address is changed %s: %s -> %s',
- dpid_lib.dpid_to_str(remote_dpid),
- self.tunnel_ip, remote_ip)
- # recreate tunnel ports.
- for tp in list(self.tunnels.values()):
- if tp.remote_dpid is None:
- # TODO:XXX
- continue
-
- self._del_tunnel_port(tp.port_no, tp.local_ip, tp.remote_ip)
-
- new_tp = self._add_tunnel_port(tp.remote_dpid, tp.remote_ip)
- self._api_update(new_tp.ofport, tp.remote_dpid)
- return
-
- if self.tunnel_ip == remote_ip:
- self.logger.error('ip conflict: %s %s %s',
- dpid_lib.dpid_to_str(self.dpid),
- dpid_lib.dpid_to_str(remote_dpid), remote_ip)
- # XXX What should we do?
- return
-
- for tp in list(self.tunnels.values()):
- if tp.remote_dpid == remote_dpid:
- if tp.remote_ip == remote_ip:
- self._api_update(tp.port_no, remote_dpid)
- continue
-
- self.logger.warn('remote ip address is changed %s: %s -> %s',
- dpid_lib.dpid_to_str(remote_dpid),
- tp.remote_ip, remote_ip)
- self._del_tunnel_port(tp.port_no, self.tunnel_ip, tp.remote_ip)
-
- new_tp = self._add_tunnel_port(remote_dpid, remote_ip)
- self._api_update(new_tp.ofport, remote_dpid)
- elif tp.remote_ip == remote_ip:
- assert tp.remote_dpid is None
- self._api_update(tp.port_no, remote_dpid)
- tp.remote_dpid = remote_dpid
-
- @staticmethod
- def _to_hex(ip_addr):
- # assuming IPv4 address
- assert netaddr.IPAddress(ip_addr).ipv4()
- return "%02x%02x%02x%02x" % netaddr.IPAddress(ip_addr).words
-
- @staticmethod
- def _port_name(local_ip, remote_ip):
- # ovs requires requires less or equals to 14 bytes length
- # gre<remote>-<local lsb>
- _PORT_NAME_LENGTH = 14
- local_hex = TunnelDP._to_hex(local_ip)
- remote_hex = TunnelDP._to_hex(remote_ip)
- return ("gre%s-%s" % (remote_hex, local_hex))[:_PORT_NAME_LENGTH]
-
- def _tunnel_port_exists(self, remote_dpid, remote_ip):
- return any(tp.remote_dpid == remote_dpid and tp.remote_ip == remote_ip
- for tp in self.tunnels.values())
-
- def _add_tunnel_port(self, remote_dpid, remote_ip):
- self.logger.debug('add_tunnel_port local %s %s remote %s %s',
- dpid_lib.dpid_to_str(self.dpid), self.tunnel_ip,
- dpid_lib.dpid_to_str(remote_dpid), remote_ip)
- if self._tunnel_port_exists(remote_dpid, remote_ip):
- self.logger.debug('add_tunnel_port nop')
- return
-
- self.logger.debug('add_tunnel_port creating port')
- port_name = self._port_name(self.tunnel_ip, remote_ip)
- self.ovs_bridge.add_tunnel_port(port_name, self.tunnel_type,
- self.tunnel_ip, remote_ip, 'flow')
-
- tp = self.ovs_bridge.get_tunnel_port(port_name, self.tunnel_type)
- self.tunnels[tp.ofport] = TunnelPort(self.dpid, tp.ofport,
- tp.local_ip, tp.remote_ip,
- remote_dpid)
- self.network_api.create_port(self.tunnel_nw_id, self.dpid, tp.ofport)
- self.tunnel_api.create_remote_dpid(self.dpid, tp.ofport, remote_dpid)
- return tp
-
- def _del_tunnel_port(self, port_no, local_ip, remote_ip):
- port_name = self._port_name(local_ip, remote_ip)
- self.ovs_bridge.del_port(port_name)
- del self.tunnels[port_no]
- self._api_delete(port_no)
-
- def _del_tunnel_port_ip(self, remote_ip):
- for tp in self.tunnels.values():
- if tp.remote_ip == remote_ip:
- self._del_tunnel_port(tp.port_no, self.tunnel_ip, remote_ip)
- break
-
- # serialize requests to this OVS DP
- _RequestUpdateRemote = collections.namedtuple('_RequestUpdateRemote',
- ('remote_dpid', 'remote_ip'))
- _RequestAddTunnelPort = collections.namedtuple('_RequestAddTunnelPort',
- ('remote_dpid',
- 'remote_ip'))
- _RequestDelTunnelPort = collections.namedtuple('_RequestDelTunnelPort',
- ('remote_ip'))
-
- class _RequestClose(object):
- pass
-
- def request_update_remote(self, remote_dpid, remote_ip):
- self.req_q.put(self._RequestUpdateRemote(remote_dpid, remote_ip))
-
- def request_add_tunnel_port(self, remote_dpid, remote_ip):
- self.req_q.put(self._RequestAddTunnelPort(remote_dpid, remote_ip))
-
- def request_del_tunnel_port(self, remote_ip):
- self.req_q.put(self._RequestDelTunnelPort(remote_ip))
-
- def close(self):
- # self.thr.kill()
- self.req_q.put(self._RequestClose())
- self.thr.join()
- self.thr = None
-
- def _serve_loop(self):
- # TODO:XXX backoff timeout
- # TOOD:XXX and then, abandon and notify the caller(TunnelPortUpdater)
-
- # TODO: if possible, squash requests?
- # For example, RequestAddTunnelPort and RequestDelTunnelPort
- # with same dpid are in the queue. AddTunnelPort request
- # can be skipped.
- # When ovsdb-server and vswitchd are over-loaded
- # (or connection to ovsdb are unstable), squashing request
- # would increase stability a bit?
- # But unsure how effective it would be.
-
- if not self.inited:
- try:
- self._init()
- except hub.Timeout:
- self.logger.warn('_init timeouted')
-
- req = None
- while True:
- if req is None:
- req = self.req_q.get()
- if isinstance(req, self._RequestClose):
- return
-
- try:
- if not self.inited:
- self._init()
-
- # shoud use dispatcher?
- if isinstance(req, self._RequestUpdateRemote):
- self.logger.debug('update_remote')
- self._update_remote(req.remote_dpid, req.remote_ip)
- elif isinstance(req, self._RequestAddTunnelPort):
- self.logger.debug('add_tunnel_port')
- self._add_tunnel_port(req.remote_dpid, req.remote_ip)
- elif isinstance(req, self._RequestDelTunnelPort):
- self.logger.debug('del_tunnel_port')
- self._del_tunnel_port_ip(req.remote_ip)
- else:
- self.logger.error('unknown request %s', req)
- except hub.Timeout:
- # timeout. try again
- self.logger.warn('timeout try again')
- continue
- else:
- # Done. move onto next request
- req = None
-
-
-class TunnelDPSet(dict):
- """ dpid -> TunndlDP """
- pass
-
-
-# import collections
-# class TunnelRequests(collections.defaultdict(set)):
-class TunnelRequests(dict):
- def add(self, dpid0, dpid1):
- self.setdefault(dpid0, set()).add(dpid1)
- self.setdefault(dpid1, set()).add(dpid0)
-
- def remove(self, dpid0, dpid1):
- self[dpid0].remove(dpid1)
- self[dpid1].remove(dpid0)
-
- def get_remote(self, dpid):
- return self.setdefault(dpid, set())
-
-
-class TunnelPortUpdater(app_manager.RyuApp):
- _CONTEXTS = {
- 'conf_switch': conf_switch.ConfSwitchSet,
- 'network': network.Network,
- 'tunnels': tunnels.Tunnels,
- }
-
- def __init__(self, *args, **kwargs):
- super(TunnelPortUpdater, self).__init__(args, kwargs)
- self.CONF.register_opts([
- cfg.StrOpt('tunnel-type', default='gre',
- help='tunnel type for ovs tunnel port')
- ])
- self.tunnel_type = self.CONF.tunnel_type
- self.cs = kwargs['conf_switch']
- self.nw = kwargs['network']
- self.tunnels = kwargs['tunnels']
- self.tunnel_dpset = TunnelDPSet()
- self.tunnel_requests = TunnelRequests()
-
- self.network_api = NetworkAPI(self.nw)
- self.tunnel_api = TunnelAPI(self.tunnels)
- self.network_api.update_network(
- _TUNNEL_TYPE_TO_NW_ID[self.tunnel_type])
-
- def _ovsdb_update(self, dpid, ovsdb_addr, ovs_tunnel_addr):
- self.logger.debug('_ovsdb_update %s %s %s',
- dpid_lib.dpid_to_str(dpid), ovsdb_addr,
- ovs_tunnel_addr)
- if dpid not in self.tunnel_dpset:
- # TODO:XXX changing ovsdb_addr, ovs_tunnel_addr
- tunnel_dp = TunnelDP(self.CONF, dpid, ovsdb_addr, ovs_tunnel_addr,
- self.tunnel_type, self.cs,
- self.network_api, self.tunnel_api,
- self.logger)
- self.tunnel_dpset[dpid] = tunnel_dp
-
- tunnel_dp = self.tunnel_dpset.get(dpid)
- assert tunnel_dp
- self._add_tunnel_ports(tunnel_dp,
- self.tunnel_requests.get_remote(dpid))
-
- @handler.set_ev_cls(conf_switch.EventConfSwitchSet)
- def conf_switch_set_handler(self, ev):
- self.logger.debug('conf_switch_set_handler %s %s %s',
- dpid_lib.dpid_to_str(ev.dpid), ev.key, ev.value)
- dpid = ev.dpid
- if (ev.key == cs_key.OVSDB_ADDR or ev.key == cs_key.OVS_TUNNEL_ADDR):
- if ((dpid, cs_key.OVSDB_ADDR) in self.cs and
- (dpid, cs_key.OVS_TUNNEL_ADDR) in self.cs):
- self._ovsdb_update(
- dpid, self.cs.get_key(dpid, cs_key.OVSDB_ADDR),
- self.cs.get_key(dpid, cs_key.OVS_TUNNEL_ADDR))
-
- if ev.key == cs_key.OVS_TUNNEL_ADDR:
- for tunnel_dp in self.tunnel_dpset.values():
- tunnel_dp.request_update_remote(ev.dpid, ev.value)
-
- @handler.set_ev_cls(conf_switch.EventConfSwitchDel)
- def conf_switch_del_handler(self, ev):
- # TODO:XXX
- pass
-
- def _add_tunnel_ports(self, tunnel_dp, remote_dpids):
- self.logger.debug('_add_tunnel_ports %s %s', tunnel_dp, remote_dpids)
- for remote_dpid in remote_dpids:
- remote_dp = self.tunnel_dpset.get(remote_dpid)
- if remote_dp is None:
- continue
- tunnel_dp.request_add_tunnel_port(remote_dp.dpid,
- remote_dp.tunnel_ip)
- remote_dp.request_add_tunnel_port(tunnel_dp.dpid,
- tunnel_dp.tunnel_ip)
-
- def _vm_port_add(self, network_id, dpid):
- self.logger.debug('_vm_port_add %s %s', network_id,
- dpid_lib.dpid_to_str(dpid))
- dpids = self.nw.get_dpids(network_id)
- dpids.remove(dpid)
- for remote_dpid in dpids:
- self.tunnel_requests.add(dpid, remote_dpid)
-
- tunnel_dp = self.tunnel_dpset.get(dpid)
- if tunnel_dp is None:
- return
- self._add_tunnel_ports(tunnel_dp, dpids)
-
- def _vm_port_del(self, network_id, dpid):
- self.logger.debug('_vm_port_del %s %s', network_id,
- dpid_lib.dpid_to_str(dpid))
- if len(self.nw.get_ports(dpid, network_id)) > 0:
- return
-
- tunnel_networks = set(p.network_id
- for p in self.nw.get_networks(dpid))
- tunnel_networks.discard(network_id)
- tunnel_networks.difference_update(rest_nw_id.RESERVED_NETWORK_IDS)
- dpids = self.nw.get_dpids(network_id).copy()
- dpids.discard(dpid)
- del_dpids = []
- for remote_dpid in dpids:
- remote_networks = set(p.network_id
- for p in self.nw.get_networks(remote_dpid))
- if tunnel_networks & remote_networks:
- continue
- self.tunnel_requests.remove(dpid, remote_dpid)
- del_dpids.append(remote_dpid)
-
- tunnel_dp = self.tunnel_dpset.get(dpid)
- if tunnel_dp is None:
- return
- for remote_dpid in del_dpids:
- remote_dp = self.tunnel_dpset.get(remote_dpid)
- if remote_dp is None:
- continue
- tunnel_dp.request_del_tunnel_port(remote_dp.tunnel_ip)
- remote_dp.request_del_tunnel_port(tunnel_dp.tunnel_ip)
-
- @handler.set_ev_cls(network.EventNetworkPort)
- def network_port_handler(self, ev):
- self.logger.debug('network_port_handler %s', ev)
- if ev.network_id in rest_nw_id.RESERVED_NETWORK_IDS:
- return
-
- if ev.add_del:
- self._vm_port_add(ev.network_id, ev.dpid)
- else:
- self._vm_port_del(ev.network_id, ev.dpid)
diff --git a/ryu/flags.py b/ryu/flags.py
index 225cbbb4..a6e5c980 100644
--- a/ryu/flags.py
+++ b/ryu/flags.py
@@ -22,36 +22,6 @@ from ryu import cfg
CONF = cfg.CONF
CONF.register_cli_opts([
- # app/quantum_adapter
- cfg.StrOpt('neutron-url', default='http://localhost:9696',
- help='URL for connecting to neutron',
- deprecated_name='quantum-url'),
- cfg.IntOpt('neutron-url-timeout', default=30,
- help='timeout value for connecting to neutron in seconds',
- deprecated_name='quantum-url-timeout'),
- cfg.StrOpt('neutron-admin-username', default='neutron',
- help='username for connecting to neutron in admin context',
- deprecated_name='quantum-admin-username'),
- cfg.StrOpt('neutron-admin-password', default='service_password',
- help='password for connecting to neutron in admin context',
- deprecated_name='quantum-admin-password'),
- cfg.StrOpt('neutron-admin-tenant-name', default='service',
- help='tenant name for connecting to neutron in admin context',
- deprecated_name='quantum-admin-tenant-name'),
- cfg.StrOpt('neutron-admin-auth-url', default='http://localhost:5000/v2.0',
- help='auth url for connecting to neutron in admin context',
- deprecated_name='quantum-admin-auth-url'),
- cfg.StrOpt('neutron-auth-strategy', default='keystone',
- help='auth strategy for connecting to neutron in admin'
- 'context',
- deprecated_name='quantum-auth-strategy'),
- cfg.StrOpt('neutron-controller-addr', default=None,
- help='openflow method:address:port to set controller of'
- 'ovs bridge',
- deprecated_name='quantum-controller-addr')
-])
-
-CONF.register_cli_opts([
# tests/switch/tester
cfg.StrOpt('target', default='0000000000000001', help='target sw dp-id'),
cfg.StrOpt('tester', default='0000000000000002', help='tester sw dp-id'),
diff --git a/ryu/lib/quantum_ifaces.py b/ryu/lib/quantum_ifaces.py
deleted file mode 100644
index 2dfd8f00..00000000
--- a/ryu/lib/quantum_ifaces.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# 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 logging
-
-from ryu.base import app_manager
-from ryu.controller import event
-
-LOG = logging.getLogger(__name__)
-
-
-class EventQuantumIfaceSet(event.EventBase):
- def __init__(self, iface_id, key, value):
- super(EventQuantumIfaceSet, self).__init__()
- self.iface_id = iface_id
- self.key = key
- self.value = value
-
- def __str__(self):
- return 'EventQuantumIfaceSet<%s, %s, %s>' % (
- self.iface_id, self.key, self.value)
-
-
-class QuantumIfaces(app_manager.RyuApp, dict):
- # iface-id => dict
- # {'iface_id': {
- # 'network_id': net-id,
- # 'ports': [{'datapath_id': dpid, 'ofport': ofport, 'name': name}]
- # }}
-
- KEY_NETWORK_ID = 'network_id'
- KEY_PORTS = 'ports'
- SUBKEY_DATAPATH_ID = 'datapath_id'
- SUBKEY_OFPORT = 'ofport'
- SUBKEY_NAME = 'name'
-
- def __init__(self):
- super(QuantumIfaces, self).__init__()
- self.name = 'quantum_ifaces'
-
- def register(self, iface_id):
- self.setdefault(iface_id, {})
-
- def unregister(self, iface_id):
- del self[iface_id]
-
- def get_iface_dict(self, iface_id):
- return self[iface_id]
-
- def list_keys(self, iface_id):
- return list(self[iface_id].keys())
-
- def get_key(self, iface_id, key):
- return self[iface_id][key]
-
- def _update_key(self, iface_id, key, value):
- if key == self.KEY_PORTS:
- ports = self[iface_id].setdefault(key, [])
- try:
- ports.remove(value)
- except ValueError:
- pass
- ports.append(value)
- else:
- self[iface_id][key] = value
- self.send_event_to_observers(
- EventQuantumIfaceSet(iface_id, key, value))
-
- def set_key(self, iface_id, key, value):
- iface = self.setdefault(iface_id, {})
- if key in iface:
- raise ValueError('trying to set already existing value '
- '%s %s -> %s', key, iface[key], value)
- self._update_key(iface_id, key, value)
-
- def update_key(self, iface_id, key, value):
- iface = self.setdefault(iface_id, {})
- if key in iface:
- err = False
- if key == self.KEY_PORTS:
- dpid = value.get(self.SUBKEY_DATAPATH_ID)
- ofport = value.get(self.SUBKEY_OFPORT)
- name = value.get(self.SUBKEY_NAME)
- if not dpid or not ofport or not name:
- raise ValueError(
- 'invalid port data: dpid=%s ofport=%s name=%s',
- dpid, ofport, name)
- for p in iface[key]:
- if (p[self.SUBKEY_DATAPATH_ID] == dpid and
- (p[self.SUBKEY_OFPORT] != ofport or
- p[self.SUBKEY_NAME] != name)):
- err = True
- break
- elif iface[key] != value:
- err = True
- if err:
- raise ValueError('unmatched updated %s %s -> %s',
- key, iface[key], value)
- self._update_key(iface_id, key, value)
-
- def del_key(self, iface_id, key, value=None):
- if iface_id not in self or key not in self[iface_id]:
- return
-
- if key != self.KEY_PORTS:
- assert value is None
- del self[iface_id][key]
- return
-
- ports = self[iface_id][key]
- try:
- ports.remove(value)
- except ValueError:
- pass
- if not ports:
- del self[iface_id][key]
- return
- return True