<commit_before>'''
Defines a ServerGroup class for running and managing servers, as well as a set of
base server types.

The following information is needed to start a server:
1- handler / wsgi app
2- address
3- ssl preference

'''
import collections
import socket
import os
import threading
import weakref

if hasattr(os, "fork"):
    import ufork
else:
    ufork = None
import gevent
from gevent import pywsgi
import gevent.server
import gevent.socket
import gevent.pool

import async
from protected import Protected
import context
import env

import ll

ml = ll.LLogger()

class ServerGroup(object):
    def __init__(self, wsgi_apps=(), stream_handlers=(), prefork=None, daemonize=None, dev=None, **kw):
        '''Create a new ServerGroup which will can be started / stopped / forked as a group.

        *wsgi_apps* should be of the form  [ (wsgi_app, address, ssl), ...  ]

        *stream_handlers* should be of the form  [ (handler_func, address), ...  ]

        address here refers to a tuple (ip, port), or more generally anything which is
        acceptable as the address parameter to
        `socket.bind() <http://docs.python.org/2/library/socket.html#socket.socket.bind>`_.

        handler_func should have the following signature: f(socket, address), following
        the `convention of gevent <http://www.gevent.org/servers.html>`_.
        '''
        ctx = context.get_context()
        self.prefork = prefork if prefork is not None else ctx.serve_ufork
        self.daemonize = daemonize if daemonize is not None else ctx.serve_daemon
        dev = dev if dev is not None else ctx.dev
        self.post_fork = kw.get('post_fork')  # callback to be executed post fork
        self.server_log = kw.get('gevent_log')
        self.wsgi_apps = wsgi_apps
        self.stream_handlers = list(stream_handlers)
        if ctx.backdoor_port is not None:
            self.stream_handlers.append((console_sock_handle, ("0.0.0.0", ctx.backdoor_port)))
        self.num_workers = ctx.num_workers
        self.servers = []
        self.socks = {}
        self.client_pool = gevent.pool.Pool(ctx.max_concurrent_clients)
        for app, address, ssl in wsgi_apps:
            sock = _make_server_sock(address)
            if isinstance(ssl, Protected):
                protected = ssl
            elif ssl:
                protected = ctx.protected
            else:
                protected = None
            if protected:
                # TODO: maybe this determination belongs centralized in context?
                if dev or env.pp_host_env() in ("STAGE2", "HYPER") or kw.get('no_client_auth_required'):
                    if ctx.ssl_client_cert_optional_in_dev or kw.get('no_client_auth_required'):
                        sslcontext = getattr(protected, 'ssl_dev_server_context')
                    else:
                        sslcontext = getattr(protected, 'ssl_server_context')
                    server = MultiProtocolWSGIServer(
                        sock, app, spawn=self.client_pool, context=sslcontext)
                else:
                    server = SslContextWSGIServer(
                        sock, app, spawn=self.client_pool, context=protected.ssl_server_context)
            else:
                server = pywsgi.WSGIServer(sock, app)
            server.log = self.server_log or RotatingGeventLog()
            self.servers.append(server)
            self.socks[server] = sock
            # prevent a "blocking" call to DNS on each request
            # (NOTE: although the OS won't block, gevent will dispatch to a threadpool which is expensive)
            server.set_environ({'SERVER_NAME': socket.getfqdn(address[0])})
        for handler, address in self.stream_handlers:
            sock = _make_server_sock(address)
            server = gevent.server.StreamServer(sock, handler, spawn=self.client_pool)
            self.servers.append(server)

    def run(self):
        ctx = context.get_context()
        try:
            import greenlet
            greenlet.settrace(ctx._trace)
        except AttributeError:
            pass  # oh well
        if not self.prefork:
            self.start()
            ml.la("The server is now really running and listening to requests-init over!")

            if ctx.dev and ctx.dev_service_repl_enabled and os.isatty(0):
                async.start_repl({'server': ctx.server_group})
            try:
                while 1:
                    async.sleep(1.0)
            finally:
                self.stop()
            return
        if not ufork:
            raise RuntimeError('attempting to run pre-forked on platform without fork')
        self.arbiter = ufork.Arbiter(
            post_fork=self._post_fork, child_pre_exit=self.stop, parent_pre_stop=ctx.stop,
            size=self.num_workers, sleep=async.sleep, fork=gevent.fork, child_memlimit=ctx.worker_memlimit)
        if self.daemonize:
            pidfile = os.path.join(ctx.pid_file_path,
                                   '{0}.pid'.format(ctx.appname))
            self.arbiter.spawn_daemon(pidfile=pidfile)
        else:
            self.arbiter.run()

    def _post_fork(self):
        # TODO: revisit this with newer gevent release
        hub = gevent.hub.get_hub()
        hub.loop.reinit()  # reinitializes libev
        hub._threadpool = None  # eliminate gevent's internal threadpools
        gevent.sleep(0)  # let greenlets run
        # finally, eliminate our threadpools
        context.get_context().thread_locals = threading.local()
        import ll
        if context.get_context().serve_daemon:
            ll.use_the_file()  # stdout was closed during daemonization.
                               # plus ufork stdout logging issue
        if self.post_fork:
            self.post_fork()
        context.get_context().cal.event('WORKER', 'STARTED', '0', {'pid': os.getpid()})
        self.start()

    def start(self):
        errs = {}
        for server in self.servers:
            try:
                server.start()
            except Exception as e:
                errs[server] = e
        if errs:
            raise RuntimeError(errs)

    def stop(self, timeout=30.0):
        gevent.joinall([gevent.spawn(server.stop, timeout) for server in self.servers], raise_error=True)

def _make_server_sock(address):
    ml.ld("about to bind to {0!r}", address)
    sock = gevent.socket.socket()
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(address)
    sock.listen(128)  # Note: 128 is a "hint" to the OS than a strict rule about backlog size
    ml.la("Listen to {0!r} gave this socket {1!r}", address, sock)
    return sock

def my_close(self):
    context.get_context().stats['fake_close'].add(1.0)

class MakeFileCloseWSGIHandler(pywsgi.WSGIHandler):
    '''
    quick work-around to re-enable gevent's work-around of the
    makefile() call in the pywsgi handler keeping sockets alive
    past their shelf life
    '''
    def __init__(self, socket, address, server, rfile=None):
        if rfile is None and hasattr(socket, "_makefile_refs"):
            rfile = socket.makefile()
            # restore gEvent's work-around of not letting wsgi.environ['input']
            # keep the socket alive in CLOSE_WAIT state after client is gone
            # to work with async.SSLSocket
            socket._makefile_refs -= 1
        self.state = context.get_context().markov_stats['wsgi_handler'].make_transitor('new')
        super(MakeFileCloseWSGIHandler, self).__init__(socket, address, server, rfile)

    def handle_one_request(self):
        self.state.transition('handling_request')
        ret = super(MakeFileCloseWSGIHandler, self).handle_one_request()
        if not ret:
            self.state.transition('done')

    def run_application(self):
        self.state.transition('running_application')
        return super(MakeFileCloseWSGIHandler, self).run_application()

    def process_result(self):
        self.state.transition('writing_response')
        ret = super(MakeFileCloseWSGIHandler, self).process_result()
        self.state.transition('done')
        return ret

class SslContextWSGIServer(pywsgi.WSGIServer):
    handler_class = MakeFileCloseWSGIHandler

    @async.timed
    def wrap_socket_and_handle(self, client_socket, address):
        context.get_context().client_sockets[client_socket] = 1

        if not self.ssl_args:
            raise ValueError('https server requires server-side'
                             ' SSL certificate (protected)')
        protocol = _socket_protocol(client_socket)
        if protocol == "ssl":
            ssl_socket = async.wrap_socket_context(client_socket, **self.ssl_args)
            return self.handle(ssl_socket, address)
        elif protocol == "http":
            client_socket.send(_NO_SSL_HTTP_RESPONSE)
            async.killsock(client_socket)
        else:
            context.get_context().intervals["server.pings"].tick()

class MultiProtocolWSGIServer(SslContextWSGIServer):
    @async.timed
    def wrap_socket_and_handle(self, client_socket, address):
        context.get_context().client_sockets[client_socket] = 1

        protocol = _socket_protocol(client_socket)
        if protocol == "http":
            return self.handle(client_socket, address)
        elif protocol == "ssl":
            ssl_socket = async.wrap_socket_context(client_socket, **self.ssl_args)
            return self.handle(ssl_socket, address)
        else:
            context.get_context().intervals["server.pings"].tick()

#http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
_HTTP_METHODS = ('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'OPTION',
                 'CONNECT', 'PATCH')

_SSL_RECORD_TYPES = {
    '\x14': 'CHANGE_CIPHER_SPEC', '\x15': 'ALERT', '\x16': 'HANDSHAKE',
    '\x17': 'APPLICATION_DATA',
}

_SSL_VERSIONS = {
    '\x03\x00': 'SSL3', '\x03\x01': 'TLS1', '\x03\x02': 'TLS1.1', '\x03\x03': 'TLS1.2'
}

_NO_SSL_HTTP_RESPONSE = "\r\n".join([
    'HTTP/1.1 401 UNAUTHORIZED',
    'Content-Type: text/plain',
    'Content-Length: ' + str(len('SSL REQUIRED')),
    '',
    'SSL REQUIRED'
    ])  # this could probably be improved, but better than just closing the socket

def _socket_protocol(client_socket):
    first_bytes = client_socket.recv(10, socket.MSG_PEEK)
    if any([method in first_bytes for method in _HTTP_METHODS]):
        return "http"
    elif not first_bytes:
        # TODO: empty requests are sometimes sent ([socket] keepalives?)
        return
    elif first_bytes[0] in _SSL_RECORD_TYPES and first_bytes[1:3] in _SSL_VERSIONS:
        return "ssl"
    else:
        raise IOError("unrecognized client protocol: {0!r}".format(first_bytes))

class RotatingGeventLog(object):
    def __init__(self, size=10000):
        self.msgs = collections.deque()
        self.size = size

    def write(self, msg):
        self.msgs.appendleft(msg)
        if len(self.msgs) > self.size:
            self.msgs.pop()

### a little helper for running an interactive console over a socket
import code
import sys

