Added server tags controller
Added new controller which allows the following: - add tag to the server - replace set of server tags with new set of tags - get list of tags for server - check if tag exists on a server - remove specified tag from server - remove all tags from server Functional tests and annotations "@wsgi.Controller.api_version(*)" for controller methods will be added in next patch with creation of new API microversion. APIImpact Implements: blueprint tag-instances Change-Id: Ibc44228aeae94c17353af7fccfcfb2c11b2e9190
This commit is contained in:
parent
0a14f0b296
commit
bfe8e7484d
@ -444,6 +444,12 @@
|
||||
"os_compute_api:os-server-usage:discoverable": "@",
|
||||
"os_compute_api:os-server-groups": "rule:admin_or_owner",
|
||||
"os_compute_api:os-server-groups:discoverable": "@",
|
||||
"os_compute_api:os-server-tags:index": "@",
|
||||
"os_compute_api:os-server-tags:show": "@",
|
||||
"os_compute_api:os-server-tags:update": "@",
|
||||
"os_compute_api:os-server-tags:update_all": "@",
|
||||
"os_compute_api:os-server-tags:delete": "@",
|
||||
"os_compute_api:os-server-tags:delete_all": "@",
|
||||
"os_compute_api:os-services": "rule:admin_api",
|
||||
"os_compute_api:os-services:discoverable": "@",
|
||||
"os_compute_api:server-metadata:discoverable": "@",
|
||||
|
43
nova/api/openstack/compute/schemas/server_tags.py
Normal file
43
nova/api/openstack/compute/schemas/server_tags.py
Normal file
@ -0,0 +1,43 @@
|
||||
# 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.
|
||||
|
||||
tag = {
|
||||
"type": "string",
|
||||
"pattern": "^[^,/]*$"
|
||||
}
|
||||
|
||||
update_all = {
|
||||
"definitions": {
|
||||
"tag": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Server tags",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/tag"
|
||||
}
|
||||
}
|
||||
},
|
||||
'required': ['tags'],
|
||||
'additionalProperties': False
|
||||
}
|
||||
|
||||
update = {
|
||||
"title": "Server tag",
|
||||
"type": "null",
|
||||
'required': [],
|
||||
'additionalProperties': False
|
||||
}
|
196
nova/api/openstack/compute/server_tags.py
Normal file
196
nova/api/openstack/compute/server_tags.py
Normal file
@ -0,0 +1,196 @@
|
||||
# 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
|
||||
|
||||
from webob import exc
|
||||
|
||||
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 extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api import validation
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova import objects
|
||||
|
||||
|
||||
ALIAS = "os-server-tags"
|
||||
authorize = extensions.os_compute_authorizer(ALIAS)
|
||||
|
||||
|
||||
def _get_tags_names(tags):
|
||||
return [t.tag for t in tags]
|
||||
|
||||
|
||||
class ServerTagsController(wsgi.Controller):
|
||||
_view_builder_class = server_tags.ViewBuilder
|
||||
|
||||
@wsgi.response(204)
|
||||
@extensions.expected_errors(404)
|
||||
def show(self, req, server_id, id):
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context, action='show')
|
||||
|
||||
try:
|
||||
exists = objects.Tag.exists(context, server_id, id)
|
||||
except exception.InstanceNotFound as e:
|
||||
raise 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 exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
@extensions.expected_errors(404)
|
||||
def index(self, req, server_id):
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context, action='index')
|
||||
|
||||
try:
|
||||
tags = objects.TagList.get_by_resource_id(context, server_id)
|
||||
except exception.InstanceNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
||||
return {'tags': _get_tags_names(tags)}
|
||||
|
||||
@extensions.expected_errors((400, 404))
|
||||
@validation.schema(schema.update)
|
||||
def update(self, req, server_id, id, body):
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context, action='update')
|
||||
|
||||
try:
|
||||
jsonschema.validate(id, schema.tag)
|
||||
except jsonschema.ValidationError as e:
|
||||
msg = (_("Tag '%(tag)s' is invalid. It must be a string without "
|
||||
"characters '/' and ','. Validation error message: "
|
||||
"%(err)s") % {'tag': id, 'err': e.message})
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
tags = objects.TagList.get_by_resource_id(context, server_id)
|
||||
except exception.InstanceNotFound as e:
|
||||
raise 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 exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
if len(id) > objects.tag.MAX_TAG_LENGTH:
|
||||
msg = (_("Tag '%(tag)s' is too long. Maximum length of a tag "
|
||||
"is %(length)d") % {'tag': id,
|
||||
'length': objects.tag.MAX_TAG_LENGTH})
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
if id in _get_tags_names(tags):
|
||||
# NOTE(snikitin): server already has specified tag
|
||||
return exc.HTTPNoContent()
|
||||
|
||||
tag = objects.Tag(context=context, resource_id=server_id, tag=id)
|
||||
|
||||
try:
|
||||
tag.create()
|
||||
except exception.InstanceNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
||||
response = exc.HTTPCreated()
|
||||
response.headers['Location'] = self._view_builder.get_location(
|
||||
req, server_id, id)
|
||||
return response
|
||||
|
||||
@extensions.expected_errors((400, 404))
|
||||
@validation.schema(schema.update_all)
|
||||
def update_all(self, req, server_id, body):
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context, action='update_all')
|
||||
|
||||
invalid_tags = []
|
||||
for tag in body['tags']:
|
||||
try:
|
||||
jsonschema.validate(tag, schema.tag)
|
||||
except jsonschema.ValidationError:
|
||||
invalid_tags.append(tag)
|
||||
if invalid_tags:
|
||||
msg = (_("Tags '%s' are invalid. Each tag must be a string "
|
||||
"without characters '/' and ','.") % invalid_tags)
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
tag_count = len(body['tags'])
|
||||
if tag_count > objects.instance.MAX_TAG_COUNT:
|
||||
msg = (_("The number of tags exceeded the per-server limit "
|
||||
"%(max)d. The number of tags in request is %(count)d.")
|
||||
% {'max': objects.instance.MAX_TAG_COUNT,
|
||||
'count': tag_count})
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
long_tags = [
|
||||
t for t in body['tags'] if len(t) > objects.tag.MAX_TAG_LENGTH]
|
||||
if long_tags:
|
||||
msg = (_("Tags %(tags)s are too long. Maximum length of a tag "
|
||||
"is %(length)d") % {'tags': long_tags,
|
||||
'length': objects.tag.MAX_TAG_LENGTH})
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
tags = objects.TagList.create(context, server_id, body['tags'])
|
||||
except exception.InstanceNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
||||
return {'tags': _get_tags_names(tags)}
|
||||
|
||||
@wsgi.response(204)
|
||||
@extensions.expected_errors(404)
|
||||
def delete(self, req, server_id, id):
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context, action='delete')
|
||||
|
||||
try:
|
||||
objects.Tag.destroy(context, server_id, id)
|
||||
except exception.InstanceTagNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
except exception.InstanceNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
||||
@wsgi.response(204)
|
||||
@extensions.expected_errors(404)
|
||||
def delete_all(self, req, server_id):
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context, action='delete_all')
|
||||
|
||||
try:
|
||||
objects.TagList.destroy(context, server_id)
|
||||
except exception.InstanceNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
||||
|
||||
class ServerTags(extensions.V21APIExtensionBase):
|
||||
"""Server tags support."""
|
||||
|
||||
name = "ServerTags"
|
||||
alias = ALIAS
|
||||
version = 1
|
||||
|
||||
def get_controller_extensions(self):
|
||||
return []
|
||||
|
||||
def get_resources(self):
|
||||
res = extensions.ResourceExtension('tags',
|
||||
ServerTagsController(),
|
||||
parent=dict(
|
||||
member_name='server',
|
||||
collection_name='servers'),
|
||||
collection_actions={
|
||||
'delete_all': 'DELETE',
|
||||
'update_all': 'PUT'})
|
||||
return [res]
|
30
nova/api/openstack/compute/views/server_tags.py
Normal file
30
nova/api/openstack/compute/views/server_tags.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright 2016 Mirantis Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack.compute.views import servers
|
||||
|
||||
|
||||
class ViewBuilder(common.ViewBuilder):
|
||||
_collection_name = "tags"
|
||||
|
||||
def __init__(self):
|
||||
super(ViewBuilder, self).__init__()
|
||||
self._server_builder = servers.ViewBuilder()
|
||||
|
||||
def get_location(self, request, server_id, tag_name):
|
||||
server_location = self._server_builder._get_href_link(
|
||||
request, server_id, "servers")
|
||||
return "%s/%s/%s" % (server_location, self._collection_name, tag_name)
|
@ -57,6 +57,9 @@ INSTANCE_OPTIONAL_ATTRS = (_INSTANCE_OPTIONAL_JOINED_FIELDS +
|
||||
INSTANCE_DEFAULT_FIELDS = ['metadata', 'system_metadata',
|
||||
'info_cache', 'security_groups']
|
||||
|
||||
# Maximum count of tags to one instance
|
||||
MAX_TAG_COUNT = 50
|
||||
|
||||
|
||||
def _expected_cols(expected_attrs):
|
||||
"""Return expected_attrs that are columns needing joining.
|
||||
|
@ -15,6 +15,8 @@ from nova import objects
|
||||
from nova.objects import base
|
||||
from nova.objects import fields
|
||||
|
||||
MAX_TAG_LENGTH = 60
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class Tag(base.NovaObject):
|
||||
|
256
nova/tests/unit/api/openstack/compute/test_server_tags.py
Normal file
256
nova/tests/unit/api/openstack/compute/test_server_tags.py
Normal file
@ -0,0 +1,256 @@
|
||||
# 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 mock
|
||||
from webob import exc
|
||||
|
||||
from nova.api.openstack.compute import extension_info
|
||||
from nova.api.openstack.compute import server_tags
|
||||
from nova.api.openstack.compute import servers
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova import exception
|
||||
from nova.objects import instance
|
||||
from nova.objects import tag as tag_obj
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
|
||||
UUID = 'b48316c5-71e8-45e4-9884-6c78055b9b13'
|
||||
TAG1 = 'tag1'
|
||||
TAG2 = 'tag2'
|
||||
TAG3 = 'tag3'
|
||||
TAGS = [TAG1, TAG2, TAG3]
|
||||
NON_EXISTING_UUID = '123'
|
||||
|
||||
|
||||
class ServerTagsTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(ServerTagsTest, self).setUp()
|
||||
self.controller = server_tags.ServerTagsController()
|
||||
|
||||
def _get_tag(self, tag_name):
|
||||
tag = models.Tag()
|
||||
tag.tag = tag_name
|
||||
tag.resource_id = UUID
|
||||
return tag
|
||||
|
||||
def _get_request(self, url, method):
|
||||
request = fakes.HTTPRequest.blank(url)
|
||||
request.method = method
|
||||
return request
|
||||
|
||||
@mock.patch('nova.db.instance_tag_exists')
|
||||
def test_show(self, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (UUID, TAG1), 'GET')
|
||||
context = req.environ["nova.context"]
|
||||
|
||||
self.controller.show(req, UUID, TAG1)
|
||||
mock_exists.assert_called_once_with(context, UUID, TAG1)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_get_by_instance_uuid')
|
||||
def test_index(self, mock_db_get_inst_tags):
|
||||
fake_tags = [self._get_tag(tag) for tag in TAGS]
|
||||
mock_db_get_inst_tags.return_value = fake_tags
|
||||
|
||||
req = self._get_request('/v2/fake/servers/%s/tags' % UUID, 'GET')
|
||||
context = req.environ["nova.context"]
|
||||
|
||||
res = self.controller.index(req, UUID)
|
||||
self.assertEqual(TAGS, res.get('tags'))
|
||||
mock_db_get_inst_tags.assert_called_once_with(context, UUID)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_set')
|
||||
def test_update_all(self, mock_db_set_inst_tags):
|
||||
fake_tags = [self._get_tag(tag) for tag in TAGS]
|
||||
mock_db_set_inst_tags.return_value = fake_tags
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags' % UUID, 'PUT')
|
||||
context = req.environ["nova.context"]
|
||||
res = self.controller.update_all(req, UUID, body={'tags': TAGS})
|
||||
|
||||
self.assertEqual(TAGS, res['tags'])
|
||||
mock_db_set_inst_tags.assert_called_once_with(context, UUID, TAGS)
|
||||
|
||||
def test_update_all_too_many_tags(self):
|
||||
fake_tags = {'tags': [str(i) for i in xrange(
|
||||
instance.MAX_TAG_COUNT + 1)]}
|
||||
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags' % UUID, 'PUT')
|
||||
self.assertRaises(exc.HTTPBadRequest, self.controller.update_all,
|
||||
req, UUID, body=fake_tags)
|
||||
|
||||
def test_update_all_forbidden_characters(self):
|
||||
req = self._get_request('/v2/fake/servers/%s/tags' % UUID, 'PUT')
|
||||
for tag in ['tag,1', 'tag/1']:
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.update_all,
|
||||
req, UUID, body={'tags': [tag, 'tag2']})
|
||||
|
||||
def test_update_all_invalid_tag_type(self):
|
||||
req = self._get_request('/v2/fake/servers/%s/tags' % UUID, 'PUT')
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.controller.update_all,
|
||||
req, UUID, body={'tags': [1]})
|
||||
|
||||
def test_update_all_too_long_tag(self):
|
||||
req = self._get_request('/v2/fake/servers/%s/tags' % UUID, 'PUT')
|
||||
tag = "a" * (tag_obj.MAX_TAG_LENGTH + 1)
|
||||
self.assertRaises(exc.HTTPBadRequest, self.controller.update_all,
|
||||
req, UUID, body={'tags': [tag]})
|
||||
|
||||
def test_update_all_invalid_tag_list_type(self):
|
||||
req = self._get_request('/v2/ake/servers/%s/tags' % UUID, 'PUT')
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.controller.update_all,
|
||||
req, UUID, body={'tags': {'tag': 'tag'}})
|
||||
|
||||
@mock.patch('nova.db.instance_tag_exists')
|
||||
def test_show_non_existing_tag(self, mock_exists):
|
||||
mock_exists.return_value = False
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (UUID, TAG1), 'GET')
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.show,
|
||||
req, UUID, TAG1)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_add')
|
||||
@mock.patch('nova.db.instance_tag_get_by_instance_uuid')
|
||||
def test_update(self, mock_db_get_inst_tags, mock_db_add_inst_tags):
|
||||
mock_db_get_inst_tags.return_value = [self._get_tag(TAG1)]
|
||||
mock_db_add_inst_tags.return_value = self._get_tag(TAG2)
|
||||
|
||||
url = '/v2/fake/servers/%s/tags/%s' % (UUID, TAG2)
|
||||
location = 'http://localhost' + url
|
||||
req = self._get_request(url, 'PUT')
|
||||
context = req.environ["nova.context"]
|
||||
res = self.controller.update(req, UUID, TAG2, body=None)
|
||||
|
||||
self.assertEqual(201, res.status_int)
|
||||
self.assertEqual(location, res.headers['Location'])
|
||||
mock_db_add_inst_tags.assert_called_once_with(context, UUID, TAG2)
|
||||
mock_db_get_inst_tags.assert_called_once_with(context, UUID)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_get_by_instance_uuid')
|
||||
def test_update_existing_tag(self, mock_db_get_inst_tags):
|
||||
mock_db_get_inst_tags.return_value = [self._get_tag(TAG1)]
|
||||
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (UUID, TAG1), 'PUT')
|
||||
context = req.environ["nova.context"]
|
||||
res = self.controller.update(req, UUID, TAG1, body=None)
|
||||
|
||||
self.assertEqual(204, res.status_int)
|
||||
mock_db_get_inst_tags.assert_called_once_with(context, UUID)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_get_by_instance_uuid')
|
||||
def test_update_tag_limit_exceed(self, mock_db_get_inst_tags):
|
||||
fake_tags = [self._get_tag(str(i))
|
||||
for i in xrange(instance.MAX_TAG_COUNT)]
|
||||
mock_db_get_inst_tags.return_value = fake_tags
|
||||
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (UUID, TAG2), 'PUT')
|
||||
self.assertRaises(exc.HTTPBadRequest, self.controller.update,
|
||||
req, UUID, TAG2, body=None)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_get_by_instance_uuid')
|
||||
def test_update_too_long_tag(self, mock_db_get_inst_tags):
|
||||
mock_db_get_inst_tags.return_value = []
|
||||
|
||||
tag = "a" * (tag_obj.MAX_TAG_LENGTH + 1)
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (UUID, tag), 'PUT')
|
||||
self.assertRaises(exc.HTTPBadRequest, self.controller.update,
|
||||
req, UUID, tag, body=None)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_get_by_instance_uuid')
|
||||
def test_update_forbidden_characters(self, mock_db_get_inst_tags):
|
||||
mock_db_get_inst_tags.return_value = []
|
||||
for tag in ['tag,1', 'tag/1']:
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (UUID, tag), 'PUT')
|
||||
self.assertRaises(exc.HTTPBadRequest, self.controller.update,
|
||||
req, UUID, tag, body=None)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_delete')
|
||||
def test_delete(self, mock_db_delete_inst_tags):
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (UUID, TAG2), 'DELETE')
|
||||
context = req.environ["nova.context"]
|
||||
self.controller.delete(req, UUID, TAG2)
|
||||
mock_db_delete_inst_tags.assert_called_once_with(context, UUID, TAG2)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_delete')
|
||||
def test_delete_non_existing_tag(self, mock_db_delete_inst_tags):
|
||||
def fake_db_delete_tag(context, instance_uuid, tag):
|
||||
self.assertEqual(UUID, instance_uuid)
|
||||
self.assertEqual(TAG1, tag)
|
||||
raise exception.InstanceTagNotFound(instance_id=instance_uuid,
|
||||
tag=tag)
|
||||
mock_db_delete_inst_tags.side_effect = fake_db_delete_tag
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (UUID, TAG1), 'DELETE')
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.delete,
|
||||
req, UUID, TAG1)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_delete_all')
|
||||
def test_delete_all(self, mock_db_delete_inst_tags):
|
||||
req = self._get_request('/v2/fake/servers/%s/tags' % UUID, 'DELETE')
|
||||
context = req.environ["nova.context"]
|
||||
self.controller.delete_all(req, UUID)
|
||||
mock_db_delete_inst_tags.assert_called_once_with(context, UUID)
|
||||
|
||||
def test_show_non_existing_instance(self):
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (NON_EXISTING_UUID, TAG1), 'GET')
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.show, req,
|
||||
NON_EXISTING_UUID, TAG1)
|
||||
|
||||
def test_show_with_details_information_non_existing_instance(self):
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s' % NON_EXISTING_UUID, 'GET')
|
||||
ext_info = extension_info.LoadedExtensionInfo()
|
||||
servers_controller = servers.ServersController(extension_info=ext_info)
|
||||
self.assertRaises(exc.HTTPNotFound, servers_controller.show, req,
|
||||
NON_EXISTING_UUID)
|
||||
|
||||
def test_index_non_existing_instance(self):
|
||||
req = self._get_request(
|
||||
'v2/fake/servers/%s/tags' % NON_EXISTING_UUID, 'GET')
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.index, req,
|
||||
NON_EXISTING_UUID)
|
||||
|
||||
def test_update_non_existing_instance(self):
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (NON_EXISTING_UUID, TAG1), 'PUT')
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.update, req,
|
||||
NON_EXISTING_UUID, TAG1, body=None)
|
||||
|
||||
def test_update_all_non_existing_instance(self):
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags' % NON_EXISTING_UUID, 'PUT')
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.update_all, req,
|
||||
NON_EXISTING_UUID, body={'tags': TAGS})
|
||||
|
||||
def test_delete_non_existing_instance(self):
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags/%s' % (NON_EXISTING_UUID, TAG1),
|
||||
'DELETE')
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.delete, req,
|
||||
NON_EXISTING_UUID, TAG1)
|
||||
|
||||
def test_delete_all_non_existing_instance(self):
|
||||
req = self._get_request(
|
||||
'/v2/fake/servers/%s/tags' % NON_EXISTING_UUID, 'DELETE')
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.delete_all,
|
||||
req, NON_EXISTING_UUID)
|
@ -321,6 +321,12 @@ policy_data = """
|
||||
"compute_extension:server_groups": "",
|
||||
"compute_extension:server_password": "",
|
||||
"os_compute_api:os-server-password": "",
|
||||
"os_compute_api:os-server-tags:index": "",
|
||||
"os_compute_api:os-server-tags:show": "",
|
||||
"os_compute_api:os-server-tags:update": "",
|
||||
"os_compute_api:os-server-tags:update_all": "",
|
||||
"os_compute_api:os-server-tags:delete": "",
|
||||
"os_compute_api:os-server-tags:delete_all": "",
|
||||
"compute_extension:server_usage": "",
|
||||
"os_compute_api:os-server-usage": "",
|
||||
"os_compute_api:os-server-groups": "",
|
||||
|
@ -692,6 +692,12 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
||||
"os_compute_api:os-server-password:discoverable",
|
||||
"os_compute_api:os-server-usage:discoverable",
|
||||
"os_compute_api:os-server-groups:discoverable",
|
||||
"os_compute_api:os-server-tags:delete",
|
||||
"os_compute_api:os-server-tags:delete_all",
|
||||
"os_compute_api:os-server-tags:index",
|
||||
"os_compute_api:os-server-tags:show",
|
||||
"os_compute_api:os-server-tags:update",
|
||||
"os_compute_api:os-server-tags:update_all",
|
||||
"os_compute_api:os-services:discoverable",
|
||||
"os_compute_api:server-metadata:discoverable",
|
||||
"os_compute_api:servers:discoverable",
|
||||
|
@ -45,6 +45,7 @@ nova.tests.unit.api.openstack.compute.test_security_groups.TestSecurityGroupRule
|
||||
nova.tests.unit.api.openstack.compute.test_security_groups.TestSecurityGroupRulesV21
|
||||
nova.tests.unit.api.openstack.compute.test_server_actions.ServerActionsControllerTestV2
|
||||
nova.tests.unit.api.openstack.compute.test_server_actions.ServerActionsControllerTestV21
|
||||
nova.tests.unit.api.openstack.compute.test_server_tags.ServerTagsTest
|
||||
nova.tests.unit.api.openstack.compute.test_serversV21.Base64ValidationTest
|
||||
nova.tests.unit.api.openstack.compute.test_serversV21.ServersControllerCreateTest
|
||||
nova.tests.unit.api.openstack.compute.test_serversV21.ServersControllerRebuildInstanceTest
|
||||
|
Loading…
x
Reference in New Issue
Block a user