Ryu 源碼解讀

Ryu 源碼解讀

註冊命令行解析

CONF.register_cli_opts([
    cfg.ListOpt('app-lists', default=[],
                help='application module name to run'),
    cfg.MultiStrOpt('app', positional=True, default=[],
                    help='application module name to run'),
    cfg.StrOpt('pid-file', default=None, help='pid file name'),
    cfg.BoolOpt('enable-debugger', default=False,
                help='don\'t overwrite Python standard threading library'
                '(use only for debugging)')
])

CONF=ConfigOpts(),ConfigOpts類的實例對象python

能夠在命令行或者配置文件中設置的配置選項
配置選項管理器:API用於註冊選項模式,分組選項,解析選項值和解鎖選項值
對'config_file'和'config_dir'的支持

解析配置文件

CONF(args=args, prog=prog,
 project='ryu', version='ryu-manager %s' % version,
 default\_config\_files=['/usr/local/etc/ryu/ryu.conf’])

args:命令行參數(默認爲sys.argv [1:])
param prog:程序的名稱(默認爲sys.argv [0] basename,不帶擴展名.py) #Ryu中此處爲None
project:頂層項目名稱,用於定位配置文件
version:程序版本
default_config_files:默認使用的配置文件web

沒有指定應用程序,默認使用ofp_handler

app_lists = ['ryu.controller.ofp_handler']app

建立AppManager實例對象

app_mgr = AppManager.get_instance() #初始化:self.applications_cls={},self.applications={},self.context_cls={},self.context={}webapp

經過app_mgr.load_apps加載應用

app_mgr.load_apps(app_lists)函數

load_apps | app_manager.py

cls = self.load_app(app_cls_name) #下一個標題有這個函數
self.applications_cls[app_cls_name] = cls #存放應用程序的實例對象
services = []
for key, context_cls in cls.context_iteritems():  #cls.context_iteritems()返回應用程序的_CONTEXTS鍵值對
    v = self.contexts_cls.setdefault(key, context_cls)  #存放依賴服務的實例對象
    assert v == context_cls
    context_modules.append(context_cls.__module__)  #存放服務的模塊名稱

    if issubclass(context_cls, RyuApp):
          services.extend(get_dependent_services(context_cls)) ##將依賴的服務模塊的依賴也添加到services列表中,其中還包括要監聽的事件的模塊
# we can't load an app that will be initiataed for
# contexts.
for i in get_dependent_services(cls):
    if i not in context_modules:
        services.append(i)
if services:
    app_lists.extend([s for s in set(services)
                      if s not in app_lists])         #服務添加到app_lists中

self.applications_cls存放的是還未初始化的類
cls.context_iteritems() :來自RyuApp的類方法,返回application的_CONTEXTS的迭代器oop

#ryu.app.ws_topology.py例子
_CONTEXTS = {
      'wsgi': WSGIApplication,
      'switches': switches.Switches,
  }

context_modules.append(context_cls.__module__) ,將已經存放到contexts_cls的模塊加入到context_modules,防止重複添加
這個函數主要是爲了得到app_lists中的_CONTEXT的類以及_CONTEXT類所依賴的類,須要監聽的事件的類
將服務也添加到app_lists中,用於後續的create_contextsthis

get_dependent_services

def get_dependent_services(cls):
    services = []
    for _k, m in inspect.getmembers(cls, _is_method): #返回實例對象的方法
        if _has_caller(m):  #判斷方法是否有caller屬性,即裝飾器的函數
            for ev_cls, c in m.callers.items(): #{事件:_Caller對象},_Caller屬性:1.生成事件的階段 2.生成事件的模塊
                service = getattr(sys.modules[ev_cls.__module__],
                                  '_SERVICE_NAME', None)  #事件所在的模塊實例
                if service:
                    # 避免註冊本身事件的cls(好比ofp_handler)
                    if cls.__module__ != service:
                        services.append(service)   #事件的模塊放到services列表中

    m = sys.modules[cls.__module__]
    services.extend(getattr(m, '_REQUIRED_APP', []))
    services = list(set(services))
    return services

load_app | app_manager.py

mod = utils.import_module(name)   #動態導入模塊
#返回加載應用的類:1)是不是類、2)是不是RyuApp子類、3)類名是否等於模塊名
clses = inspect.getmembers(mod,
                           lambda cls: (inspect.isclass(cls) and
                                        issubclass(cls, RyuApp) and
                                        mod.__name__ ==
                                        cls.__module__))
if clses:
    return clses[0][1]  #返回類的對象,clses[0][0]是類名
return None

app_mgr.create_contexts

contexts = app_mgr.create_contexts()spa

create_contexts() | app_manager.py

for key, cls in self.contexts_cls.items():
    if issubclass(cls, RyuApp):
        # hack for dpset
        context = self._instantiate(None, cls)
    else:
        context = cls() #參考ws_topology.py中的‘wsgi’的WSGIApplication並非其RyuAPP
    LOG.info('creating context %s', key)
    assert key not in self.contexts   
    self.contexts[key] = context   #建立好的應用程序存放到contexts中
return self.contexts

由上面可知contexts_cls{}存放的是未初始化的類,初始化結束的存放到contexts{}中命令行

_instantiate(None,cls) | app_manager.py

def _instantiate(self, app_name, cls, *args, **kwargs):
    # for now, only single instance of a given module
    # Do we need to support multiple instances?
    # Yes, maybe for slicing.
    LOG.info('instantiating app %s of %s', app_name, cls.__name__)    #若是跑帶有_CONTEXT的應用程序,就能看到None

    if hasattr(cls, 'OFP_VERSIONS') and cls.OFP_VERSIONS is not None:
        ofproto_protocol.set_app_supported_versions(cls.OFP_VERSIONS)  #查看應用程序的版本是否支持,不支持的話拋出異常

    if app_name is not None:
        assert app_name not in self.applications
    app = cls(*args, **kwargs)    #建立類實例對象
    register_app(app)     #註冊應用程序實例 ,存到SERVICE_BRICKS中,SERVICE_BRICKS[app.name] = app。
    assert app.name not in self.applications
    self.applications[app.name] = app      #將應用程序保存到applications的字典中
    return app

初次調用的時候傳入的app_name = None,經過--verbose啓動應用程序時候能夠看到:線程

$ryu-manager ryu.app.ws_topology --verbose 
loading app ryu.app.ws_topology
loading app ryu.controller.ofp_handler
instantiating app None of Switches          #初次調用的時候傳入的app_name = None
creating context switches
creating context wsgi

register_app(app)主要是註冊應用實例,存放到SERVICE_BRICKS{「應用名」:應用},同時註冊應用程序裏裝飾器的handler。
self.applications存放的是已經初始化好的應用,注意此處存的是context中繼承RyuApp的應用。

register_app(app) | handler.py

SERVICE_BRICKS[app.name] = app
以後調用register_instance

def register_instance(i):
    for _k, m in inspect.getmembers(i, inspect.ismethod):
        # LOG.debug('instance %s k %s m %s', i, _k, m)
        if _has_caller(m):                  #查看有無裝飾器
            for ev_cls, c in m.callers.items():
                i.register_handler(ev_cls, m)   #將事件的處理函數註冊,在app_manager.py中有對應的函數

事件處理器註冊到event_handlers {「事件」:[方法1,方法2,...]}

services.extend(app_mgr.instantiate_apps(**contexts))

def instantiate_apps(self, *args, **kwargs):
        for app_name, cls in self.applications_cls.items():
            self._instantiate(app_name, cls, *args, **kwargs)    #剛纔調用這個函數是初始化_CONTEXT裏的,如今是對應用程序的初始化

        self._update_bricks() #註冊事件監聽器
        self.report_bricks()  #啓動應用時添加」--verbose「能夠查看到事件監聽的信息,如:PROVIDES EventOFPPortStatus TO {'switches': set(['main']), 'monitor': set(['main'])}


        threads = []
        for app in self.applications.values():
            t = app.start() #調用應用程序的啓動方法
            if t is not None:
                app.set_main_thread(t)  #設置當前應用程序的線程,若是不設置,就沒辦法在stop()函數裏刪除這個線程
                threads.append(t) #將這個線程添加到線程列表裏
        return threads

self._instantiate(app_name, cls, args, *kwargs)
此處初始化的是啓動的應用程序和非RyuApp的應用,剛纔已經將應用程序的依賴已經初始化完成了。
app.start()調用應用程序的啓動函數,如默認啓動的應用程序OFPHandler的start()會將OpenFlowController啓動

_update_bricks | app_manager.py

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:   #ev_source爲定義的模塊,能夠到handle.py中查看
                    continue

                brick = _lookup_service_brick_by_mod_name(c.ev_source)   #取出定義事件的模塊取出對應的SERVICE實例
                if brick:
                    brick.register_observer(ev_cls, i.name,
                                            c.dispatchers)      #c.dispatchers表明事件產生的階段,如MAIN_DISPATCHER

                # 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)

註冊監聽器
基類裏說明了_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.
"""

_EVENTS是子類生成的事件列表,可是當且僅當事件類是在RyuApp子類的不一樣python模塊中定義時才應該指定。

不知道理解有沒有問題,查看Ryu的文檔,自定義類看看

register_observer | app_manager.py

註冊事件監聽器

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) #將對應事件的狀態更新到鍵值對中

report_bricks() | app_manager.py

@staticmethod
  def report_bricks():
      for brick, i in SERVICE_BRICKS.items():
          AppManager._report_brick(brick, i)

報告監聽的事件信息,能夠啓動ws_topology.py,經過--verbose查看

BRICK switches
  PROVIDES EventLinkAdd TO {'WebSocketTopology': set([])}
  PROVIDES EventSwitchEnter TO {'WebSocketTopology': set([])}
  PROVIDES EventLinkDelete TO {'WebSocketTopology': set([])}
  PROVIDES EventSwitchLeave TO {'WebSocketTopology': set([])}
  PROVIDES EventHostAdd TO {'WebSocketTopology': set([])}
  CONSUMES EventSwitchRequest
  CONSUMES EventOFPStateChange
  CONSUMES EventHostRequest
  CONSUMES EventOFPPacketIn
  CONSUMES EventLinkRequest
  CONSUMES EventOFPPortStatus
BRICK WebSocketTopology
  CONSUMES EventLinkAdd
  CONSUMES EventSwitchEnter
  CONSUMES EventLinkDelete
  CONSUMES EventSwitchLeave
  CONSUMES EventHostAdd
BRICK ofp_event
  PROVIDES EventOFPPacketIn TO {'switches': set(['main'])}
  PROVIDES EventOFPPortStatus TO {'switches': set(['main'])}
  PROVIDES EventOFPStateChange TO {'switches': set(['main', 'dead'])}
  CONSUMES EventOFPSwitchFeatures
  CONSUMES EventOFPPortDescStatsReply
  CONSUMES EventOFPHello
  CONSUMES EventOFPErrorMsg
  CONSUMES EventOFPEchoRequest
  CONSUMES EventOFPPortStatus
  CONSUMES EventOFPEchoReply

app.start | app_manager.py

啓動初始化後調用
self.threads.append(hub.spawn(self._event_loop))
當有新的事件來了,獲取事件對應的handlers,而後再依次把事件放到handler中處理
不一樣的應用程序還有進行其餘處理,如OFPHandler的start()會將OpenFlowController啓動

webapp=wsgi.start_service(app_mgr)| manager.py

def start_service(app_mgr):
    for instance in app_mgr.contexts.values():
        if instance.__class__ == WSGIApplication:  #若是實例的類是WSGIApplication
            return WSGIServer(instance)

    return None

僅有依賴的服務模塊中含有WSGIApplication的時候才啓動WSGI服務的實例,提供web應用。

hub.joinall(services) | manager.py

將全部的應用做爲任務,做爲coroutine的task去執行,join使得程序必須等待全部的task都執行完成才能夠退出程序

app_mgr.close() | manager.py

關閉程序,釋放資源

相關文章
相關標籤/搜索