class SockConsole(code.InteractiveConsole):
    def __init__(self, sock):
        code.InteractiveConsole.__init__(self)
        self.sock = sock
        self.sock_file = sock.makefile()

    def write(self, data):
        self.sock.sendall(data)

    def raw_input(self, prompt=""):
        self.write(prompt)
        inp = self.sock_file.readline()
        if inp == "":
            raise OSError("client socket closed")
        inp = inp[:-1]
        return inp

    def _display_hook(self, obj):
        '''
        Handle an item returned from the exec statement.
        '''
        self._last = ""
        if obj is None:
            return
        self.locals['_'] = None
        # clean up output by adding \n here; so you don't see "hello world!">>>
        # TODO: is there a better way to handle this?
        self._last = repr(obj) + "\n"
        self.locals['_'] = obj

    def runcode(self, _code):
        '''
        Wraps base runcode to put displayhook around it.
        TODO: add a new displayhook dispatcher so that the
        current SockConsole is a greenlet-tree local.
        (In case the statement being executed itself
        causes greenlet switches.)
        '''
        prev = sys.displayhook
        sys.displayhook = self._display_hook
        self._last = None
        code.InteractiveConsole.runcode(self, _code)
        sys.displayhook = prev
        if self._last is not None:
            self.write(self._last)

def console_sock_handle(sock, address):
    console = SockConsole(sock)
    console.interact("Connected to pid {0} from {1}...".format(
        os.getpid(), address))
<commit_msg>back ports from XOE tuning<commit_after>'''
Defines a ServerGroup class for running and managing servers, as well as a set of
base server types.

The following information is needed to start a server:
1- handler / wsgi app
2- address
3- ssl preference

'''
import collections
import socket
import os
import threading
import weakref

if hasattr(os, "fork"):
    import ufork
else:
    ufork = None
import gevent
from gevent import pywsgi
import gevent.server
import gevent.socket
import gevent.pool

import async
from protected import Protected
import context
import env

import ll

ml = ll.LLogger()

class ServerGroup(object):
    def __init__(self, wsgi_apps=(), stream_handlers=(), prefork=None, daemonize=None, dev=None, **kw):
        '''Create a new ServerGroup which will can be started / stopped / forked as a group.

        *wsgi_apps* should be of the form  [ (wsgi_app, address, ssl), ...  ]

        *stream_handlers* should be of the form  [ (handler_func, address), ...  ]

        address here refers to a tuple (ip, port), or more generally anything which is
        acceptable as the address parameter to
        `socket.bind() <http://docs.python.org/2/library/socket.html#socket.socket.bind>`_.

        handler_func should have the following signature: f(socket, address), following
        the `convention of gevent <http://www.gevent.org/servers.html>`_.
        '''
        ctx = context.get_context()
        self.prefork = prefork if prefork is not None else ctx.serve_ufork
        self.daemonize = daemonize if daemonize is not None else ctx.serve_daemon
        dev = dev if dev is not None else ctx.dev
        self.post_fork = kw.get('post_fork')  # callback to be executed post fork
        self.server_log = kw.get('gevent_log')
        self.wsgi_apps = wsgi_apps
        self.stream_handlers = list(stream_handlers)
        if ctx.backdoor_port is not None:
            self.stream_handlers.append((console_sock_handle, ("0.0.0.0", ctx.backdoor_port)))
        self.num_workers = ctx.num_workers
        self.servers = []
        self.socks = {}
        self.client_pool = gevent.pool.Pool(ctx.max_concurrent_clients)
        for app, address, ssl in wsgi_apps:
            sock = _make_server_sock(address)
            if isinstance(ssl, Protected):
                protected = ssl
            elif ssl:
                protected = ctx.protected
            else:
                protected = None
            if protected:
                # TODO: maybe this determination belongs centralized in context?
                if dev or env.pp_host_env() in ("STAGE2", "HYPER") or kw.get('no_client_auth_required'):
                    if ctx.ssl_client_cert_optional_in_dev or kw.get('no_client_auth_required'):
                        sslcontext = getattr(protected, 'ssl_dev_server_context')
                    else:
                        sslcontext = getattr(protected, 'ssl_server_context')
                    server = MultiProtocolWSGIServer(
                        sock, app, spawn=self.client_pool, context=sslcontext)
                else:
                    server = SslContextWSGIServer(
                        sock, app, spawn=self.client_pool, context=protected.ssl_server_context)
            else:
                server = pywsgi.WSGIServer(sock, app)
            server.log = self.server_log or RotatingGeventLog()
            self.servers.append(server)
            self.socks[server] = sock
            # prevent a "blocking" call to DNS on each request
            # (NOTE: although the OS won't block, gevent will dispatch to a threadpool which is expensive)
            server.set_environ({'SERVER_NAME': socket.getfqdn(address[0]),
                                'wsgi.multiprocess': True})
        for handler, address in self.stream_handlers:
            sock = _make_server_sock(address)
            server = gevent.server.StreamServer(sock, handler, spawn=self.client_pool)
            self.servers.append(server)

    def run(self):
        ctx = context.get_context()
        try:
            import greenlet
            greenlet.settrace(ctx._trace)
        except AttributeError:
            pass  # oh well
        if not self.prefork:
            self.start()
            ml.la("The server is now really running and listening to requests-init over!")

            if ctx.dev and ctx.dev_service_repl_enabled and os.isatty(0):
                async.start_repl({'server': ctx.server_group})
            try:
                while 1:
                    async.sleep(1.0)
            finally:
                self.stop()
            return
        if not ufork:
            raise RuntimeError('attempting to run pre-forked on platform without fork')
        self.arbiter = ufork.Arbiter(
            post_fork=self._post_fork, child_pre_exit=self.stop, parent_pre_stop=ctx.stop,
            size=self.num_workers, sleep=async.sleep, fork=gevent.fork, child_memlimit=ctx.worker_memlimit)
        if self.daemonize:
            pidfile = os.path.join(ctx.pid_file_path,
                                   '{0}.pid'.format(ctx.appname))
            self.arbiter.spawn_daemon(pidfile=pidfile)
        else:
            self.arbiter.run()

    def _post_fork(self):
        # TODO: revisit this with newer gevent release
        hub = gevent.hub.get_hub()
        hub.loop.reinit()  # reinitializes libev
        hub._threadpool = None  # eliminate gevent's internal threadpools
        gevent.sleep(0)  # let greenlets run
        # finally, eliminate our threadpools
        context.get_context().thread_locals = threading.local()
        import ll
        if context.get_context().serve_daemon:
            ll.use_the_file()  # stdout was closed during daemonization.
                               # plus ufork stdout logging issue
        if self.post_fork:
            self.post_fork()
        context.get_context().cal.event('WORKER', 'STARTED', '0', {'pid': os.getpid()})
        self.start()

    def start(self):
        errs = {}
        for server in self.servers:
            try:
                server.start()
            except Exception as e:
                errs[server] = e
        if errs:
            raise RuntimeError(errs)

    def stop(self, timeout=30.0):
        gevent.joinall([gevent.spawn(server.stop, timeout) for server in self.servers], raise_error=True)

def _make_server_sock(address):
    ml.ld("about to bind to {0!r}", address)
    sock = gevent.socket.socket()
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(address)
    sock.listen(128)  # Note: 128 is a "hint" to the OS than a strict rule about backlog size
    ml.la("Listen to {0!r} gave this socket {1!r}", address, sock)
    return sock

def my_close(self):
    context.get_context().stats['fake_close'].add(1.0)

class MakeFileCloseWSGIHandler(pywsgi.WSGIHandler):
    '''
    quick work-around to re-enable gevent's work-around of the
    makefile() call in the pywsgi handler keeping sockets alive
    past their shelf life
    '''
    def __init__(self, socket, address, server, rfile=None):
        if rfile is None and hasattr(socket, "_makefile_refs"):
            rfile = socket.makefile()
            # restore gEvent's work-around of not letting wsgi.environ['input']
            # keep the socket alive in CLOSE_WAIT state after client is gone
            # to work with async.SSLSocket
            socket._makefile_refs -= 1
        self.state = context.get_context().markov_stats['wsgi_handler'].make_transitor('new')
        super(MakeFileCloseWSGIHandler, self).__init__(socket, address, server, rfile)

    def handle_one_request(self):
        self.state.transition('handling_request')
        ret = super(MakeFileCloseWSGIHandler, self).handle_one_request()
        if not ret:
            self.state.transition('done')

    def run_application(self):
        self.state.transition('running_application')
        return super(MakeFileCloseWSGIHandler, self).run_application()

    def process_result(self):
        self.state.transition('writing_response')
        ret = super(MakeFileCloseWSGIHandler, self).process_result()
        self.state.transition('done')
        return ret

class SslContextWSGIServer(pywsgi.WSGIServer):
    handler_class = MakeFileCloseWSGIHandler

    @async.timed
    def wrap_socket_and_handle(self, client_socket, address):
        context.get_context().client_sockets[client_socket] = 1

        if not self.ssl_args:
            raise ValueError('https server requires server-side'
                             ' SSL certificate (protected)')
        protocol = _socket_protocol(client_socket)
        if protocol == "ssl":
            ssl_socket = async.wrap_socket_context(client_socket, **self.ssl_args)
            return self.handle(ssl_socket, address)
        elif protocol == "http":
            client_socket.send(_NO_SSL_HTTP_RESPONSE)
            async.killsock(client_socket)
        else:
            context.get_context().intervals["server.pings"].tick()

class MultiProtocolWSGIServer(SslContextWSGIServer):
    @async.timed
    def wrap_socket_and_handle(self, client_socket, address):
        context.get_context().client_sockets[client_socket] = 1

        protocol = _socket_protocol(client_socket)
        if protocol == "http":
            return self.handle(client_socket, address)
        elif protocol == "ssl":
            ssl_socket = async.wrap_socket_context(client_socket, **self.ssl_args)
            return self.handle(ssl_socket, address)
        else:
            context.get_context().intervals["server.pings"].tick()

#http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
_HTTP_METHODS = ('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'OPTION',
                 'CONNECT', 'PATCH')

_SSL_RECORD_TYPES = {
    '\x14': 'CHANGE_CIPHER_SPEC', '\x15': 'ALERT', '\x16': 'HANDSHAKE',
    '\x17': 'APPLICATION_DATA',
}

