diff options
-rw-r--r-- | doc/source/components.rst | 36 | ||||
-rw-r--r-- | ryu/app/client.py | 286 | ||||
-rw-r--r-- | ryu/app/gre_tunnel.py | 980 | ||||
-rw-r--r-- | ryu/app/quantum_adapter.py | 446 | ||||
-rw-r--r-- | ryu/app/rest.py | 270 | ||||
-rw-r--r-- | ryu/app/rest_nw_id.py | 41 | ||||
-rw-r--r-- | ryu/app/rest_quantum.py | 136 | ||||
-rw-r--r-- | ryu/app/rest_tunnel.py | 218 | ||||
-rw-r--r-- | ryu/app/simple_isolation.py | 351 | ||||
-rw-r--r-- | ryu/app/simple_vlan.py | 229 | ||||
-rw-r--r-- | ryu/app/tunnel_port_updater.py | 473 | ||||
-rw-r--r-- | ryu/flags.py | 30 | ||||
-rw-r--r-- | ryu/lib/quantum_ifaces.py | 131 |
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 |