Basically looks good.
Can you please add prefix '_' to private methods?
SshServer.{pty_loop, handle_shell_request}
Cli.ssh_thread

thanks,

On Tue, Mar 05, 2013 at 01:18:11PM +0900, YAMAMOTO Takashi wrote:
> Signed-off-by: YAMAMOTO Takashi <[email protected]>
> ---
>  ryu/app/cli.py | 227 
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 227 insertions(+)
>  create mode 100644 ryu/app/cli.py
> 
> diff --git a/ryu/app/cli.py b/ryu/app/cli.py
> new file mode 100644
> index 0000000..8bdf1dd
> --- /dev/null
> +++ b/ryu/app/cli.py
> @@ -0,0 +1,227 @@
> +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
> +# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co jp>
> +#
> +# 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.
> +
> +# a management cli application.
> +
> +from __future__ import print_function
> +
> +import cmd
> +import functools
> +import gevent
> +import gevent.server
> +import logging
> +import os
> +import paramiko
> +import pty
> +import select
> +import signal
> +import sys
> +
> +from oslo.config import cfg
> +
> +from ryu import version
> +from ryu.base import app_manager
> +from ryu.base import management
> +from ryu import call_via_pipe
> +from ryu.plogger import PrefixedLogger
> +
> +
> +CONF = cfg.CONF
> +CONF.register_opts([
> +    cfg.ListOpt('cli-transports', default=[], help='cli transports to 
> enable'),
> +    cfg.StrOpt('cli-ssh-host', default='', help='cli ssh listen host'),
> +    cfg.IntOpt('cli-ssh-port', default=4990, help='cli ssh listen port'),
> +    cfg.StrOpt('cli-ssh-hostkey', default=None, help='cli ssh host key 
> file'),
> +    cfg.StrOpt('cli-ssh-username', default=None, help='cli ssh username'),
> +    cfg.StrOpt('cli-ssh-password', default=None, help='cli ssh password')
> +])
> +
> +
> +def command_log(f):
> +    @functools.wraps(f)
> +    def wrapper(self, params):
> +        name = wrapper.__name__
> +        assert(name.startswith('do_'))
> +        command_name = name[len('do_'):]
> +        self.logger.info("command %s %s" % (command_name, params))
> +        f(self, params)
> +    return wrapper
> +
> +
> +class CliCmd(cmd.Cmd):
> +    prompt = 'ryu-manager %s> ' % version
> +
> +    def __init__(self, rpipe, wpipe, *args, **kwargs):
> +        cmd.Cmd.__init__(self, *args, **kwargs)
> +        # it's safe to use the same set of pipes as far as we are
> +        # single-threaded.
> +        self.management = call_via_pipe.CallViaPipe(rpipe, wpipe, 
> "management")
> +        self.logger = call_via_pipe.CallViaPipe(rpipe, wpipe, "logger")
> +
> +    @command_log
> +    def do_set_log_level(self, params):
> +        '''<logger> <level>
> +        set log level of the specified logger
> +        '''
> +        try:
> +            params = params.split()
> +            name = params[0]
> +            newlvl = int(params[1])
> +        except (ValueError, IndexError):
> +            print('invalid parameter')
> +            return
> +        try:
> +            oldlvl = self.management.get_log_level(name)
> +            self.management.set_log_level(name, newlvl)
> +        except LookupError:
> +            print('logger %s is unknown' % (name,))
> +            return
> +        print('logger %s level %s -> %s' % (name, oldlvl, newlvl))
> +
> +    @command_log
> +    def do_show_bricks(self, params):
> +        '''
> +        show a list of configured bricks
> +        '''
> +        map(lambda b: print('%s' % (b,)), self.management.list_bricks())
> +
> +    @command_log
> +    def do_show_loggers(self, params):
> +        '''
> +        show loggers
> +        '''
> +        map(lambda name: print('logger %s level %s' %
> +                               (name, self.management.get_log_level(name))),
> +            self.management.list_loggers())
> +
> +    @command_log
> +    def do_show_options(self, params):
> +        '''
> +        show options
> +        '''
> +        # NOTE: this shows CONF of the child process.
> +        # currently it isn't a problem because we don't modify CONF
> +        # after startup.
> +        class MyLogger:
> +            def log(mylogger_self, lvl, fmt, *args):
> +                print(fmt % args)
> +        CONF.log_opt_values(MyLogger(), None)
> +
> +
> +class SshServer(paramiko.ServerInterface):
> +    def __init__(self, logger, sock, addr):
> +        super(SshServer, self).__init__()
> +        self.logger = PrefixedLogger(logger, "CLI-SSH %s" % (addr,))
> +        transport = paramiko.Transport(sock)
> +        transport.load_server_moduli()
> +        host_key = 
> paramiko.RSAKey.from_private_key_file(CONF.cli_ssh_hostkey)
> +        transport.add_server_key(host_key)
> +        self.transport = transport
> +        transport.start_server(server=self)
> +
> +    def check_auth_password(self, username, password):
> +        print("check_auth_password", username, password)
> +        if username == CONF.cli_ssh_username and \
> +                password == CONF.cli_ssh_password:
> +            return paramiko.AUTH_SUCCESSFUL
> +        return paramiko.AUTH_FAILED
> +
> +    def check_channel_request(self, kind, chanid):
> +        if kind == 'session':
> +            return paramiko.OPEN_SUCCEEDED
> +        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
> +
> +    def check_channel_shell_request(self, chan):
> +        gevent.spawn(self.handle_shell_request)
> +        return True
> +
> +    def check_channel_pty_request(self, chan, term, width, height,
> +                                  pixelwidth, pixelheight, modes):
> +        self.term = term
> +        return True
> +
> +    def pty_loop(self, chan, fd, rpipe, wpipe):
> +        while True:
> +            rfds, wfds, xfds = select.select([chan.fileno(), fd, rpipe], [],
> +                                             [])
> +            if fd in rfds:
> +                data = os.read(fd, 1024)
> +                if len(data) == 0:
> +                    break
> +                chan.send(data)
> +            if chan.fileno() in rfds:
> +                data = chan.recv(1024)
> +                if len(data) == 0:
> +                    break
> +                os.write(fd, data)
> +            if rpipe in rfds:
> +                logger = self.logger
> +                call_via_pipe.serve(rpipe, wpipe, [locals(), globals()])
> +        chan.close()
> +
> +    def handle_shell_request(self):
> +        self.logger.info("session start")
> +        chan = self.transport.accept(20)
> +        if not chan:
> +            self.logger.info("transport.accept timed out")
> +            return
> +        rpipe_request, wpipe_request = os.pipe()
> +        rpipe_reply, wpipe_reply = os.pipe()
> +        child_pid, master_fd = pty.fork()
> +        if not child_pid:
> +            os.close(rpipe_request)
> +            os.close(wpipe_reply)
> +            CliCmd(rpipe_reply, wpipe_request).cmdloop()
> +            return
> +        os.close(wpipe_request)
> +        os.close(rpipe_reply)
> +        self.pty_loop(chan, master_fd, rpipe_request, wpipe_reply)
> +        self.logger.info("session end")
> +        os.kill(child_pid, signal.SIGTERM)
> +        os.waitpid(child_pid, 0)
> +
> +
> +class SshServerFactory(object):
> +    def __init__(self, logger, *args, **kwargs):
> +        super(SshServerFactory, self).__init__(*args, **kwargs)
> +        self.logger = logger
> +
> +    def streamserver_handle(self, sock, addr):
> +        SshServer(self.logger, sock, addr)
> +
> +
> +class Cli(app_manager.RyuApp):
> +    def __init__(self, *args, **kwargs):
> +        super(Cli, self).__init__(*args, **kwargs)
> +        something_started = False
> +        if 'ssh' in CONF.cli_transports:
> +            self.logger.info("starting ssh server at %s:%d",
> +                             CONF.cli_ssh_host, CONF.cli_ssh_port)
> +            gevent.spawn(self.ssh_thread)
> +            something_started = True
> +        if not something_started:
> +            self.logger.warn("cli app has no valid transport configured")
> +            self.logger.debug("cli-transports=%s", CONF.cli_transports)
> +            self.logger.debug("cli-ssh-hostkey=%s", CONF.cli_ssh_hostkey)
> +
> +    def ssh_thread(self):
> +        logging.getLogger('paramiko')
> +        logging.getLogger('paramiko.transport')
> +        factory = SshServerFactory(self.logger)
> +        server = gevent.server.StreamServer((CONF.cli_ssh_host,
> +                                            CONF.cli_ssh_port),
> +                                            factory.streamserver_handle)
> +        server.serve_forever()
> -- 
> 1.8.0.1
> 
> 
> ------------------------------------------------------------------------------
> Everyone hates slow websites. So do we.
> Make your web apps faster with AppDynamics
> Download AppDynamics Lite for free today:
> http://p.sf.net/sfu/appdyn_d2d_feb
> _______________________________________________
> Ryu-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/ryu-devel
> 

-- 
yamahata

------------------------------------------------------------------------------
Symantec Endpoint Protection 12 positioned as A LEADER in The Forrester  
Wave(TM): Endpoint Security, Q1 2013 and "remains a good choice" in the  
endpoint security space. For insight on selecting the right partner to 
tackle endpoint security challenges, access the full report. 
http://p.sf.net/sfu/symantec-dev2dev
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to