FUJITA Tomonori f4063f2f06 move to the main mode right after sending BARRIER
We had better to move on to the main state after getting the response
of the barrier since it guarantees that the switch is in the known
state (all the flows were removed). However, cbench doesn't work
because it ignores the barrier. Also, the above "known" state doesn't
always work (for example, the secondary controller should not remove
all the flows in the case of HA configuration). Let's move on to the
main state here for now. I guess that we need API to enable
applications to initialize switches in their own ways.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
2012-02-05 16:02:22 +09:00

239 lines
7.9 KiB
Python

# Copyright (C) 2011, 2012 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2011, 2012 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
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 _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))
msg.datapath.is_active = False
@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)
#
# drop all flows in order to put datapath into unknown state
#
datapath.send_delete_all_flows()
datapath.send_barrier()
# We had better to move on to the main state after getting the
# response of the barrier since it guarantees that the switch
# is in the known state (all the flows were removed). However,
# cbench doesn't work because it ignores the barrier. Also,
# the above "known" state doesn't always work (for example,
# the secondary controller should not remove all the flows in
# the case of HA configuration). Let's move on to the main
# state here for now. I guess that we need API to enable
# applications to initialize switches in their own ways.
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)