ff5e007b0d
calling send_delete_all_flows function in the core (for all applications) is a bad idea since some applications don't want such (e.g. failover). send_delete_all_flows function doesn't work for OF1.2. nxm code needs the specific code for each OF version. I really don't want to do so. So let's not call this function in the core. At this point, it's impossible to write applications that can work with any OF versions (incluing nxm). Let applications do whatever they want. We should remove OF protocol version dependent functions (send_delete_all_flows, send_flow_mod, and send_flow_del). Also trying to enable NXM blindly is very bad for buggy hardware switches. But they are not called in the core code so I leave them alone for now. Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
222 lines
7.1 KiB
Python
222 lines
7.1 KiB
Python
# Copyright (C) 2011, 2012 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.
|
|
|
|
import copy
|
|
import inspect
|
|
import logging
|
|
|
|
from ryu.controller import dispatcher
|
|
from ryu.controller import ofp_event
|
|
|
|
LOG = logging.getLogger('ryu.controller.handler')
|
|
|
|
QUEUE_NAME_OFP_MSG = 'ofp_msg'
|
|
DISPATCHER_NAME_OFP_HANDSHAKE = 'ofp_handshake'
|
|
HANDSHAKE_DISPATCHER = dispatcher.EventDispatcher(
|
|
DISPATCHER_NAME_OFP_HANDSHAKE)
|
|
DISPATCHER_NAME_OFP_CONFIG = 'ofp_config'
|
|
CONFIG_DISPATCHER = dispatcher.EventDispatcher(DISPATCHER_NAME_OFP_CONFIG)
|
|
DISPATCHER_NAME_OFP_MAIN = 'ofp_main'
|
|
MAIN_DISPATCHER = dispatcher.EventDispatcher(DISPATCHER_NAME_OFP_MAIN)
|
|
DISPATCHER_NAME_OFP_DEAD = 'ofp_dead'
|
|
DEAD_DISPATCHER = dispatcher.EventDispatcher(DISPATCHER_NAME_OFP_DEAD)
|
|
|
|
|
|
def set_ev_cls(ev_cls, dispatchers=None):
|
|
def _set_ev_cls_dec(handler):
|
|
handler.ev_cls = ev_cls
|
|
if dispatchers is not None:
|
|
handler.dispatchers = dispatchers
|
|
return handler
|
|
return _set_ev_cls_dec
|
|
|
|
|
|
def _is_ev_handler(meth):
|
|
return 'ev_cls' in meth.__dict__
|
|
|
|
|
|
def _listify(may_list):
|
|
if may_list is None:
|
|
may_list = []
|
|
if not isinstance(may_list, list):
|
|
may_list = [may_list]
|
|
return may_list
|
|
|
|
|
|
def _get_hnd_spec_dispatchers(handler, dispatchers):
|
|
hnd_spec_dispatchers = _listify(getattr(handler, 'dispatchers', None))
|
|
# LOG.debug("hnd_spec_dispatchers %s", hnd_spec_dispatchers)
|
|
if hnd_spec_dispatchers:
|
|
_dispatchers = copy.copy(dispatchers)
|
|
_dispatchers.extend(hnd_spec_dispatchers)
|
|
else:
|
|
_dispatchers = dispatchers
|
|
|
|
return _dispatchers
|
|
|
|
|
|
def register_cls(dispatchers=None):
|
|
dispatchers = _listify(dispatchers)
|
|
|
|
def _register_cls_method(cls):
|
|
for _k, f in inspect.getmembers(cls, inspect.isfunction):
|
|
# LOG.debug('cls %s k %s f %s', cls, _k, f)
|
|
if not _is_ev_handler(f):
|
|
continue
|
|
|
|
_dispatchers = _get_hnd_spec_dispatchers(f, dispatchers)
|
|
# LOG.debug("_dispatchers %s", _dispatchers)
|
|
for d in _dispatchers:
|
|
# LOG.debug('register dispatcher %s ev %s cls %s k %s f %s',
|
|
# d.name, f.ev_cls, cls, k, f)
|
|
d.register_handler(f.ev_cls, f)
|
|
return cls
|
|
|
|
return _register_cls_method
|
|
|
|
|
|
def register_instance(i, dispatchers=None):
|
|
dispatchers = _listify(dispatchers)
|
|
|
|
for _k, m in inspect.getmembers(i, inspect.ismethod):
|
|
# LOG.debug('instance %s k %s m %s', i, _k, m)
|
|
if not _is_ev_handler(m):
|
|
continue
|
|
|
|
_dispatchers = _get_hnd_spec_dispatchers(m, dispatchers)
|
|
# LOG.debug("_dispatchers %s", _dispatchers)
|
|
for d in _dispatchers:
|
|
# LOG.debug('register dispatcher %s ev %s k %s m %s',
|
|
# d.name, m.ev_cls, _k, m)
|
|
d.register_handler(m.ev_cls, m)
|
|
|
|
|
|
@register_cls([HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
|
|
class EchoHandler(object):
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPEchoRequest)
|
|
def echo_request_handler(ev):
|
|
msg = ev.msg
|
|
# LOG.debug('echo request msg %s %s', msg, str(msg.data))
|
|
datapath = msg.datapath
|
|
echo_reply = datapath.ofproto_parser.OFPEchoReply(datapath)
|
|
echo_reply.xid = msg.xid
|
|
echo_reply.data = msg.data
|
|
datapath.send_msg(echo_reply)
|
|
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPEchoReply)
|
|
def echo_reply_handler(ev):
|
|
# do nothing
|
|
# msg = ev.msg
|
|
# LOG.debug('echo reply ev %s %s', msg, str(msg.data))
|
|
pass
|
|
|
|
|
|
@register_cls([HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
|
|
class ErrorMsgHandler(object):
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPErrorMsg)
|
|
def error_msg_handler(ev):
|
|
msg = ev.msg
|
|
LOG.debug('error msg ev %s type 0x%x code 0x%x %s',
|
|
msg, msg.type, msg.code, str(msg.data))
|
|
|
|
|
|
@register_cls(HANDSHAKE_DISPATCHER)
|
|
class HandShakeHandler(object):
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPHello)
|
|
def hello_handler(ev):
|
|
LOG.debug('hello ev %s', ev)
|
|
msg = ev.msg
|
|
datapath = msg.datapath
|
|
|
|
# TODO: check if received version is supported.
|
|
# pre 1.0 is not supported
|
|
if msg.version not in datapath.supported_ofp_version:
|
|
# send the error
|
|
error_msg = datapath.ofproto_parser.OFPErrorMsg(datapath)
|
|
error_msg.type = datapath.ofproto.OFPET_HELLO_FAILED
|
|
error_msg.code = datapath.ofproto.OFPHFC_INCOMPATIBLE
|
|
error_msg.data = 'unsupported version 0x%x' % msg.version
|
|
datapath.send_msg(error_msg)
|
|
return
|
|
|
|
# should we again send HELLO with the version that the switch
|
|
# supports?
|
|
# msg.version != datapath.ofproto.OFP_VERSION:
|
|
|
|
datapath.set_version(msg.version)
|
|
|
|
# now send feature
|
|
features_reqeust = datapath.ofproto_parser.OFPFeaturesRequest(datapath)
|
|
datapath.send_msg(features_reqeust)
|
|
|
|
# now move on to config state
|
|
LOG.debug('move onto config mode')
|
|
datapath.ev_q.set_dispatcher(CONFIG_DISPATCHER)
|
|
|
|
|
|
@register_cls(CONFIG_DISPATCHER)
|
|
class ConfigHandler(object):
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPSwitchFeatures)
|
|
def switch_features_handler(ev):
|
|
msg = ev.msg
|
|
datapath = msg.datapath
|
|
LOG.debug('switch features ev %s', msg)
|
|
|
|
datapath.id = msg.datapath_id
|
|
datapath.ports = msg.ports
|
|
|
|
ofproto = datapath.ofproto
|
|
ofproto_parser = datapath.ofproto_parser
|
|
set_config = ofproto_parser.OFPSetConfig(
|
|
datapath, ofproto.OFPC_FRAG_NORMAL,
|
|
128 # TODO:XXX
|
|
)
|
|
datapath.send_msg(set_config)
|
|
|
|
LOG.debug('move onto main mode')
|
|
ev.msg.datapath.ev_q.set_dispatcher(MAIN_DISPATCHER)
|
|
|
|
# The above OFPC_DELETE request may trigger flow removed ofp_event.
|
|
# Just ignore them.
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPFlowRemoved)
|
|
def flow_removed_handler(ev):
|
|
LOG.debug("flow removed ev %s msg %s", ev, ev.msg)
|
|
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPBarrierReply)
|
|
def barrier_reply_handler(ev):
|
|
LOG.debug('barrier reply ev %s msg %s', ev, ev.msg)
|
|
|
|
|
|
@register_cls(MAIN_DISPATCHER)
|
|
class MainHandler(object):
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPFlowRemoved)
|
|
def flow_removed_handler(ev):
|
|
pass
|
|
|
|
@staticmethod
|
|
@set_ev_cls(ofp_event.EventOFPPortStatus)
|
|
def port_status_handler(ev):
|
|
msg = ev.msg
|
|
LOG.debug('port status %s', msg.reason)
|