_SSL_VERSIONS = {
    '\x03\x00': 'SSL3', '\x03\x01': 'TLS1', '\x03\x02': 'TLS1.1', '\x03\x03': 'TLS1.2'
}

_NO_SSL_HTTP_RESPONSE = "\r\n".join([
    'HTTP/1.1 401 UNAUTHORIZED',
    'Content-Type: text/plain',
    'Content-Length: ' + str(len('SSL REQUIRED')),
    '',
    'SSL REQUIRED'
    ])  # this could probably be improved, but better than just closing the socket

def _socket_protocol(client_socket):
    first_bytes = client_socket.recv(10, socket.MSG_PEEK)
    if any([method in first_bytes for method in _HTTP_METHODS]):
        return "http"
    elif not first_bytes:
        # TODO: empty requests are sometimes sent ([socket] keepalives?)
        return
    elif first_bytes[0] in _SSL_RECORD_TYPES and first_bytes[1:3] in _SSL_VERSIONS:
        return "ssl"
    else:
        raise IOError("unrecognized client protocol: {0!r}".format(first_bytes))

class RotatingGeventLog(object):
    def __init__(self, size=10000):
        self.msgs = collections.deque()
        self.size = size

    def write(self, msg):
        self.msgs.appendleft(msg)
        if len(self.msgs) > self.size:
            self.msgs.pop()

### a little helper for running an interactive console over a socket
import code
import sys

class SockConsole(code.InteractiveConsole):
    def __init__(self, sock):
        code.InteractiveConsole.__init__(self)
        self.sock = sock
        self.sock_file = sock.makefile()

    def write(self, data):
        self.sock.sendall(data)

    def raw_input(self, prompt=""):
        self.write(prompt)
        inp = self.sock_file.readline()
        if inp == "":
            raise OSError("client socket closed")
        inp = inp[:-1]
        return inp

    def _display_hook(self, obj):
        '''
        Handle an item returned from the exec statement.
        '''
        self._last = ""
        if obj is None:
            return
        self.locals['_'] = None
        # clean up output by adding \n here; so you don't see "hello world!">>>
        # TODO: is there a better way to handle this?
        self._last = repr(obj) + "\n"
        self.locals['_'] = obj

    def runcode(self, _code):
        '''
        Wraps base runcode to put displayhook around it.
        TODO: add a new displayhook dispatcher so that the
        current SockConsole is a greenlet-tree local.
        (In case the statement being executed itself
        causes greenlet switches.)
        '''
        prev = sys.displayhook
        sys.displayhook = self._display_hook
        self._last = None
        code.InteractiveConsole.runcode(self, _code)
        sys.displayhook = prev
        if self._last is not None:
            self.write(self._last)

def console_sock_handle(sock, address):
    console = SockConsole(sock)
    console.interact("Connected to pid {0} from {1}...".format(
        os.getpid(), address))
<|endoftext|>


<commit_before># -*- Mode: Python; coding: iso-8859-1 -*-
# vi:si:et:sw=4:sts=4:ts=4

##
## Stoqdrivers
## Copyright (C) 2005 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
## USA.
##
## Author(s): Henrique Romano             <henrique@async.com.br>
##
"""
stoqdrivers/devices/printers/base.py:

    Generic base class implementation for all printers
"""

import os

from zope.interface import providedBy
from kiwi.python import namedAny

from stoqdrivers.log import Logger
from stoqdrivers.configparser import StoqdriversConfig
from stoqdrivers.exceptions import CriticalError, ConfigError
from stoqdrivers.devices import printers
from stoqdrivers.devices.printers.interface import (ICouponPrinter,
                                                    IChequePrinter)

class BasePrinter(Logger):
    def __init__(self, brand=None, model=None, device=None, config_file=None):
        Logger.__init__(self)
        self.brand, self.model, self.device = brand, model, device
        self._load_configuration(config_file)

    def _load_configuration(self, config_file):
        self.config = StoqdriversConfig(config_file)

        if not self.config.has_section("Printer"):
            raise ConfigError("There is no printer configured!")

        self.brand = self.brand or self.config.get_option("brand", "Printer")
        self.device = self.device or self.config.get_option("device", "Printer")
        self.model = self.model or self.config.get_option("model", "Printer")

        name = 'stoqdrivers.devices.printers.%s.%s' % (self.brand, self.model)
        try:
            module = __import__(name, None, None, 'stoqdevices')
        except ImportError, reason:
            raise CriticalError(("Could not load driver %s %s: %s"
                                 % (self.brand.capitalize(),
                                    self.model.upper(), reason)))

        class_name = self.model

        driver_class = getattr(module, class_name, None)
        if not driver_class:
            raise CriticalError(("Printer driver %s needs a class called %s"
                                 % (name, class_name)))

        self._driver = driver_class(device=self.device)

        self.debug(("Config data: brand=%s,device=%s,model=%s\n"
                    % (self.brand, self.device, self.model)))

        driver_interfaces = providedBy(self._driver)
        if not (ICouponPrinter in driver_interfaces):
            raise TypeError("The driver %s doesn't implements a known "
                            "interface")

def get_virtual_printer():
    from stoqdrivers.devices.printers.fiscal import FiscalPrinter
    return FiscalPrinter(brand='virtual', model='Simple')

def get_supported_printers():
    printers_dir = os.path.dirname(printers.__file__)
    result = {}

    for brand in os.listdir(printers_dir):
        brand_dir = os.path.join(printers_dir, brand)
        if ((not os.path.isdir(brand_dir)) or brand.startswith(".")
            or brand.startswith("virtual")):
            continue

        result[brand] = []
        for model in os.listdir(brand_dir):
            if not model.endswith(".py") or model.startswith('__init__.py'):
                continue
            if not os.path.isfile(os.path.join(brand_dir, model)):
                continue

            model_name = model[:-3]
            try:
                obj = namedAny(("stoqdrivers.devices.printers.%s.%s.%s"
                                % (brand, model_name, model_name)))
            except AttributeError:
                raise ImportError("Can't find class %s for module %s"
                                  % (model_name, model_name))
            if not (IChequePrinter.implementedBy(obj) or
                    ICouponPrinter.implementedBy(obj)):
                raise TypeError("The driver %s %s doesn't implements a valid "
                                "interface" % (brand, model_name))

            result[brand].append(obj)

    return result
<commit_msg>If brand, model and device is specified, don't try load a config file.<commit_after># -*- Mode: Python; coding: iso-8859-1 -*-
# vi:si:et:sw=4:sts=4:ts=4

##
## Stoqdrivers
## Copyright (C) 2005 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
## USA.
##
## Author(s): Henrique Romano             <henrique@async.com.br>
##
"""
stoqdrivers/devices/printers/base.py:

    Generic base class implementation for all printers
"""

import os

from zope.interface import providedBy
from kiwi.python import namedAny

from stoqdrivers.log import Logger
from stoqdrivers.configparser import StoqdriversConfig
from stoqdrivers.exceptions import CriticalError, ConfigError
from stoqdrivers.devices import printers
from stoqdrivers.devices.printers.interface import (ICouponPrinter,
                                                    IChequePrinter)

class BasePrinter(Logger):
    def __init__(self, brand=None, model=None, device=None, config_file=None):
        Logger.__init__(self)
        self.brand, self.model, self.device = brand, model, device
        self._load_configuration(config_file)

    def _load_configuration(self, config_file):
        if not self.model or not self.brand or not self.device:
            self.config = StoqdriversConfig(config_file)
            if not self.config.has_section("Printer"):
                raise ConfigError("There is no printer configured!")
            self.brand = self.config.get_option("brand", "Printer")
            self.device = self.config.get_option("device", "Printer")
            self.model = self.config.get_option("model", "Printer")

        name = 'stoqdrivers.devices.printers.%s.%s' % (self.brand, self.model)
        try:
            module = __import__(name, None, None, 'stoqdevices')
        except ImportError, reason:
            raise CriticalError(("Could not load driver %s %s: %s"
                                 % (self.brand.capitalize(),
                                    self.model.upper(), reason)))

        class_name = self.model

        driver_class = getattr(module, class_name, None)
        if not driver_class:
            raise CriticalError(("Printer driver %s needs a class called %s"
                                 % (name, class_name)))

        self._driver = driver_class(device=self.device)

        self.debug(("Config data: brand=%s,device=%s,model=%s\n"
                    % (self.brand, self.device, self.model)))

        driver_interfaces = providedBy(self._driver)
        if not (ICouponPrinter in driver_interfaces):
            raise TypeError("The driver %s doesn't implements a known "
                            "interface")

def get_virtual_printer():
    from stoqdrivers.devices.printers.fiscal import FiscalPrinter
    return FiscalPrinter(brand='virtual', model='Simple')

def get_supported_printers():
    printers_dir = os.path.dirname(printers.__file__)
    result = {}

    for brand in os.listdir(printers_dir):
        brand_dir = os.path.join(printers_dir, brand)
        if ((not os.path.isdir(brand_dir)) or brand.startswith(".")
            or brand.startswith("virtual")):
            continue

        result[brand] = []
        for model in os.listdir(brand_dir):
            if not model.endswith(".py") or model.startswith('__init__.py'):
                continue
            if not os.path.isfile(os.path.join(brand_dir, model)):
                continue

            model_name = model[:-3]
            try:
                obj = namedAny(("stoqdrivers.devices.printers.%s.%s.%s"
                                % (brand, model_name, model_name)))
            except AttributeError:
                raise ImportError("Can't find class %s for module %s"
                                  % (model_name, model_name))
            if not (IChequePrinter.implementedBy(obj) or
                    ICouponPrinter.implementedBy(obj)):
                raise TypeError("The driver %s %s doesn't implements a valid "
                                "interface" % (brand, model_name))

            result[brand].append(obj)

    return result
<|endoftext|>


<commit_before>'''
Defines a ServerGroup class for running and managing servers, as well as a set of
base server types.

The following information is needed to start a server:
1- handler / wsgi app
2- address
3- ssl preference

'''
import collections
import socket
import os
import threading

if hasattr(os, "fork"):
    import ufork
else:
    ufork = None
from gevent import pywsgi
import gevent.server
import gevent.socket

import async
from protected import Protected
import context
import env

import ll

ml = ll.LLogger()

