Hi

We add more features for this patch, this patch provide these commands:

1. list: list all available ryu application from ryu.app module
2. install: install a ryu application by app id
3. uninstall: uninstall a ryu application by app id
4. bricks: Show service bricks from app manager
5. topology: Display topology



2015-08-20 15:59 GMT+08:00 Yi Tseng <a86487...@gmail.com>:

> Original repo:
> https://github.com/TakeshiTseng/ryu-dynamic-loader
>
> We developed a ryu application as a plugin and use this plugin to
> control ryu application manager(AppManager).
>
> This plugin allow users to install/uninstall ryu application
> dynamically without restart ryu-manager.
>
> To use this plugin, start ryu application with dal_plugin
>
> $ ryu-manager ryu.app.dal_plugin ryu.controller.ofp_handler
>
> And start ryu-cli
> $ ryu-cli
>
> Now command line provide 3 commands:
> 1. list: list all available ryu application from ryu.app module
> 2. install: install a ryu application by app id
> 3. uninstall: uninstall a ryu application by app id
>
> Contributors:
> @TakeshiTseng : develop plugin and cli
> @John-Lin : add error handling and prompt
>
> --
> Yi Tseng (a.k.a Takeshi)
> Taiwan National Chiao Tung University
> Department of Computer Science
> W2CNLab
>
> http://blog.takeshi.tw
>



-- 
Yi Tseng (a.k.a Takeshi)
Taiwan National Chiao Tung University
Department of Computer Science
W2CNLab

http://blog.takeshi.tw
From 2d68b0dd16ee5f7ad92136935a7f3306fc4d9def Mon Sep 17 00:00:00 2001
From: Takeshi <a86487...@gmail.com>
Date: Wed, 26 Aug 2015 18:02:53 +0800
Subject: [PATCH] Add dynamic application loader for ryu

Original repo:
https://github.com/TakeshiTseng/ryu-dynamic-loader

We developed a ryu application as a plugin and use this plugin to
control ryu application manager(AppManager).

This plugin allow users to install/uninstall ryu application
dynamically without restart ryu-manager.

To use this plugin, start ryu application with dal_plugin

$ ryu-manager ryu.app.dal_plugin ryu.controller.ofp_handler

And start ryu-cli
$ ryu-cli

Now command line provide 3 commands:
1. list: list all available ryu application from ryu.app module
2. install: install a ryu application by app id
3. uninstall: uninstall a ryu application by app id
4. bricks: Show service bricks from app manager
5. topology: Display topology

Contributors:
@TakeshiTseng : develop plugin and cli
@John-Lin : add error handling and prompt

Signed-off-by: Takeshi <a86487...@gmail.com>
---
 bin/ryu-cli            |  19 ++++
 debian/ryu-bin.install |   1 +
 ryu/app/dal_plugin.py  | 267 +++++++++++++++++++++++++++++++++++++++++++++
 ryu/cmd/ryu_cli.py     | 291 +++++++++++++++++++++++++++++++++++++++++++++++++
 ryu/lib/ascii_topo.py  |  90 +++++++++++++++
 ryu/lib/dal_lib.py     |  77 +++++++++++++
 setup.cfg              |   1 +
 7 files changed, 746 insertions(+)
 create mode 100755 bin/ryu-cli
 create mode 100644 ryu/app/dal_plugin.py
 create mode 100755 ryu/cmd/ryu_cli.py
 create mode 100644 ryu/lib/ascii_topo.py
 create mode 100644 ryu/lib/dal_lib.py

diff --git a/bin/ryu-cli b/bin/ryu-cli
new file mode 100755
index 0000000..61ccfde
--- /dev/null
+++ b/bin/ryu-cli
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2011, 2012 Nippon Telegraph and Telephone Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ryu.cmd.ryu_cli import main
+main()
diff --git a/debian/ryu-bin.install b/debian/ryu-bin.install
index e101cdb..209369c 100644
--- a/debian/ryu-bin.install
+++ b/debian/ryu-bin.install
@@ -1,4 +1,5 @@
 usr/bin/ryu-manager usr/bin
 usr/bin/ryu usr/bin
