FUJITA Tomonori 7578e7d602 replace dispatcher mechanism
This is purely internal change and no API for applications is
changed. At least, I confirmed that folsom OpenStack plugin works.

With the current dispatcher mechanism, multiple greenlets call
applications' handlers and might be blocked anywhere so we need
various locks to handle that concurrency. This makes things difficult
for application developers.

With this patch, each applications are connected with events. Each
application has the own greenlet(s) to handle events and might send
events to other applications.

If an application registers handlers for some OF events, it subscribes
to OF component and registers the OF events that it's interested. OF
application delivers such OF events to the application and the
application's greenlet executes the handlers.

With this, we can completely remove dispatcher.py and its friends.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
2013-02-04 09:33:28 +09:00

171 lines
7.0 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 logging
import ryu.base.app_manager
from ryu import utils
from ryu.controller import handler
from ryu.controller import ofp_event
from ryu.controller.handler import set_ev_cls, set_ev_handler
from ryu.controller.handler import HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER,\
MAIN_DISPATCHER
LOG = logging.getLogger('ryu.controller.ofp_handler')
# The state transition: HANDSHAKE -> CONFIG -> MAIN
#
# HANDSHAKE: if it receives HELLO message with the valid OFP version,
# sends Features Request message, and moves to CONFIG.
#
# CONFIG: it receives Features Reply message and moves to MAIN
#
# MAIN: it does nothing. Applications are expected to register their
# own handlers.
#
# Note that at any state, when we receive Echo Request message, send
# back Echo Reply message.
class OFPHandler(ryu.base.app_manager.RyuApp):
def __init__(self, *args, **kwargs):
super(OFPHandler, self).__init__(*args, **kwargs)
self.name = 'ofp_event'
@staticmethod
def hello_failed(datapath, error_desc):
LOG.error(error_desc)
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 = error_desc
datapath.send_msg(error_msg)
@set_ev_handler(ofp_event.EventOFPHello, HANDSHAKE_DISPATCHER)
def hello_handler(self, ev):
LOG.debug('hello ev %s', ev)
msg = ev.msg
datapath = msg.datapath
# check if received version is supported.
# pre 1.0 is not supported
elements = getattr(msg, 'elements', None)
if elements:
usable_versions = []
for elem in elements:
usable_versions += elem.versions or []
else:
usable_versions = [version for version
in datapath.supported_ofp_version
if version <= msg.version]
if (usable_versions and
max(usable_versions) != min(msg.version,
datapath.ofproto.OFP_VERSION)):
# The version of min(msg.version, datapath.ofproto.OFP_VERSION)
# should be used according to the spec. But we can't.
# So log it and use max(usable_versions) with the hope that
# the switch is able to understand lower version.
# e.g.
# OF 1.1 from switch
# OF 1.2 from Ryu and supported_ofp_version = (1.0, 1.2)
# In this case, 1.1 should be used according to the spec,
# but 1.1 can't be used.
#
# OF1.3.1 6.3.1
# Upon receipt of this message, the recipient must
# calculate the OpenFlow protocol version to be used. If
# both the Hello message sent and the Hello message
# received contained a OFPHET_VERSIONBITMAP hello element,
# and if those bitmaps have some common bits set, the
# negotiated version must be the highest version set in
# both bitmaps. Otherwise, the negotiated version must be
# the smaller of the version number that was sent and the
# one that was received in the version fields. If the
# negotiated version is supported by the recipient, then
# the connection proceeds. Otherwise, the recipient must
# reply with an OFPT_ERROR message with a type field of
# OFPET_HELLO_FAILED, a code field of OFPHFC_INCOMPATIBLE,
# and optionally an ASCII string explaining the situation
# in data, and then terminate the connection.
version = max(usable_versions)
error_desc = 'no compatible version found: '
'switch 0x%x controller 0x%x, but found usable 0x%x. '
'If possible, set the switch to use OF version 0x%x' % (
msg.version, datapath.ofproto.OFP_VERSION,
version, version)
self.hello_failed(error_desc)
return
if not usable_versions:
error_desc = 'unsupported version 0x%x. '
'If possible, set the switch to use one of the versions %s' % (
msg.version, datapath.supported_ofp_version.keys())
self.hello_failed(error_desc)
return
datapath.set_version(max(usable_versions))
# 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.set_state(CONFIG_DISPATCHER)
@set_ev_handler(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
LOG.debug('switch features ev %s', msg)
datapath.id = msg.datapath_id
# hacky workaround, will be removed. OF1.3 doesn't have
# ports. An application should not depend on them. But there
# might be such bad applications so keep this workaround for
# while.
if datapath.ofproto.OFP_VERSION < 0x04:
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.set_state(MAIN_DISPATCHER)
@set_ev_handler(ofp_event.EventOFPEchoRequest,
[HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
def echo_request_handler(self, ev):
msg = ev.msg
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)
@set_ev_handler(ofp_event.EventOFPErrorMsg,
[HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
def error_msg_handler(self, ev):
msg = ev.msg
LOG.debug('error msg ev %s type 0x%x code 0x%x %s',
msg, msg.type, msg.code, utils.hex_array(msg.data))