From 32f644fbe91a1399534cde28182ee81018e4fd03 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Tue, 29 May 2012 18:26:57 +0900 Subject: [PATCH] base/app_manager: introduce application context The ryu-manager creates structures which applications share. Currently it is hard-coded in ryu-managers. Concretely network.Network and dpset.DPSet. It is difficult to maintain the code appropriately by hand. When the application is changed or new application comes in, ryu-manager also must be updated. So introduce the notion of application context so that application manager can determine what structures applications want to share and create them. Signed-off-by: Isaku Yamahata Signed-off-by: FUJITA Tomonori --- bin/ryu-manager | 15 +++++-- ryu/base/app_manager.py | 97 +++++++++++++++++++++++++++++++++++------ 2 files changed, 95 insertions(+), 17 deletions(-) diff --git a/bin/ryu-manager b/bin/ryu-manager index cb57e623..a6944e2e 100755 --- a/bin/ryu-manager +++ b/bin/ryu-manager @@ -49,11 +49,18 @@ def main(): _args = FLAGS(sys.argv) log.init_log() - nw = network.Network() - dpset_ = dpset.DPSet() + # to make non-converted apps work. Once all of them converted, + # this will be removed. + kwargs = { + 'network': network.Network(), + 'dpset': dpset.DPSet(), + } app_mgr = AppManager() - app_mgr.load_apps(FLAGS.app_lists, network=nw, dpset=dpset_) + app_mgr.load_apps(FLAGS.app_lists) + contexts = app_mgr.create_contexts() + contexts.update(kwargs) + app_mgr.instantiate_apps(**contexts) services = [] @@ -67,6 +74,8 @@ def main(): services.append(thr) gevent.joinall(services) + app_mgr.close() + if __name__ == "__main__": main() diff --git a/ryu/base/app_manager.py b/ryu/base/app_manager.py index 4aae2903..19e93160 100644 --- a/ryu/base/app_manager.py +++ b/ryu/base/app_manager.py @@ -23,24 +23,93 @@ from ryu.controller.handler import register_instance LOG = logging.getLogger('ryu.base.app_manager') +class RyuAppContext(object): + """ + Base class for Ryu application context + """ + def __init__(self): + super(RyuAppContext, self).__init__() + + def close(self): + """ + teardown method + The method name, close, is chosen for python context manager + """ + pass + + +class RyuApp(object): + """ + Base class for Ryu network application + """ + _CONTEXTS = {} + + @classmethod + def context_iteritems(cls): + """ + Return iterator over the (key, contxt class) of application context + """ + return cls._CONTEXTS.iteritems() + + def __init__(self, *_args, **_kwargs): + super(RyuApp, self).__init__() + + def close(self): + """ + teardown method. + The method name, close, is chosen for python context manager + """ + pass + + class AppManager(object): def __init__(self): + self.applications_cls = {} self.applications = {} + self.contexts_cls = {} + self.contexts = {} - def load(self, app_mod_name, *args, **kwargs): - # for now, only single instance of a given module - # Do we need to support multiple instances? - # Yes, maybe for slicing. - assert app_mod_name not in self.applications + def load_apps(self, app_lists): + for app_cls_name in itertools.chain.from_iterable([app_list.split(',') + for app_list + in app_lists]): + LOG.info('loading app %s', app_cls_name) - cls = utils.import_object(app_mod_name) - app = cls(*args, **kwargs) - register_instance(app) + # for now, only single instance of a given module + # Do we need to support multiple instances? + # Yes, maybe for slicing. + assert app_cls_name not in self.applications_cls - self.applications[app_mod_name] = app + cls = utils.import_object(app_cls_name) + self.applications_cls[app_cls_name] = cls - def load_apps(self, app_lists, *args, **kwargs): - for app in itertools.chain.from_iterable([app_list.split(',') - for app_list in app_lists]): - self.load(app, *args, **kwargs) - LOG.info('loading app %s', app) + for key, context_cls in cls.context_iteritems(): + cls = self.contexts_cls.setdefault(key, context_cls) + assert cls == context_cls + + def create_contexts(self): + for key, cls in self.contexts_cls.items(): + self.contexts[key] = cls() + return self.contexts + + def instantiate_apps(self, *args, **kwargs): + for app_name, cls in self.applications_cls.items(): + # 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', app_name) + assert app_name not in self.applications + app = cls(*args, **kwargs) + register_instance(app) + self.applications[app_name] = app + + def close(self): + def close_all(close_dict): + for app in close_dict: + close_method = getattr(app, 'close', None) + if callable(close_method): + close_method() + close_dict.clear() + + close_all(self.applications) + close_all(self.contexts)