Adds missing server tags APIs to servers client.
Currently, there is no server tags API testing implemented in Tempest [1]. This API was introduced to Nova in 2.26 [2]. This patch adds the server tags APIs to the servers client, as well as corresponding unit test and schema changes. Mocking was added for the unit tests, because these tests explicitly require that the COMPUTE_MICROVERSION header in the base_compute_client be '2.26', or else the schema that is returned by get_schema in base_compute_client will return the v2.1 schema, resulting in validation errors. [1] https://github.com/openstack/tempest/tree/master/tempest/lib/services/compute [2] https://developer.openstack.org/api-ref/compute/ Change-Id: I06b9c6f42e066863310b4fe7ee7030c1dfb89467 Implements: blueprint missing-server-tags-api-test
This commit is contained in:
parent
0b5c2c91de
commit
7c95befefb
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add server tags APIs to the servers_client library.
|
||||
This feature enables the possibility of upating, deleting
|
||||
and checking existence of a tag on a server, as well
|
||||
as updating and deleting all tags on a server.
|
||||
|
108
tempest/api/compute/servers/test_server_tags.py
Normal file
108
tempest/api/compute/servers/test_server_tags.py
Normal file
@ -0,0 +1,108 @@
|
||||
# Copyright 2017 AT&T Corp.
|
||||
# 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.
|
||||
|
||||
import six
|
||||
|
||||
from tempest.api.compute import base
|
||||
from tempest.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest import test
|
||||
|
||||
|
||||
class ServerTagsTestJSON(base.BaseV2ComputeTest):
|
||||
|
||||
min_microversion = '2.26'
|
||||
max_microversion = 'latest'
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(ServerTagsTestJSON, cls).skip_checks()
|
||||
if not test.is_extension_enabled('os-server-tags', 'compute'):
|
||||
msg = "os-server-tags extension is not enabled."
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(ServerTagsTestJSON, cls).setup_clients()
|
||||
cls.client = cls.servers_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(ServerTagsTestJSON, cls).resource_setup()
|
||||
cls.server = cls.create_test_server(wait_until='ACTIVE')
|
||||
|
||||
def _update_server_tags(self, server_id, tags):
|
||||
if not isinstance(tags, (list, tuple)):
|
||||
tags = [tags]
|
||||
for tag in tags:
|
||||
self.client.update_tag(server_id, tag)
|
||||
self.addCleanup(self.client.delete_all_tags, server_id)
|
||||
|
||||
@decorators.idempotent_id('8d95abe2-c658-4c42-9a44-c0258500306b')
|
||||
def test_create_delete_tag(self):
|
||||
# Check that no tags exist.
|
||||
fetched_tags = self.client.list_tags(self.server['id'])['tags']
|
||||
self.assertEmpty(fetched_tags)
|
||||
|
||||
# Add server tag to the server.
|
||||
assigned_tag = data_utils.rand_name('tag')
|
||||
self._update_server_tags(self.server['id'], assigned_tag)
|
||||
|
||||
# Check that added tag exists.
|
||||
fetched_tags = self.client.list_tags(self.server['id'])['tags']
|
||||
self.assertEqual([assigned_tag], fetched_tags)
|
||||
|
||||
# Remove assigned tag from server and check that it was removed.
|
||||
self.client.delete_tag(self.server['id'], assigned_tag)
|
||||
fetched_tags = self.client.list_tags(self.server['id'])['tags']
|
||||
self.assertEmpty(fetched_tags)
|
||||
|
||||
@decorators.idempotent_id('a2c1af8c-127d-417d-974b-8115f7e3d831')
|
||||
def test_update_all_tags(self):
|
||||
# Add server tags to the server.
|
||||
tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
|
||||
self._update_server_tags(self.server['id'], tags)
|
||||
|
||||
# Replace tags with new tags and check that they are present.
|
||||
new_tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
|
||||
replaced_tags = self.client.update_all_tags(
|
||||
self.server['id'], new_tags)['tags']
|
||||
six.assertCountEqual(self, new_tags, replaced_tags)
|
||||
|
||||
# List the tags and check that the tags were replaced.
|
||||
fetched_tags = self.client.list_tags(self.server['id'])['tags']
|
||||
six.assertCountEqual(self, new_tags, fetched_tags)
|
||||
|
||||
@decorators.idempotent_id('a63b2a74-e918-4b7c-bcab-10c855f3a57e')
|
||||
def test_delete_all_tags(self):
|
||||
# Add server tags to the server.
|
||||
assigned_tags = [data_utils.rand_name('tag'),
|
||||
data_utils.rand_name('tag')]
|
||||
self._update_server_tags(self.server['id'], assigned_tags)
|
||||
|
||||
# Delete tags from the server and check that they were deleted.
|
||||
self.client.delete_all_tags(self.server['id'])
|
||||
fetched_tags = self.client.list_tags(self.server['id'])['tags']
|
||||
self.assertEmpty(fetched_tags)
|
||||
|
||||
@decorators.idempotent_id('81279a66-61c3-4759-b830-a2dbe64cbe08')
|
||||
def test_check_tag_existence(self):
|
||||
# Add server tag to the server.
|
||||
assigned_tag = data_utils.rand_name('tag')
|
||||
self._update_server_tags(self.server['id'], assigned_tag)
|
||||
|
||||
# Check that added tag exists. Throws a 404 if not found, else a 204,
|
||||
# which was already checked by the schema validation.
|
||||
self.client.check_tag_existence(self.server['id'], assigned_tag)
|
@ -1,4 +1,5 @@
|
||||
# Copyright 2016 IBM Corp.
|
||||
# Copyright 2017 AT&T Corp.
|
||||
#
|
||||
# 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
|
||||
@ -45,3 +46,41 @@ list_servers_detail['response_body']['properties']['servers']['items'][
|
||||
# list response schema wasn't changed for v2.26 so use v2.1
|
||||
|
||||
list_servers = copy.deepcopy(servers21.list_servers)
|
||||
|
||||
list_tags = {
|
||||
'status_code': [200],
|
||||
'response_body': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'tags': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': False,
|
||||
'required': ['tags']
|
||||
}
|
||||
}
|
||||
|
||||
update_all_tags = copy.deepcopy(list_tags)
|
||||
|
||||
delete_all_tags = {'status_code': [204]}
|
||||
|
||||
check_tag_existence = {'status_code': [204]}
|
||||
|
||||
update_tag = {
|
||||
'status_code': [201, 204],
|
||||
'response_header': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'location': {
|
||||
'type': 'string'
|
||||
}
|
||||
},
|
||||
'required': ['location']
|
||||
}
|
||||
}
|
||||
|
||||
delete_tag = {'status_code': [204]}
|
||||
|
@ -1,5 +1,6 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2017 AT&T Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -732,3 +733,92 @@ class ServersClient(base_compute_client.BaseComputeClient):
|
||||
self.validate_response(security_groups_schema.list_security_groups,
|
||||
resp, body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_tags(self, server_id):
|
||||
"""Lists all tags for a server.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/compute/#list-tags
|
||||
"""
|
||||
url = 'servers/%s/tags' % server_id
|
||||
resp, body = self.get(url)
|
||||
body = json.loads(body)
|
||||
schema = self.get_schema(self.schema_versions_info)
|
||||
self.validate_response(schema.list_tags, resp, body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def update_all_tags(self, server_id, tags):
|
||||
"""Replaces all tags on specified server with the new set of tags.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/compute/#replace-tags
|
||||
|
||||
:param tags: List of tags to replace current server tags with.
|
||||
"""
|
||||
url = 'servers/%s/tags' % server_id
|
||||
put_body = {'tags': tags}
|
||||
resp, body = self.put(url, json.dumps(put_body))
|
||||
body = json.loads(body)
|
||||
schema = self.get_schema(self.schema_versions_info)
|
||||
self.validate_response(schema.update_all_tags, resp, body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_all_tags(self, server_id):
|
||||
"""Deletes all tags from the specified server.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/compute/#delete-all-tags
|
||||
"""
|
||||
url = 'servers/%s/tags' % server_id
|
||||
resp, body = self.delete(url)
|
||||
schema = self.get_schema(self.schema_versions_info)
|
||||
self.validate_response(schema.delete_all_tags, resp, body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def check_tag_existence(self, server_id, tag):
|
||||
"""Checks tag existence on the server.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/compute/#check-tag-existence
|
||||
|
||||
:param tag: Check for existence of tag on specified server.
|
||||
"""
|
||||
url = 'servers/%s/tags/%s' % (server_id, tag)
|
||||
resp, body = self.get(url)
|
||||
schema = self.get_schema(self.schema_versions_info)
|
||||
self.validate_response(schema.check_tag_existence, resp, body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def update_tag(self, server_id, tag):
|
||||
"""Adds a single tag to the server if server has no specified tag.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/compute/#add-a-single-tag
|
||||
|
||||
:param tag: Tag to be added to the specified server.
|
||||
"""
|
||||
url = 'servers/%s/tags/%s' % (server_id, tag)
|
||||
resp, body = self.put(url, None)
|
||||
schema = self.get_schema(self.schema_versions_info)
|
||||
self.validate_response(schema.update_tag, resp, body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_tag(self, server_id, tag):
|
||||
"""Deletes a single tag from the specified server.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/compute/#delete-a-single-tag
|
||||
|
||||
:param tag: Tag to be removed from the specified server.
|
||||
"""
|
||||
url = 'servers/%s/tags/%s' % (server_id, tag)
|
||||
resp, body = self.delete(url)
|
||||
schema = self.get_schema(self.schema_versions_info)
|
||||
self.validate_response(schema.delete_tag, resp, body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Copyright 2015 IBM Corp.
|
||||
# Copyright 2017 AT&T Corp.
|
||||
#
|
||||
# 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
|
||||
@ -14,6 +15,9 @@
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
|
||||
from tempest.lib.services.compute import base_compute_client
|
||||
from tempest.lib.services.compute import servers_client
|
||||
from tempest.tests.lib import fake_auth_provider
|
||||
from tempest.tests.lib.services import base
|
||||
@ -186,6 +190,9 @@ class TestServersClient(base.BaseServiceTest):
|
||||
FAKE_REBUILD_SERVER = copy.deepcopy(FAKE_SERVER_GET)
|
||||
FAKE_REBUILD_SERVER['server']['adminPass'] = 'fake-admin-pass'
|
||||
|
||||
FAKE_TAGS = ["foo", "bar"]
|
||||
REPLACE_FAKE_TAGS = ["baz", "qux"]
|
||||
|
||||
server_id = FAKE_SERVER_GET['server']['id']
|
||||
network_id = 'a6b0875b-6b5d-4a5a-81eb-0c3aa62e5fdb'
|
||||
|
||||
@ -194,6 +201,7 @@ class TestServersClient(base.BaseServiceTest):
|
||||
fake_auth = fake_auth_provider.FakeAuthProvider()
|
||||
self.client = servers_client.ServersClient(
|
||||
fake_auth, 'compute', 'regionOne')
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
|
||||
def test_list_servers_with_str_body(self):
|
||||
self._test_list_servers()
|
||||
@ -1031,3 +1039,113 @@ class TestServersClient(base.BaseServiceTest):
|
||||
{'security_groups': self.FAKE_SECURITY_GROUPS},
|
||||
server_id=self.server_id,
|
||||
)
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_list_tags_str_body(self, _):
|
||||
self._test_list_tags()
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_list_tags_byte_body(self, _):
|
||||
self._test_list_tags(bytes_body=True)
|
||||
|
||||
def _test_list_tags(self, bytes_body=False):
|
||||
expected = {"tags": self.FAKE_TAGS}
|
||||
self.check_service_client_function(
|
||||
self.client.list_tags,
|
||||
'tempest.lib.common.rest_client.RestClient.get',
|
||||
expected,
|
||||
server_id=self.server_id,
|
||||
to_utf=bytes_body)
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_update_all_tags_str_body(self, _):
|
||||
self._test_update_all_tags()
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_update_all_tags_byte_body(self, _):
|
||||
self._test_update_all_tags(bytes_body=True)
|
||||
|
||||
def _test_update_all_tags(self, bytes_body=False):
|
||||
expected = {"tags": self.REPLACE_FAKE_TAGS}
|
||||
self.check_service_client_function(
|
||||
self.client.update_all_tags,
|
||||
'tempest.lib.common.rest_client.RestClient.put',
|
||||
expected,
|
||||
server_id=self.server_id,
|
||||
tags=self.REPLACE_FAKE_TAGS,
|
||||
to_utf=bytes_body)
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_delete_all_tags(self, _):
|
||||
self.check_service_client_function(
|
||||
self.client.delete_all_tags,
|
||||
'tempest.lib.common.rest_client.RestClient.delete',
|
||||
{},
|
||||
server_id=self.server_id,
|
||||
status=204)
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_check_tag_existence_str_body(self, _):
|
||||
self._test_check_tag_existence()
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_check_tag_existence_byte_body(self, _):
|
||||
self._test_check_tag_existence(bytes_body=True)
|
||||
|
||||
def _test_check_tag_existence(self, bytes_body=False):
|
||||
self.check_service_client_function(
|
||||
self.client.check_tag_existence,
|
||||
'tempest.lib.common.rest_client.RestClient.get',
|
||||
{},
|
||||
server_id=self.server_id,
|
||||
tag=self.FAKE_TAGS[0],
|
||||
status=204,
|
||||
to_utf=bytes_body)
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_update_tag_str_body(self, _):
|
||||
self._test_update_tag()
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_update_tag_byte_body(self, _):
|
||||
self._test_update_tag(bytes_body=True)
|
||||
|
||||
def _test_update_tag(self, bytes_body=False):
|
||||
self.check_service_client_function(
|
||||
self.client.update_tag,
|
||||
'tempest.lib.common.rest_client.RestClient.put',
|
||||
{},
|
||||
server_id=self.server_id,
|
||||
tag=self.FAKE_TAGS[0],
|
||||
status=201,
|
||||
headers={'location': 'fake_location'},
|
||||
to_utf=bytes_body)
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_delete_tag_str_body(self, _):
|
||||
self._test_delete_tag()
|
||||
|
||||
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
|
||||
new_callable=mock.PropertyMock(return_value='2.26'))
|
||||
def test_delete_tag_byte_body(self, _):
|
||||
self._test_delete_tag(bytes_body=True)
|
||||
|
||||
def _test_delete_tag(self, bytes_body=False):
|
||||
self.check_service_client_function(
|
||||
self.client.delete_tag,
|
||||
'tempest.lib.common.rest_client.RestClient.delete',
|
||||
{},
|
||||
server_id=self.server_id,
|
||||
tag=self.FAKE_TAGS[0],
|
||||
status=204,
|
||||
to_utf=bytes_body)
|
||||
|
Loading…
Reference in New Issue
Block a user