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
app_lists = ['ryu.controller.ofp_handler']
app
app_mgr = AppManager.get_instance()
#初始化:self.applications_cls={},self.applications={},self.context_cls={},self.context={}webapp
app_mgr.load_apps(app_lists)
函數
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
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
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
contexts = app_mgr.create_contexts()spa
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{}中命令行
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的應用。
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,...]}
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啓動
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的文檔,自定義類看看
註冊事件監聽器
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) #將對應事件的狀態更新到鍵值對中
@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
啓動初始化後調用
self.threads.append(hub.spawn(self._event_loop))
當有新的事件來了,獲取事件對應的handlers,而後再依次把事件放到handler中處理
不一樣的應用程序還有進行其餘處理,如OFPHandler的start()會將OpenFlowController啓動
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應用。
將全部的應用做爲任務,做爲coroutine的task去執行,join使得程序必須等待全部的task都執行完成才能夠退出程序
關閉程序,釋放資源