Exemple #1
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['compute.context']
        authorize(context)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            self.host_api.service_delete(context, id)
        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
Exemple #2
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['compute.context']
        authorize(context)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            self.host_api.service_delete(context, id)
        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
Exemple #3
0
    def delete(self, req, id):
        """Deletes an existing agent build."""
        context = req.environ['compute.context']
        authorize(context)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.destroy()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())
Exemple #4
0
    def delete(self, req, id):
        """Deletes an existing agent build."""
        context = req.environ['compute.context']
        authorize(context)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.destroy()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())
Exemple #5
0
    def update(self, req, id, body):
        """Update an existing agent build."""
        context = req.environ['compute.context']
        authorize(context)
        # NOTE(alex_xu): back-compatible with db layer hard-code admin
        # permission checks.
        nova_context.require_admin_context(context)
        try:
            para = body['para']
            url = para['url']
            md5hash = para['md5hash']
            version = para['version']
        except (TypeError, KeyError) as ex:
            msg = _("Invalid request body: %s") % ex
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            utils.validate_integer(id, 'id')
            utils.check_string_length(url, 'url', max_length=255)
            utils.check_string_length(md5hash, 'md5hash', max_length=255)
            utils.check_string_length(version, 'version', max_length=255)
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.obj_reset_changes()
            agent.version = version
            agent.url = url
            agent.md5hash = md5hash
            agent.save()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())

        # NOTE(alex_xu): The agent_id should be integer that consistent with
        # create/index actions. But parameter 'id' is string type that parsed
        # from url. This is a bug, but because back-compatibility, it can't be
        # fixed for v2 API. This will be fixed in v2.1 API by Microversions in
        # the future. lp bug #1333494
        return {
            "agent": {
                'agent_id': id,
                'version': version,
                'url': url,
                'md5hash': md5hash
            }
        }
Exemple #6
0
    def delete(self, req, id):
        """Deletes an existing agent build."""
        context = req.environ['compute.context']
        authorize(context)
        # NOTE(alex_xu): back-compatible with db layer hard-code admin
        # permission checks.
        nova_context.require_admin_context(context)
        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.destroy()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())
Exemple #7
0
def _get_int_param(request, param):
    """Extract integer param from request or fail."""
    try:
        int_param = utils.validate_integer(request.GET[param], param,
                                           min_value=0)
    except exception.InvalidInput as e:
        raise webob.exc.HTTPBadRequest(explanation=e.format_message())
    return int_param
Exemple #8
0
    def delete(self, req, id):
        """Deletes an existing agent build."""
        context = req.environ['compute.context']
        authorize(context)
        # NOTE(alex_xu): back-compatible with db layer hard-code admin
        # permission checks.
        nova_context.require_admin_context(context)
        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.destroy()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())
Exemple #9
0
    def update(self, req, id, body):
        """Update an existing agent build."""
        context = req.environ['compute.context']
        authorize(context)

        # TODO(oomichi): This parameter name "para" is different from the ones
        # of the other APIs. Most other names are resource names like "server"
        # etc. This name should be changed to "agent" for consistent naming
        # with v2.1+microversions.
        para = body['para']

        url = para['url']
        md5hash = para['md5hash']
        version = para['version']

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        agent = objects.Agent(context=context, id=id)
        agent.obj_reset_changes()
        agent.version = version
        agent.url = url
        agent.md5hash = md5hash
        try:
            agent.save()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())

        # TODO(alex_xu): The agent_id should be integer that consistent with
        # create/index actions. But parameter 'id' is string type that parsed
        # from url. This is a bug, but because back-compatibility, it can't be
        # fixed for v2 API. This will be fixed in v2.1 API by Microversions in
        # the future. lp bug #1333494
        return {
            "agent": {
                'agent_id': id,
                'version': version,
                'url': url,
                'md5hash': md5hash
            }
        }
Exemple #10
0
def validate_and_default_volume_size(bdm):
    if bdm.get('volume_size'):
        try:
            bdm['volume_size'] = utils.validate_integer(bdm['volume_size'],
                                                        'volume_size',
                                                        min_value=0)
        except exception.InvalidInput:
            # NOTE: We can remove this validation code after removing
            # Nova v2.0 API code because v2.1 API validates this case
            # already at its REST API layer.
            raise exception.InvalidBDMFormat(details=_("Invalid volume_size."))