class ServerGroup(object):
    def __init__(self, wsgi_apps=(), stream_handlers=(), prefork=False, daemonize=False, dev=False, **kw):
        '''Create a new ServerGroup which will can be started / stopped / forked as a group.

        *wsgi_apps* should be of the form  [ (wsgi_app, address, ssl), ...  ]

        *stream_handlers* should be of the form  [ (handler_func, address), ...  ]

        address here refers to a tuple (ip, port), or more generally anything which is
        acceptable as the address parameter to
        `socket.bind() <http://docs.python.org/2/library/socket.html#socket.socket.bind>`_.

        handler_func should have the following signature: f(socket, address), following
        the `convention of gevent <http://www.gevent.org/servers.html>`_.
        '''
        self.prefork = prefork
        self.daemonize = daemonize
        self.post_fork = kw.get('post_fork')  # callback to be executed post fork
        self.server_log = kw.get('gevent_log')
        self.wsgi_apps = wsgi_apps
        self.stream_handlers = list(stream_handlers)
        ctx = context.get_context()
        if ctx.backdoor_port is not None:
            self.stream_handlers.append((console_sock_handle, ("0.0.0.0", ctx.backdoor_port)))
        self.num_workers = ctx.num_workers
        self.servers = []
        self.socks = {}
        for app, address, ssl in wsgi_apps:
            sock = _make_server_sock(address)
            if isinstance(ssl, Protected):
                protected = ssl
            elif ssl:
                protected = ctx.protected
            else:
                protected = None
            if protected:
                # TODO: maybe this determination belongs centralized in context?
                if dev or env.pp_host_env() in ("STAGE2", "HYPER"):
                    if ctx.ssl_client_cert_optional_in_dev:
                        sslcontext = getattr(protected, 'ssl_dev_server_context')
                    else:
                        sslcontext = getattr(protected, 'ssl_server_context')
                    server = MultiProtocolWSGIServer(
                        sock, app, spawn=10000, context=sslcontext)
                else:
                    server = SslContextWSGIServer(
                        sock, app, spawn=10000, context=protected.ssl_server_context)
            else:
                server = pywsgi.WSGIServer(sock, app)
            server.log = self.server_log or RotatingGeventLog()
            self.servers.append(server)
            self.socks[server] = sock
            # prevent a "blocking" call to DNS on each request
            # (NOTE: although the OS won't block, gevent will dispatch to a threadpool which is expensive)
            server.set_environ({'SERVER_NAME': socket.getfqdn(address[0])})
        for handler, address in self.stream_handlers:
            sock = _make_server_sock(address)
            server = gevent.server.StreamServer(sock, handler, spawn=10000)
            self.servers.append(server)

    def run(self):
        if not self.prefork:
            self.start()
            ml.ld("Now really running-init over")
            try:
                while 1:
                    async.sleep(1.0)
            finally:
                self.stop()
            return
        if not ufork:
            raise RuntimeError('attempting to run pre-forked on platform without fork')
        self.arbiter = ufork.Arbiter(
            post_fork=self._post_fork, child_pre_exit=self.stop, parent_pre_stop=self.stop,
            size=self.num_workers, sleep=async.sleep, fork=gevent.fork)
        if self.daemonize:
            ctx = context.get_context()
            pidfile = os.path.join(ctx.pid_file_path,
                                   '{0}.pid'.format(ctx.appname))
            self.arbiter.spawn_daemon(pidfile=pidfile)
        else:
            self.arbiter.run()

    def _post_fork(self):
        # TODO: revisit this with newer gevent release
        hub = gevent.hub.get_hub()
        hub.loop.reinit()  # reinitializes libev
        hub._threadpool = None  # eliminate gevent's internal threadpools
        gevent.sleep(0)  # let greenlets run
        # finally, eliminate our threadpools
        context.get_context().thread_locals = threading.local()
        import ll
        if context.get_context().serve_daemon:
            ll.use_the_file()  # stdout was closed during daemonization.
                               # plus ufork stdout logging issue
        if self.post_fork:
            self.post_fork()
        context.get_context().cal.event('WORKER', 'STARTED', '0', {'pid': os.getpid()})
        self.start()

    def start(self):
        errs = {}
        for server in self.servers:
            try:
                server.start()
            except Exception as e:
                errs[server] = e
        if errs:
            raise RuntimeError(errs)

    def stop(self, timeout=30.0):
        async.join([async.spawn(server.stop, timeout) for server in self.servers], raise_exc=True)

def _make_server_sock(address):
    sock = gevent.socket.socket()
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(address)
    sock.listen(128)  # Note: 128 is a "hint" to the OS than a strict rule about backlog size
    ml.la("Listen to {0!r} gave this socket {1!r}", address, sock)
    return sock

def my_close(self):
    context.get_context().stats['fake_close'].add(1.0)

class MakeFileCloseWSGIHandler(pywsgi.WSGIHandler):
    '''
    quick work-around to re-enable gevent's work-around of the
    makefile() call in the pywsgi handler keeping sockets alive
    past their shelf life
    '''
    def __init__(self, socket, address, server, rfile=None):
        if rfile is None and hasattr(socket, "_makefile_refs"):
            rfile = socket.makefile()
            # restore gEvent's work-around of not letting wsgi.environ['input']
            # keep the socket alive in CLOSE_WAIT state after client is gone
            # to work with async.SSLSocket
            socket._makefile_refs -= 1
        super(MakeFileCloseWSGIHandler, self).__init__(socket, address, server, rfile)

class SslContextWSGIServer(pywsgi.WSGIServer):
    handler_class = MakeFileCloseWSGIHandler

    def wrap_socket_and_handle(self, client_socket, address):
        context.get_context().client_sockets[client_socket] = 1

        if not self.ssl_args:
            raise ValueError('https server requires server-side'
                             ' SSL certificate (protected)')
        protocol = _socket_protocol(client_socket)
        if protocol == "ssl":
            ssl_socket = async.wrap_socket_context(client_socket, **self.ssl_args)
            return self.handle(ssl_socket, address)
        elif protocol == "http":
            client_socket.send(_NO_SSL_HTTP_RESPONSE)
            async.killsock(client_socket)
        else:
            context.get_context().counts["server.pings"] += 1

class MultiProtocolWSGIServer(SslContextWSGIServer):
    def wrap_socket_and_handle(self, client_socket, address):
        context.get_context().client_sockets[client_socket] = 1

        protocol = _socket_protocol(client_socket)
        if protocol == "http":
            return self.handle(client_socket, address)
        elif protocol == "ssl":
            ssl_socket = async.wrap_socket_context(client_socket, **self.ssl_args)
            return self.handle(ssl_socket, address)
        else:
            context.get_context().counts["server.pings"] += 1

#http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
_HTTP_METHODS = ('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'OPTION',
                 'CONNECT', 'PATCH')

_SSL_RECORD_TYPES = {
    '\x14': 'CHANGE_CIPHER_SPEC', '\x15': 'ALERT', '\x16': 'HANDSHAKE',
    '\x17': 'APPLICATION_DATA',
}

_SSL_VERSIONS = {
    '\x03\x00': 'SSL3', '\x03\x01': 'TLS1', '\x03\x02': 'TLS1.1', '\x03\x03': 'TLS1.2'
}

_NO_SSL_HTTP_RESPONSE = "\r\n".join([
    'HTTP/1.1 401 UNAUTHORIZED',
    'Content-Type: text/plain',
    'Content-Length: ' + str(len('SSL REQUIRED')),
    '',
    'SSL REQUIRED'
    ])  # this could probably be improved, but better than just closing the socket

def _socket_protocol(client_socket):
    first_bytes = client_socket.recv(10, socket.MSG_PEEK)
    if any([method in first_bytes for method in _HTTP_METHODS]):
        return "http"
    elif not first_bytes:
        # TODO: empty requests are sometimes sent ([socket] keepalives?)
        return
    elif first_bytes[0] in _SSL_RECORD_TYPES and first_bytes[1:3] in _SSL_VERSIONS:
        return "ssl"
    else:
        raise IOError("unrecognized client protocol: {0!r}".format(first_bytes))

class RotatingGeventLog(object):
    def __init__(self, size=10000):
        self.msgs = collections.deque()
        self.size = size

    def write(self, msg):
        self.msgs.appendleft(msg)
        if len(self.msgs) > self.size:
            self.msgs.pop()

### a little helper for running an interactive console over a socket
import code
import sys

class SockConsole(code.InteractiveConsole):
    def __init__(self, sock):
        code.InteractiveConsole.__init__(self)
        self.sock = sock
        self.sock_file = sock.makefile()

    def write(self, data):
        self.sock.sendall(data)

    def raw_input(self, prompt=""):
        self.write(prompt)
        inp = self.sock_file.readline()
        if inp == "":
            raise OSError("client socket closed")
        inp = inp[:-1]
        return inp

    def _display_hook(self, obj):
        '''
        Handle an item returned from the exec statement.
        '''
        self._last = ""
        if obj is None:
            return
        self.locals['_'] = None
        self._last = repr(obj)
        self.locals['_'] = obj

    def runcode(self, _code):
        '''
        Wraps base runcode to put displayhook around it.
        TODO: add a new displayhook dispatcher so that the
        current SockConsole is a greenlet-tree local.
        (In case the statement being executed itself
        causes greenlet switches.)
        '''
        prev = sys.displayhook
        sys.displayhook = self._display_hook
        self._last = None
        code.InteractiveConsole.runcode(self, _code)
        sys.displayhook = prev
        if self._last is not None:
            self.write(self._last)

def console_sock_handle(sock, address):
    console = SockConsole(sock)
    console.interact("Connected to pid {0} from {1}...".format(
        os.getpid(), address))
<commit_msg>Better error msg on not in topo<commit_after>'''
Defines a ServerGroup class for running and managing servers, as well as a set of
base server types.

The following information is needed to start a server:
1- handler / wsgi app
2- address
3- ssl preference

'''
import collections
import socket
import os
import threading

if hasattr(os, "fork"):
    import ufork
else:
    ufork = None
from gevent import pywsgi
import gevent.server
import gevent.socket

import async
from protected import Protected
import context
import env

import ll

ml = ll.LLogger()