+usr/bin/ryu-cli usr/bin
 debian/ryu.conf etc/ryu
 debian/log.conf etc/ryu
diff --git a/ryu/app/dal_plugin.py b/ryu/app/dal_plugin.py
new file mode 100644
index 0000000..8f540f1
--- /dev/null
+++ b/ryu/app/dal_plugin.py
@@ -0,0 +1,267 @@
+# -*- codeing: utf-8 -*-
+import logging
+import pkgutil
+import inspect
+
+from ryu import app as ryu_app
+from ryu.lib import hub
+from ryu.app.wsgi import WSGIApplication
+from ryu.base import app_manager
+from ryu.controller.handler import MAIN_DISPATCHER
+from ryu.controller.handler import set_ev_cls
+from ryu.base.app_manager import RyuApp, AppManager
+from ryu.topology import api as topo_api
+
+from ryu.lib.dal_lib import DLController
+
+
+_REQUIRED_APP = ['ryu.controller.ofp_handler', 'ryu.topology.switches']
+LOG = logging.getLogger('DynamicLoader')
+
+
+def deep_import(mod_name):
+    mod = __import__(mod_name)
+    components = mod_name.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+
+class DynamicLoader(RyuApp):
+
+    def __init__(self, *args, **kwargs):
+        super(DynamicLoader, self).__init__(*args, **kwargs)
+        self.ryu_mgr = AppManager.get_instance()
+        self.available_app = []
+        self.init_apps()
+        wsgi = self.create_wsgi_app('0.0.0.0', 5566)
+        mapper = wsgi.mapper
+        wsgi.registory['DLController'] = self
+
+        self.init_mapper(mapper)
+
+    def create_wsgi_app(self, host, port):
+        wsgi = WSGIApplication()
+        webapp = hub.WSGIServer((host, port), wsgi)
+        hub.spawn(webapp.serve_forever)
+        return wsgi
+
+    def init_apps(self):
+        # init all available apps
+        for _, name, is_pkg in pkgutil.walk_packages(ryu_app.__path__):
+            LOG.debug(
+                'Find %s : %s',
+                'package' if is_pkg else 'module',
+                name)
+
+            if is_pkg:
+                continue
+
+            try:
+                _app_module = deep_import('ryu.app.' + name)
+
+                for _attr_name in dir(_app_module):
+                    _attr = getattr(_app_module, _attr_name)
+
+                    if inspect.isclass(_attr) and _attr.__bases__[0] == RyuApp:
+                        LOG.debug('\tFind ryu app : %s.%s',
+                                  _attr.__module__,
+                                  _attr.__name__)
+                        _full_name = '%s' % (_attr.__module__,)
+                        self.available_app.append((_full_name, _attr))
+
+            except ImportError:
+                LOG.debug('Import Error')
+
+    def init_mapper(self, mapper):
+        mapper.connect('list', '/list', controller=DLController,
+                       action='list_all_apps',
+                       conditions=dict(method=['GET']))
+
+        mapper.connect('list', '/install', controller=DLController,
+                       action='install_app',
+                       conditions=dict(method=['POST']))
+
+        mapper.connect('list', '/installed', controller=DLController,
+                       action='list_installed_app',
+                       conditions=dict(method=['GET']))
+
+        mapper.connect('list', '/uninstall', controller=DLController,
+                       action='uninstall_app',
+                       conditions=dict(method=['POST']))
+
+        mapper.connect('list', '/bricks', controller=DLController,
+                       action='report_brick',
+                       conditions=dict(method=['GET']))
+
+        mapper.connect('list', '/switches', controller=DLController,
+                       action='list_switches',
+                       conditions=dict(method=['GET']))
+
+        mapper.connect('list', '/links', controller=DLController,
+                       action='list_links',
+                       conditions=dict(method=['GET']))
+
+        mapper.connect('list', '/hosts', controller=DLController,
+                       action='list_hosts',
+                       conditions=dict(method=['GET']))
+
+    def create_context(self, key, cls):
+        context = None
+
+        if issubclass(cls, RyuApp):
+            context = self.ryu_mgr._instantiate(None, cls)
+        else:
+            context = cls()
+
+        LOG.info('creating context %s', key)
+
+        if key in self.ryu_mgr.contexts:
+            return None
+
+        self.ryu_mgr.contexts.setdefault(key, context)
+        return context
+
+    def list_all_apps(self):
+        res = []
+        installed_apps = self.ryu_mgr.applications
+
+        for app_info in self.available_app:
+            _cls = app_info[1]
+            installed_apps_cls =\
+                [obj.__class__ for obj in installed_apps.values()]
+
+            if _cls in installed_apps_cls:
+                res.append({
+                    'name': app_info[0],
+                    'installed': True
+                })
+
+            else:
+                res.append({
+                    'name': app_info[0],
+                    'installed': False
+                })
+
+        return res
+
+    def _install_app(self, app_cls):
+        app_contexts = app_cls._CONTEXTS
+        installed_apps = self.ryu_mgr.applications
+        installed_apps_cls =\
+            [obj.__class__ for obj in installed_apps.values()]
+
+        if app_cls in installed_apps_cls:
+            # app was installed
+            LOG.debug('Application already installed')
+            ex = ValueError('Application already installed')
+            raise ex
+
+        new_contexts = []
+
+        for k in app_contexts:
+            context_cls = app_contexts[k]
+            ctx = self.create_context(k, context_cls)
+
+            if ctx and issubclass(context_cls, RyuApp):
+                new_contexts.append(ctx)
+
+        app = self.ryu_mgr.instantiate(app_cls, **self.ryu_mgr.contexts)
+        new_contexts.append(app)
+
+        for ctx in new_contexts:
+            t = ctx.start()
+            # t should be join to some where?
+
+    def uninstall_app(self, path):
+        app_cls = self.ryu_mgr.load_app(path)
+        app = None
+
+        for _app in self.ryu_mgr.applications.values():
+            if isinstance(_app, app_cls):
+                app = _app
+                break
+
+        else:
+            raise ValueError('Can\'t find application')
+
+        self.ryu_mgr.uninstantiate(app.name)
+        app.stop()
+
+        # after we stoped application, chack it context
+        app_contexts = app_cls._CONTEXTS
+        installed_apps = self.ryu_mgr.applications
+        installed_apps_cls =\
+            [obj.__class__ for obj in installed_apps.values()]
+
+        for ctx_name in app_contexts:
+            for app_cls in installed_apps_cls:
+                if ctx_name in app_cls._CONTEXTS:
+                    break
+
+            else:
+                # remove this context
+                ctx_cls = app_contexts[ctx_name]
+                ctx = self.ryu_mgr.contexts[ctx_name]
+                if issubclass(ctx_cls, RyuApp):
+                    ctx.stop()
+
+                if ctx.name in self.ryu_mgr.applications:
+                    del self.ryu_mgr.applications[ctx.name]
+
+                if ctx_name in self.ryu_mgr.contexts:
+                    del self.ryu_mgr.contexts[ctx_name]
+
+                if ctx.name in app_manager.SERVICE_BRICKS:
+                    del app_manager.SERVICE_BRICKS[ctx.name]
+
+                ctx.logger.info('Uninstall app %s successfully', ctx.name)
+
+                # handler hacking, remove all stream handler to avoid it log many times!
+                ctx.logger.handlers = []
+
+    def install_app(self, path):
+        context_modules = [x.__module__ for x in self.ryu_mgr.contexts_cls.values()]
+
+        if path in context_modules:
+            ex = ValueError('Application already exist in contexts')
+            raise ex
+
+        LOG.info('loading app %s', path)
+        app_cls = self.ryu_mgr.load_app(path)
+
+        if app_cls is None:
+            ex = ValueError('Can\'t find application module')
+            raise ex
+
+        self._install_app(app_cls)
+
+    def list_installed_apps(self):
+        res = []
+        installed_apps = self.ryu_mgr.applications.values()
+        res = [installed_app.__module__ for installed_app in installed_apps]
+        return res
+
+    def report_brick(self):
+        res = {}
+
+        for name, app in app_manager.SERVICE_BRICKS.items():
+            res.setdefault(name, {'provide': [], 'consume': []})
+            for ev_cls, list_ in app.observers.items():
+                res[name]['provide'].append((ev_cls.__name__, [capp for capp in list_]))
+            for ev_cls in app.event_handlers.keys():
+                res[name]['consume'].append((ev_cls.__name__))
+
+        return res
+
+    def list_switches(self):
+        switches = topo_api.get_all_switch(self)
+        return [switch.to_dict() for switch in switches]
+
+    def list_links(self):
+        links = topo_api.get_all_link(self)
+        return [link.to_dict() for link in links]
+
+    def list_hosts(self):
+        hosts = topo_api.get_all_host(self)
+        return [host.to_dict() for host in hosts]
diff --git a/ryu/cmd/ryu_cli.py b/ryu/cmd/ryu_cli.py
new file mode 100755
index 0000000..baced47
--- /dev/null
+++ b/ryu/cmd/ryu_cli.py
@@ -0,0 +1,291 @@
+#! /usr/bin/env python
+# -*- codeing: utf-8 -*-
+from __future__ import print_function
+
+import cmd
+import six
+import json
+
+from ryu.lib.ascii_topo import print_topo
+
+if six.PY2:
+    import urllib2 as urllib
+
+else:
+    import urllib3 as urllib
+
+CLI_BASE_URL = 'http://127.0.0.1:5566'
+CLI_LIST_PATH = '/list'
+CLI_INSTALL_PATH = '/install'
+CLI_INSTALLED_PATH = '/installed'
+CLI_BRICKS_PATH = '/bricks'
+CLI_UNINSTALL_PATH = '/uninstall'
+
+# for topology
+CLI_SWITCHES_PATH = '/switches'
+CLI_LINKS_PATH = '/links'
+CLI_HOSTS_PATH = '/hosts'
+
+
+def http_get(url):
+    '''
+    do http GET method
+    return python dictionary data
+
+    param:
+        url: url for http GET, string type
+    '''
+    result = None
+
+    if six.PY2:
+        try:
+            response = urllib.urlopen(url)
+        except urllib.URLError as e:
+            return e.reason
+        result = json.load(response)
+
+    else:
+        http = urllib.PoolManager()
+        response = http.request('GET', url)
+        result = json.loads(response.data.decode('utf8'))
+
+    return result
+
+
+def http_post(url, req_body):
+    '''
+    do http POST method
+    return python dictionary data
+
+    param:
+        url: url for http GET, string type
+        req_body: data to send, string type
+    '''
+    result = None
+
+    if six.PY2:
+        try:
+            response = urllib.urlopen(url, data=req_body)
+        except urllib.URLError as e:
+            return e.reason
+        result = json.load(response)
+
+    else:
+        http = urllib.PoolManager()
+        response = http.urlopen('POST', url, body=req_body)
+        result = json.load(response)
+
+    return result
+
+
+class Bcolors:
+    HEADER = '\033[95m'
+    OKBLUE = '\033[94m'
+    OKGREEN = '\033[92m'
+    WARNING = '\033[93m'
+    FAIL = '\033[91m'
+    ENDC = '\033[0m'
+    BOLD = '\033[1m'
+    UNDERLINE = '\033[4m'
+
+
+class DlCli(cmd.Cmd):
+    """
+    Ryu dynamic loader command line
+    """
+
+    _msg = 'Welcome to the Ryu CLI. Type help or ? to list commands.\n'
+
+    _anscii_art = """
+        ____
+       / __ \__  ____  __
+      / /_/ / / / / / / /
+     / _, _/ /_/ / /_/ /
+    /_/ |_|\__, /\__,_/
+          /____/
+    """
+
+    _hint_msg = """
+    \n\nHit '{0}<Tab>{1}' key to auto-complete the commands \
+    \nand '{0}<ctrl-d>{1}' or type '{0}exit{1}' to exit Ryu CLI.\n
+    """.format(Bcolors.BOLD, Bcolors.ENDC)
+
+    intro = (_msg + Bcolors.OKGREEN + _anscii_art + Bcolors.ENDC +
+             _hint_msg + Bcolors.ENDC)
+
+    prompt = Bcolors.WARNING + 'ryu-cli> ' + Bcolors.ENDC
+
+    def __init__(self):
+        cmd.Cmd.__init__(self)
+        self.app_list = None
+
+    def do_list(self, line):
+        '''
+        List all available applications.
+        '''
+        print('{}Available built-in Ryu applications:{}'.format(Bcolors.OKBLUE, Bcolors.ENDC))
+        self.app_list = http_get(CLI_BASE_URL + CLI_LIST_PATH)
+
+        if not type(self.app_list) == list:
+            print(self.app_list)
+            return False
+
+        for app_info in self.app_list:
+
+            if not app_info['installed']:
+                print('{}'.format(app_info['name']))
+
+        print('\n{}Installed Ryu applications:{}'.format(Bcolors.OKBLUE, Bcolors.ENDC))
+        installed_list = http_get(CLI_BASE_URL + CLI_INSTALLED_PATH)
+
+        if not type(installed_list) == list:
+            print(installed_list)
+            return False
+
+        for install_mod in installed_list:
+            print('{}'.format(install_mod))
+
+    def do_install(self, line):
+        '''
+        Install ryu application by using module path
+        Usage:
+            install [app path]
+        Example:
+            install ryu.app.simple_switch
+        '''
+        req_body = json.dumps({'path': line})
+        result = http_post(CLI_BASE_URL + CLI_INSTALL_PATH, req_body)
+
+        if not type(result) == dict:
+            print(result)
+            return
+
+        if result['result'] == 'ok':
+            print('Successfully installed!')
+
+        else:
+            print(result['details'])
+
+    def complete_install(self, text, line, begidx, endidx):
+
+        self.app_list = http_get(CLI_BASE_URL + CLI_LIST_PATH)
+
+        if not text:
+            completions = [app_info['name'] for app_info in self.app_list]
+
+        else:
+            completions = [app_info['name']
+                           for app_info in self.app_list
+                           if app_info['name'].startswith(text)
+                           ]
+
+        return completions
+
+    def do_uninstall(self, line):
+        '''
+        Uninstall ryu application
+        Usage:
+            uninstall [application module path]
+        Example:
+            uninstall ryu.app.simple_switch
+        '''
+        req_body = json.dumps({'path': line})
+        result = http_post(CLI_BASE_URL + CLI_UNINSTALL_PATH, req_body)
+
+        if not type(result) == dict:
+            print(result)
+            return
+
+        if result['result'] == 'ok':
+            print('Successfully uninstalled!')
+
+        else:
+            print(result['details'])
+
+    def complete_uninstall(self, text, line, begidx, endidx):
+
+        installed_list = http_get(CLI_BASE_URL + CLI_INSTALLED_PATH)
+
+        if not type(installed_list) == list:
+            return []
+
+        if not text:
+            completions = [installed_mod
+                           for installed_mod in installed_list
+                           ]
+
+        else:
+            completions = [installed_mod
+                           for installed_mod in installed_list
+                           if installed_mod.startswith(text)
+                           ]
+
+        return completions
+
+    def do_bricks(self, line):
+        '''
+        Show service bricks from app manager
+        '''
+        bricks = http_get(CLI_BASE_URL + CLI_BRICKS_PATH)
+
+        print('{}Bricks:{}'.format(Bcolors.OKBLUE, Bcolors.ENDC))
+        for name in bricks:
+            brick = bricks[name]
+            print('BRICK {}'.format(name))
+
+            for provide_ev in brick['provide']:
+                print('PROVIDES {} to {}'.format(provide_ev[0], provide_ev[1]))
+
+            for consume_ev in brick['consume']:
+                print('CONSUMES {}'.format(consume_ev))
+
+    def do_topology(self, line):
+        '''
+        Display current topology
+        '''
+        switches = http_get(CLI_BASE_URL + CLI_SWITCHES_PATH)
+
+        if type(switches) != list:
+            print('Error to fetching topology data, {}'.format(switches))
+            return False
+
+        links = http_get(CLI_BASE_URL + CLI_LINKS_PATH)
+        hosts = http_get(CLI_BASE_URL + CLI_HOSTS_PATH)
+
+        print_topo(switches, links, hosts)
+
+    def default(self, line):
+        fail_msg = Bcolors.FAIL + 'Command not found: ' + line + Bcolors.ENDC
+        print (fail_msg)
+
+    def do_exit(self, line):
+        '''
+        Exit from command line
+        '''
+        return True
+
+    def do_EOF(self, line):
+        '''
+        Use ctrl + D to exit
+        '''
+        return True
+
+
+def main(args=None):
+    '''
+    Usage:
+        ./cli [Base url]
+
+        Base url: RESTful API server base url, default is http://127.0.0.1:5566
+    '''
+    import sys
+    if len(sys.argv) >= 2:
+        CLI_BASE_URL = sys.argv[1]
+
+    try:
+        DlCli().cmdloop()
+    except KeyboardInterrupt:
+        pass
+
+if __name__ == '__main__':
+    main()
diff --git a/ryu/lib/ascii_topo.py b/ryu/lib/ascii_topo.py
new file mode 100644
index 0000000..4e509b7
--- /dev/null
+++ b/ryu/lib/ascii_topo.py
@@ -0,0 +1,90 @@
+from __future__ import print_function
+import json
+
+colors = [
+    '\033[90m',
+    '\033[91m',
+    '\033[92m',
+    '\033[93m',
+    '\033[94m',
+    '\033[95m',
+    '\033[96m',
+]
+end_color = '\033[0m'
+num_colors = len(colors)
+
+
+def print_topo(switches=[], links=[], hosts=[]):
+    tlinks = []
+    thosts = []
+
+    for switch in switches:
+        print('{}┐{:>8}'.format(switch['dpid'], ' '), end='')
+
+        for t in tlinks:
+            cindex = tlinks.index(t)
+            if t == None:
+                print(' ', end='')
+            else:
+                print('{}|{}'.format(colors[cindex % num_colors], end_color), end='')
+
+        print()
+
+        for port in switch['ports']:
+            if port != switch['ports'][-1]:
+                print('{:>19}{}'.format('├', port['port_no']), end='')
+
+            else:
+                print('{:>19}{}'.format('└', port['port_no']), end='')
+
+            for link in links:
+                if port['hw_addr'] == link['src']['hw_addr'] and\
+                   int(link['src']['dpid'], 16) < int(link['dst']['dpid'], 16):
+
+                    if None in tlinks:
+                        nindex = tlinks.index(None)
+                        tlinks[nindex] = link
+
+                    else:
+                        tlinks.append(link)
+
+            for host in hosts:
+                if port['hw_addr'] == host['port']['hw_addr']:
+                    thosts.append(host)
+
+            for t in tlinks:
+                if len(thosts) > 0:
+                    print('-' * (len(tlinks) + 1), end='')
+                    print('-'.join([th['mac'] for th in thosts]), end='')
+                    thosts = []
+                    break
+
+                cindex = tlinks.index(t)
+                src_ports = [l['src'] if l != None else None for l in tlinks]
+                dst_ports = [l['dst'] if l != None else None for l in tlinks]
+
+                if t == None:
+                    if (port in dst_ports) and dst_ports.index(port) > cindex:
+                        dindex = dst_ports.index(port)
+                        print('{}-{}'.format(colors[dindex % num_colors], end_color), end='')
+
+                    elif (port in src_ports) and src_ports.index(port) > cindex:
+                        sindex = src_ports.index(port)
+                        print('{}-{}'.format(colors[sindex % num_colors], end_color), end='')
+                    else:
+                        print(' ', end='')
+
+                elif port['hw_addr'] == t['src']['hw_addr']:
+                    print('{}┐{}'.format(colors[cindex % num_colors], end_color), end='')
+
+                elif port['hw_addr'] == t['dst']['hw_addr']:
+                    print('{}┘{}'.format(colors[cindex % num_colors], end_color), end='')
+                    tlinks[cindex] = None
+
+                    while len(tlinks) > 0 and tlinks[-1] == None:
+                        del tlinks[-1]
+
+                else:
+                    print('{}|{}'.format(colors[cindex % num_colors], end_color), end='')
+
+            print()
diff --git a/ryu/lib/dal_lib.py b/ryu/lib/dal_lib.py
new file mode 100644
index 0000000..4fe34a0
--- /dev/null
+++ b/ryu/lib/dal_lib.py
@@ -0,0 +1,77 @@
+# -*- codeing: utf-8 -*-
+import logging
+import json
+from webob import Response
+from ryu.app.wsgi import ControllerBase
+from ryu.app.wsgi import WSGIApplication
+
+LOG = logging.getLogger('DLController')
+
+
+# REST command template
+def rest_command(func):
+    def _rest_command(*args, **kwargs):
+        try:
+            msg = func(*args, **kwargs)
+            return Response(content_type='application/json',
+                            body=json.dumps(msg))
+
+        except SyntaxError as e:
+            details = e.msg
+        except (ValueError, NameError) as e:
+            details = e.message
+        except IndexError as e:
+            details = e.args[0]
+
+        msg = {'result': 'failure',
+               'details': details}
+        return Response(body=json.dumps(msg))
+
+    return _rest_command
+
+
+class DLController(ControllerBase):
+
+    def __init__(self, req, link, data, **config):
+        super(DLController, self).__init__(req, link, data, **config)
+        self.ryu_app = data
+
+    @rest_command
+    def list_all_apps(self, req, **_kwargs):
+        return self.ryu_app.list_all_apps()
+
+    @rest_command
+    def list_installed_app(self, req, **_kwargs):
+        return self.ryu_app.list_installed_apps()
+
+    @rest_command
+    def report_brick(self, req, **_kwargs):
+        return self.ryu_app.report_brick()
+
+    @rest_command
+    def install_app(self, req, **_kwargs):
+        body = json.loads(req.body)
+        path = body['path']
+
+        self.ryu_app.install_app(path)
+        return {'result': 'ok'}
+
+    @rest_command
+    def uninstall_app(self, req, **_kwargs):
+        body = json.loads(req.body)
+        path = body['path']
+
+        self.ryu_app.uninstall_app(path)
+        return {'result': 'ok'}
+
+    @rest_command
+    def list_switches(self, req, **_kwargs):
+        return self.ryu_app.list_switches()
+
+    @rest_command
+    def list_links(self, req, **_kwargs):
+        return self.ryu_app.list_links()
+
+    @rest_command
+    def list_hosts(self, req, **_kwargs):
+        return self.ryu_app.list_hosts()
diff --git a/setup.cfg b/setup.cfg
index 626e0f8..af91500 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -53,3 +53,4 @@ setup-hooks =
 console_scripts =
     ryu-manager = ryu.cmd.manager:main
     ryu = ryu.cmd.ryu_base:main
+    ryu-cli = ryu.cmd.ryu_cli:main
-- 
2.3.2 (Apple Git-55)

------------------------------------------------------------------------------
_______________________________________________
Ryu-devel mailing list
Ryu-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to