> Basically looks good.
> Can you please add prefix '_' to private methods?

i'll do so next time.  thanks.

YAMAMOTO Takashi

> 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

------------------------------------------------------------------------------
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