class ServerGroup(object):
    def __init__(self, wsgi_apps=(), stream_handlers=(), prefork=False, daemonize=False, dev=False, **kw):
        '''Create a new ServerGroup which will can be started / stopped / forked as a group.

        *wsgi_apps* should be of the form  [ (wsgi_app, address, ssl), ...  ]

        *stream_handlers* should be of the form  [ (handler_func, address), ...  ]

        address here refers to a tuple (ip, port), or more generally anything which is
        acceptable as the address parameter to
        `socket.bind() <http://docs.python.org/2/library/socket.html#socket.socket.bind>`_.

        handler_func should have the following signature: f(socket, address), following
        the `convention of gevent <http://www.gevent.org/servers.html>`_.
        '''
        self.prefork = prefork
        self.daemonize = daemonize
        self.post_fork = kw.get('post_fork')  # callback to be executed post fork
        self.server_log = kw.get('gevent_log')
        self.wsgi_apps = wsgi_apps
        self.stream_handlers = list(stream_handlers)
        ctx = context.get_context()
        if ctx.backdoor_port is not None:
            self.stream_handlers.append((console_sock_handle, ("0.0.0.0", ctx.backdoor_port)))
        self.num_workers = ctx.num_workers
        self.servers = []
        self.socks = {}
        for app, address, ssl in wsgi_apps:
            sock = _make_server_sock(address)
            if isinstance(ssl, Protected):
                protected = ssl
            elif ssl:
                protected = ctx.protected
            else:
                protected = None
            if protected:
                # TODO: maybe this determination belongs centralized in context?
                if dev or env.pp_host_env() in ("STAGE2", "HYPER"):
                    if ctx.ssl_client_cert_optional_in_dev:
                        sslcontext = getattr(protected, 'ssl_dev_server_context')
                    else:
                        sslcontext = getattr(protected, 'ssl_server_context')
                    server = MultiProtocolWSGIServer(
                        sock, app, spawn=10000, context=sslcontext)
                else:
                    server = SslContextWSGIServer(
                        sock, app, spawn=10000, context=protected.ssl_server_context)
            else:
                server = pywsgi.WSGIServer(sock, app)
            server.log = self.server_log or RotatingGeventLog()
            self.servers.append(server)
            self.socks[server] = sock
            # prevent a "blocking" call to DNS on each request
            # (NOTE: although the OS won't block, gevent will dispatch to a threadpool which is expensive)
            server.set_environ({'SERVER_NAME': socket.getfqdn(address[0])})
        for handler, address in self.stream_handlers:
            sock = _make_server_sock(address)
            server = gevent.server.StreamServer(sock, handler, spawn=10000)
            self.servers.append(server)

    def run(self):
        if not self.prefork:
            self.start()
            ml.ld("Now really running-init over")
            try:
                while 1:
                    async.sleep(1.0)
            finally:
                self.stop()
            return
        if not ufork:
            raise RuntimeError('attempting to run pre-forked on platform without fork')
        self.arbiter = ufork.Arbiter(
            post_fork=self._post_fork, child_pre_exit=self.stop, parent_pre_stop=self.stop,
            size=self.num_workers, sleep=async.sleep, fork=gevent.fork)
        if self.daemonize:
            ctx = context.get_context()
            pidfile = os.path.join(ctx.pid_file_path,
                                   '{0}.pid'.format(ctx.appname))
            self.arbiter.spawn_daemon(pidfile=pidfile)
        else:
            self.arbiter.run()

    def _post_fork(self):
        # TODO: revisit this with newer gevent release
        hub = gevent.hub.get_hub()
        hub.loop.reinit()  # reinitializes libev
        hub._threadpool = None  # eliminate gevent's internal threadpools
        gevent.sleep(0)  # let greenlets run
        # finally, eliminate our threadpools
        context.get_context().thread_locals = threading.local()
        import ll
        if context.get_context().serve_daemon:
            ll.use_the_file()  # stdout was closed during daemonization.
                               # plus ufork stdout logging issue
        if self.post_fork:
            self.post_fork()
        context.get_context().cal.event('WORKER', 'STARTED', '0', {'pid': os.getpid()})
        self.start()

    def start(self):
        errs = {}
        for server in self.servers:
            try:
                server.start()
            except Exception as e:
                errs[server] = e
        if errs:
            raise RuntimeError(errs)

    def stop(self, timeout=30.0):
        async.join([async.spawn(server.stop, timeout) for server in self.servers], raise_exc=True)

def _make_server_sock(address):
    ml.ld("about to bind to {0!r}", address)
    sock = gevent.socket.socket()
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(address)
    sock.listen(128)  # Note: 128 is a "hint" to the OS than a strict rule about backlog size
    ml.la("Listen to {0!r} gave this socket {1!r}", address, sock)
    return sock

def my_close(self):
    context.get_context().stats['fake_close'].add(1.0)

class MakeFileCloseWSGIHandler(pywsgi.WSGIHandler):
    '''
    quick work-around to re-enable gevent's work-around of the
    makefile() call in the pywsgi handler keeping sockets alive
    past their shelf life
    '''
    def __init__(self, socket, address, server, rfile=None):
        if rfile is None and hasattr(socket, "_makefile_refs"):
            rfile = socket.makefile()
            # restore gEvent's work-around of not letting wsgi.environ['input']
            # keep the socket alive in CLOSE_WAIT state after client is gone
            # to work with async.SSLSocket
            socket._makefile_refs -= 1
        super(MakeFileCloseWSGIHandler, self).__init__(socket, address, server, rfile)

class SslContextWSGIServer(pywsgi.WSGIServer):
    handler_class = MakeFileCloseWSGIHandler

    def wrap_socket_and_handle(self, client_socket, address):
        context.get_context().client_sockets[client_socket] = 1

        if not self.ssl_args:
            raise ValueError('https server requires server-side'
                             ' SSL certificate (protected)')
        protocol = _socket_protocol(client_socket)
        if protocol == "ssl":
            ssl_socket = async.wrap_socket_context(client_socket, **self.ssl_args)
            return self.handle(ssl_socket, address)
        elif protocol == "http":
            client_socket.send(_NO_SSL_HTTP_RESPONSE)
            async.killsock(client_socket)
        else:
            context.get_context().counts["server.pings"] += 1

class MultiProtocolWSGIServer(SslContextWSGIServer):
    def wrap_socket_and_handle(self, client_socket, address):
        context.get_context().client_sockets[client_socket] = 1

        protocol = _socket_protocol(client_socket)
        if protocol == "http":
            return self.handle(client_socket, address)
        elif protocol == "ssl":
            ssl_socket = async.wrap_socket_context(client_socket, **self.ssl_args)
            return self.handle(ssl_socket, address)
        else:
            context.get_context().counts["server.pings"] += 1

#http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
_HTTP_METHODS = ('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'OPTION',
                 'CONNECT', 'PATCH')

_SSL_RECORD_TYPES = {
    '\x14': 'CHANGE_CIPHER_SPEC', '\x15': 'ALERT', '\x16': 'HANDSHAKE',
    '\x17': 'APPLICATION_DATA',
}

_SSL_VERSIONS = {
    '\x03\x00': 'SSL3', '\x03\x01': 'TLS1', '\x03\x02': 'TLS1.1', '\x03\x03': 'TLS1.2'
}

_NO_SSL_HTTP_RESPONSE = "\r\n".join([
    'HTTP/1.1 401 UNAUTHORIZED',
    'Content-Type: text/plain',
    'Content-Length: ' + str(len('SSL REQUIRED')),
    '',
    'SSL REQUIRED'
    ])  # this could probably be improved, but better than just closing the socket

def _socket_protocol(client_socket):
    first_bytes = client_socket.recv(10, socket.MSG_PEEK)
    if any([method in first_bytes for method in _HTTP_METHODS]):
        return "http"
    elif not first_bytes:
        # TODO: empty requests are sometimes sent ([socket] keepalives?)
        return
    elif first_bytes[0] in _SSL_RECORD_TYPES and first_bytes[1:3] in _SSL_VERSIONS:
        return "ssl"
    else:
        raise IOError("unrecognized client protocol: {0!r}".format(first_bytes))

class RotatingGeventLog(object):
    def __init__(self, size=10000):
        self.msgs = collections.deque()
        self.size = size

    def write(self, msg):
        self.msgs.appendleft(msg)
        if len(self.msgs) > self.size:
            self.msgs.pop()

### a little helper for running an interactive console over a socket
import code
import sys

class SockConsole(code.InteractiveConsole):
    def __init__(self, sock):
        code.InteractiveConsole.__init__(self)
        self.sock = sock
        self.sock_file = sock.makefile()

    def write(self, data):
        self.sock.sendall(data)

    def raw_input(self, prompt=""):
        self.write(prompt)
        inp = self.sock_file.readline()
        if inp == "":
            raise OSError("client socket closed")
        inp = inp[:-1]
        return inp

    def _display_hook(self, obj):
        '''
        Handle an item returned from the exec statement.
        '''
        self._last = ""
        if obj is None:
            return
        self.locals['_'] = None
        self._last = repr(obj)
        self.locals['_'] = obj

    def runcode(self, _code):
        '''
        Wraps base runcode to put displayhook around it.
        TODO: add a new displayhook dispatcher so that the
        current SockConsole is a greenlet-tree local.
        (In case the statement being executed itself
        causes greenlet switches.)
        '''
        prev = sys.displayhook
        sys.displayhook = self._display_hook
        self._last = None
        code.InteractiveConsole.runcode(self, _code)
        sys.displayhook = prev
        if self._last is not None:
            self.write(self._last)

def console_sock_handle(sock, address):
    console = SockConsole(sock)
    console.interact("Connected to pid {0} from {1}...".format(
        os.getpid(), address))
<|endoftext|>


<commit_before># coding: utf-8

from __future__ import absolute_import, unicode_literals, division, print_function

__author__ = "adrn <adrn@astro.columbia.edu>"

# Standard library
import os, sys
import time as pytime
import copy

# Third-party
import numpy as np
import pytest
import astropy.units as u
from astropy.io.misc import fnpickle
import matplotlib.pyplot as plt

from ... import usys
from ..core import *
from ...potential.lm10 import LawMajewski2010
from ...coordinates import _gc_to_hel
from ..parameter import *
from ..prior import *

plot_path = "plots/tests/inference"
if not os.path.exists(plot_path):
    os.makedirs(plot_path)

