
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
222 lines
7.0 KiB
Python
222 lines
7.0 KiB
Python
# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
|
|
# Copyright (C) 2011 Isaku Yamahata <yamahata at valinux co jp>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, version 3 of the License.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import copy
|
|
import inspect
|
|
import logging
|
|
import struct
|
|
|
|
from ryu.controller import event
|
|
from ryu.controller import dispatcher
|
|
from ryu.lib.mac import haddr_to_bin
|
|
|
|
LOG = logging.getLogger('ryu.controller.handler')
|
|
|
|
QUEUE_NAME_OFP_MSG = 'ofp_msg'
|
|
handshake_dispatcher = dispatcher.EventDispatcher('handshake')
|
|
config_dispatcher = dispatcher.EventDispatcher('config')
|
|
main_dispatcher = dispatcher.EventDispatcher('main')
|
|
|
|
|
|
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 _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(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(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(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))
|
|
msg.datapath.is_active = False
|
|
|
|
|
|
@register_cls(handshake_dispatcher)
|
|
class HandShakeHandler(object):
|
|
@staticmethod
|
|
@set_ev_cls(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
|
|
|
|
datapath.version = min(datapath.version_sent, msg.version)
|
|
datapath.set_version(datapath.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(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)
|
|
|
|
#
|
|
# drop all flows in order to put datapath into unknown state
|
|
#
|
|
datapath.send_delete_all_flows()
|
|
|
|
datapath.send_barrier()
|
|
|
|
# The above OFPC_DELETE request may trigger flow removed event.
|
|
# Just ignore them.
|
|
@staticmethod
|
|
@set_ev_cls(event.EventOFPFlowRemoved)
|
|
def flow_removed_handler(ev):
|
|
LOG.debug("flow removed ev %s msg %s", ev, ev.msg)
|
|
|
|
@staticmethod
|
|
@set_ev_cls(event.EventOFPBarrierReply)
|
|
def barrier_reply_handler(ev):
|
|
LOG.debug('barrier reply ev %s msg %s', ev, ev.msg)
|
|
|
|
# move on to main state
|
|
LOG.debug('move onto main mode')
|
|
ev.msg.datapath.ev_q.set_dispatcher(main_dispatcher)
|
|
|
|
|
|
@register_cls(main_dispatcher)
|
|
class MainHandler(object):
|
|
@staticmethod
|
|
@set_ev_cls(event.EventOFPFlowRemoved)
|
|
def flow_removed_handler(ev):
|
|
msg = ev.msg
|
|
|
|
@staticmethod
|
|
@set_ev_cls(event.EventOFPPortStatus)
|
|
def port_status_handler(ev):
|
|
msg = ev.msg
|
|
LOG.debug('port status %s', msg.reason)
|