text: #    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"]
          context.can(st_policies.POLICY_ROOT % 'show')
  
          try:
              im = objects.InstanceMapping.get_by_instance_uuid(context,
                                                                server_id)
              with nova_context.target_cell(context, im.cell_mapping) as cctxt:
                  exists = objects.Tag.exists(cctxt, server_id, id)
          except (exception.InstanceNotFound,
                  exception.InstanceMappingNotFound) 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"]
          context.can(st_policies.POLICY_ROOT % 'index')
  
          try:
              im = objects.InstanceMapping.get_by_instance_uuid(context,
                                                                server_id)
              with nova_context.target_cell(context, im.cell_mapping) as cctxt:
                  tags = objects.TagList.get_by_resource_id(cctxt, server_id)
          except (exception.InstanceNotFound,
                  exception.InstanceMappingNotFound) 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"]
          context.can(st_policies.POLICY_ROOT % 'update')
          im = _get_instance_mapping(context, server_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"]
          context.can(st_policies.POLICY_ROOT % 'update_all')
          im = _get_instance_mapping(context, server_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"]
          context.can(st_policies.POLICY_ROOT % 'delete')
          im = _get_instance_mapping(context, server_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"]
          context.can(st_policies.POLICY_ROOT % 'delete_all')
          im = _get_instance_mapping(context, server_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")
  