'''
def test_statistical_model():
    """ Test with example from main Emcee docs """

    def lnprob(x, ivar=1.7):
        return -0.5 * np.sum(ivar * x ** 2)

    parameters = ['x']
    ndim, nwalkers = 1, 100
    ivar = 1. / np.random.rand(ndim)
    p0 = [np.random.rand(ndim) for i in range(nwalkers)]

    sm = StatisticalModel(parameters,
                          ln_likelihood=lnprob,
                          likelihood_args=(1.7,),
                          parameter_bounds={'x':(-1,100)})

    sampler = sm.run(p0, 100, 0)

    plt.hist(sampler.flatchain, bins=100)
    plt.savefig(os.path.join(plot_path, "emcee_test.png"))
'''

class TestStreamModel(object):

    def setup(self):

        self.plot_path = os.path.join(plot_path, "StreamModel")
        if not os.path.exists(self.plot_path):
            os.mkdir(self.plot_path)

        from streams.io import SgrSimulation

        np.random.seed(52)
        self.Nparticles = 10
        self.simulation = SgrSimulation(mass="2.5e8")

        self.particles = self.simulation.particles(N=self.Nparticles,
                                                   expr="tub!=0")

        self.potential = LawMajewski2010()
        self.satellite = self.simulation.satellite()

    def test_call(self):

        params = []
        params.append(ModelParameter(target=self.potential.q1,
                        attr="_value",
                        ln_prior=LogUniformPrior(*self.potential.q1._range)))

        model = StreamModel(self.potential, self.simulation, self.satellite,
                            self.particles.copy(), parameters=params)

        model([1.4])
        assert model.vector[0] == self.potential.q1.value

    def test_vector(self):
        particles = self.particles.copy()

        params = []
        params.append(ModelParameter(target=self.potential.q1,
                                attr="_value",
                                ln_prior=LogUniformPrior(*self.potential.q1._range)))
        params.append(ModelParameter(target=particles,
                                attr="_X"))
        params.append(ModelParameter(target=particles,
                                attr="tub"))

        model = StreamModel(self.potential, self.simulation,
                            self.satellite, particles,
                            parameters=params)

        model([1.4] + list(np.random.random(size=self.Nparticles*6)) + \
              list(np.random.randint(6266, size=self.Nparticles)))

        assert model.vector[0] == self.potential.q1.value
        assert self.potential.q1.value == self.potential.parameters["q1"].value

    def test_indices(self):
        particles = self.particles.copy()

        params = []
        params.append(ModelParameter(target=self.potential.q1,
                                attr="_value",
                                ln_prior=LogUniformPrior(*self.potential.q1._range)))
        params.append(ModelParameter(target=particles,
                                attr="_X"))
        params.append(ModelParameter(target=particles,
                                attr="tub"))

        model = StreamModel(self.potential, self.simulation,
                            self.satellite, particles,
                            parameters=params)

        for ii in range(len(params)):
            print(model.param_idx_to_chain_idx(ii))

    def test_potential_likelihood(self):

        fig = plt.figure()
        ax = fig.add_subplot(111)

        for p_name in self.potential.parameters.keys():
            potential = LawMajewski2010()

            particles = self.particles.to_frame("heliocentric")
            satellite = self.satellite.to_frame("heliocentric")

            p = potential.parameters[p_name]

            params = []
            params.append(ModelParameter(target=p,
                                    attr="_value",
                                    ln_prior=LogUniformPrior(*p._range)))

            model = StreamModel(potential, self.simulation,
                                satellite, particles,
                                parameters=params)

            Ls = []
            vals = np.linspace(0.8, 1.2, 31)*p._truth
            for q in vals:
                Ls.append(model([q]))

            ax.cla()
            ax.plot(vals, Ls)
            ax.axvline(p._truth, color='r')
            ax.set_ylabel("ln likelihood", fontsize=24)
            ax.set_xlabel(p.latex, fontsize=24)
            fig.savefig(os.path.join(self.plot_path,
                                     "{0}.png".format(p_name)))

    def test_potential_likelihood_w_particle_X(self):

        fig = plt.figure()
        ax = fig.add_subplot(111)

        for p_name in self.potential.parameters.keys():
            potential = LawMajewski2010()

            particles = self.particles.to_frame("heliocentric")
            satellite = self.satellite.to_frame("heliocentric")

            p = potential.parameters[p_name]

            params = []
            params.append(ModelParameter(target=p,
                                    attr="_value",
                                    ln_prior=LogUniformPrior(*p._range)))
            params.append(ModelParameter(target=particles,
                                         attr="_X",
                                         ln_prior=LogPrior()))

            model = StreamModel(potential, self.simulation,
                                satellite, particles,
                                parameters=params)

            Ls = []
            vals = np.linspace(0.8, 1.2, 31)*p._truth
            for q in vals:
                Ls.append(model([q] + list(np.ravel(particles._X))))

            ax.cla()
            ax.plot(vals, Ls)
            ax.axvline(p._truth, color='r')
            ax.set_ylabel("ln likelihood", fontsize=24)
            ax.set_xlabel(p.latex, fontsize=24)
            fig.savefig(os.path.join(self.plot_path,
                                     "{0}_X.png".format(p_name)))

    def test_particle_position_likelihood(self):
        fig = plt.figure()
        ax = fig.add_subplot(111)

        potential = LawMajewski2010()

        _particles = self.particles.to_frame("heliocentric")
        particles = self.particles.to_frame("heliocentric")
        satellite = self.satellite.to_frame("heliocentric")

        params = []
        params.append(ModelParameter(target=particles,
                                     attr="_X",
                                     ln_prior=LogPrior()))

        model = StreamModel(potential, self.simulation,
                            satellite, particles,
                            parameters=params)

        Ls = []
        vals = np.linspace(0.5, 1.5, 21)
        for v in vals:
            sampled_X = np.ravel(_particles._X)*v
            Ls.append(model(list(sampled_X)))

        ax.cla()
        ax.plot(vals, Ls)
        ax.axvline(1., color='r')
        ax.set_ylabel("ln likelihood", fontsize=24)
        fig.savefig(os.path.join(self.plot_path, "particles.png"))

    def test_particle_tub_likelihood(self):
        fig = plt.figure()
        ax = fig.add_subplot(111)

        _particles = self.particles.to_frame("heliocentric")
        for ii in range(self.Nparticles):
            potential = LawMajewski2010()

            particles = self.particles.to_frame("heliocentric")
            satellite = self.satellite.to_frame("heliocentric")

            params = []
            params.append(ModelParameter(target=particles,
                                         attr="tub",
                                         ln_prior=LogPrior()))

            model = StreamModel(potential, self.simulation,
                                satellite, particles,
                                parameters=params)

            true_tub = np.array(_particles.tub)
            tub = _particles.tub

            Ls = []
            vals = np.linspace(0.8, 1.2, 31)*true_tub[ii]
            for v in vals:
                tub[ii] = v
                Ls.append(model(list(tub)))

            ax.cla()
            ax.plot(vals, Ls)
            ax.axvline(true_tub[ii], color='r')
            ax.set_title("particle {0}".format(ii))
            ax.set_ylabel("ln likelihood", fontsize=24)
            ax.set_xlabel("particle {0}, tub".format(ii), fontsize=24)
            fig.savefig(os.path.join(self.plot_path,
                        "tub_particle_{0}.png".format(ii)))

    def test_pickle(self):
        from ...observation.gaia import gaia_spitzer_errors

        potential = LawMajewski2010()

        particles = self.particles.to_frame("heliocentric")
        satellite = self.satellite.to_frame("heliocentric")

        particle_errors = gaia_spitzer_errors(particles)
        particles = particles.observe(particle_errors)

        params = []
        for p_name in self.potential.parameters.keys():
            p = potential.parameters[p_name]
            params.append(ModelParameter(target=p,
                                    attr="_value",
                                    ln_prior=LogUniformPrior(*p._range)))

        params.append(ModelParameter(target=particles,
                                     attr="tub",
                                     ln_prior=LogPrior()))

        sigmas = np.array([particles.errors[n].decompose(usys).value \
                    for n in particles.names]).T
        covs = [np.diag(s**2) for s in sigmas]

        prior = LogNormalPrior(np.array(particles._X),
                               cov=np.array(covs))
        params.append(ModelParameter(target=particles,
                                     attr="_X",
                                     ln_prior=prior))

        params.append(ModelParameter(target=satellite,
                                     attr="_X",
                                     ln_prior=LogPrior()))

        model = StreamModel(potential, self.simulation,
                            satellite, particles,
                            parameters=params)

        fnpickle(model, os.path.join(self.plot_path, "test.pickle"))<commit_msg>add test for out of order parameters<commit_after># coding: utf-8

from __future__ import absolute_import, unicode_literals, division, print_function

__author__ = "adrn <adrn@astro.columbia.edu>"

# Standard library
import os, sys
import time as pytime
import copy

# Third-party
import numpy as np
import pytest
import astropy.units as u
from astropy.io.misc import fnpickle
import matplotlib.pyplot as plt

from ... import usys
from ..core import *
from ...potential.lm10 import LawMajewski2010
from ...coordinates import _gc_to_hel
from ..parameter import *
from ..prior import *

plot_path = "plots/tests/inference"
if not os.path.exists(plot_path):
    os.makedirs(plot_path)

'''
def test_statistical_model():
    """ Test with example from main Emcee docs """

    def lnprob(x, ivar=1.7):
        return -0.5 * np.sum(ivar * x ** 2)

    parameters = ['x']
    ndim, nwalkers = 1, 100
    ivar = 1. / np.random.rand(ndim)
    p0 = [np.random.rand(ndim) for i in range(nwalkers)]

    sm = StatisticalModel(parameters,
                          ln_likelihood=lnprob,
                          likelihood_args=(1.7,),
                          parameter_bounds={'x':(-1,100)})

    sampler = sm.run(p0, 100, 0)

    plt.hist(sampler.flatchain, bins=100)
    plt.savefig(os.path.join(plot_path, "emcee_test.png"))
'''

