def parse(manifest_str): '''Takes a string and returns a dict containing the parsed structure. This includes determination of whether the string is using the JSON or YAML format. ''' if not manifest_str: msg = _("'manifest' can't be empty") raise ValueError(msg) if manifest_str.startswith('{'): manifest = json.loads(manifest_str) else: try: manifest = yaml.load(manifest_str, Loader=yaml_loader) except yaml.YAMLError as yea: yea = six.text_type(yea) msg = _('Error parsing manifest: %s') % yea raise ValueError(msg) else: if manifest is None: manifest = {} if not isinstance(manifest, dict): raise ValueError( _('The manifest is not a JSON object ' 'or YAML mapping.')) # TODO(yuanying): check manifest version return manifest
def parse(manifest_str): '''Takes a string and returns a dict containing the parsed structure. This includes determination of whether the string is using the JSON or YAML format. ''' if not manifest_str: msg = _("'manifest' can't be empty") raise ValueError(msg) if manifest_str.startswith('{'): manifest = json.loads(manifest_str) else: try: manifest = yaml.load(manifest_str, Loader=yaml_loader) except yaml.YAMLError as yea: yea = six.text_type(yea) msg = _('Error parsing manifest: %s') % yea raise ValueError(msg) else: if manifest is None: manifest = {} if not isinstance(manifest, dict): raise ValueError(_('The manifest is not a JSON object ' 'or YAML mapping.')) # TODO(yuanying): check manifest version return manifest
def _v3_client_init(self): kwargs = { 'auth_url': self.v3_endpoint, 'endpoint': self.v3_endpoint } # Note try trust_id first, as we can't reuse auth_token in that case if self.context.trust_id is not None: # We got a trust_id, so we use the admin credentials # to authenticate with the trust_id so we can use the # trust impersonating the trustor user. kwargs.update(self._service_admin_creds()) kwargs['trust_id'] = self.context.trust_id kwargs.pop('project_name') elif self.context.auth_token_info is not None: # The auth_ref version must be set according to the token version if 'access' in self.context.auth_token_info: kwargs['auth_ref'] = copy.deepcopy( self.context.auth_token_info['access']) kwargs['auth_ref']['version'] = 'v2.0' kwargs['auth_ref']['token']['id'] = self.context.auth_token elif 'token' in self.context.auth_token_info: kwargs['auth_ref'] = copy.deepcopy( self.context.auth_token_info['token']) kwargs['auth_ref']['version'] = 'v3' kwargs['auth_ref']['auth_token'] = self.context.auth_token else: LOG.error("Unknown version in auth_token_info") raise exception.AuthorizationFailure() elif self.context.auth_token is not None: kwargs['token'] = self.context.auth_token kwargs['project_id'] = self.context.tenant else: LOG.error(_("Keystone v3 API connection failed, no password " "trust or auth_token!")) raise exception.AuthorizationFailure() client = kc_v3.Client(**kwargs) if 'auth_ref' not in kwargs: client.authenticate() # If we are authenticating with a trust set the context auth_token # with the trust scoped token if 'trust_id' in kwargs: # Sanity check if not client.auth_ref.trust_scoped: LOG.error(_("trust token re-scoping failed!")) raise exception.AuthorizationFailure() # All OK so update the context with the token self.context.auth_token = client.auth_ref.auth_token self.context.auth_url = self.v3_endpoint self.context.user = client.auth_ref.user_id self.context.tenant = client.auth_ref.project_id self.context.user_name = client.auth_ref.username return client
def update_service(self, service_id, values): # NOTE(dtantsur): this can lead to very strange errors if 'uuid' in values: msg = _("Cannot overwrite UUID for an existing Service.") raise exception.InvalidParameterValue(err=msg) return self._do_update_service(service_id, values)
def validate_sort_dir(sort_dir): if sort_dir not in ['asc', 'desc']: raise wsme.exc.ClientSideError( _("Invalid sort direction: %s. " "Acceptable values are " "'asc' or 'desc'") % sort_dir) return sort_dir
def report_deprecated_feature(logger, msg, *args, **kwargs): """Call this function 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 CONF.register_opts(deprecated_opts) if CONF.fatal_deprecations: logger.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 = _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) logger.warn(stdmsg, *args, **kwargs)
def save(self, context): """Save the changed fields back to the store. This is optional for subclasses, but is presented here in the base class for consistency among those that do. """ raise NotImplementedError(_("Cannot save anything in the base class"))
def apply_jsonpatch(doc, patch): for p in patch: if p['op'] == 'add' and p['path'].count('/') == 1: if p['path'].lstrip('/') not in doc: msg = _('Adding a new attribute (%s) to the root of ' ' the resource is not allowed') raise wsme.exc.ClientSideError(msg % p['path']) return jsonpatch.apply_patch(doc, jsonpatch.JsonPatch(patch))
def obj_load_attr(self, attrname): """Load an additional attribute from the real object. This should use self._conductor, and cache any data that might be useful for future load operations. """ raise NotImplementedError( _("Cannot load '%(attrname)s' in the base class") % {'attrname': attrname})
def validate(patch): if patch.path in patch.internal_attrs(): msg = _("'%s' is an internal attribute and can not be updated") raise wsme.exc.ClientSideError(msg % patch.path) if patch.path in patch.mandatory_attrs() and patch.op == 'remove': msg = _("'%s' is a mandatory attribute and can not be removed") raise wsme.exc.ClientSideError(msg % patch.path) if patch.op != 'remove': if not patch.value: msg = _("'add' and 'replace' operations needs value") raise wsme.exc.ClientSideError(msg) ret = {'path': patch.path, 'op': patch.op} if patch.value: ret['value'] = patch.value return ret
def get(url, allowed_schemes=('http', 'https')): """Get the data at the specified URL. The URL must use the http: or https: schemes. The file: scheme is also supported if you override the allowed_schemes argument. Raise an IOError if getting the data fails. """ LOG.info(_LI('Fetching data from %s'), url) components = urllib.parse.urlparse(url) if components.scheme not in allowed_schemes: raise URLFetchError(_('Invalid URL scheme %s') % components.scheme) if components.scheme == 'file': try: return urllib.request.urlopen(url).read() except urllib.error.URLError as uex: raise URLFetchError(_('Failed to retrieve manifest: %s') % uex) try: resp = requests.get(url, stream=True) resp.raise_for_status() # We cannot use resp.text here because it would download the # entire file, and a large enough file would bring down the # engine. The 'Content-Length' header could be faked, so it's # necessary to download the content in chunks to until # max_manifest_size is reached. The chunk_size we use needs # to balance CPU-intensive string concatenation with accuracy # (eg. it's possible to fetch 1000 bytes greater than # max_manifest_size with a chunk_size of 1000). reader = resp.iter_content(chunk_size=1000) result = "" for chunk in reader: result += chunk if len(result) > cfg.CONF.max_manifest_size: raise URLFetchError("Manifest exceeds maximum allowed size (%s" " bytes)" % cfg.CONF.max_manifest_size) return result except exceptions.RequestException as ex: raise URLFetchError(_('Failed to retrieve manifest: %s') % ex)
def validate(self, value): for t in self.types: try: return wtypes.validate_value(t, value) except (exception.InvalidUUID, ValueError): pass else: raise ValueError( _("Wrong type. Expected '%(type)s', got '%(value)s'") % {'type': self.types, 'value': type(value)})
def validate(self, value): for t in self.types: if t is wsme.types.text and isinstance(value, wsme.types.bytes): value = value.decode() if isinstance(value, t): return value else: raise ValueError( _("Wrong type. Expected '%(type)s', got '%(value)s'") % {'type': self.types, 'value': type(value)})
def update_node(self, node_id, values): # NOTE(dtantsur): this can lead to very strange errors if 'uuid' in values: msg = _("Cannot overwrite UUID for an existing Node.") raise exception.InvalidParameterValue(err=msg) try: return self._do_update_node(node_id, values) except db_exc.DBDuplicateEntry: raise exception.InstanceAssociated( instance_uuid=values['ironic_node_id'], node=node_id)
class MagnumException(Exception): """Base Magnum Exception To correctly use this class, inherit from it and define a 'message' property. That message will get printf'd with the keyword arguments provided to the constructor. """ message = _("An unknown exception occurred.") code = 500 def __init__(self, message=None, **kwargs): self.kwargs = kwargs if 'code' not in self.kwargs: try: self.kwargs['code'] = self.code except AttributeError: pass if message: self.message = message try: self.message = self.message % kwargs except Exception as e: # kwargs doesn't match a variable in the message # log the issue and the kwargs LOG.exception(_LE('Exception in string format operation')) for name, value in kwargs.iteritems(): LOG.error( _LE("%(name)s: %(value)s") % { 'name': name, 'value': value }) if CONF.fatal_exception_format_errors: raise e super(MagnumException, self).__init__(self.message) def __str__(self): if six.PY3: return self.message return self.message.encode('utf-8') def __unicode__(self): return self.message def format_message(self): if self.__class__.__name__.endswith('_Remote'): return self.args[0] else: return six.text_type(self)
def install(app, conf): if conf.get('enable_authentication'): return AuthProtocolWrapper(app, conf=dict(conf.get(OPT_GROUP_NAME))) else: LOG.warning(_('Keystone authentication is disabled by Magnum ' 'configuration parameter enable_authentication. ' 'Magnum will not authenticate incoming request. ' 'In order to enable authentication set ' 'enable_authentication option to True.')) return app
def _get_roles(self, req): """Get the list of roles.""" if 'X-Roles' in req.headers: roles = req.headers.get('X-Roles', '') else: # Fallback to deprecated role header: roles = req.headers.get('X-Role', '') if roles: LOG.warn(_("X-Roles is missing. Using deprecated X-Role " "header")) return [r.strip() for r in roles.split(',')]
def obj_attr_is_set(self, attrname): """Test object to see if attrname is present. Returns True if the named attribute has a value set, or False if not. Raises AttributeError if attrname is not a valid attribute for this object. """ if attrname not in self.obj_fields: raise AttributeError( _("%(objname)s object has no attribute '%(attrname)s'") % {'objname': self.obj_name(), 'attrname': attrname}) return hasattr(self, get_attrname(attrname))
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(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) else: print(encodeutils.safe_encode(pt.get_string(**kwargs)))
def __init__(self, app, conf, public_api_routes=[]): route_pattern_tpl = '%s(\.json|\.xml)?$' try: self.public_api_routes = [re.compile(route_pattern_tpl % route_tpl) for route_tpl in public_api_routes] except re.error as e: msg = _('Cannot compile public API routes: %s') % e LOG.error(msg) raise exception.ConfigInvalid(error_msg=msg) super(AuthTokenMiddleware, self).__init__(app, conf)
def get(self, key, value=NotSpecifiedSentinel): """For backwards-compatibility with dict-based objects. NOTE(danms): May be removed in the future. """ if key not in self.obj_fields: raise AttributeError( _("'%(objclass)s' object has no attribute '%(attrname)s'") % {'objclass': self.__class__, 'attrname': key}) if value != NotSpecifiedSentinel and not self.obj_attr_is_set(key): return value else: return self[key]
def datetime_or_none(dt): """Validate a datetime or None value.""" if dt is None: return None elif isinstance(dt, datetime.datetime): if dt.utcoffset() is None: # NOTE(danms): Legacy objects from sqlalchemy are stored in UTC, # but are returned without a timezone attached. # As a transitional aid, assume a tz-naive object is in UTC. return dt.replace(tzinfo=iso8601.iso8601.Utc()) else: return dt raise ValueError(_("A datetime.datetime is required here"))
def main(): service.prepare_service(sys.argv) app = api_app.setup_app() # Create the WSGI server and start it host, port = cfg.CONF.api.host, cfg.CONF.api.port srv = simple_server.make_server(host, port, app) LOG.info(_('Starting server in PID %s') % os.getpid()) LOG.debug("Configuration:") cfg.CONF.log_opt_values(LOG, std_logging.DEBUG) if host == '0.0.0.0': LOG.info(_('serving on 0.0.0.0:%(port)s, ' 'view at http://127.0.0.1:%(port)s') % dict(port=port)) else: LOG.info(_('serving on http://%(host)s:%(port)s') % dict(host=host, port=port)) srv.serve_forever()
def obj_attr_is_set(self, attrname): """Test object to see if attrname is present. Returns True if the named attribute has a value set, or False if not. Raises AttributeError if attrname is not a valid attribute for this object. """ if attrname not in self.obj_fields: raise AttributeError( _("%(objname)s object has no attribute '%(attrname)s'") % { 'objname': self.obj_name(), 'attrname': attrname }) return hasattr(self, get_attrname(attrname))
def get(self, key, value=NotSpecifiedSentinel): """For backwards-compatibility with dict-based objects. NOTE(danms): May be removed in the future. """ if key not in self.obj_fields: raise AttributeError( _("'%(objclass)s' object has no attribute '%(attrname)s'") % { 'objclass': self.__class__, 'attrname': key }) if value != NotSpecifiedSentinel and not self.obj_attr_is_set(key): return value else: return self[key]
def check_object_version(server, client): try: client_major, _client_minor = client.split('.') server_major, _server_minor = server.split('.') client_minor = int(_client_minor) server_minor = int(_server_minor) except ValueError: raise exception.IncompatibleObjectVersion(_('Invalid version string')) if client_major != server_major: raise exception.IncompatibleObjectVersion( dict(client=client_major, server=server_major)) if client_minor > server_minor: raise exception.IncompatibleObjectVersion( dict(client=client_minor, server=server_minor))
def main(): cfg.CONF(sys.argv[1:], project='magnum') logging.setup('magnum') LOG.info(_('Starting server in PID %s') % os.getpid()) LOG.debug("Configuration:") cfg.CONF.log_opt_values(LOG, std_logging.DEBUG) cfg.CONF.import_opt('topic', 'magnum.conductor.config', group='conductor') cfg.CONF.import_opt('host', 'magnum.conductor.config', group='conductor') endpoints = [ default_handler.Handler(), ] server = service.Service(cfg.CONF.conductor.topic, cfg.CONF.conductor.host, endpoints) server.serve()
def check_object_version(server, client): try: client_major, _client_minor = client.split('.') server_major, _server_minor = server.split('.') client_minor = int(_client_minor) server_minor = int(_server_minor) except ValueError: raise exception.IncompatibleObjectVersion( _('Invalid version string')) if client_major != server_major: raise exception.IncompatibleObjectVersion( dict(client=client_major, server=server_major)) if client_minor > server_minor: raise exception.IncompatibleObjectVersion( dict(client=client_minor, server=server_minor))
def get_id(source_uuid): """Derive a short (12 character) id from a random UUID. The supplied UUID must be a version 4 UUID object. """ if isinstance(source_uuid, six.string_types): source_uuid = uuid.UUID(source_uuid) if source_uuid.version != 4: raise ValueError(_('Invalid UUID version (%d)') % source_uuid.version) # The "time" field of a v4 UUID contains 60 random bits # (see RFC4122, Section 4.4) random_bytes = _to_byte_string(source_uuid.time, 60) # The first 12 bytes (= 60 bits) of base32-encoded output is our data encoded = base64.b32encode(random_bytes)[:12] return encoded.lower()
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(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) else: print(encodeutils.safe_encode(pt.get_string(**kwargs)))
def replacement_start_response(status, headers, exc_info=None): """Overrides the default response to make errors parsable.""" try: status_code = int(status.split(' ')[0]) state['status_code'] = status_code except (ValueError, TypeError): # pragma: nocover raise Exception( _('ErrorDocumentMiddleware received an invalid ' 'status %s') % status) else: if (state['status_code'] // 100) not in (2, 3): # 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 not in ('Content-Length', 'Content-Type')] # Save the headers in case we need to modify them. state['headers'] = headers return start_response(status, headers, exc_info)
def main(): cfg.CONF(sys.argv[1:], project='magnum') logging.setup('magnum') LOG.info(_('Starting server in PID %s') % os.getpid()) LOG.debug("Configuration:") cfg.CONF.log_opt_values(LOG, std_logging.DEBUG) cfg.CONF.import_opt('topic', 'magnum.backend.config', group='backend') cfg.CONF.import_opt('host', 'magnum.backend.config', group='backend') endpoints = [ docker_backend.Handler(), k8s_backend.Handler(), bay_heat.Handler(), bay_ironic.Handler() ] server = service.Service(cfg.CONF.backend.topic, cfg.CONF.backend.host, endpoints) server.serve()
def replacement_start_response(status, headers, exc_info=None): """Overrides the default response to make errors parsable.""" try: status_code = int(status.split(' ')[0]) state['status_code'] = status_code except (ValueError, TypeError): # pragma: nocover raise Exception(_( 'ErrorDocumentMiddleware received an invalid ' 'status %s') % status) else: if (state['status_code'] // 100) not in (2, 3): # 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 not in ('Content-Length', 'Content-Type') ] # Save the headers in case we need to modify them. state['headers'] = headers return start_response(status, headers, exc_info)
def _find_facility_from_conf(): facility_names = logging.handlers.SysLogHandler.facility_names facility = getattr(logging.handlers.SysLogHandler, CONF.syslog_log_facility, None) if facility is None and CONF.syslog_log_facility in facility_names: facility = facility_names.get(CONF.syslog_log_facility) if facility is None: valid_facilities = facility_names.keys() consts = ['LOG_AUTH', 'LOG_AUTHPRIV', 'LOG_CRON', 'LOG_DAEMON', 'LOG_FTP', 'LOG_KERN', 'LOG_LPR', 'LOG_MAIL', 'LOG_NEWS', 'LOG_AUTH', 'LOG_SYSLOG', 'LOG_USER', 'LOG_UUCP', 'LOG_LOCAL0', 'LOG_LOCAL1', 'LOG_LOCAL2', 'LOG_LOCAL3', 'LOG_LOCAL4', 'LOG_LOCAL5', 'LOG_LOCAL6', 'LOG_LOCAL7'] valid_facilities.extend(consts) raise TypeError(_('syslog facility must be one of: %s') % ', '.join("'%s'" % fac for fac in valid_facilities)) return facility
def __init__(self, **kwargs): self.kwargs = kwargs if CONF.fatal_exception_format_errors: assert isinstance(self.msg_fmt, six.text_type) try: self.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'), extra=dict( private=dict( msg=self.msg_fmt, args=kwargs ) ) ) if CONF.fatal_exception_format_errors: raise
def ssh_connect(connection): """Method to connect to a remote system using ssh protocol. :param connection: a dict of connection parameters. :returns: paramiko.SSHClient -- an active ssh connection. :raises: SSHConnectFailed """ try: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) key_contents = connection.get('key_contents') if key_contents: data = six.moves.StringIO(key_contents) if "BEGIN RSA PRIVATE" in key_contents: pkey = paramiko.RSAKey.from_private_key(data) elif "BEGIN DSA PRIVATE" in key_contents: pkey = paramiko.DSSKey.from_private_key(data) else: # Can't include the key contents - secure material. raise ValueError(_("Invalid private key")) else: pkey = None ssh.connect(connection.get('host'), username=connection.get('username'), password=connection.get('password'), port=connection.get('port', 22), pkey=pkey, key_filename=connection.get('key_filename'), timeout=connection.get('timeout', 10)) # send TCP keepalive packets every 20 seconds ssh.get_transport().set_keepalive(20) except Exception as e: LOG.debug("SSH connect failed: %s" % e) raise exception.SSHConnectFailed(host=connection.get('host')) return ssh
def bay_update(self, context, bay): LOG.debug('k8s_heat bay_update') osc = clients.OpenStackClients(context) stack = osc.heat().stacks.get(bay.stack_id) if (stack.stack_status != 'CREATE_COMPLETE' and stack.stack_status != 'UPDATE_COMPLETE'): operation = _('Updating a bay when stack status is ' '"%s"') % stack.stack_status raise exception.NotSupported(operation=operation) delta = set(bay.obj_what_changed()) if 'node_count' in delta: delta.remove('node_count') _update_stack(context, osc, bay) self._poll_and_check(osc, bay) if delta: raise exception.InvalidParameterValue(err=( "cannot change bay property(ies) %s." % ", ".join(delta))) bay.save() return bay
from oslo_config import cfg from magnum.common import clients from magnum.common import exception from magnum.conductor.handlers.common import kube_utils from magnum import objects from magnum.openstack.common._i18n import _ from magnum.openstack.common._i18n import _LW from magnum.openstack.common import log as logging LOG = logging.getLogger(__name__) kubernetes_opts = [ cfg.StrOpt('k8s_protocol', default='http', help=_('Default protocol of k8s master endpoint' ' (http or https).')), cfg.IntOpt('k8s_port', default=8080, help=_('Default port of the k8s master endpoint.')), ] cfg.CONF.register_opts(kubernetes_opts, group='kubernetes') def _retrieve_bay(context, obj): bay_uuid = obj.bay_uuid return objects.Bay.get_by_uuid(context, bay_uuid) def _retrieve_baymodel(context, obj): return objects.BayModel.get_by_uuid(context, obj.baymodel_id)
def update_rc(self, rc_id, values): if 'uuid' in values: msg = _("Cannot overwrite UUID for an existing rc.") raise exception.InvalidParameterValue(err=msg) return self._do_update_rc(rc_id, values)
class ReplicationControllerNotFound(ResourceNotFound): message = _("ReplicationController %(rc)s could not be found.")
class ContainerAlreadyExists(Conflict): message = _("A container with UUID %(uuid)s already exists.")
class NotSupported(MagnumException): message = _("%(operation)s is not supported.") code = 400
class ServiceAlreadyExists(Conflict): message = _("A node with UUID %(uuid)s already exists.")
class ServiceNotFound(ResourceNotFound): message = _("Service %(service)s could not be found.")
class ReplicationControllerAlreadyExists(Conflict): message = _("A ReplicationController with UUID %(uuid)s already exists.")
class ContainerNotFound(ResourceNotFound): message = _("Container %(container)s could not be found.")
class PodNotFound(ResourceNotFound): message = _("Pod %(pod)s could not be found.")