nova/nova/api/openstack/compute/server_tags.py

224 lines
9.4 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import jsonschema
import webob
from nova.api.openstack import common
from nova.api.openstack.compute.schemas import server_tags as schema
from nova.api.openstack.compute.views import server_tags
from nova.api.openstack import wsgi
from nova.api import validation
from nova.api.validation import parameter_types
from nova.compute import api as compute
from nova.compute import vm_states
from nova import context as nova_context
from nova import exception
from nova.i18n import _
from nova.notifications import base as notifications_base
from nova import objects
from nova.policies import server_tags as st_policies
def _get_tags_names(tags):
return [t.tag for t in tags]
def _get_instance_mapping(context, server_id):
try:
return objects.InstanceMapping.get_by_instance_uuid(context,
server_id)
except exception.InstanceMappingNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
class ServerTagsController(wsgi.Controller):
_view_builder_class = server_tags.ViewBuilder
def __init__(self):
super(ServerTagsController, self).__init__()
self.compute_api = compute.API()
def _check_instance_in_valid_state(self, context, server_id, action):
instance = common.get_instance(self.compute_api, context, server_id)
if instance.vm_state not in (vm_states.ACTIVE, vm_states.PAUSED,
vm_states.SUSPENDED, vm_states.STOPPED):
exc = exception.InstanceInvalidState(attr='vm_state',
instance_uuid=instance.uuid,
state=instance.vm_state,
method=action)
common.raise_http_conflict_for_instance_invalid_state(exc, action,
server_id)
return instance
@wsgi.Controller.api_version("2.26")
@wsgi.response(204)
@wsgi.expected_errors(404)
def show(self, req, server_id, id):
context = req.environ["nova.context"]
im = _get_instance_mapping(context, server_id)
context.can(st_policies.POLICY_ROOT % 'show',
target={'project_id': im.project_id})
try:
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
exists = objects.Tag.exists(cctxt, server_id, id)
except (exception.InstanceNotFound) as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
if not exists:
msg = (_("Server %(server_id)s has no tag '%(tag)s'")
% {'server_id': server_id, 'tag': id})
raise webob.exc.HTTPNotFound(explanation=msg)
@wsgi.Controller.api_version("2.26")
@wsgi.expected_errors(404)
def index(self, req, server_id):
context = req.environ["nova.context"]
im = _get_instance_mapping(context, server_id)
context.can(st_policies.POLICY_ROOT % 'index',
target={'project_id': im.project_id})
try:
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
tags = objects.TagList.get_by_resource_id(cctxt, server_id)
except (exception.InstanceNotFound) as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
return {'tags': _get_tags_names(tags)}
@wsgi.Controller.api_version("2.26")
@wsgi.expected_errors((400, 404, 409))
@validation.schema(schema.update)
def update(self, req, server_id, id, body):
context = req.environ["nova.context"]
im = _get_instance_mapping(context, server_id)
context.can(st_policies.POLICY_ROOT % 'update',
target={'project_id': im.project_id})
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
instance = self._check_instance_in_valid_state(
cctxt, server_id, 'update tag')
try:
jsonschema.validate(id, parameter_types.tag)
except jsonschema.ValidationError as e:
msg = (_("Tag '%(tag)s' is invalid. It must be a non empty string "
"without characters '/' and ','. Validation error "
"message: %(err)s") % {'tag': id, 'err': e.message})
raise webob.exc.HTTPBadRequest(explanation=msg)
try:
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
tags = objects.TagList.get_by_resource_id(cctxt, server_id)
except exception.InstanceNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
if len(tags) >= objects.instance.MAX_TAG_COUNT:
msg = (_("The number of tags exceeded the per-server limit %d")
% objects.instance.MAX_TAG_COUNT)
raise webob.exc.HTTPBadRequest(explanation=msg)
if id in _get_tags_names(tags):
# NOTE(snikitin): server already has specified tag
return webob.Response(status_int=204)
try:
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
tag = objects.Tag(context=cctxt, resource_id=server_id, tag=id)
tag.create()
instance.tags = objects.TagList.get_by_resource_id(cctxt,
server_id)
except exception.InstanceNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
notifications_base.send_instance_update_notification(
context, instance, service="nova-api")
response = webob.Response(status_int=201)
response.headers['Location'] = self._view_builder.get_location(
req, server_id, id)
return response
@wsgi.Controller.api_version("2.26")
@wsgi.expected_errors((404, 409))
@validation.schema(schema.update_all)
def update_all(self, req, server_id, body):
context = req.environ["nova.context"]
im = _get_instance_mapping(context, server_id)
context.can(st_policies.POLICY_ROOT % 'update_all',
target={'project_id': im.project_id})
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
instance = self._check_instance_in_valid_state(
cctxt, server_id, 'update tags')
try:
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
tags = objects.TagList.create(cctxt, server_id, body['tags'])
instance.tags = tags
except exception.InstanceNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
notifications_base.send_instance_update_notification(
context, instance, service="nova-api")
return {'tags': _get_tags_names(tags)}
@wsgi.Controller.api_version("2.26")
@wsgi.response(204)
@wsgi.expected_errors((404, 409))
def delete(self, req, server_id, id):
context = req.environ["nova.context"]
im = _get_instance_mapping(context, server_id)
context.can(st_policies.POLICY_ROOT % 'delete',
target={'project_id': im.project_id})
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
instance = self._check_instance_in_valid_state(
cctxt, server_id, 'delete tag')
try:
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
objects.Tag.destroy(cctxt, server_id, id)
instance.tags = objects.TagList.get_by_resource_id(cctxt,
server_id)
except (exception.InstanceTagNotFound,
exception.InstanceNotFound) as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
notifications_base.send_instance_update_notification(
context, instance, service="nova-api")
@wsgi.Controller.api_version("2.26")
@wsgi.response(204)
@wsgi.expected_errors((404, 409))
def delete_all(self, req, server_id):
context = req.environ["nova.context"]
im = _get_instance_mapping(context, server_id)
context.can(st_policies.POLICY_ROOT % 'delete_all',
target={'project_id': im.project_id})
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
instance = self._check_instance_in_valid_state(
cctxt, server_id, 'delete tags')
try:
with nova_context.target_cell(context, im.cell_mapping) as cctxt:
objects.TagList.destroy(cctxt, server_id)
instance.tags = objects.TagList()
except exception.InstanceNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
notifications_base.send_instance_update_notification(
context, instance, service="nova-api")