class TestStreamModel(object):

    def setup(self):

        self.plot_path = os.path.join(plot_path, "StreamModel")
        if not os.path.exists(self.plot_path):
            os.mkdir(self.plot_path)

        from streams.io import SgrSimulation

        np.random.seed(52)
        self.Nparticles = 10
        self.simulation = SgrSimulation(mass="2.5e8")

        self.particles = self.simulation.particles(N=self.Nparticles,
                                                   expr="tub!=0")

        self.potential = LawMajewski2010()
        self.satellite = self.simulation.satellite()

    def test_call(self):

        params = []
        params.append(ModelParameter(target=self.potential.q1,
                        attr="_value",
                        ln_prior=LogUniformPrior(*self.potential.q1._range)))

        model = StreamModel(self.potential, self.simulation, self.satellite,
                            self.particles.copy(), parameters=params)

        model([1.4])
        assert model.vector[0] == self.potential.q1.value

    def test_vector(self):
        particles = self.particles.copy()

        params = []
        params.append(ModelParameter(target=self.potential.q1,
                                attr="_value",
                                ln_prior=LogUniformPrior(*self.potential.q1._range)))
        params.append(ModelParameter(target=particles,
                                attr="_X"))
        params.append(ModelParameter(target=particles,
                                attr="tub"))

        model = StreamModel(self.potential, self.simulation,
                            self.satellite, particles,
                            parameters=params)

        model([1.4] + list(np.random.random(size=self.Nparticles*6)) + \
              list(np.random.randint(6266, size=self.Nparticles)))

        assert model.vector[0] == self.potential.q1.value
        assert self.potential.q1.value == self.potential.parameters["q1"].value

    def test_indices(self):
        particles = self.particles.copy()

        params = []
        params.append(ModelParameter(target=self.potential.q1,
                                attr="_value",
                                ln_prior=LogUniformPrior(*self.potential.q1._range)))
        params.append(ModelParameter(target=particles,
                                attr="_X"))
        params.append(ModelParameter(target=particles,
                                attr="tub"))

        model = StreamModel(self.potential, self.simulation,
                            self.satellite, particles,
                            parameters=params)

        for ii in range(len(params)):
            print(model.param_idx_to_chain_idx(ii))

    def test_indices2(self):
        particles = self.particles.copy()

        params = []
        params.append(ModelParameter(target=particles,
                                attr="_X"))
        params.append(ModelParameter(target=self.potential.q1,
                                attr="_value",
                                ln_prior=LogUniformPrior(*self.potential.q1._range)))
        params.append(ModelParameter(target=particles,
                                attr="tub"))

        model = StreamModel(self.potential, self.simulation,
                            self.satellite, particles,
                            parameters=params)

        for ii in range(len(params)):
            print(model.param_idx_to_chain_idx(ii))

    def test_potential_likelihood(self):

        fig = plt.figure()
        ax = fig.add_subplot(111)

        for p_name in self.potential.parameters.keys():
            potential = LawMajewski2010()

            particles = self.particles.to_frame("heliocentric")
            satellite = self.satellite.to_frame("heliocentric")

            p = potential.parameters[p_name]

            params = []
            params.append(ModelParameter(target=p,
                                    attr="_value",
                                    ln_prior=LogUniformPrior(*p._range)))

            model = StreamModel(potential, self.simulation,
                                satellite, particles,
                                parameters=params)

            Ls = []
            vals = np.linspace(0.8, 1.2, 31)*p._truth
            for q in vals:
                Ls.append(model([q]))

            ax.cla()
            ax.plot(vals, Ls)
            ax.axvline(p._truth, color='r')
            ax.set_ylabel("ln likelihood", fontsize=24)
            ax.set_xlabel(p.latex, fontsize=24)
            fig.savefig(os.path.join(self.plot_path,
                                     "{0}.png".format(p_name)))

    def test_potential_likelihood_w_particle_X(self):

        fig = plt.figure()
        ax = fig.add_subplot(111)

        for p_name in self.potential.parameters.keys():
            potential = LawMajewski2010()

            particles = self.particles.to_frame("heliocentric")
            satellite = self.satellite.to_frame("heliocentric")

            p = potential.parameters[p_name]

            params = []
            params.append(ModelParameter(target=p,
                                    attr="_value",
                                    ln_prior=LogUniformPrior(*p._range)))
            params.append(ModelParameter(target=particles,
                                         attr="_X",
                                         ln_prior=LogPrior()))

            model = StreamModel(potential, self.simulation,
                                satellite, particles,
                                parameters=params)

            Ls = []
            vals = np.linspace(0.8, 1.2, 31)*p._truth
            for q in vals:
                Ls.append(model([q] + list(np.ravel(particles._X))))

            ax.cla()
            ax.plot(vals, Ls)
            ax.axvline(p._truth, color='r')
            ax.set_ylabel("ln likelihood", fontsize=24)
            ax.set_xlabel(p.latex, fontsize=24)
            fig.savefig(os.path.join(self.plot_path,
                                     "{0}_X.png".format(p_name)))

    def test_particle_position_likelihood(self):
        fig = plt.figure()
        ax = fig.add_subplot(111)

        potential = LawMajewski2010()

        _particles = self.particles.to_frame("heliocentric")
        particles = self.particles.to_frame("heliocentric")
        satellite = self.satellite.to_frame("heliocentric")

        params = []
        params.append(ModelParameter(target=particles,
                                     attr="_X",
                                     ln_prior=LogPrior()))

        model = StreamModel(potential, self.simulation,
                            satellite, particles,
                            parameters=params)

        Ls = []
        vals = np.linspace(0.5, 1.5, 21)
        for v in vals:
            sampled_X = np.ravel(_particles._X)*v
            Ls.append(model(list(sampled_X)))

        ax.cla()
        ax.plot(vals, Ls)
        ax.axvline(1., color='r')
        ax.set_ylabel("ln likelihood", fontsize=24)
        fig.savefig(os.path.join(self.plot_path, "particles.png"))

    def test_particle_tub_likelihood(self):
        fig = plt.figure()
        ax = fig.add_subplot(111)

        _particles = self.particles.to_frame("heliocentric")
        for ii in range(self.Nparticles):
            potential = LawMajewski2010()

            particles = self.particles.to_frame("heliocentric")
            satellite = self.satellite.to_frame("heliocentric")

            params = []
            params.append(ModelParameter(target=particles,
                                         attr="tub",
                                         ln_prior=LogPrior()))

            model = StreamModel(potential, self.simulation,
                                satellite, particles,
                                parameters=params)

            true_tub = np.array(_particles.tub)
            tub = _particles.tub

            Ls = []
            vals = np.linspace(0.8, 1.2, 31)*true_tub[ii]
            for v in vals:
                tub[ii] = v
                Ls.append(model(list(tub)))

            ax.cla()
            ax.plot(vals, Ls)
            ax.axvline(true_tub[ii], color='r')
            ax.set_title("particle {0}".format(ii))
            ax.set_ylabel("ln likelihood", fontsize=24)
            ax.set_xlabel("particle {0}, tub".format(ii), fontsize=24)
            fig.savefig(os.path.join(self.plot_path,
                        "tub_particle_{0}.png".format(ii)))

    def test_pickle(self):
        from ...observation.gaia import gaia_spitzer_errors

        potential = LawMajewski2010()

        particles = self.particles.to_frame("heliocentric")
        satellite = self.satellite.to_frame("heliocentric")

        particle_errors = gaia_spitzer_errors(particles)
        particles = particles.observe(particle_errors)

        params = []
        for p_name in self.potential.parameters.keys():
            p = potential.parameters[p_name]
            params.append(ModelParameter(target=p,
                                    attr="_value",
                                    ln_prior=LogUniformPrior(*p._range)))

        params.append(ModelParameter(target=particles,
                                     attr="tub",
                                     ln_prior=LogPrior()))

        sigmas = np.array([particles.errors[n].decompose(usys).value \
                    for n in particles.names]).T
        covs = [np.diag(s**2) for s in sigmas]

        prior = LogNormalPrior(np.array(particles._X),
                               cov=np.array(covs))
        params.append(ModelParameter(target=particles,
                                     attr="_X",
                                     ln_prior=prior))

        params.append(ModelParameter(target=satellite,
                                     attr="_X",
                                     ln_prior=LogPrior()))

        model = StreamModel(potential, self.simulation,
                            satellite, particles,
                            parameters=params)

        fnpickle(model, os.path.join(self.plot_path, "test.pickle"))<|endoftext|>


<commit_before># Fibonacci Exercise
# Session02 - Oct. 5, 2016

def fibonacci(n):
    """ Fibonacci Series Exercise """
    for i in range(0, n):
        answer = (i-2) + (i-1)
        if answer <= 0:
            answer = 0
        if answer <= 1:
            answer = 1

        print(answer)

fibonacci(25)

#     answer = 0
#     if x == 0:
#         return 0
#     elif x == 1:
#         return 1
#     else:
#         answer = fibonacci(x-2) + fibonacci(x-1)
#     fibonacci(answer)

# fibonacci(5)

# answers = [fibonacci(x) for x in range(0, 1)]

# print(answers)

# print("Unendlicher Fibonacci-Generator Rekursiv")

# def fib(n):

#     if n == 0:
#         return 0
#     elif n == 1:
#         return 1
#     else:
#         return fib(n-1) + fib(n-2)

# row=[fib(n) for n in range(0,25)]
# print (row[-1])
<commit_msg>Fibonacci Fail...work in progress<commit_after># Fibonacci Exercise
# Session02 - Oct. 5, 2016

def fibonacci(n):
    """ Fibonacci Series Exercise """
    for i in range(0, n):
        answer = (i-2) + (i-1)
        if answer <= 0:
            answer = 0
        if answer <= 1:
            answer = 1

        print(answer)

fibonacci(25)

<|endoftext|>


<commit_before># Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging
import os
import queue

import netifaces
import yaml

from subiquitycore.async import Async
from subiquitycore.models import NetworkModel
from subiquitycore.ui.views import (NetworkView,
                                    NetworkSetDefaultRouteView,
                                    NetworkBondInterfacesView,
                                    NetworkConfigureInterfaceView,
                                    NetworkConfigureIPv4InterfaceView)
from subiquitycore.ui.views.network import ApplyingConfigWidget
from subiquitycore.ui.dummy import DummyView
from subiquitycore.controller import BaseController
from subiquitycore.utils import run_command_start, run_command_summarize

log = logging.getLogger("subiquitycore.controller.network")

