事件的收发是多个app在运行阶段,进行的事件接收发送(ryu/ryu/base/app_manager.py)
class RyuApp(object):
"""
The base class for Ryu applications.
RyuApp subclasses are instantiated after ryu-manager loaded
all requested Ryu application modules.
__init__ should call RyuApp.__init__ with the same arguments.
It's illegal to send any events in __init__.
The instance attribute 'name' is the name of the class used for
message routing among Ryu applications. (Cf. send_event)
It's set to __class__.__name__ by RyuApp.__init__.
It's discouraged for subclasses to override this.
"""
_CONTEXTS = {}
"""
A dictionary to specify contexts which this Ryu application wants to use.
Its key is a name of context and its value is an ordinary class
which implements the context. The class is instantiated by app_manager
and the instance is shared among RyuApp subclasses which has _CONTEXTS
member with the same key. A RyuApp subclass can obtain a reference to
the instance via its __init__'s kwargs as the following.
Example::
_CONTEXTS = {
'network': network.Network
}
def __init__(self, *args, *kwargs):
self.network = kwargs['network']
"""
_EVENTS = []
"""
A list of event classes which this RyuApp subclass would generate.
This should be specified if and only if event classes are defined in
a different python module from the RyuApp subclass is.
"""
OFP_VERSIONS = None
"""
A list of supported OpenFlow versions for this RyuApp.
The default is all versions supported by the framework.
Examples::
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION,
ofproto_v1_2.OFP_VERSION]
If multiple Ryu applications are loaded in the system,
the intersection of their OFP_VERSIONS is used.
"""
@classmethod
def context_iteritems(cls):
"""
Return iterator over the (key, contxt class) of application context
"""
return iter(cls._CONTEXTS.items())
def __init__(self, *_args, **_kwargs):
super(RyuApp, self).__init__()
self.name = self.__class__.__name__
self.event_handlers = {} # ev_cls -> handlers:list
self.observers = {} # ev_cls -> observer-name -> states:set
self.threads = []
self.main_thread = None
self.events = hub.Queue(128)
self._events_sem = hub.BoundedSemaphore(self.events.maxsize)
if hasattr(self.__class__, 'LOGGER_NAME'):
self.logger = logging.getLogger(self.__class__.LOGGER_NAME)
else:
self.logger = logging.getLogger(self.name)
self.CONF = cfg.CONF
# prevent accidental creation of instances of this class outside RyuApp
class _EventThreadStop(event.EventBase):
pass
self._event_stop = _EventThreadStop()
self.is_active = True
def register_handler(self, ev_cls, handler):
assert callable(handler)
self.event_handlers.setdefault(ev_cls, [])
self.event_handlers[ev_cls].append(handler)
def unregister_handler(self, ev_cls, handler):
assert callable(handler)
self.event_handlers[ev_cls].remove(handler)
if not self.event_handlers[ev_cls]:
del self.event_handlers[ev_cls]
def register_observer(self, ev_cls, name, states=None):
states = states or set()
ev_cls_observers = self.observers.setdefault(ev_cls, {})
ev_cls_observers.setdefault(name, set()).update(states)
def unregister_observer(self, ev_cls, name):
observers = self.observers.get(ev_cls, {})
observers.pop(name)
def unregister_observer_all_event(self, name):
for observers in self.observers.values():
observers.pop(name, None)
def observe_event(self, ev_cls, states=None):
brick = _lookup_service_brick_by_ev_cls(ev_cls)
if brick is not None:
brick.register_observer(ev_cls, self.name, states)
def unobserve_event(self, ev_cls):
brick = _lookup_service_brick_by_ev_cls(ev_cls)
if brick is not None:
brick.unregister_observer(ev_cls, self.name)
def get_handlers(self, ev, state=None):
"""Returns a list of handlers for the specific event.
:param ev: The event to handle.
:param state: The current state. ("dispatcher")
If None is given, returns all handlers for the event.
Otherwise, returns only handlers that are interested
in the specified state.
The default is None.
"""
ev_cls = ev.__class__
handlers = self.event_handlers.get(ev_cls, [])
if state is None:
return handlers
def test(h):
if not hasattr(h, 'callers') or ev_cls not in h.callers:
# dynamically registered handlers does not have
# h.callers element for the event.
return True
states = h.callers[ev_cls].dispatchers
if not states:
# empty states means all states
return True
return state in states
return filter(test, handlers)
def register_observer(self, ev_cls, name, states=None):
states = states or set()
ev_cls_observers = self.observers.setdefault(ev_cls, {})
ev_cls_observers.setdefault(name, set()).update(states)
def unregister_observer(self, ev_cls, name):
observers = self.observers.get(ev_cls, {})
observers.pop(name)
def unregister_observer_all_event(self, name):
for observers in self.observers.values():
observers.pop(name, None)
def observe_event(self, ev_cls, states=None):
brick = _lookup_service_brick_by_ev_cls(ev_cls)
if brick is not None:
brick.register_observer(ev_cls, self.name, states)
def unobserve_event(self, ev_cls):
brick = _lookup_service_brick_by_ev_cls(ev_cls)
if brick is not None:
brick.unregister_observer(ev_cls, self.name)
def get_handlers(self, ev, state=None):
"""Returns a list of handlers for the specific event.
:param ev: The event to handle.
:param state: The current state. ("dispatcher")
If None is given, returns all handlers for the event.
Otherwise, returns only handlers that are interested
in the specified state.
The default is None.
"""
ev_cls = ev.__class__
handlers = self.event_handlers.get(ev_cls, [])
if state is None:
return handlers
def test(h):
if not hasattr(h, 'callers') or ev_cls not in h.callers:
# dynamically registered handlers does not have
# h.callers element for the event.
return True
states = h.callers[ev_cls].dispatchers
if not states:
# empty states means all states
return True
return state in states
return filter(test, handlers)
def get_observers(self, ev, state):
observers = []
for k, v in self.observers.get(ev.__class__, {}).items():
if not state or not v or state in v:
observers.append(k)
return observers
def send_request(self, req):
"""
Make a synchronous request.
Set req.sync to True, send it to a Ryu application specified by
req.dst, and block until receiving a reply.
Returns the received reply.
The argument should be an instance of EventRequestBase.
"""
assert isinstance(req, EventRequestBase)
req.sync = True
req.reply_q = hub.Queue()
self.send_event(req.dst, req)
# going to sleep for the reply
return req.reply_q.get()
def _event_loop(self):
while self.is_active or not self.events.empty():
ev, state = self.events.get()
self._events_sem.release()
if ev == self._event_stop:
continue
handlers = self.get_handlers(ev, state)
for handler in handlers:
try:
handler(ev)
except hub.TaskExit:
# Normal exit.
# Propagate upwards, so we leave the event loop.
raise
except:
LOG.exception('%s: Exception occurred during handler processing. '
'Backtrace from offending handler '
'[%s] servicing event [%s] follows.',
self.name, handler.__name__, ev.__class__.__name__)
def _send_event(self, ev, state):
self._events_sem.acquire()
self.events.put((ev, state))
def send_event(self, name, ev, state=None):
"""
Send the specified event to the RyuApp instance specified by name.
"""
if name in SERVICE_BRICKS:
if isinstance(ev, EventRequestBase):
ev.src = self.name
LOG.debug("EVENT %s->%s %s",
self.name, name, ev.__class__.__name__)
SERVICE_BRICKS[name]._send_event(ev, state)
else:
LOG.debug("EVENT LOST %s->%s %s",
self.name, name, ev.__class__.__name__)
def send_event_to_observers(self, ev, state=None):
"""
Send the specified event to all observers of this RyuApp.
"""
for observer in self.get_observers(ev, state):
self.send_event(observer, ev, state)
接下来分析app基类中注册事件相关的函数
self.observers是一个字典,key是本app能够产生的事件类型,value还是一个字典,key是监听这个事件的app名字,value是他指定的监听生效的阶段
register_observer与unregister_observer函数被其他逻辑调用,从而向本app注册或取消注册事件的目的地
observe_event和unobserve_event则被本app调用,调用对方app的register_observer,向其他app声明自己的监听
320行send_event_to_observers被本app中产生事件的函数所调用,向已经跟本app注册了监听的app们发送事件
def send_request(self, req):
"""
Make a synchronous request.
Set req.sync to True, send it to a Ryu application specified by
req.dst, and block until receiving a reply.
Returns the received reply.
The argument should be an instance of EventRequestBase.
"""
assert isinstance(req, EventRequestBase)
req.sync = True
req.reply_q = hub.Queue()
self.send_event(req.dst, req)
# going to sleep for the reply
return req.reply_q.get()
def _event_loop(self):
while self.is_active or not self.events.empty():
ev, state = self.events.get()
self._events_sem.release()
if ev == self._event_stop:
continue
handlers = self.get_handlers(ev, state)
for handler in handlers:
try:
handler(ev)
except hub.TaskExit:
# Normal exit.
# Propagate upwards, so we leave the event loop.
raise
except:
LOG.exception('%s: Exception occurred during handler processing. '
'Backtrace from offending handler '
'[%s] servicing event [%s] follows.',
self.name, handler.__name__, ev.__class__.__name__)
def _send_event(self, ev, state):
self._events_sem.acquire()
self.events.put((ev, state))
def send_event(self, name, ev, state=None):
"""
Send the specified event to the RyuApp instance specified by name.
"""
if name in SERVICE_BRICKS:
if isinstance(ev, EventRequestBase):
ev.src = self.name
LOG.debug("EVENT %s->%s %s",
self.name, name, ev.__class__.__name__)
SERVICE_BRICKS[name]._send_event(ev, state)
else:
LOG.debug("EVENT LOST %s->%s %s",
self.name, name, ev.__class__.__name__)
def send_event_to_observers(self, ev, state=None):
"""
Send the specified event to all observers of this RyuApp.
"""
for observer in self.get_observers(ev, state):
self.send_event(observer, ev, state)
def reply_to_request(self, req, rep):
"""
Send a reply for a synchronous request sent by send_request.
The first argument should be an instance of EventRequestBase.
The second argument should be an instance of EventReplyBase.
"""
assert isinstance(req, EventRequestBase)
assert isinstance(rep, EventReplyBase)
rep.dst = req.src
if req.sync:
req.reply_q.put(rep)
else:
self.send_event(rep.dst, rep)
def start(self):
"""
Hook that is called after startup initialization is done.
"""
self.threads.append(hub.spawn(self._event_loop))
def stop(self):
if self.main_thread:
hub.kill(self.main_thread)
self.is_active = False
self._send_event(self._event_stop, None)
hub.joinall(self.threads)
def set_main_thread(self, thread):
"""
Set self.main_thread so that stop() can terminate it.
Only AppManager.instantiate_apps should call this function.
"""
self.main_thread = thread
def register_handler(self, ev_cls, handler):
assert callable(handler)
self.event_handlers.setdefault(ev_cls, [])
self.event_handlers[ev_cls].append(handler)
def unregister_handler(self, ev_cls, handler):
assert callable(handler)
self.event_handlers[ev_cls].remove(handler)
if not self.event_handlers[ev_cls]:
del self.event_handlers[ev_cls]
def register_observer(self, ev_cls, name, states=None):
states = states or set()
ev_cls_observers = self.observers.setdefault(ev_cls, {})
ev_cls_observers.setdefault(name, set()).update(states)
def unregister_observer(self, ev_cls, name):
observers = self.observers.get(ev_cls, {})
observers.pop(name)
def unregister_observer_all_event(self, name):
for observers in self.observers.values():
observers.pop(name, None)
def observe_event(self, ev_cls, states=None):
brick = _lookup_service_brick_by_ev_cls(ev_cls)
if brick is not None:
brick.register_observer(ev_cls, self.name, states)
def unobserve_event(self, ev_cls):
brick = _lookup_service_brick_by_ev_cls(ev_cls)
if brick is not None:
brick.unregister_observer(ev_cls, self.name)
def get_handlers(self, ev, state=None):
"""Returns a list of handlers for the specific event.
:param ev: The event to handle.
:param state: The current state. ("dispatcher")
If None is given, returns all handlers for the event.
Otherwise, returns only handlers that are interested
in the specified state.
The default is None.
"""
ev_cls = ev.__class__
handlers = self.event_handlers.get(ev_cls, [])
if state is None:
return handlers
def test(h):
if not hasattr(h, 'callers') or ev_cls not in h.callers:
# dynamically registered handlers does not have
# h.callers element for the event.
return True
states = h.callers[ev_cls].dispatchers
if not states:
# empty states means all states
return True
return state in states
return filter(test, handlers)
def get_observers(self, ev, state):
observers = []
for k, v in self.observers.get(ev.__class__, {}).items():
if not state or not v or state in v:
observers.append(k)
return observers
def send_request(self, req):
"""
Make a synchronous request.
Set req.sync to True, send it to a Ryu application specified by
req.dst, and block until receiving a reply.
Returns the received reply.
The argument should be an instance of EventRequestBase.
"""
assert isinstance(req, EventRequestBase)
req.sync = True
req.reply_q = hub.Queue()
self.send_event(req.dst, req)
# going to sleep for the reply
return req.reply_q.get()
def _event_loop(self):
while self.is_active or not self.events.empty():
ev, state = self.events.get()
self._events_sem.release()
if ev == self._event_stop:
continue
handlers = self.get_handlers(ev, state)
for handler in handlers:
try:
handler(ev)
except hub.TaskExit:
# Normal exit.
# Propagate upwards, so we leave the event loop.
raise
except:
LOG.exception('%s: Exception occurred during handler processing. '
'Backtrace from offending handler '
'[%s] servicing event [%s] follows.',
self.name, handler.__name__, ev.__class__.__name__)
def _update_bricks(self):
for i in SERVICE_BRICKS.values():
for _k, m in inspect.getmembers(i, inspect.ismethod):
if not hasattr(m, 'callers'):
continue
for ev_cls, c in m.callers.items():
if not c.ev_source:
continue
brick = _lookup_service_brick_by_mod_name(c.ev_source)
if brick:
brick.register_observer(ev_cls, i.name,
c.dispatchers)
# allow RyuApp and Event class are in different module
for brick in SERVICE_BRICKS.values():
if ev_cls in brick._EVENTS:
brick.register_observer(ev_cls, i.name,
c.dispatchers)
小结:
参考:
[1] ohmyadd:https://www.jianshu.com/p/ff59c7c5f056
[2] Ryubook:http://osrg.github.io/ryu-book/en/html/
[3] Ryu官方文档:https://ryu.readthedocs.io/en/latest/