Exemple #11
0
def validate_and_default_volume_size(bdm):
    if bdm.get('volume_size'):
        try:
            bdm['volume_size'] = utils.validate_integer(
                bdm['volume_size'], 'volume_size', min_value=0)
        except exception.InvalidInput:
            # NOTE: We can remove this validation code after removing
            # Nova v2.0 API code because v2.1 API validates this case
            # already at its REST API layer.
            raise exception.InvalidBDMFormat(
                details=_("Invalid volume_size."))
Exemple #12
0
    def update(self, req, id, body):
        """Update an existing agent build."""
        context = req.environ['compute.context']
        authorize(context)
        # NOTE(alex_xu): back-compatible with db layer hard-code admin
        # permission checks.
        nova_context.require_admin_context(context)
        try:
            para = body['para']
            url = para['url']
            md5hash = para['md5hash']
            version = para['version']
        except (TypeError, KeyError) as ex:
            msg = _("Invalid request body: %s") % ex
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            utils.validate_integer(id, 'id')
            utils.check_string_length(url, 'url', max_length=255)
            utils.check_string_length(md5hash, 'md5hash', max_length=255)
            utils.check_string_length(version, 'version', max_length=255)
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            agent = objects.Agent(context=context, id=id)
            agent.obj_reset_changes()
            agent.version = version
            agent.url = url
            agent.md5hash = md5hash
            agent.save()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())

        # NOTE(alex_xu): The agent_id should be integer that consistent with
        # create/index actions. But parameter 'id' is string type that parsed
        # from url. This is a bug, but because back-compatibility, it can't be
        # fixed for v2 API. This will be fixed in v2.1 API by Microversions in
        # the future. lp bug #1333494
        return {"agent": {'agent_id': id, 'version': version,
                'url': url, 'md5hash': md5hash}}
Exemple #13
0
    def delete(self, req, id):
        """Deletes the specified service."""
        if not self.ext_mgr.is_loaded('os-extended-services-delete'):
            raise webob.exc.HTTPMethodNotAllowed()

        context = req.environ['compute.context']
        authorize(context)
        # NOTE(alex_xu): back-compatible with db layer hard-code admin
        # permission checks
        nova_context.require_admin_context(context)

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        try:
            self.host_api.service_delete(context, id)
        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
Exemple #14
0
    def update(self, req, id, body):
        """Update an existing agent build."""
        context = req.environ['compute.context']
        authorize(context)

        # TODO(oomichi): This parameter name "para" is different from the ones
        # of the other APIs. Most other names are resource names like "server"
        # etc. This name should be changed to "agent" for consistent naming
        # with v2.1+microversions.
        para = body['para']

        url = para['url']
        md5hash = para['md5hash']
        version = para['version']

        try:
            utils.validate_integer(id, 'id')
        except exception.InvalidInput as exc:
            raise webob.exc.HTTPBadRequest(explanation=exc.format_message())

        agent = objects.Agent(context=context, id=id)
        agent.obj_reset_changes()
        agent.version = version
        agent.url = url
        agent.md5hash = md5hash
        try:
            agent.save()
        except exception.AgentBuildNotFound as ex:
            raise webob.exc.HTTPNotFound(explanation=ex.format_message())

        # TODO(alex_xu): The agent_id should be integer that consistent with
        # create/index actions. But parameter 'id' is string type that parsed
        # from url. This is a bug, but because back-compatibility, it can't be
        # fixed for v2 API. This will be fixed in v2.1 API by Microversions in
        # the future. lp bug #1333494
        return {"agent": {'agent_id': id, 'version': version,
                'url': url, 'md5hash': md5hash}}
