一:模塊間通信機制分析
Ryu是一款非常輕便的SDN控制器,在科研方面得到了廣泛的應用。相比其他控制器,受益於Python語言,在Ryu上開發SDN應用的效率要遠高於其他控制器。為了解決復雜的業務,有時需要在Ryu上開發多模塊來協同工作,從而共同完成復雜的業務。這里只考慮lookup_service_brick獲取模塊實例對象,從而獲取動態實例對象的數據!!!
以:SDN實驗---Ryu的應用開發(五)網絡拓撲發現為例(TopoDetect.py),作為自定義的app,被加載到一個新的app中(Test.py)
(一)使用lookup_service_brick加載模塊實例時,對於我們自己定義的app(需要被加載的),我們需要在類中定義self.name。
(二)源碼分析: 查看如何使用self.name
參考:SDN實驗---Ryu的源碼分析
1.從main入口查找:ryu/cmd/manager.py
from ryu.base.app_manager import AppManager
def main(args=None, prog=None):
app_lists = CONF.app_lists + CONF.app #從ryu-manager傳入的App參數中獲取app_list (詳細可看參考)---重點:我們需要同時將被加載的類傳入
app_mgr = AppManager.get_instance() #單例對象,管理所有的app app_mgr.load_apps(app_lists) #加載所有app:重點 contexts = app_mgr.create_contexts() #創建上下文 services = [] services.extend(app_mgr.instantiate_apps(**contexts)) #重點:實例化app----下一步分析 webapp = wsgi.start_service(app_mgr) if webapp: thr = hub.spawn(webapp) services.append(thr) try: hub.joinall(services) except KeyboardInterrupt: logger.debug("Keyboard Interrupt received. " "Closing RYU application manager...") finally: app_mgr.close()
2.app_mgr.instantiate_apps實例化app : ryu.base.app_manager
class AppManager(object): def load_app(self, name): mod = utils.import_module(name) clses = inspect.getmembers(mod, lambda cls: (inspect.isclass(cls) and issubclass(cls, RyuApp) and mod.__name__ == cls.__module__)) if clses: return clses[0][1] return None def load_apps(self, app_lists): app_lists = [app for app in itertools.chain.from_iterable(app.split(',') for app in app_lists)] while len(app_lists) > 0: app_cls_name = app_lists.pop(0) context_modules = [x.__module__ for x in self.contexts_cls.values()] if app_cls_name in context_modules: continue LOG.info('loading app %s', app_cls_name) cls = self.load_app(app_cls_name) if cls is None: continue self.applications_cls[app_cls_name] = cls services = [] for key, context_cls in cls.context_iteritems(): 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)) # 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]) def instantiate_apps(self, *args, **kwargs): for app_name, cls in self.applications_cls.items(): self._instantiate(app_name, cls, *args, **kwargs) self._update_bricks() self.report_bricks() threads = [] for app in self.applications.values(): t = app.start() if t is not None: app.set_main_thread(t) threads.append(t) return threads 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__) 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) #注冊app 重點!!! assert app.name not in self.applications self.applications[app.name] = app return app
3.注冊App實例對象到服務鏈中 :ryu.base.app_manager(與2同文件下)
SERVICE_BRICKS = {} def lookup_service_brick(name): return SERVICE_BRICKS.get(name) #重點2:由重點1可以知道,我們最終將app實例保存在了全局字典中,所以我們可以直接使用該方法獲取對於實例對象,實現模塊之間通信(數據分享) def _lookup_service_brick_by_ev_cls(ev_cls): return _lookup_service_brick_by_mod_name(ev_cls.__module__) def _lookup_service_brick_by_mod_name(mod_name): return lookup_service_brick(mod_name.split('.')[-1]) def register_app(app): assert isinstance(app, RyuApp) assert app.name not in SERVICE_BRICKS SERVICE_BRICKS[app.name] = app #重點1:將app的name成員作為key,app對象作為value放入全局字典中 register_instance(app)
完結撒花!!!
(三)實例演示
1.Test.py使用lookup_service_brick函數
from ryu.base import app_manager from ryu.base.app_manager import lookup_service_brick from ryu.ofproto import ofproto_v1_3 from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER,DEAD_DISPATCHER,HANDSHAKE_DISPATCHER #只是表示datapath數據路徑的狀態 from ryu.controller.handler import set_ev_cls from ryu.topology.switches import Switches from ryu.topology.switches import LLDPPacket class Test(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self,*args,**kwargs): super(Test,self).__init__(*args,**kwargs) self.name = "delay" self.topology = lookup_service_brick("topology") #注意:我們使用lookup_service_brick加載模塊實例時,對於我們自己定義的app,我們需要在類中定義self.name。 #此外,最重要的是:我們啟動本模塊DelayDetect時,必須同時啟動自定義的模塊!!! 比如:ryu-manager ./TopoDetect.py ./DelayDetect.py --verbose --observe-links
self.switches = lookup_service_brick("switches") #該app是在ryu內部被實例化了,所以我們可以直接加載,同樣還有ofp_event也是被實例化,可以直接加載 @set_ev_cls(ofp_event.EventOFPStateChange,[MAIN_DISPATCHER, DEAD_DISPATCHER]) def _state_change_handler(self, ev): if self.topology == None: self.topology = lookup_service_brick("topology") print("-----------------------_state_change_handler-----------------------") print(self.topology.show_topology()) #調用lookup_service_brick載入的實力對象
2.啟動Ryu:需要載入對應的自定義模塊
ryu-manager ./Test.py ./TopoDetect.py --verbose --observe-links
查看BRICK name即可發現topology被載入!!
對應數據、方法調用成功!!!
完結撒花!!!