• Ryu源码之模块功能分析


    一:模块间通信机制分析

    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被载入!!

     

    对应数据、方法调用成功!!!

    完结撒花!!! 

  • 相关阅读:
    周末小练习
    第十二届全国大学生信息安全竞赛总结与反思
    sql注入学习心得与sqlmap使用心得
    2019“嘉韦思”杯RSA256题目wp
    斐波那契数列求解的三种方法
    二叉树的下一个节点
    替换空格
    二维数组中的查找
    不修改数组找出重复数字
    数组中重复数字
  • 原文地址:https://www.cnblogs.com/ssyfj/p/14193034.html
Copyright © 2020-2023  润新知