Exemple #15
0
    def update(self, req, id, body):
        context = req.environ['compute.context']
        authorize(context)
        try:
            utils.check_string_length(id,
                                      'quota_class_name',
                                      min_length=1,
                                      max_length=255)
        except exception.InvalidInput as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        quota_class = id
        bad_keys = []

        if not self.is_valid_body(body, 'quota_class_set'):
            msg = _("quota_class_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_class_set = body['quota_class_set']
        for key in quota_class_set.keys():
            if key not in self.supported_quotas:
                bad_keys.append(key)
                continue
            try:
                body['quota_class_set'][key] = utils.validate_integer(
                    body['quota_class_set'][key], key, max_value=db.MAX_INT)
            except exception.InvalidInput as e:
                raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            # NOTE(alex_xu): back-compatible with compute layer hard-code admin
            # permission checks. This has to be left only for API v2.0 because
            # this version has to be stable even if it means that only admins
            # can call this method while the policy could be changed.
            jacket.context.require_admin_context(context)
        except exception.AdminRequired:
            raise webob.exc.HTTPForbidden()

        for key, value in quota_class_set.items():
            try:
                db.quota_class_update(context, quota_class, key, value)
            except exception.QuotaClassNotFound:
                db.quota_class_create(context, quota_class, key, value)

        values = QUOTAS.get_class_quotas(context, quota_class)
        return self._format_quota_set(None, values)
Exemple #16
0
    def update(self, req, id, body):
        context = req.environ['compute.context']
        authorize(context)
        try:
            utils.check_string_length(id, 'quota_class_name',
                                      min_length=1, max_length=255)
        except exception.InvalidInput as e:
                raise webob.exc.HTTPBadRequest(
                    explanation=e.format_message())

        quota_class = id
        bad_keys = []

        if not self.is_valid_body(body, 'quota_class_set'):
            msg = _("quota_class_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_class_set = body['quota_class_set']
        for key in quota_class_set.keys():
            if key not in self.supported_quotas:
                bad_keys.append(key)
                continue
            try:
                body['quota_class_set'][key] = utils.validate_integer(
                    body['quota_class_set'][key], key, max_value=db.MAX_INT)
            except exception.InvalidInput as e:
                raise webob.exc.HTTPBadRequest(
                    explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        try:
            # NOTE(alex_xu): back-compatible with compute layer hard-code admin
            # permission checks. This has to be left only for API v2.0 because
            # this version has to be stable even if it means that only admins
            # can call this method while the policy could be changed.
            jacket.context.require_admin_context(context)
        except exception.AdminRequired:
            raise webob.exc.HTTPForbidden()

        for key, value in quota_class_set.items():
            try:
                db.quota_class_update(context, quota_class, key, value)
            except exception.QuotaClassNotFound:
                db.quota_class_create(context, quota_class, key, value)

        values = QUOTAS.get_class_quotas(context, quota_class)
        return self._format_quota_set(None, values)
Exemple #17
0
def limited(items, request, max_limit=CONF.osapi_max_limit):
    """Return a slice of items according to requested offset and limit.

    :param items: A sliceable entity
    :param request: ``wsgi.Request`` possibly containing 'offset' and 'limit'
                    GET variables. 'offset' is where to start in the list,
                    and 'limit' is the maximum number of items to return. If
                    'limit' is not specified, 0, or > max_limit, we default
                    to max_limit. Negative values for either offset or limit
                    will cause exc.HTTPBadRequest() exceptions to be raised.
    :kwarg max_limit: The maximum number of items to return from 'items'
    """
    offset = request.GET.get("offset", 0)
    limit = request.GET.get('limit', max_limit)

    try:
        offset = utils.validate_integer(offset, "offset", min_value=0)
        limit = utils.validate_integer(limit, "limit", min_value=0)
    except exception.InvalidInput as e:
        raise webob.exc.HTTPBadRequest(explanation=e.format_message())

    limit = min(max_limit, limit or max_limit)
    range_end = offset + limit
    return items[offset:range_end]
Exemple #18
0
    def _create_backup(self, req, id, body):
        """Backup a server instance.

        Images now have an `image_type` associated with them, which can be
        'snapshot' or the backup type, like 'daily' or 'weekly'.

        If the image_type is backup-like, then the rotation factor can be
        included and that will cause the oldest backups that exceed the
        rotation factor to be deleted.

        """
        context = req.environ["compute.context"]
        authorize(context, 'createBackup')
        entity = body["createBackup"]

        try:
            image_name = entity["name"]
            backup_type = entity["backup_type"]
            rotation = entity["rotation"]

        except KeyError as missing_key:
            msg = _("createBackup entity requires %s attribute") % missing_key
            raise exc.HTTPBadRequest(explanation=msg)

        except TypeError:
            msg = _("Malformed createBackup entity")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            rotation = utils.validate_integer(rotation, "rotation",
                                              min_value=0)
        except exception.InvalidInput as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        props = {}
        metadata = entity.get('metadata', {})
        common.check_img_metadata_properties_quota(context, metadata)
        try:
            props.update(metadata)
        except ValueError:
            msg = _("Invalid metadata")
            raise exc.HTTPBadRequest(explanation=msg)

        instance = common.get_instance(self.compute_api, context, id)
        try:
            image = self.compute_api.backup(context, instance, image_name,
                    backup_type, rotation, extra_properties=props)
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'createBackup', id)
        except exception.InvalidRequest as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        resp = webob.Response(status_int=202)

        # build location of newly-created image entity if rotation is not zero
        if rotation > 0:
            image_id = str(image['id'])
            image_ref = common.url_join(req.application_url, 'images',
                                        image_id)
            resp.headers['Location'] = image_ref

        return resp
Exemple #19
0
def create(name,
           memory,
           vcpus,
           root_gb,
           ephemeral_gb=0,
           flavorid=None,
           swap=0,
           rxtx_factor=1.0,
           is_public=True):
    """Creates flavors."""
    if not flavorid:
        flavorid = uuid.uuid4()

    kwargs = {
        'memory_mb': memory,
        'vcpus': vcpus,
        'root_gb': root_gb,
        'ephemeral_gb': ephemeral_gb,
        'swap': swap,
        'rxtx_factor': rxtx_factor,
    }

    if isinstance(name, six.string_types):
        name = name.strip()
    # ensure name do not exceed 255 characters
    utils.check_string_length(name, 'name', min_length=1, max_length=255)

    # ensure name does not contain any special characters
    valid_name = parameter_types.valid_name_regex_obj.search(name)
    if not valid_name:
        msg = _("Flavor names can only contain printable characters "
                "and horizontal spaces.")
        raise exception.InvalidInput(reason=msg)

    # NOTE(vish): Internally, flavorid is stored as a string but it comes
    #             in through json as an integer, so we convert it here.
    flavorid = six.text_type(flavorid)

    # ensure leading/trailing whitespaces not present.
    if flavorid.strip() != flavorid:
        msg = _("id cannot contain leading and/or trailing whitespace(s)")
        raise exception.InvalidInput(reason=msg)

    # ensure flavor id does not exceed 255 characters
    utils.check_string_length(flavorid, 'id', min_length=1, max_length=255)

    # ensure flavor id does not contain any special characters
    valid_flavor_id = VALID_ID_REGEX.search(flavorid)
    if not valid_flavor_id:
        msg = _("Flavor id can only contain letters from A-Z (both cases), "
                "periods, dashes, underscores and spaces.")
        raise exception.InvalidInput(reason=msg)

    # NOTE(wangbo): validate attributes of the creating flavor.
    # ram and vcpus should be positive ( > 0) integers.
    # disk, ephemeral and swap should be non-negative ( >= 0) integers.
    flavor_attributes = {
        'memory_mb': ('ram', 1),
        'vcpus': ('vcpus', 1),
        'root_gb': ('disk', 0),
        'ephemeral_gb': ('ephemeral', 0),
        'swap': ('swap', 0)
    }

    for key, value in flavor_attributes.items():
        kwargs[key] = utils.validate_integer(kwargs[key], value[0], value[1],
                                             db.MAX_INT)

    # rxtx_factor should be a positive float
    try:
        kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
        if (kwargs['rxtx_factor'] <= 0
                or kwargs['rxtx_factor'] > SQL_SP_FLOAT_MAX):
            raise ValueError()
    except ValueError:
        msg = (_("'rxtx_factor' argument must be a float between 0 and %g") %
               SQL_SP_FLOAT_MAX)
        raise exception.InvalidInput(reason=msg)

    kwargs['name'] = name
    kwargs['flavorid'] = flavorid
    # ensure is_public attribute is boolean
    try:
        kwargs['is_public'] = strutils.bool_from_string(is_public, strict=True)
    except ValueError:
        raise exception.InvalidInput(reason=_("is_public must be a boolean"))

    flavor = objects.Flavor(context=context.get_admin_context(), **kwargs)
    flavor.create()
    return flavor
Exemple #20
0
    def update(self, req, id, body):
        context = req.environ['compute.context']
        authorize_update(context)
        try:
            # NOTE(alex_xu): back-compatible with compute layer hard-code admin
            # permission checks. This has to be left only for API v2.0 because
            # this version has to be stable even if it means that only admins
            # can call this method while the policy could be changed.
            jacket.context.require_admin_context(context)
        except exception.AdminRequired:
            raise webob.exc.HTTPForbidden()

        project_id = id

        bad_keys = []

        # By default, we can force update the quota if the extended
        # is not loaded
        force_update = True
        extended_loaded = False
        if self.ext_mgr.is_loaded('os-extended-quotas'):
            # force optional has been enabled, the default value of
            # force_update need to be changed to False
            extended_loaded = True
            force_update = False

        user_id = None
        if self.ext_mgr.is_loaded('os-user-quotas'):
            # Update user quotas only if the extended is loaded
            params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
            user_id = params.get('user_id', [None])[0]

        try:
            # NOTE(alex_xu): back-compatible with compute layer hard-code admin
            # permission checks.
            jacket.context.authorize_project_context(context, id)
            settable_quotas = QUOTAS.get_settable_quotas(context, project_id,
                                                         user_id=user_id)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']

        # NOTE(dims): Pass #1 - In this loop for quota_set.items(), we figure
        # out if we have bad keys or if we need to forcibly set quotas or
        # if some of the values for the quotas can be converted to integers.
        for key, value in quota_set.items():
            if (key not in self.supported_quotas
                and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue
            if key == 'force' and extended_loaded:
                # only check the force optional when the extended has
                # been loaded
                force_update = strutils.bool_from_string(value)
            elif key not in NON_QUOTA_KEYS and value:
                try:
                    utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # NOTE(dims): Pass #2 - In this loop for quota_set.items(), based on
        # force_update flag we validate the quota limit. A loop just for
        # the validation of min/max values ensure that we can bail out if
        # any of the items in the set is bad.
        valid_quotas = {}
        for key, value in quota_set.items():
            if key in NON_QUOTA_KEYS or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if not force_update:
                minimum = settable_quotas[key]['minimum']
                maximum = settable_quotas[key]['maximum']
                self._validate_quota_limit(key, value, minimum, maximum)
            valid_quotas[key] = value

        # NOTE(dims): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one
        # shot without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                objects.Quotas.create_limit(context, project_id,
                                            key, value, user_id=user_id)
            except exception.QuotaExists:
                objects.Quotas.update_limit(context, project_id,
                                            key, value, user_id=user_id)
        values = self._get_quotas(context, id, user_id=user_id)
        return self._format_quota_set(None, values)
Exemple #21
0
    def create(self, req, body):
        """Creates a new server for a given user."""
        if not self.is_valid_body(body, 'server'):
            raise exc.HTTPUnprocessableEntity()

        context = req.environ['compute.context']
        server_dict = body['server']
        password = self._get_server_admin_password(server_dict)

        if 'name' not in server_dict:
            msg = _("Server name is not defined")
            raise exc.HTTPBadRequest(explanation=msg)

        name = server_dict['name']
        self._validate_server_name(name)
        name = name.strip()

        image_uuid = self._image_from_req_data(body)

        personality = server_dict.get('personality')
        config_drive = None
        if self.ext_mgr.is_loaded('os-config-drive'):
            config_drive = server_dict.get('config_drive')

        injected_files = []
        if personality:
            injected_files = self._get_injected_files(personality)

        sg_names = []
        if self.ext_mgr.is_loaded('os-security-groups'):
            security_groups = server_dict.get('security_groups')
            if security_groups is not None:
                try:
                    sg_names = [sg['name'] for sg in security_groups
                                if sg.get('name')]
                except AttributeError:
                    msg = _("Invalid input for field/attribute %(path)s."
                           " Value: %(value)s. %(message)s") % {
                               'path': 'security_groups',
                               'value': security_groups,
                               'message': ''
                           }
                    raise exc.HTTPBadRequest(explanation=msg)
        if not sg_names:
            sg_names.append('default')

        sg_names = list(set(sg_names))

        requested_networks = self._determine_requested_networks(server_dict)

        (access_ip_v4, ) = server_dict.get('accessIPv4'),
        if access_ip_v4 is not None:
            self._validate_access_ipv4(access_ip_v4)

        (access_ip_v6, ) = server_dict.get('accessIPv6'),
        if access_ip_v6 is not None:
            self._validate_access_ipv6(access_ip_v6)

        flavor_id = self._flavor_id_from_req_data(body)

        # optional openstack extensions:
        key_name = self._extract(server_dict, 'os-keypairs', 'key_name')
        availability_zone = self._extract(server_dict, 'os-availability-zone',
                                          'availability_zone')
        user_data = self._extract(server_dict, 'os-user-data', 'user_data')
        self._validate_user_data(user_data)

        image_uuid_specified = bool(image_uuid)
        legacy_bdm, block_device_mapping = self._extract_bdm(server_dict,
            image_uuid_specified)

        ret_resv_id = False
        # min_count and max_count are optional.  If they exist, they may come
        # in as strings.  Verify that they are valid integers and > 0.
        # Also, we want to default 'min_count' to 1, and default
        # 'max_count' to be 'min_count'.
        min_count = 1
        max_count = 1
        if self.ext_mgr.is_loaded('os-multiple-create'):
            ret_resv_id = server_dict.get('return_reservation_id', False)
            min_count = server_dict.get('min_count', 1)
            max_count = server_dict.get('max_count', min_count)

        try:
            min_count = utils.validate_integer(
                min_count, "min_count", min_value=1)
            max_count = utils.validate_integer(
                max_count, "max_count", min_value=1)
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if min_count > max_count:
            msg = _('min_count must be <= max_count')
            raise exc.HTTPBadRequest(explanation=msg)

        auto_disk_config = False
        if self.ext_mgr.is_loaded('OS-DCF'):
            auto_disk_config = server_dict.get('auto_disk_config')

        scheduler_hints = {}
        if self.ext_mgr.is_loaded('OS-SCH-HNT'):
            scheduler_hints = server_dict.get('scheduler_hints', {})
        parse_az = self.compute_api.parse_availability_zone
        availability_zone, host, node = parse_az(context, availability_zone)

        check_server_group_quota = self.ext_mgr.is_loaded(
                'os-server-group-quotas')
        try:
            _get_inst_type = flavors.get_flavor_by_flavor_id
            inst_type = _get_inst_type(flavor_id, ctxt=context,
                                       read_deleted="no")

            (instances, resv_id) = self.compute_api.create(context,
                        inst_type,
                        image_uuid,
                        display_name=name,
                        display_description=name,
                        key_name=key_name,
                        metadata=server_dict.get('metadata', {}),
                        access_ip_v4=access_ip_v4,
                        access_ip_v6=access_ip_v6,
                        injected_files=injected_files,
                        admin_password=password,
                        min_count=min_count,
                        max_count=max_count,
                        requested_networks=requested_networks,
                        security_group=sg_names,
                        user_data=user_data,
                        availability_zone=availability_zone,
                        forced_host=host, forced_node=node,
                        config_drive=config_drive,
                        block_device_mapping=block_device_mapping,
                        auto_disk_config=auto_disk_config,
                        scheduler_hints=scheduler_hints,
                        legacy_bdm=legacy_bdm,
                        check_server_group_quota=check_server_group_quota)
        except (exception.QuotaError,
                exception.PortLimitExceeded) as error:
            raise exc.HTTPForbidden(
                explanation=error.format_message())
        except messaging.RemoteError as err:
            msg = "%(err_type)s: %(err_msg)s" % {'err_type': err.exc_type,
                                                 'err_msg': err.value}
            raise exc.HTTPBadRequest(explanation=msg)
        except UnicodeDecodeError as error:
            msg = "UnicodeError: %s" % error
            raise exc.HTTPBadRequest(explanation=msg)
        except Exception:
            # The remaining cases can be handled in a standard fashion.
            self._handle_create_exception(*sys.exc_info())

        # If the caller wanted a reservation_id, return it
        if ret_resv_id:
            return wsgi.ResponseObject({'reservation_id': resv_id})

        req.cache_db_instances(instances)
        server = self._view_builder.create(req, instances[0])

        if CONF.enable_instance_password:
            server['server']['adminPass'] = password

        robj = wsgi.ResponseObject(server)

        return self._add_location(robj)
Exemple #22
0
    def _create_backup(self, req, id, body):
        """Backup a server instance.

        Images now have an `image_type` associated with them, which can be
        'snapshot' or the backup type, like 'daily' or 'weekly'.

        If the image_type is backup-like, then the rotation factor can be
        included and that will cause the oldest backups that exceed the
        rotation factor to be deleted.

        """
        context = req.environ["compute.context"]
        authorize(context, 'createBackup')
        entity = body["createBackup"]

        try:
            image_name = entity["name"]
            backup_type = entity["backup_type"]
            rotation = entity["rotation"]

        except KeyError as missing_key:
            msg = _("createBackup entity requires %s attribute") % missing_key
            raise exc.HTTPBadRequest(explanation=msg)

        except TypeError:
            msg = _("Malformed createBackup entity")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            rotation = utils.validate_integer(rotation,
                                              "rotation",
                                              min_value=0)
        except exception.InvalidInput as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        props = {}
        metadata = entity.get('metadata', {})
        common.check_img_metadata_properties_quota(context, metadata)
        try:
            props.update(metadata)
        except ValueError:
            msg = _("Invalid metadata")
            raise exc.HTTPBadRequest(explanation=msg)

        instance = common.get_instance(self.compute_api, context, id)
        try:
            image = self.compute_api.backup(context,
                                            instance,
                                            image_name,
                                            backup_type,
                                            rotation,
                                            extra_properties=props)
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(
                state_error, 'createBackup', id)
        except exception.InvalidRequest as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        resp = webob.Response(status_int=202)

        # build location of newly-created image entity if rotation is not zero
        if rotation > 0:
            image_id = str(image['id'])
            image_ref = common.url_join(req.application_url, 'images',
                                        image_id)
            resp.headers['Location'] = image_ref

        return resp
Exemple #23
0
    def update(self, req, id, body):
        context = req.environ['compute.context']
        authorize_update(context)
        try:
            # NOTE(alex_xu): back-compatible with compute layer hard-code admin
            # permission checks. This has to be left only for API v2.0 because
            # this version has to be stable even if it means that only admins
            # can call this method while the policy could be changed.
            jacket.context.require_admin_context(context)
        except exception.AdminRequired:
            raise webob.exc.HTTPForbidden()

        project_id = id

        bad_keys = []

        # By default, we can force update the quota if the extended
        # is not loaded
        force_update = True
        extended_loaded = False
        if self.ext_mgr.is_loaded('os-extended-quotas'):
            # force optional has been enabled, the default value of
            # force_update need to be changed to False
            extended_loaded = True
            force_update = False

        user_id = None
        if self.ext_mgr.is_loaded('os-user-quotas'):
            # Update user quotas only if the extended is loaded
            params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
            user_id = params.get('user_id', [None])[0]

        try:
            # NOTE(alex_xu): back-compatible with compute layer hard-code admin
            # permission checks.
            jacket.context.authorize_project_context(context, id)
            settable_quotas = QUOTAS.get_settable_quotas(context,
                                                         project_id,
                                                         user_id=user_id)
        except exception.Forbidden:
            raise webob.exc.HTTPForbidden()

        if not self.is_valid_body(body, 'quota_set'):
            msg = _("quota_set not specified")
            raise webob.exc.HTTPBadRequest(explanation=msg)
        quota_set = body['quota_set']

        # NOTE(dims): Pass #1 - In this loop for quota_set.items(), we figure
        # out if we have bad keys or if we need to forcibly set quotas or
        # if some of the values for the quotas can be converted to integers.
        for key, value in quota_set.items():
            if (key not in self.supported_quotas
                    and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue
            if key == 'force' and extended_loaded:
                # only check the force optional when the extended has
                # been loaded
                force_update = strutils.bool_from_string(value)
            elif key not in NON_QUOTA_KEYS and value:
                try:
                    utils.validate_integer(value, key)
                except exception.InvalidInput as e:
                    raise webob.exc.HTTPBadRequest(
                        explanation=e.format_message())

        if bad_keys:
            msg = _("Bad key(s) %s in quota_set") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # NOTE(dims): Pass #2 - In this loop for quota_set.items(), based on
        # force_update flag we validate the quota limit. A loop just for
        # the validation of min/max values ensure that we can bail out if
        # any of the items in the set is bad.
        valid_quotas = {}
        for key, value in quota_set.items():
            if key in NON_QUOTA_KEYS or (not value and value != 0):
                continue
            # validate whether already used and reserved exceeds the new
            # quota, this check will be ignored if admin want to force
            # update
            value = int(value)
            if not force_update:
                minimum = settable_quotas[key]['minimum']
                maximum = settable_quotas[key]['maximum']
                self._validate_quota_limit(key, value, minimum, maximum)
            valid_quotas[key] = value

        # NOTE(dims): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one
        # shot without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                objects.Quotas.create_limit(context,
                                            project_id,
                                            key,
                                            value,
                                            user_id=user_id)
            except exception.QuotaExists:
                objects.Quotas.update_limit(context,
                                            project_id,
                                            key,
                                            value,
                                            user_id=user_id)
        values = self._get_quotas(context, id, user_id=user_id)
        return self._format_quota_set(None, values)