def load_paste_app(app_name=None): """ Builds and returns a WSGI app from a paste config file. We assume the last config file specified in the supplied ConfigOpts object is the paste config file. :param app_name: name of the application to load :raises RuntimeError when config file cannot be located or application cannot be loaded from config file """ if app_name is None: app_name = cfg.CONF.prog conf_file = _get_deployment_config_file() if conf_file is None: raise RuntimeError(_("Unable to locate config file")) try: app = paste_deploy_app(conf_file, app_name, cfg.CONF) # Log the options used when starting if we're in debug mode... if cfg.CONF.debug: cfg.CONF.log_opt_values(logging.getLogger(app_name), sys_logging.DEBUG) return app except (LookupError, ImportError) as e: raise RuntimeError(_("Unable to load %(app_name)s from " "configuration file %(conf_file)s." "\nGot: %(e)r") % {'app_name': app_name, 'conf_file': conf_file, 'e': e})
def get_socket(conf, default_port): """ Bind socket to bind ip:port in conf note: Mostly comes from Swift with a few small changes... :param conf: a cfg.ConfigOpts object :param default_port: port to bind to if none is specified in conf :returns : a socket object as returned from socket.listen or ssl.wrap_socket if conf specifies cert_file """ bind_addr = get_bind_addr(conf, default_port) # TODO(jaypipes): eventlet's greened socket module does not actually # support IPv6 in getaddrinfo(). We need to get around this in the # future or monitor upstream for a fix address_family = [addr[0] for addr in socket.getaddrinfo(bind_addr[0], bind_addr[1], socket.AF_UNSPEC, socket.SOCK_STREAM) if addr[0] in (socket.AF_INET, socket.AF_INET6)][0] cert_file = conf.cert_file key_file = conf.key_file use_ssl = cert_file or key_file if use_ssl and (not cert_file or not key_file): raise RuntimeError(_("When running server in SSL mode, you must " "specify both a cert_file and key_file " "option value in your configuration file")) sock = None retry_until = time.time() + 30 while not sock and time.time() < retry_until: try: sock = eventlet.listen(bind_addr, backlog=conf.backlog, family=address_family) if use_ssl: sock = ssl.wrap_socket(sock, certfile=cert_file, keyfile=key_file) except socket.error as err: if err.args[0] != errno.EADDRINUSE: raise eventlet.sleep(0.1) if not sock: raise RuntimeError(_("Could not bind to %(bind_addr)s" "after trying for 30 seconds") % {'bind_addr': bind_addr}) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # in my experience, sockets can hang around forever without keepalive sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # This option isn't available in the OS X version of eventlet if hasattr(socket, 'TCP_KEEPIDLE'): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 600) return sock
def run_child(self): pid = os.fork() if pid == 0: signal.signal(signal.SIGHUP, signal.SIG_DFL) signal.signal(signal.SIGTERM, signal.SIG_DFL) self.run_server() self.LOG.info(_('Child %d exiting normally') % os.getpid()) return else: self.LOG.info(_('Started child %s') % pid) self.children.append(pid)
def _single_run(self, application, sock): """Start a WSGI server in a new green thread.""" self.LOG.info(_("Starting single process server")) eventlet.wsgi.server(sock, application, custom_pool=self.pool, url_length_limit=URL_LENGTH_LIMIT, log=WritableLogger(self.LOG), debug=cfg.CONF.debug)
def hup(*args): """ Shuts down the server(s), but allows running requests to complete """ self.LOG.error(_('SIGHUP received')) signal.signal(signal.SIGHUP, signal.SIG_IGN) os.killpg(0, signal.SIGHUP) signal.signal(signal.SIGHUP, hup)
def wait_on_children(self): while self.running: try: pid, status = os.wait() if os.WIFEXITED(status) or os.WIFSIGNALED(status): self.LOG.error(_('Removing dead child %s') % pid) self.children.remove(pid) self.run_child() except OSError as err: if err.errno not in (errno.EINTR, errno.ECHILD): raise except KeyboardInterrupt: self.LOG.info(_('Caught keyboard interrupt. Exiting.')) os.killpg(0, signal.SIGTERM) break eventlet.greenio.shutdown_safe(self.sock) self.sock.close() self.LOG.debug('Exited')
def from_json(self, datastring): try: if len(datastring) > cfg.CONF.max_json_body_size: msg = _('JSON body size (%(len)s bytes) exceeds maximum ' 'allowed size (%(limit)s bytes).') % \ {'len': len(datastring), 'limit': cfg.CONF.max_json_body_size} raise exception.RequestLimitExceeded(message=msg) return json.loads(datastring) except ValueError as ex: raise webob.exc.HTTPBadRequest(six.text_type(ex))
def __init__(self, **kwargs): self.kwargs = kwargs try: self.message = self.msg_fmt % kwargs except KeyError: exc_info = sys.exc_info() # kwargs doesn't match a variable in the message # log the issue and the kwargs LOG.exception(_('Exception in string format operation')) for name, value in six.iteritems(kwargs): LOG.error("%s: %s" % (name, value)) # noqa if _FATAL_EXCEPTION_FORMAT_ERRORS: raise exc_info[0], exc_info[1], exc_info[2]
def main(): try: LOG.debug('starting inicialization') app = wsgi.load_paste_app("robotice.api") port = CONF.command.bind_port host = CONF.command.bind_host LOG.info(_('Starting Robotice ReST API on %(host)s:%(port)s'), {'host': host, 'port': port}) server = wsgi.Server() server.start(app, CONF.command, default_port=port) server.wait() except RuntimeError as e: msg = six.text_type(e) sys.exit("ERROR: %s" % msg)
def print_list(objs, fields, formatters=None, sortby_index=0, mixed_case_fields=None, field_labels=None): """Print a list or objects as a table, one row per object. :param objs: iterable of :class:`Resource` :param fields: attributes that correspond to columns, in order :param formatters: `dict` of callables for field formatting :param sortby_index: index of the field for sorting table rows :param mixed_case_fields: fields corresponding to object attributes that have mixed case names (e.g., 'serverId') :param field_labels: Labels to use in the heading of the table, default to fields. """ formatters = formatters or {} mixed_case_fields = mixed_case_fields or [] field_labels = field_labels or fields if len(field_labels) != len(fields): raise ValueError(_("Field labels list %(labels)s has different number " "of elements than fields list %(fields)s"), {'labels': field_labels, 'fields': fields}) if sortby_index is None: kwargs = {} else: kwargs = {'sortby': field_labels[sortby_index]} pt = prettytable.PrettyTable(field_labels) pt.align = 'l' for o in objs: row = [] for field in fields: if field in formatters: row.append(formatters[field](o)) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, '') row.append(data) pt.add_row(row) if six.PY3: print(pt.get_string(**kwargs)) else: print(pt.get_string(**kwargs))
def start(self, application, conf, default_port): """ Run a WSGI server with the given application. :param application: The application to run in the WSGI server :param conf: a cfg.ConfigOpts object :param default_port: Port to bind to if none is specified in conf """ def kill_children(*args): """Kills the entire process group.""" self.LOG.error(_('SIGTERM received')) signal.signal(signal.SIGTERM, signal.SIG_IGN) self.running = False os.killpg(0, signal.SIGTERM) def hup(*args): """ Shuts down the server(s), but allows running requests to complete """ self.LOG.error(_('SIGHUP received')) signal.signal(signal.SIGHUP, signal.SIG_IGN) os.killpg(0, signal.SIGHUP) signal.signal(signal.SIGHUP, hup) eventlet.wsgi.MAX_HEADER_LINE = self.conf.max_header_line self.application = application self.sock = get_socket(self.conf, default_port) self.LOG = logging.getLogger('eventlet.wsgi.server') if self.conf.workers == 0: # Useful for profiling, test, debug etc. self.pool = eventlet.GreenPool(size=self.threads) self.pool.spawn_n(self._single_run, application, self.sock) return self.LOG.info(_("Starting %d workers") % self.conf.workers) signal.signal(signal.SIGTERM, kill_children) signal.signal(signal.SIGHUP, hup) while len(self.children) < self.conf.workers: self.run_child()
import routes.middleware import six import webob.dec import webob.exc from paste import deploy from robotice.common.i18n import _ from robotice.common import exception from robotice.utils import serializers URL_LENGTH_LIMIT = 50000 api_opts = [ cfg.StrOpt('bind_host', default='0.0.0.0', help=_('Address to bind the server. Useful when ' 'selecting a particular network interface.'), deprecated_group='DEFAULT'), cfg.IntOpt('bind_port', default=8004, help=_('The port on which the server will listen.'), deprecated_group='DEFAULT'), cfg.IntOpt('backlog', default=4096, help=_("Number of backlog requests " "to configure the socket with."), deprecated_group='DEFAULT'), cfg.StrOpt('cert_file', help=_("Location of the SSL certificate file " "to use for SSL mode."), deprecated_group='DEFAULT'), cfg.StrOpt('key_file', help=_("Location of the SSL key file to use " "for enabling SSL mode."),
def __init__(self, missing): self.missing = missing msg = _("Missing arguments: %s") % ", ".join(missing) super(MissingArgs, self).__init__(msg)
def __init__(self, msg_fmt=_('Not found')): self.msg_fmt = msg_fmt super(NotFound, self).__init__()
def kill_children(*args): """Kills the entire process group.""" self.LOG.error(_('SIGTERM received')) signal.signal(signal.SIGTERM, signal.SIG_IGN) self.running = False os.killpg(0, signal.SIGTERM)
def log_exception(err, exc_info): args = {'exc_info': exc_info} if cfg.CONF.verbose or cfg.CONF.debug else {} logging.error(_("Unexpected error occurred serving API: %s") % err, **args)
def __call__(self, request): """WSGI method that controls (de)serialization and method dispatch.""" action_args = self.get_action_args(request.environ) action = action_args.pop('action', None) content_type = request.params.get("ContentType") try: deserialized_request = self.dispatch(self.deserializer, action, request) action_args.update(deserialized_request) logging.debug( ('Calling %(controller)s : %(action)s'), {'controller': self.controller, 'action': action}) action_result = self.dispatch(self.controller, action, request, **action_args) except TypeError as err: logging.error(_('Exception handling resource: %s') % err) msg = _('The server could not comply with the request since ' 'it is either malformed or otherwise incorrect.') err = webob.exc.HTTPBadRequest(msg) http_exc = translate_exception(err, request.best_match_language()) # NOTE(luisg): We disguise HTTP exceptions, otherwise they will be # treated by wsgi as responses ready to be sent back and they # won't make it into the pipeline app that serializes errors raise exception.HTTPExceptionDisguise(http_exc) except webob.exc.HTTPException as err: if not isinstance(err, webob.exc.HTTPError): # Some HTTPException are actually not errors, they are # responses ready to be sent back to the users, so we don't # error log, disguise or translate those raise if isinstance(err, webob.exc.HTTPServerError): logging.error( _("Returning %(code)s to user: %(explanation)s"), {'code': err.code, 'explanation': err.explanation}) http_exc = translate_exception(err, request.best_match_language()) raise exception.HTTPExceptionDisguise(http_exc) except exception.RoboticeException as err: raise translate_exception(err, request.best_match_language()) except Exception as err: log_exception(err, sys.exc_info()) raise translate_exception(err, request.best_match_language()) # Here we support either passing in a serializer or detecting it # based on the content type. try: serializer = self.serializer if serializer is None: if content_type == "JSON": serializer = serializers.JSONResponseSerializer() else: serializer = serializers.XMLResponseSerializer() response = webob.Response(request=request) self.dispatch(serializer, action, response, action_result) return response # return unserializable result (typically an exception) except Exception: # Here we should get API exceptions derived from HeatAPIException # these implement get_unserialized_body(), which allow us to get # a dict containing the unserialized error response. # We only need to serialize for JSON content_type, as the # exception body is pre-serialized to the default XML in the # HeatAPIException constructor # If we get something else here (e.g a webob.exc exception), # this will fail, and we just return it without serializing, # which will not conform to the expected AWS error response format if content_type == "JSON": try: err_body = action_result.get_unserialized_body() serializer.default(action_result, err_body) except Exception: logging.warning(_LW("Unable to serialize exception " "response")) return action_result