def _multi_send(method, context, topic, msg, timeout=None, envelope=False, _msg_id=None): """Wraps the sending of messages. Dispatches to the matchmaker and sends message to all relevant hosts. """ conf = CONF LOG.debug("%(msg)s" % {'msg': ' '.join(map(pformat, (topic, msg)))}) queues = _get_matchmaker().queues(topic) LOG.debug("Sending message(s) to: %s", queues) # Don't stack if we have no matchmaker results if not queues: LOG.warn(_("No matchmaker results. Not casting.")) # While not strictly a timeout, callers know how to handle # this exception and a timeout isn't too big a lie. raise rpc_common.Timeout(_("No match from matchmaker.")) # This supports brokerless fanout (addresses > 1) for queue in queues: (_topic, ip_addr) = queue _addr = "tcp://%s:%s" % (ip_addr, conf.rpc_zmq_port) if method.__name__ == '_cast': eventlet.spawn_n(method, _addr, context, _topic, msg, timeout, envelope, _msg_id) return return method(_addr, context, _topic, msg, timeout, envelope)
def wait_on_children(self): while self.running: try: pid, status = os.wait() if os.WIFEXITED(status) or os.WIFSIGNALED(status): self.logger.info(_('Removing dead child %s') % pid) self.children.remove(pid) if os.WIFEXITED(status) and os.WEXITSTATUS(status) != 0: self.logger.error(_('Not respawning child %d, cannot ' 'recover from termination') % pid) if not self.children: self.logger.info( _('All workers have terminated. Exiting')) self.running = False else: self.run_child() except OSError as err: if err.errno not in (errno.EINTR, errno.ECHILD): raise except KeyboardInterrupt: self.logger.info(_('Caught keyboard interrupt. Exiting.')) break eventlet.greenio.shutdown_safe(self.sock) self.sock.close() self.logger.debug(_('Exited'))
def get_volume_meta_from_headers(response): """ Processes HTTP headers from a supplied response that match the x-volume-meta and x-volume-meta-property and returns a mapping of volume metadata and properties :param response: Response to process """ result = {} properties = {} if hasattr(response, 'getheaders'): # httplib.HTTPResponse headers = response.getheaders() else: # webob.Response headers = response.headers.items() for key, value in headers: key = str(key.lower()) if key.startswith('x-volume-meta-property-'): field_name = key[len('x-volume-meta-property-'):].replace('-', '_') properties[field_name] = value or None elif key.startswith('x-volume-meta-'): field_name = key[len('x-volume-meta-'):].replace('-', '_') if 'x-volume-meta-' + field_name not in volume_META_HEADERS: msg = _("Bad header: %(header_name)s") % {'header_name': key} raise exc.HTTPBadRequest(msg, content_type="text/plain") result[field_name] = value or None result['properties'] = properties for key in ('size', 'min_disk', 'min_ram'): if key in result: try: result[key] = int(result[key]) except ValueError: extra = (_("Cannot convert volume %(key)s '%(value)s' " "to an integer.") % {'key': key, 'value': result[key]}) raise exception.InvalidParameterValue(value=result[key], param=key, extra_msg=extra) if result[key] < 0: extra = (_("volume %(key)s must be >= 0 " "('%(value)s' specified).") % {'key': key, 'value': result[key]}) raise exception.InvalidParameterValue(value=result[key], param=key, extra_msg=extra) for key in ('is_public', 'deleted', 'protected'): if key in result: result[key] = strutils.bool_from_string(result[key]) return result
def acquire(self): basedir = os.path.dirname(self.fname) if not os.path.exists(basedir): fileutils.ensure_tree(basedir) LOG.info(_LI('Created lock path: %s'), basedir) self.lockfile = open(self.fname, 'w') while True: try: # Using non-blocking locks since green threads are not # patched to deal with blocking locking calls. # Also upon reading the MSDN docs for locking(), it seems # to have a laughable 10 attempts "blocking" mechanism. self.trylock() LOG.debug('Got file lock "%s"', self.fname) return True except IOError as e: if e.errno in (errno.EACCES, errno.EAGAIN): # external locks synchronise things like iptables # updates - give it some time to prevent busy spinning time.sleep(0.01) else: raise threading.ThreadError(_("Unable to acquire lock on" " `%(filename)s` due to" " %(exception)s") % { 'filename': self.fname, 'exception': e, })
def __init__(self, addr, zmq_type, bind=True, subscribe=None): self.sock = _get_ctxt().socket(zmq_type) self.addr = addr self.type = zmq_type self.subscriptions = [] # Support failures on sending/receiving on wrong socket type. self.can_recv = zmq_type in (zmq.PULL, zmq.SUB) self.can_send = zmq_type in (zmq.PUSH, zmq.PUB) self.can_sub = zmq_type in (zmq.SUB, ) # Support list, str, & None for subscribe arg (cast to list) do_sub = { list: subscribe, str: [subscribe], type(None): [] }[type(subscribe)] for f in do_sub: self.subscribe(f) str_data = {'addr': addr, 'type': self.socket_s(), 'subscribe': subscribe, 'bind': bind} LOG.debug("Connecting to %(addr)s with %(type)s", str_data) LOG.debug("-> Subscribed to %(subscribe)s", str_data) LOG.debug("-> bind: %(bind)s", str_data) try: if bind: self.sock.bind(addr) else: self.sock.connect(addr) except Exception: raise RPCException(_("Could not open socket."))
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) LOG.debug(_("request.environ = %(environ)s"), {'environ': request.environ}) try: deserialized_request = self.dispatch(self.deserializer, action, request) action_args.update(deserialized_request) action_result = self.dispatch(self.controller, action, request, **action_args) except webob.exc.WSGIHTTPException as e: exc_info = sys.exc_info() raise translate_exception(request, e), None, exc_info[2] try: response = webob.Response(request=request) self.dispatch(self.serializer, action, response, action_result) return response except webob.exc.WSGIHTTPException as e: return translate_exception(request, e) except webob.exc.HTTPException as e: return e # return unserializable result (typically a webob exc) except Exception: return action_result
def __init__(self, info=None, topic=None, method=None): """Initiates Timeout object. :param info: Extra info to convey to the user :param topic: The topic that the rpc call was sent to :param rpc_method_name: The name of the rpc method being called """ self.info = info self.topic = topic self.method = method super(Timeout, self).__init__( None, info=info or _('<unknown>'), topic=topic or _('<unknown>'), method=method or _('<unknown>'))
def deprecated(self, msg, *args, **kwargs): """Call this method when a deprecated feature is used. If the system is configured for fatal deprecations then the message is logged at the 'critical' level and :class:`DeprecatedConfig` will be raised. Otherwise, the message will be logged (once) at the 'warn' level. :raises: :class:`DeprecatedConfig` if the system is configured for fatal deprecations. """ stdmsg = _("Deprecated: %s") % msg if CONF.fatal_deprecations: self.critical(stdmsg, *args, **kwargs) raise DeprecatedConfig(msg=stdmsg) # Using a list because a tuple with dict can't be stored in a set. sent_args = self._deprecated_messages_sent.setdefault(msg, list()) if args in sent_args: # Already logged this message, so don't log it again. return sent_args.append(args) self.warn(stdmsg, *args, **kwargs)
def bool_from_string(subject, strict=False, default=False): """Interpret a string as a boolean. A case-insensitive match is performed such that strings matching 't', 'true', 'on', 'y', 'yes', or '1' are considered True and, when `strict=False`, anything else returns the value specified by 'default'. Useful for JSON-decoded stuff and config file parsing. If `strict=True`, unrecognized values, including None, will raise a ValueError which is useful when parsing values passed in from an API call. Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. """ if not isinstance(subject, six.string_types): subject = str(subject) lowered = subject.strip().lower() if lowered in TRUE_STRINGS: return True elif lowered in FALSE_STRINGS: return False elif strict: acceptable = ', '.join( "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) msg = _("Unrecognized value '%(val)s', acceptable values are:" " %(acceptable)s") % {'val': subject, 'acceptable': acceptable} raise ValueError(msg) else: return default
def publisher(waiter): LOG.info(_LI("Creating proxy for topic: %s"), topic) try: # The topic is received over the network, # don't trust this input. if self.badchars.search(topic) is not None: emsg = _("Topic contained dangerous characters.") LOG.warn(emsg) raise RPCException(emsg) out_sock = ZmqSocket("ipc://%s/zmq_topic_%s" % (ipc_dir, topic), sock_type, bind=True) except RPCException: waiter.send_exception(*sys.exc_info()) return self.topic_proxy[topic] = eventlet.queue.LightQueue( CONF.rpc_zmq_topic_backlog) self.sockets.append(out_sock) # It takes some time for a pub socket to open, # before we can have any faith in doing a send() to it. if sock_type == zmq.PUB: eventlet.sleep(.5) waiter.send(True) while(True): data = self.topic_proxy[topic].get() out_sock.send(data, copy=False)
def _get_authenticated_context(self, req): #NOTE(bcwaldon): X-Roles is a csv string, but we need to parse # it into a list to be useful roles_header = req.headers.get('X-Roles', '') roles = [r.strip().lower() for r in roles_header.split(',')] #NOTE(bcwaldon): This header is deprecated in favor of X-Auth-Token deprecated_token = req.headers.get('X-Storage-Token') service_catalog = None if req.headers.get('X-Service-Catalog') is not None: try: catalog_header = req.headers.get('X-Service-Catalog') service_catalog = jsonutils.loads(catalog_header) except ValueError: raise webob.exc.HTTPInternalServerError( _('Invalid service catalog json.')) kwargs = { 'user': req.headers.get('X-User-Id'), 'tenant': req.headers.get('X-Tenant-Id'), 'roles': roles, 'is_admin': CONF.admin_role.strip().lower() in roles, 'auth_tok': req.headers.get('X-Auth-Token', deprecated_token), 'owner_is_tenant': CONF.owner_is_tenant, 'service_catalog': service_catalog, 'policy_enforcer': self.policy_enforcer, } return context.RequestContext(**kwargs)
def remove(self, req, volume_id, peer_id=None, body=None): """ Remove the volume metadata from Volt :param req: The WSGI/Webob Request object :param volume_id: The volume identifier of the volume. :param peer_id: The opaque volume identifier allocated by volt :raises HttpNotFound if volume is not available """ #self._enforce(req, 'remove_volume') params = self._get_query_params(body) assert(peer_id is not None) try: self.executor.delete_volume_metadata(volume_id, peer_id, **params) except exception.NotFound as e: msg = _("Failed to find volume to delete: %(e)s") % {'e': e} for line in msg.split('\n'): LOG.info(line) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") else: return Response(body='', status=200)
def run_child(self): pid = os.fork() if pid == 0: signal.signal(signal.SIGHUP, signal.SIG_DFL) signal.signal(signal.SIGTERM, signal.SIG_DFL) # ignore the interrupt signal to avoid a race whereby # a child worker receives the signal before the parent # and is respawned unnecessarily as a result signal.signal(signal.SIGINT, signal.SIG_IGN) self.run_server() self.logger.info(_('Child %d exiting normally') % os.getpid()) # self.pool.waitall() has been called by run_server, so # its safe to exit here sys.exit(0) else: self.logger.info(_('Started child %s') % pid) self.children.append(pid)
def process_response(self, resp): try: request_id = resp.request.context.request_id except AttributeError: LOG.warn(_('Unable to retrieve request id from context')) else: resp.headers['x-openstack-request-id'] = 'req-%s' % request_id return resp
def _find_policy_file(): """Locate the policy json data file""" policy_file = CONF.find_file(CONF.policy_file) if policy_file: return policy_file else: LOG.warn(_('Unable to find policy file')) return None
def string_to_bytes(text, unit_system='IEC', return_int=False): """Converts a string into an float representation of bytes. The units supported for IEC :: Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it) KB, KiB, MB, MiB, GB, GiB, TB, TiB The units supported for SI :: kb(it), Mb(it), Gb(it), Tb(it) kB, MB, GB, TB Note that the SI unit system does not support capital letter 'K' :param text: String input for bytes size conversion. :param unit_system: Unit system for byte size conversion. :param return_int: If True, returns integer representation of text in bytes. (default: decimal) :returns: Numerical representation of text in bytes. :raises ValueError: If text has an invalid value. """ try: base, reg_ex = UNIT_SYSTEM_INFO[unit_system] except KeyError: msg = _('Invalid unit system: "%s"') % unit_system raise ValueError(msg) match = reg_ex.match(text) if match: magnitude = float(match.group(1)) unit_prefix = match.group(2) if match.group(3) in ['b', 'bit']: magnitude /= 8 else: msg = _('Invalid string format: %s') % text raise ValueError(msg) if not unit_prefix: res = magnitude else: res = magnitude * pow(base, UNIT_PREFIX_EXPONENT[unit_prefix]) if return_int: return int(math.ceil(res)) return res
def register(self, req, volume_id, peer_id, body=None): """ Adds the volume metadata to the registry and assigns an volume identifier if one is not supplied in the request headers. :param req: The WSGI/Webob Request object :param volume_meta: The volume metadata :raises HTTPConflict if volume already exists :raises HTTPBadRequest if volume metadata is not valid """ #self._enforce(req, 'register_volume') params = self._get_query_params(body) #if self.scanning_thread.status == 'init': # self.scanning_thread.start() # self.scanning_thread.status = 'running' try: volume_meta = self.executor.add_volume_metadata(volume_id, peer_id, **params) except exception.DuplicateItem: msg = (_("An volume with identifier %s already exists") % volume_id) LOG.debug(msg) raise HTTPConflict(explanation=msg, request=req, content_type="text/plain") except exception.Invalid as e: msg = _("Failed to register volume. Got error: %(e)s") % {'e': e} for line in msg.split('\n'): LOG.debug(line) raise HTTPBadRequest(explanation=msg, request=req, content_type="text/plain") except exception.NotFound as e: msg = _("Failed to register volume. Got error: %(e)s") % {'e': e} for line in msg.split('\n'): LOG.debug(line) raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") return volume_meta
def heartbeat(self, req): """ Client send periodical heartbeat to Volt server. :param req: the Request object coming from the wsgi layer """ #self._enforce(req, 'heartbeat') host = req.environ['REMOTE_ADDR'] LOG.debug(_("host_ip = %(host)s."), {'host': host}) try: result = self.executor.update_status(host=host) except exception.NotFound: msg = _("Host %s not found") % host LOG.debug(msg) raise HTTPNotFound(msg) return result
def _get_matchmaker(*args, **kwargs): global matchmaker if not matchmaker: mm = CONF.rpc_zmq_matchmaker if mm.endswith('matchmaker.MatchMakerRing'): mm.replace('matchmaker', 'matchmaker_ring') LOG.warn(_('rpc_zmq_matchmaker = %(orig)s is deprecated; use' ' %(new)s instead') % dict( orig=CONF.rpc_zmq_matchmaker, new=mm)) matchmaker = importutils.import_object(mm, *args, **kwargs) return matchmaker
def is_enabled(): cert_file = CONF.ssl.cert_file key_file = CONF.ssl.key_file ca_file = CONF.ssl.ca_file use_ssl = cert_file or key_file if cert_file and not os.path.exists(cert_file): raise RuntimeError(_("Unable to find cert_file : %s") % cert_file) if ca_file and not os.path.exists(ca_file): raise RuntimeError(_("Unable to find ca_file : %s") % ca_file) if key_file and not os.path.exists(key_file): raise RuntimeError(_("Unable to find key_file : %s") % 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")) return use_ssl
def reconnect(self): """Handles reconnecting and re-establishing queues. Will retry up to self.max_retries number of times. self.max_retries = 0 means to retry forever. Sleep between tries, starting at self.interval_start seconds, backing off self.interval_stepping number of seconds each attempt. """ attempt = 0 while True: params = self.params_list[attempt % len(self.params_list)] attempt += 1 try: self._connect(params) return except (IOError, self.connection_errors) as e: pass except Exception as e: # NOTE(comstud): Unfortunately it's possible for amqplib # to return an error not covered by its transport # connection_errors in the case of a timeout waiting for # a protocol response. (See paste link in LP888621) # So, we check all exceptions for 'timeout' in them # and try to reconnect in this case. if 'timeout' not in str(e): raise log_info = {} log_info['err_str'] = str(e) log_info['max_retries'] = self.max_retries log_info.update(params) if self.max_retries and attempt == self.max_retries: msg = _('Unable to connect to AMQP server on ' '%(hostname)s:%(port)d after %(max_retries)d ' 'tries: %(err_str)s') % log_info LOG.error(msg) raise rpc_common.RPCException(msg) if attempt == 1: sleep_time = self.interval_start or 1 elif attempt > 1: sleep_time += self.interval_stepping if self.interval_max: sleep_time = min(sleep_time, self.interval_max) log_info['sleep_time'] = sleep_time LOG.error(_LE('AMQP server on %(hostname)s:%(port)d is ' 'unreachable: %(err_str)s. Trying again in ' '%(sleep_time)d seconds.') % log_info) time.sleep(sleep_time)
def validate_key_cert(key_file, cert_file): try: error_key_name = "private key" error_filename = key_file key_str = open(key_file, "r").read() key = crypto.load_privatekey(crypto.FILETYPE_PEM, key_str) error_key_name = "certficate" error_filename = cert_file cert_str = open(cert_file, "r").read() cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_str) except IOError as ioe: raise RuntimeError(_("There is a problem with your %(error_key_name)s " "%(error_filename)s. Please verify it." " Error: %(ioe)s") % {'error_key_name': error_key_name, 'error_filename': error_filename, 'ioe': ioe}) except crypto.Error as ce: raise RuntimeError(_("There is a problem with your %(error_key_name)s " "%(error_filename)s. Please verify it. OpenSSL" " error: %(ce)s") % {'error_key_name': error_key_name, 'error_filename': error_filename, 'ce': ce}) try: data = str(uuid.uuid4()) digest = "sha1" out = crypto.sign(key, data, digest) crypto.verify(cert, out, data, digest) except crypto.Error as ce: raise RuntimeError(_("There is a problem with your key pair. " "Please verify that cert %(cert_file)s and " "key %(key_file)s belong together. OpenSSL " "error %(ce)s") % {'cert_file': cert_file, 'key_file': key_file, 'ce': ce})
def load_rules(self): """Set the rules found in the json file on disk""" if self.policy_path: rules = self._read_policy_file() rule_type = "" else: rules = DEFAULT_RULES rule_type = "default " text_rules = dict((k, str(v)) for k, v in rules.items()) msg = (_('Loaded %(rule_type)spolicy rules: %(text_rules)s') % {'rule_type': rule_type, 'text_rules': text_rules}) LOG.debug(msg) self.set_rules(rules)
def load_app(self, name): """Return the paste URLMap wrapped WSGI application. :param name: Name of the application to load. :returns: Paste URLMap object wrapping the requested application. :raises: `nova.exception.PasteAppNotFound` """ try: LOG.debug(_("Loading app %(name)s from %(path)s") % {'name': name, 'path': self.config_path}) return deploy.loadapp("config:%s" % self.config_path, name=name) except LookupError as err: LOG.error(err) raise exception.PasteAppNotFound(name=name, path=self.config_path)
def _read_policy_file(self): """Read contents of the policy file This re-caches policy data if the file has been changed. """ mtime = os.path.getmtime(self.policy_path) if not self.policy_file_contents or mtime != self.policy_file_mtime: LOG.debug(_("Loading policy from %s") % self.policy_path) with open(self.policy_path) as fap: raw_contents = fap.read() rules_dict = jsonutils.loads(raw_contents) self.policy_file_contents = dict( (k, policy.parse_rule(v)) for k, v in rules_dict.items()) self.policy_file_mtime = mtime return self.policy_file_contents
def start_heartbeat(self): """Implementation of MatchMakerBase.start_heartbeat. Launches greenthread looping send_heartbeats(), yielding for CONF.matchmaker_heartbeat_freq seconds between iterations. """ if not self.hosts: raise MatchMakerException(_("Register before starting heartbeat.")) def do_heartbeat(): while True: self.send_heartbeats() eventlet.sleep(CONF.matchmaker_heartbeat_freq) self._heart = eventlet.spawn(do_heartbeat)
def set_rules(self, rules, overwrite=True, use_conf=False): """Create a new Rules object based on the provided dict of rules. :param rules: New rules to use. It should be an instance of dict. :param overwrite: Whether to overwrite current rules or update them with the new rules. :param use_conf: Whether to reload rules from cache or config file. """ if not isinstance(rules, dict): raise TypeError(_("Rules must be an instance of dict or Rules, " "got %s instead") % type(rules)) self.use_conf = use_conf if overwrite: self.rules = Rules(rules, self.default_rule) else: self.rules.update(rules)
def run_server(self): """Run a WSGI server.""" eventlet.wsgi.HttpProtocol.default_request_version = "HTTP/1.0" try: eventlet.hubs.use_hub(cfg.CONF.eventlet_hub) except Exception: msg = _("eventlet '%s' hub is not available on this platform") raise exception.WorkerCreationFailure( reason=msg % cfg.CONF.eventlet_hub) self.pool = self.create_pool() try: eventlet.wsgi.server(self.sock, self.application, log=logging.WritableLogger(self.logger), custom_pool=self.pool, debug=False) except socket.error as err: if err[0] != errno.EINVAL: raise self.pool.waitall()
def query(self, req, volume_id, body=None): """ Returns detailed information for all available volumes with id <volume_id> :param req: The WSGI/Webob Request object :param id: The volume id of the query :retval The response body is a mapping of the following form:: {'volumes': [ {'host': <HOST>, 'ip': <IP>, 'iqn': <IQN>, 'lun': <LUN>}, ... ]} """ #self._enforce(req, 'get_volumes') params = self._get_query_params(body) #host = params.get('host', None) host = req.environ['REMOTE_ADDR'] peer_id = params.get('peer_id', None) if self.scanning_thread.status == 'init': self.scanning_thread.start() self.scanning_thread.status = 'running' try: target = self.executor.get_volume_parents(volume_id=volume_id, peer_id=peer_id, host=host) except exception.NotFound as e: msg = _("this volume is not found in tracker.") raise HTTPNotFound(explanation=msg, request=req, content_type="text/plain") except exception.InvalidParameterValue: raise HTTPBadRequest() except exception.Duplicate: raise HTTPConflict() return target
def start(self, default_port): """ Run a WSGI server with the given application. :param default_port: Port to bind to if none is specified in conf """ pgid = os.getpid() os.setpgid(pgid, pgid) def kill_children(*args): """Kills the entire process group.""" signal.signal(signal.SIGTERM, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN) self.running = False os.killpg(pgid, signal.SIGTERM) def hup(*args): """ Shuts down the server, but allows running requests to complete """ signal.signal(signal.SIGHUP, signal.SIG_IGN) self.running = False self.sock = get_socket(default_port) os.umask(0o27) # ensure files are created with the correct privileges self.logger = logging.getLogger('volt.wsgi.server') if CONF.workers == 0: # Useful for profiling, test, debug etc. self.pool = self.create_pool() self.pool.spawn_n(self._single_run, self.application, self.sock) return else: self.logger.info(_("Starting %d workers") % CONF.workers) signal.signal(signal.SIGTERM, kill_children) signal.signal(signal.SIGINT, kill_children) signal.signal(signal.SIGHUP, hup) while len(self.children) < CONF.workers: self.run_child()
class InvalidContentType(VoltException): message = _("Invalid content type %(content_type)s")
class BadDriverConfiguration(VoltException): message = _("Driver %(driver_name)s could not be configured correctly. " "Reason: %(reason)s")
class InvalidPropertyProtectionConfiguration(Invalid): message = _("Invalid configuration in property protection file.")
class NotAuthenticated(VoltException): message = _("You are not authenticated.")
class AuthUrlNotFound(VoltException): message = _("Auth service at URL %(url)s not found.")
class ProtectedImageDelete(Forbidden): message = _("Image %(image_id)s is protected and cannot be deleted.")
class UnexpectedStatus(VoltException): message = _("The request returned an unexpected status: %(status)s." "\n\nThe response body:\n%(body)s")
class ClientConnectionError(VoltException): message = _("There was an error connecting to a server")
class AuthorizationRedirect(VoltException): message = _("Redirecting to %(uri)s for authorization.")
class StoreAddDisabled(VoltException): message = _("Configuration for store failed. Adding images to this " "store is disabled.")
class BadRegistryConnectionConfiguration(VoltException): message = _("Registry was not configured correctly on API server. " "Reason: %(reason)s")
class StoreAddNotSupported(VoltException): message = _("Adding images to this store is not supported.")
class StoreGetNotSupported(VoltException): message = _("Getting images from this store is not supported.")
class StoreDeleteNotSupported(VoltException): message = _("Deleting images from this store is not supported.")
class InvalidSortKey(Invalid): message = _("Sort key supplied was not valid.")
class ReservedProperty(Forbidden): message = _("Attribute '%(property)s' is reserved.")
class Invalid(VoltException): message = _("Data supplied was not valid.")
class StorageWriteDenied(VoltException): message = _("Permission to write image storage media denied.")
class ForbiddenPublicImage(Forbidden): message = _("You are not authorized to complete this action.")
def send(self, data, **kwargs): if not self.can_send: raise RPCException(_("You cannot send on this socket.")) self.sock.send_multipart(data, **kwargs)
class Forbidden(VoltException): message = _("You are not authorized to complete this action.")
class ServerError(VoltException): message = _("The request returned 500 Internal Server Error.")
class AuthorizationFailure(VoltException): message = _("Authorization failed.")
class InvalidFilterRangeValue(Invalid): message = _("Unable to filter using the specified range.")
class AuthBadRequest(VoltException): message = _("Connect error/bad request to Auth service at URL %(url)s.")
class ClientConfigurationError(VoltException): message = _("There was an error configuring the client.")
def _call(addr, context, topic, msg, timeout=None, envelope=False): # timeout_response is how long we wait for a response timeout = timeout or CONF.rpc_response_timeout # The msg_id is used to track replies. msg_id = uuid.uuid4().hex # Replies always come into the reply service. reply_topic = "zmq_replies.%s" % CONF.rpc_zmq_host LOG.debug("Creating payload") # Curry the original request into a reply method. mcontext = RpcContext.marshal(context) payload = { 'method': '-reply', 'args': { 'msg_id': msg_id, 'topic': reply_topic, # TODO(ewindisch): safe to remove mcontext in I. 'msg': [mcontext, msg] } } LOG.debug("Creating queue socket for reply waiter") # Messages arriving async. # TODO(ewindisch): have reply consumer with dynamic subscription mgmt with Timeout(timeout, exception=rpc_common.Timeout): try: msg_waiter = ZmqSocket("ipc://%s/zmq_topic_zmq_replies.%s" % (CONF.rpc_zmq_ipc_dir, CONF.rpc_zmq_host), zmq.SUB, subscribe=msg_id, bind=False) LOG.debug("Sending cast") _cast(addr, context, topic, payload, envelope) LOG.debug("Cast sent; Waiting reply") # Blocks until receives reply msg = msg_waiter.recv() LOG.debug("Received message: %s", msg) LOG.debug("Unpacking response") if msg[2] == 'cast': # Legacy version raw_msg = _deserialize(msg[-1])[-1] elif msg[2] == 'impl_zmq_v2': rpc_envelope = unflatten_envelope(msg[4:]) raw_msg = rpc_common.deserialize_msg(rpc_envelope) else: raise rpc_common.UnsupportedRpcEnvelopeVersion( _("Unsupported or unknown ZMQ envelope returned.")) responses = raw_msg['args']['response'] # ZMQError trumps the Timeout error. except zmq.ZMQError: raise RPCException("ZMQ Socket Error") except (IndexError, KeyError): raise RPCException(_("RPC Message Invalid.")) finally: if 'msg_waiter' in vars(): msg_waiter.close() # It seems we don't need to do all of the following, # but perhaps it would be useful for multicall? # One effect of this is that we're checking all # responses for Exceptions. for resp in responses: if isinstance(resp, types.DictType) and 'exc' in resp: raise rpc_common.deserialize_remote_exception(CONF, resp['exc']) return responses[-1]
class ReadonlyProperty(Forbidden): message = _("Attribute '%(property)s' is read-only.")
class BadStoreConfiguration(VoltException): message = _("Store %(store_name)s could not be configured correctly. " "Reason: %(reason)s")
class MultipleChoices(VoltException): message = _("The request returned a 302 Multiple Choices. This generally " "means that you have not included a version indicator in a " "request URI.\n\nThe body of response returned:\n%(body)s")