Basically looks good.
Can you please add prefix '_' to private methods?
SshServer.{pty_loop, handle_shell_request}
Cli.ssh_threadthanks, 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