class CommandSequence:
    def __init__(self, loop, cmds, watcher):
        self.loop = loop
        self.cmds = cmds
        self.watcher = watcher
        self.canceled = False
        self.queue = queue.Queue()

    def run(self):
        self._run1()

    def cancel(self):
        self.canceled = True
        try:
            self.proc.terminate()
        except ProcessLookupError:
            pass

    def _run1(self):
        self.stage, cmd = self.cmds[0]
        self.cmds = self.cmds[1:]
        log.debug('running %s for stage %s', cmd, self.stage)
        self.proc = run_command_start(cmd)
        self.pipe = self.loop.watch_pipe(self._thread_callback)
        Async.pool.submit(self._communicate)

    def _communicate(self):
        stdout, stderr = self.proc.communicate()
        result = run_command_summarize(self.proc, stdout, stderr)
        self.call_from_thread(self._complete, result)

    def call_from_thread(self, func, *args):
        self.queue.put((func, args))
        os.write(self.pipe, b'x')

    def _thread_callback(self, ignored):
        func, args = self.queue.get()
        func(*args)

    def _complete(self, result):
        if self.canceled:
            return
        if result['status'] != 0:
            self.watcher.cmd_error(self.stage)
            return
        self.watcher.cmd_complete(self.stage)
        if len(self.cmds) == 0:
            self.watcher.cmd_finished()
        else:
            self._run1()

class NetworkController(BaseController):
    def __init__(self, common):
        super().__init__(common)
        self.model = NetworkModel(self.prober, self.opts)

    def network(self):
        title = "Network connections"
        excerpt = ("Configure at least the main interface this server will "
                   "use to talk to the store.")
        footer = ("Additional networking info here")
        self.ui.set_header(title, excerpt)
        self.ui.set_footer(footer, 20)
        self.ui.set_body(NetworkView(self.model, self.signal))

    def network_finish(self, config):
        log.debug("network config: \n%s", yaml.dump(config, default_flow_style=False))

        if self.opts.dry_run:
            if hasattr(self, 'tried_once'):
                cmds = [
                    ('one', ['sleep', '1']),
                    ('two', ['sleep', '1']),
                    ('three', ['sleep', '1']),
                    ]
            else:
                self.tried_once = True
                cmds = [
                    ('one', ['sleep', '1']),
                    ('two', ['sleep', '1']),
                    ('three', ['false']),
                    ('four', ['sleep 1']),
                    ]
        else:
            with open('/etc/netplan/01-console-conf.yaml', 'w') as w:
                w.write(yaml.dump(config))
            cmds = [
                ('generate', ['/lib/netplan/generate']),
                ('apply', ['netplan', 'apply']),
                ('timeout', ['/lib/systemd/systemd-networkd-wait-online', '--timeout=30']),
                ]

        def cancel():
            self.cs.cancel()
            self.cmd_error('canceled')
        self.acw = ApplyingConfigWidget(len(cmds), cancel)
        self.ui.frame.body.show_overlay(self.acw)

        self.cs = CommandSequence(self.loop, cmds, self)
        self.cs.run()

    def cmd_complete(self, stage):
        self.acw.advance()

    def cmd_error(self, stage):
        self.ui.frame.body.remove_overlay(self.acw)
        self.ui.frame.body.show_network_error(stage)

    def cmd_finished(self):
        self.signal.emit_signal('menu:identity:main')

    def set_default_v4_route(self):
        self.ui.set_header("Default route")
        self.ui.set_body(NetworkSetDefaultRouteView(self.model,
                                                    netifaces.AF_INET,
                                                    self.signal))

    def set_default_v6_route(self):
        self.ui.set_header("Default route")
        self.ui.set_body(NetworkSetDefaultRouteView(self.model,
                                                    netifaces.AF_INET6,
                                                    self.signal))

    def bond_interfaces(self):
        self.ui.set_header("Bond interfaces")
        self.ui.set_body(NetworkBondInterfacesView(self.model,
                                                   self.signal))

    def network_configure_interface(self, iface):
        self.ui.set_header("Network interface {}".format(iface))
        self.ui.set_body(NetworkConfigureInterfaceView(self.model,
                                                       self.signal,
                                                       iface))

    def network_configure_ipv4_interface(self, iface):
        self.model.prev_signal = ('Back to configure interface menu',
                                  'network:configure-interface-menu',
                                  'network_configure_interface')
        self.ui.set_header("Network interface {} manual IPv4 "
                           "configuration".format(iface))
        self.ui.set_body(NetworkConfigureIPv4InterfaceView(self.model,
                                                           self.signal,
                                                           iface))

    def network_configure_ipv6_interface(self, iface):
        self.model.prev_signal = ('Back to configure interface menu',
                                  'network:configure-interface-menu',
                                  'network_configure_interface')
        self.ui.set_body(DummyView(self.signal))

    def install_network_driver(self):
        self.ui.set_body(DummyView(self.signal))

<commit_msg>a touch more logging<commit_after># Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging
import os
import queue

import netifaces
import yaml

from subiquitycore.async import Async
from subiquitycore.models import NetworkModel
from subiquitycore.ui.views import (NetworkView,
                                    NetworkSetDefaultRouteView,
                                    NetworkBondInterfacesView,
                                    NetworkConfigureInterfaceView,
                                    NetworkConfigureIPv4InterfaceView)
from subiquitycore.ui.views.network import ApplyingConfigWidget
from subiquitycore.ui.dummy import DummyView
from subiquitycore.controller import BaseController
from subiquitycore.utils import run_command_start, run_command_summarize

log = logging.getLogger("subiquitycore.controller.network")

class CommandSequence:
    def __init__(self, loop, cmds, watcher):
        self.loop = loop
        self.cmds = cmds
        self.watcher = watcher
        self.canceled = False
        self.stage = None
        self.queue = queue.Queue()

    def run(self):
        self._run1()

    def cancel(self):
        log.debug('canceling stage %s', self.stage)
        self.canceled = True
        try:
            self.proc.terminate()
        except ProcessLookupError:
            pass

    def _run1(self):
        self.stage, cmd = self.cmds[0]
        self.cmds = self.cmds[1:]
        log.debug('running %s for stage %s', cmd, self.stage)
        self.proc = run_command_start(cmd)
        self.pipe = self.loop.watch_pipe(self._thread_callback)
        Async.pool.submit(self._communicate)

    def _communicate(self):
        stdout, stderr = self.proc.communicate()
        result = run_command_summarize(self.proc, stdout, stderr)
        self.call_from_thread(self._complete, result)

    def call_from_thread(self, func, *args):
        self.queue.put((func, args))
        os.write(self.pipe, b'x')

    def _thread_callback(self, ignored):
        func, args = self.queue.get()
        func(*args)

    def _complete(self, result):
        if self.canceled:
            return
        if result['status'] != 0:
            self.watcher.cmd_error(self.stage)
            return
        self.watcher.cmd_complete(self.stage)
        if len(self.cmds) == 0:
            self.watcher.cmd_finished()
        else:
            self._run1()

class NetworkController(BaseController):
    def __init__(self, common):
        super().__init__(common)
        self.model = NetworkModel(self.prober, self.opts)

    def network(self):
        title = "Network connections"
        excerpt = ("Configure at least the main interface this server will "
                   "use to talk to the store.")
        footer = ("Additional networking info here")
        self.ui.set_header(title, excerpt)
        self.ui.set_footer(footer, 20)
        self.ui.set_body(NetworkView(self.model, self.signal))

    def network_finish(self, config):
        log.debug("network config: \n%s", yaml.dump(config, default_flow_style=False))

        if self.opts.dry_run:
            if hasattr(self, 'tried_once'):
                cmds = [
                    ('one', ['sleep', '1']),
                    ('two', ['sleep', '1']),
                    ('three', ['sleep', '1']),
                    ]
            else:
                self.tried_once = True
                cmds = [
                    ('one', ['sleep', '1']),
                    ('two', ['sleep', '1']),
                    ('three', ['false']),
                    ('four', ['sleep 1']),
                    ]
        else:
            with open('/etc/netplan/01-console-conf.yaml', 'w') as w:
                w.write(yaml.dump(config))
            cmds = [
                ('generate', ['/lib/netplan/generate']),
                ('apply', ['netplan', 'apply']),
                ('timeout', ['/lib/systemd/systemd-networkd-wait-online', '--timeout=30']),
                ]

        def cancel():
            self.cs.cancel()
            self.cmd_error('canceled')
        self.acw = ApplyingConfigWidget(len(cmds), cancel)
        self.ui.frame.body.show_overlay(self.acw)

        self.cs = CommandSequence(self.loop, cmds, self)
        self.cs.run()

    def cmd_complete(self, stage):
        self.acw.advance()

    def cmd_error(self, stage):
        self.ui.frame.body.remove_overlay(self.acw)
        self.ui.frame.body.show_network_error(stage)

    def cmd_finished(self):
        self.signal.emit_signal('menu:identity:main')

    def set_default_v4_route(self):
        self.ui.set_header("Default route")
        self.ui.set_body(NetworkSetDefaultRouteView(self.model,
                                                    netifaces.AF_INET,
                                                    self.signal))

    def set_default_v6_route(self):
        self.ui.set_header("Default route")
        self.ui.set_body(NetworkSetDefaultRouteView(self.model,
                                                    netifaces.AF_INET6,
                                                    self.signal))

    def bond_interfaces(self):
        self.ui.set_header("Bond interfaces")
        self.ui.set_body(NetworkBondInterfacesView(self.model,
                                                   self.signal))

    def network_configure_interface(self, iface):
        self.ui.set_header("Network interface {}".format(iface))
        self.ui.set_body(NetworkConfigureInterfaceView(self.model,
                                                       self.signal,
                                                       iface))

    def network_configure_ipv4_interface(self, iface):
        self.model.prev_signal = ('Back to configure interface menu',
                                  'network:configure-interface-menu',
                                  'network_configure_interface')
        self.ui.set_header("Network interface {} manual IPv4 "
                           "configuration".format(iface))
        self.ui.set_body(NetworkConfigureIPv4InterfaceView(self.model,
                                                           self.signal,
                                                           iface))

    def network_configure_ipv6_interface(self, iface):
        self.model.prev_signal = ('Back to configure interface menu',
                                  'network:configure-interface-menu',
                                  'network_configure_interface')
        self.ui.set_body(DummyView(self.signal))

    def install_network_driver(self):
        self.ui.set_body(DummyView(self.signal))

<|endoftext|>