class ClimateException(Exception): """Base Climate Exception. To correctly use this class, inherit from it and define a 'msg_fmt' and 'code' properties. """ msg_fmt = _("An unknown exception occurred") code = 500 def __init__(self, message=None, **kwargs): self.kwargs = kwargs if 'code' not in self.kwargs: self.kwargs['code'] = self.code if not message: try: message = self.msg_fmt % kwargs except KeyError: # 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 kwargs.iteritems(): LOG.error("%s: %s" % (name, value)) message = self.msg_fmt super(ClimateException, self).__init__(message)
def request_data(): """Method called to process POST and PUT REST methods.""" if hasattr(flask.request, 'parsed_data'): return flask.request.parsed_data if not flask.request.content_length > 0: LOG.debug("Empty body provided in request") return dict() if flask.request.file_upload: return flask.request.data deserializer = None content_type = flask.request.mimetype if not content_type or content_type in RT_JSON: deserializer = jsonutils else: abort_and_log(400, _("Content type '%s' isn't supported") % content_type) return # parsed request data to avoid unwanted re-parsings parsed_data = deserializer.loads(flask.request.data) flask.request.parsed_data = parsed_data return flask.request.parsed_data
def _event(self): """Tries to commit event. If there is an event in Climate DB to be done, do it and change its status to 'DONE'. """ LOG.debug('Trying to get event from DB.') event = db_api.event_get_first_sorted_by_filters( sort_key='time', sort_dir='asc', filters={'status': 'UNDONE'}) if not event: return if event['time'] < datetime.datetime.utcnow(): db_api.event_update(event['id'], {'status': 'IN_PROGRESS'}) event_type = event['event_type'] event_fn = getattr(self, event_type, None) if event_fn is None: raise exceptions.EventError(error='Event type %s is not ' 'supported' % event_type) try: eventlet.spawn_n(service_utils.with_empty_context(event_fn), event['lease_id'], event['id']) lease = db_api.lease_get(event['lease_id']) with trusts.create_ctx_from_trust(lease['trust_id']) as ctx: self._send_notification(lease, ctx, events=['event.%s' % event_type]) except Exception: db_api.event_update(event['id'], {'status': 'ERROR'}) LOG.exception(_('Error occurred while event handling.'))
def setup_db(): try: engine = db_session.EngineFacade(cfg.CONF.database.connection, sqlite_fk=True).get_engine() models.Lease.metadata.create_all(engine) except sa.exc.OperationalError as e: LOG.error(_("Database registration exception: %s"), e) return False return True
def abort_and_log(status_code, descr, exc=None): """Process occurred errors.""" LOG.error(_("Request aborted with status code %(code)s and " "message '%(msg)s'"), {'code': status_code, 'msg': descr}) if exc is not None: LOG.error(traceback.format_exc()) flask.abort(status_code, description=descr)
def drop_db(): try: engine = db_session.EngineFacade(cfg.CONF.database.connection, sqlite_fk=True).get_engine() models.Lease.metadata.drop_all(engine) except Exception as e: LOG.error(_("Database shutdown exception: %s"), e) return False return True
def __init__(self): extensions = [] self.extension_manager = enabled.EnabledExtensionManager( check_func=lambda ext: ext.name in CONF.api.api_v2_controllers, namespace='climate.api.v2.controllers.extensions', invoke_on_load=True) self._log_missing_plugins(CONF.api.api_v2_controllers) for ext in self.extension_manager.extensions: try: setattr(self, ext.obj.name, ext.obj) except TypeError: raise exceptions.ClimateException( _("API name must be specified for " "extension {0}").format(ext.name)) self._routes.update(ext.obj.extra_routes) extensions.append(ext.obj.name) LOG.debug(_("Loaded extensions: {0}").format(extensions))
def main(): config = alembic_config.Config( os.path.join(os.path.dirname(__file__), 'alembic.ini')) config.climate_config = CONF CONF() db_options.set_defaults(CONF) if not CONF.database.connection: raise SystemExit( _("Provide a configuration file with DB connection information")) CONF.command.func(config, CONF.command.name)
def do_upgrade_downgrade(config, cmd): if not CONF.command.revision and not CONF.command.delta: raise SystemExit(_('You must provide a revision or relative delta')) revision = CONF.command.revision if CONF.command.delta: sign = '+' if CONF.command.name == 'upgrade' else '-' revision = sign + str(CONF.command.delta) else: revision = CONF.command.revision do_alembic_command(config, cmd, revision, sql=CONF.command.sql)
def post(self, lease): """Creates a new lease. :param lease: a lease within the request body. """ # FIXME(sbauza): DB exceptions are currently catched and return a lease # equal to None instead of being sent to the API lease_dct = lease.as_dict() lease = pecan.request.rpcapi.create_lease(lease_dct) if lease is not None: return Lease.convert(lease) else: raise exceptions.ClimateException(_("Lease can't be created"))
def internal_error(status_code, descr, exc=None): """Called if internal error occurred.""" LOG.error(_("Request aborted with status code %(code)s " "and message '%(msg)s'"), {'code': status_code, 'msg': descr}) if exc is not None: LOG.error(traceback.format_exc()) error_code = "INTERNAL_SERVER_ERROR" if status_code == 501: error_code = "NOT_IMPLEMENTED_ERROR" return render_error_message(status_code, descr, error_code)
def post(self, host): """Creates a new host. :param host: a host within the request body. """ # here API should go to Keystone API v3 and create trust host_dct = host.as_dict() # FIXME(sbauza): DB exceptions are currently catched and return a lease # equal to None instead of being sent to the API host = pecan.request.hosts_rpcapi.create_computehost(host_dct) if host is not None: return Host.convert(host) else: raise exceptions.ClimateException(_("Host can't be created"))
def render(result=None, response_type=None, status=None, **kwargs): """Render response to return.""" if not result: result = {} if type(result) is dict: result.update(kwargs) elif kwargs: # can't merge kwargs into the non-dict res abort_and_log(500, _("Non-dict and non-empty kwargs passed to render.")) return status_code = getattr(flask.request, 'status_code', None) if status: status_code = status if not status_code: status_code = 200 if not response_type: response_type = getattr(flask.request, 'resp_type', RT_JSON) serializer = None if "application/json" in response_type: response_type = RT_JSON serializer = jsonutils else: abort_and_log(400, _("Content type '%s' isn't supported") % response_type) return body = serializer.dumps(result) response_type = str(response_type) return flask.Response(response=body, status=status_code, mimetype=response_type)
def __getattr__(self, name): try: method = getattr(self.__endpoint, name) def run_method(__ctx, **kwargs): with context.ClimateContext(**__ctx): return method(**kwargs) return run_method except AttributeError: LOG.error( _("No %(method)s method found implemented in " "%(class)s class"), { 'method': name, 'class': self.__endpoint })
def _route(self, args): """Overrides the default routing behavior. It allows to map controller URL with correct controller instance. By default, it maps with the same name. """ try: route = self._routes.get(args[0], args[0]) if route is None: # NOTE(sbauza): Route must map to a non-existing controller args[0] = 'http404-nonexistingcontroller' else: args[0] = route except IndexError: LOG.error(_("No args found on V2 controller")) return super(V2Controller, self)._route(args)
def __init__(self, message=None, **kwargs): self.kwargs = kwargs if 'code' not in self.kwargs: self.kwargs['code'] = self.code if not message: try: message = self.msg_fmt % kwargs except KeyError: # 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 kwargs.iteritems(): LOG.error("%s: %s" % (name, value)) message = self.msg_fmt super(ClimateException, self).__init__(message)
def replacement_start_response(status, headers, exc_info=None): """Overrides the default response to make errors parsable.""" try: status_code = int(status.split(' ')[0]) except (ValueError, TypeError): # pragma: nocover raise exceptions.ClimateException(_( 'Status {0} was unexpected').format(status)) else: if status_code >= 400: # Remove some headers so we can replace them later # when we have the full error message and can # compute the length. headers = [(h, v) for (h, v) in headers if h.lower() != 'content-length' ] # Save the headers as we need to modify them. state['status_code'] = status_code state['headers'] = headers state['exc_info'] = exc_info return start_response(status, headers, exc_info)
class InvalidInput(ClimateException): code = 400 msg_fmt = _("Expected a %(cls)s type but received %(value)s.")
class Timeout(ClimateException): msg_fmt = _('Current task failed with timeout')
class TaskFailed(ClimateException): msg_fmt = _('Current task failed')
class ServiceClient(ClimateException): msg_fmt = _("Service %(service)s have some problems")
class WrongFormat(ClimateException): msg_fmt = _("Unenxpectable object format")
class ServiceCatalogNotFound(NotFound): msg_fmt = _("Could not find service catalog")
class ConfigNotFound(ClimateException): msg_fmt = _("Could not find config at %(path)s")
class PolicyNotAuthorized(NotAuthorized): msg_fmt = _("Policy doesn't allow %(action)s to be performed")
class NotAuthorized(ClimateException): msg_fmt = _("Not authorized") code = 403
class NotFound(ClimateException): """Object not found exception.""" msg_fmt = _("Object with %(object)s not found") code = 404
class MissingTrustId(exceptions.ClimateException): msg_fmt = _("A trust id is required")
class CantAddExtraCapability(exceptions.ClimateException): code = 409 msg_fmt = _("Can't add extracapabilities %(keys)s to Host %(host)s")
def _log_missing_plugins(self, names): for name in names: if name not in self.extension_manager.names(): LOG.error(_("API Plugin %s was not loaded") % name)