注冊命令行解析
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類的實(shí)例對象
可以在命令行或者配置文件中設(shè)置的配置選項(xiàng)
配置選項(xiàng)管理器:API用于注冊選項(xiàng)模式陌兑,分組選項(xiàng)啥供,解析選項(xiàng)值和解鎖選項(xiàng)值
對'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:命令行參數(shù)(默認(rèn)為sys.argv [1:])
param prog:程序的名稱(默認(rèn)為sys.argv [0] basename舅锄,不帶擴(kuò)展名.py) #Ryu中此處為None
project:頂層項(xiàng)目名稱谜悟,用于定位配置文件
version:程序版本
default_config_files:默認(rèn)使用的配置文件
沒有指定應(yīng)用程序孵奶,默認(rèn)使用ofp_handler
app_lists = ['ryu.controller.ofp_handler']
創(chuàng)建AppManager實(shí)例對象
app_mgr = AppManager.get_instance()
#初始化:self.applications_cls={},self.applications={},self.context_cls={},self.context={}
通過app_mgr.load_apps加載應(yīng)用
app_mgr.load_apps(app_lists)
load_apps | app_manager.py
cls = self.load_app(app_cls_name) #下一個(gè)標(biāo)題有這個(gè)函數(shù)
self.applications_cls[app_cls_name] = cls #存放應(yīng)用程序的實(shí)例對象
services = []
for key, context_cls in cls.context_iteritems(): #cls.context_iteritems()返回應(yīng)用程序的_CONTEXTS鍵值對
v = self.contexts_cls.setdefault(key, context_cls) #存放依賴服務(wù)的實(shí)例對象
assert v == context_cls
context_modules.append(context_cls.__module__) #存放服務(wù)的模塊名稱
if issubclass(context_cls, RyuApp):
services.extend(get_dependent_services(context_cls)) ##將依賴的服務(wù)模塊的依賴也添加到services列表中,其中還包括要監(jiān)聽的事件的模塊
# 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]) #服務(wù)添加到app_lists中
self.applications_cls存放的是還未初始化的類
cls.context_iteritems() :來自RyuApp的類方法疲酌,返回application的_CONTEXTS的迭代器
#ryu.app.ws_topology.py例子
_CONTEXTS = {
'wsgi': WSGIApplication,
'switches': switches.Switches,
}
context_modules.append(context_cls.module) ,將已經(jīng)存放到contexts_cls的模塊加入到context_modules了袁,防止重復(fù)添加
這個(gè)函數(shù)主要是為了獲得app_lists中的_CONTEXT的類以及_CONTEXT類所依賴的類朗恳,需要監(jiān)聽的事件的類
將服務(wù)也添加到app_lists中,用于后續(xù)的create_contexts
get_dependent_services
def get_dependent_services(cls):
services = []
for _k, m in inspect.getmembers(cls, _is_method): #返回實(shí)例對象的方法
if _has_caller(m): #判斷方法是否有caller屬性载绿,即裝飾器的函數(shù)
for ev_cls, c in m.callers.items(): #{事件:_Caller對象}粥诫,_Caller屬性:1.生成事件的階段 2.生成事件的模塊
service = getattr(sys.modules[ev_cls.__module__],
'_SERVICE_NAME', None) #事件所在的模塊實(shí)例
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) #動(dòng)態(tài)導(dǎo)入模塊
#返回加載應(yīng)用的類: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()
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 #創(chuàng)建好的應(yīng)用程序存放到contexts中
return self.contexts
由上面可知contexts_cls{}存放的是未初始化的類,初始化結(jié)束的存放到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的應(yīng)用程序怕享,就能看到None
if hasattr(cls, 'OFP_VERSIONS') and cls.OFP_VERSIONS is not None:
ofproto_protocol.set_app_supported_versions(cls.OFP_VERSIONS) #查看應(yīng)用程序的版本是否支持执赡,不支持的話拋出異常
if app_name is not None:
assert app_name not in self.applications
app = cls(*args, **kwargs) #創(chuàng)建類實(shí)例對象
register_app(app) #注冊應(yīng)用程序?qū)嵗?,存到SERVICE_BRICKS中函筋,SERVICE_BRICKS[app.name] = app沙合。
assert app.name not in self.applications
self.applications[app.name] = app #將應(yīng)用程序保存到applications的字典中
return app
初次調(diào)用的時(shí)候傳入的app_name = None,通過--verbose啟動(dòng)應(yīng)用程序時(shí)候可以看到:
$ryu-manager ryu.app.ws_topology --verbose
loading app ryu.app.ws_topology
loading app ryu.controller.ofp_handler
instantiating app None of Switches #初次調(diào)用的時(shí)候傳入的app_name = None
creating context switches
creating context wsgi
register_app(app)主要是注冊應(yīng)用實(shí)例跌帐,存放到SERVICE_BRICKS{“應(yīng)用名”:應(yīng)用}首懈,同時(shí)注冊應(yīng)用程序里裝飾器的handler。
self.applications存放的是已經(jīng)初始化好的應(yīng)用谨敛,注意此處存的是context中繼承RyuApp的應(yīng)用究履。
register_app(app) | handler.py
SERVICE_BRICKS[app.name] = app
之后調(diào)用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) #將事件的處理函數(shù)注冊,在app_manager.py中有對應(yīng)的函數(shù)
事件處理器注冊到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) #剛才調(diào)用這個(gè)函數(shù)是初始化_CONTEXT里的,現(xiàn)在是對應(yīng)用程序的初始化
self._update_bricks() #注冊事件監(jiān)聽器
self.report_bricks() #啟動(dòng)應(yīng)用時(shí)添加”--verbose“可以查看到事件監(jiān)聽的信息肥惭,如:PROVIDES EventOFPPortStatus TO {'switches': set(['main']), 'monitor': set(['main'])}
threads = []
for app in self.applications.values():
t = app.start() #調(diào)用應(yīng)用程序的啟動(dòng)方法
if t is not None:
app.set_main_thread(t) #設(shè)置當(dāng)前應(yīng)用程序的線程盯仪,如果不設(shè)置,就沒辦法在stop()函數(shù)里刪除這個(gè)線程
threads.append(t) #將這個(gè)線程添加到線程列表里
return threads
self._instantiate(app_name, cls, *args, **kwargs)
此處初始化的是啟動(dòng)的應(yīng)用程序和非RyuApp的應(yīng)用蜜葱,剛才已經(jīng)將應(yīng)用程序的依賴已經(jīng)初始化完成了全景。
app.start()調(diào)用應(yīng)用程序的啟動(dòng)函數(shù),如默認(rèn)啟動(dòng)的應(yīng)用程序OFPHandler的start()會(huì)將OpenFlowController啟動(dòng)
_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) #取出定義事件的模塊取出對應(yīng)的SERVICE實(shí)例
if brick:
brick.register_observer(ev_cls, i.name,
c.dispatchers) #c.dispatchers代表事件產(chǎn)生的階段爸黄,如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)
注冊監(jiān)聽器
基類里說明了_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是子類生成的事件列表滞伟,但是當(dāng)且僅當(dāng)事件類是在RyuApp子類的不同python模塊中定義時(shí)才應(yīng)該指定。
不知道理解有沒有問題炕贵,查看Ryu的文檔梆奈,自定義類看看
register_observer | app_manager.py
注冊事件監(jiān)聽器
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) #將對應(yīng)事件的狀態(tài)更新到鍵值對中
report_bricks() | app_manager.py
@staticmethod
def report_bricks():
for brick, i in SERVICE_BRICKS.items():
AppManager._report_brick(brick, i)
報(bào)告監(jiān)聽的事件信息,可以啟動(dòng)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
啟動(dòng)初始化后調(diào)用
self.threads.append(hub.spawn(self._event_loop))
當(dāng)有新的事件來了亩钟,獲取事件對應(yīng)的handlers,然后再依次把事件放到handler中處理
不同的應(yīng)用程序還有進(jìn)行其他處理鳖轰,如OFPHandler的start()會(huì)將OpenFlowController啟動(dòng)
webapp=wsgi.start_service(app_mgr)| manager.py
def start_service(app_mgr):
for instance in app_mgr.contexts.values():
if instance.__class__ == WSGIApplication: #如果實(shí)例的類是WSGIApplication
return WSGIServer(instance)
return None
僅有依賴的服務(wù)模塊中含有WSGIApplication的時(shí)候才啟動(dòng)WSGI服務(wù)的實(shí)例清酥,提供web應(yīng)用。
hub.joinall(services) | manager.py
將所有的應(yīng)用作為任務(wù)蕴侣,作為coroutine的task去執(zhí)行焰轻,join使得程序必須等待所有的task都執(zhí)行完成才可以退出程序
app_mgr.close() | manager.py
關(guān)閉程序,釋放資源