Merge "Skip additional test if 'xenapi_apis' flag is False"
diff --git a/releasenotes/notes/new-placement-client-methods-e35c473e29494928.yaml b/releasenotes/notes/new-placement-client-methods-e35c473e29494928.yaml
new file mode 100644
index 0000000..9e6d49a
--- /dev/null
+++ b/releasenotes/notes/new-placement-client-methods-e35c473e29494928.yaml
@@ -0,0 +1,11 @@
+---
+features:
+ - |
+ Add ``placement`` API methods for testing Routed Provider Networks feature.
+ The following API calls are available for tempest from now in the new
+ resource_providers_client:
+
+ * GET /resource_providers
+ * GET /resource_providers/{uuid}
+ * GET /resource_providers/{uuid}/inventories
+ * GET /resource_providers/{uuid}/aggregates
diff --git a/releasenotes/notes/tempest-victoria-release-27000c02edc5a112.yaml b/releasenotes/notes/tempest-victoria-release-27000c02edc5a112.yaml
index bb91213..574f6d9 100644
--- a/releasenotes/notes/tempest-victoria-release-27000c02edc5a112.yaml
+++ b/releasenotes/notes/tempest-victoria-release-27000c02edc5a112.yaml
@@ -1,3 +1,4 @@
+---
prelude: |
This release is to tag the Tempest for OpenStack Victoria release.
This release marks the start of Victoria release support in Tempest.
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index 37f5aec..89152d6 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -94,6 +94,16 @@
# Now boot a server with the copied flavor.
server = self.create_test_server(
wait_until='ACTIVE', flavor=flavor['id'])
+ server = self.servers_client.show_server(server['id'])['server']
+
+ # If 'id' not in server['flavor'], we can only compare the flavor
+ # details, so here we should save the to-be-deleted flavor's details,
+ # for the flavor comparison after the server resizing.
+ if not server['flavor'].get('id'):
+ pre_flavor = {}
+ body = self.flavors_client.show_flavor(flavor['id'])['flavor']
+ for key in ['name', 'ram', 'vcpus', 'disk']:
+ pre_flavor[key] = body[key]
# Delete the flavor we used to boot the instance.
self._flavor_clean_up(flavor['id'])
@@ -110,7 +120,18 @@
'ACTIVE')
server = self.servers_client.show_server(server['id'])['server']
- self.assert_flavor_equal(flavor['id'], server['flavor'])
+ if server['flavor'].get('id'):
+ msg = ('server flavor is not same as flavor!')
+ self.assertEqual(flavor['id'], server['flavor']['id'], msg)
+ else:
+ self.assertEqual(pre_flavor['name'],
+ server['flavor']['original_name'],
+ "original_name in server flavor is not same as "
+ "flavor name!")
+ for key in ['ram', 'vcpus', 'disk']:
+ msg = ('attribute %s in server flavor is not same as '
+ 'flavor!' % key)
+ self.assertEqual(pre_flavor[key], server['flavor'][key], msg)
def _test_cold_migrate_server(self, revert=False):
if CONF.compute.min_compute_nodes < 2:
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index ae7b3e4..d3dc19a 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -18,6 +18,7 @@
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
import tempest.test
CONF = config.CONF
@@ -155,6 +156,15 @@
namespace_name)
return namespace
+ @classmethod
+ def get_available_stores(cls):
+ stores = []
+ try:
+ stores = cls.client.info_stores()['stores']
+ except exceptions.NotFound:
+ pass
+ return stores
+
class BaseV2MemberImageTest(BaseV2ImageTest):
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index c1a7211..28299a4 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -20,6 +20,7 @@
from oslo_log import log as logging
from tempest.api.image import base
+from tempest.common import waiters
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -113,6 +114,95 @@
self.client.wait_for_resource_activation(image['id'])
+class MultiStoresImportImagesTest(base.BaseV2ImageTest):
+ """Test importing image in multiple stores"""
+ @classmethod
+ def skip_checks(cls):
+ super(MultiStoresImportImagesTest, cls).skip_checks()
+ if not CONF.image_feature_enabled.import_image:
+ skip_msg = (
+ "%s skipped as image import is not available" % cls.__name__)
+ raise cls.skipException(skip_msg)
+
+ @classmethod
+ def resource_setup(cls):
+ super(MultiStoresImportImagesTest, cls).resource_setup()
+ cls.available_import_methods = cls.client.info_import()[
+ 'import-methods']['value']
+ if not cls.available_import_methods:
+ raise cls.skipException('Server does not support '
+ 'any import method')
+
+ # NOTE(pdeore): Skip if glance-direct import method and mutlistore
+ # are not enabled/configured, or only one store is configured in
+ # multiple stores setup.
+ cls.available_stores = cls.get_available_stores()
+ if ('glance-direct' not in cls.available_import_methods or
+ not len(cls.available_stores) > 1):
+ raise cls.skipException(
+ 'Either glance-direct import method not present in %s or '
+ 'None or only one store is '
+ 'configured %s' % (cls.available_import_methods,
+ cls.available_stores))
+
+ def _create_and_stage_image(self, all_stores=False):
+ """Create Image & stage image file for glance-direct import method."""
+ image_name = data_utils.rand_name('test-image')
+ container_format = CONF.image.container_formats[0]
+ disk_format = CONF.image.disk_formats[0]
+ image = self.create_image(name=image_name,
+ container_format=container_format,
+ disk_format=disk_format,
+ visibility='private')
+ self.assertEqual('queued', image['status'])
+
+ self.client.stage_image_file(
+ image['id'],
+ six.BytesIO(data_utils.random_bytes(10485760)))
+ # Check image status is 'uploading'
+ body = self.client.show_image(image['id'])
+ self.assertEqual(image['id'], body['id'])
+ self.assertEqual('uploading', body['status'])
+
+ if all_stores:
+ stores_list = ','.join([store['id']
+ for store in self.available_stores])
+ else:
+ stores = [store['id'] for store in self.available_stores]
+ stores_list = stores[::len(stores) - 1]
+
+ return body, stores_list
+
+ @decorators.idempotent_id('bf04ff00-3182-47cb-833a-f1c6767b47fd')
+ def test_glance_direct_import_image_to_all_stores(self):
+ """Test image is imported in all available stores
+
+ Create image, import image to all available stores using glance-direct
+ import method and verify that import succeeded.
+ """
+ image, stores = self._create_and_stage_image(all_stores=True)
+
+ self.client.image_import(
+ image['id'], method='glance-direct', all_stores=True)
+
+ waiters.wait_for_image_imported_to_stores(self.client,
+ image['id'], stores)
+
+ @decorators.idempotent_id('82fb131a-dd2b-11ea-aec7-340286b6c574')
+ def test_glance_direct_import_image_to_specific_stores(self):
+ """Test image is imported in all available stores
+
+ Create image, import image to specified store(s) using glance-direct
+ import method and verify that import succeeded.
+ """
+ image, stores = self._create_and_stage_image()
+ self.client.image_import(image['id'], method='glance-direct',
+ stores=stores)
+
+ waiters.wait_for_image_imported_to_stores(self.client, image['id'],
+ (','.join(stores)))
+
+
class BasicOperationsImagesTest(base.BaseV2ImageTest):
"""Here we test the basic operations of images"""
diff --git a/tempest/clients.py b/tempest/clients.py
index 8363a8d..6d19a0c 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -44,7 +44,7 @@
self._set_object_storage_clients()
self._set_image_clients()
self._set_network_clients()
- self.placement_client = self.placement.PlacementClient()
+ self._set_placement_clients()
# TODO(andreaf) This is maintained for backward compatibility
# with plugins, but it should removed eventually, since it was
# never a stable interface and it's not useful anyways
@@ -139,6 +139,11 @@
self.snapshots_extensions_client = self.compute.SnapshotsClient(
**params_volume)
+ def _set_placement_clients(self):
+ self.placement_client = self.placement.PlacementClient()
+ self.resource_providers_client = \
+ self.placement.ResourceProvidersClient()
+
def _set_identity_clients(self):
# Clients below use the admin endpoint type of Keystone API v2
params_v2_admin = {
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index fc25914..cc8778b 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -187,6 +187,28 @@
raise lib_exc.TimeoutException(message)
+def wait_for_image_imported_to_stores(client, image_id, stores):
+ """Waits for an image to be imported to all requested stores.
+
+ The client should also have build_interval and build_timeout attributes.
+ """
+
+ start = int(time.time())
+ while int(time.time()) - start < client.build_timeout:
+ image = client.show_image(image_id)
+ if image['status'] == 'active' and image['stores'] == stores:
+ return
+
+ time.sleep(client.build_interval)
+
+ message = ('Image %(image_id)s failed to import '
+ 'on stores: %s' % str(image['os_glance_failed_import']))
+ caller = test_utils.find_test_caller()
+ if caller:
+ message = '(%s) %s' % (caller, message)
+ raise lib_exc.TimeoutException(message)
+
+
def wait_for_volume_resource_status(client, resource_id, status):
"""Waits for a volume resource to reach a given status.
diff --git a/tempest/config.py b/tempest/config.py
index 5b7efa5..2f2c2e9 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1199,7 +1199,7 @@
The best use case is investigating used resources of one test.
A test can be run as follows:
- $ ostestr --pdb TEST_ID
+ $ stestr run --pdb TEST_ID
or
$ python -m testtools.run TEST_ID"""),
]
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 71ecb32..b34066f 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -16,6 +16,7 @@
import argparse
import ast
+import contextlib
import importlib
import inspect
import os
@@ -28,7 +29,7 @@
DECORATOR_MODULE = 'decorators'
DECORATOR_NAME = 'idempotent_id'
-DECORATOR_IMPORT = 'tempest.%s' % DECORATOR_MODULE
+DECORATOR_IMPORT = 'tempest.lib.%s' % DECORATOR_MODULE
IMPORT_LINE = 'from tempest.lib import %s' % DECORATOR_MODULE
DECORATOR_TEMPLATE = "@%s.%s('%%s')" % (DECORATOR_MODULE,
DECORATOR_NAME)
@@ -180,34 +181,125 @@
elif isinstance(node, ast.ImportFrom):
return '%s.%s' % (node.module, node.names[0].name)
+ @contextlib.contextmanager
+ def ignore_site_packages_paths(self):
+ """Removes site-packages directories from the sys.path
+
+ Source:
+ - StackOverflow: https://stackoverflow.com/questions/22195382/
+ - Author: https://stackoverflow.com/users/485844/
+ """
+
+ paths = sys.path
+ # remove all third-party paths
+ # so that only stdlib imports will succeed
+ sys.path = list(filter(
+ None,
+ filter(lambda i: 'site-packages' not in i, sys.path)
+ ))
+ yield
+ sys.path = paths
+
+ def is_std_lib(self, module):
+ """Checks whether the module is part of the stdlib or not
+
+ Source:
+ - StackOverflow: https://stackoverflow.com/questions/22195382/
+ - Author: https://stackoverflow.com/users/485844/
+ """
+
+ if module in sys.builtin_module_names:
+ return True
+
+ with self.ignore_site_packages_paths():
+ imported_module = sys.modules.pop(module, None)
+ try:
+ importlib.import_module(module)
+ except ImportError:
+ return False
+ else:
+ return True
+ finally:
+ if imported_module:
+ sys.modules[module] = imported_module
+
def _add_import_for_test_uuid(self, patcher, src_parsed, source_path):
- with open(source_path) as f:
- src_lines = f.read().split('\n')
- line_no = 0
- tempest_imports = [node for node in src_parsed.body
+ import_list = [node for node in src_parsed.body
+ if isinstance(node, ast.Import) or
+ isinstance(node, ast.ImportFrom)]
+
+ if not import_list:
+ print("(WARNING) %s: The file is not valid as it does not contain "
+ "any import line! Therefore the import needed by "
+ "@decorators.idempotent_id is not added!" % source_path)
+ return
+
+ tempest_imports = [node for node in import_list
if self._import_name(node) and
'tempest.' in self._import_name(node)]
- if not tempest_imports:
- import_snippet = '\n'.join(('', IMPORT_LINE, ''))
- else:
- for node in tempest_imports:
- if self._import_name(node) < DECORATOR_IMPORT:
- continue
- else:
- line_no = node.lineno
- import_snippet = IMPORT_LINE
- break
+
+ for node in tempest_imports:
+ if self._import_name(node) < DECORATOR_IMPORT:
+ continue
else:
- line_no = tempest_imports[-1].lineno
- while True:
- if (not src_lines[line_no - 1] or
- getattr(self._next_node(src_parsed.body,
- tempest_imports[-1]),
- 'lineno') == line_no or
- line_no == len(src_lines)):
- break
- line_no += 1
- import_snippet = '\n'.join((IMPORT_LINE, ''))
+ line_no = node.lineno
+ break
+ else:
+ if tempest_imports:
+ line_no = tempest_imports[-1].lineno + 1
+
+ # Insert import line between existing tempest imports
+ if tempest_imports:
+ patcher.add_patch(source_path, IMPORT_LINE, line_no)
+ return
+
+ # Group space separated imports together
+ grouped_imports = {}
+ first_import_line = import_list[0].lineno
+ for idx, import_line in enumerate(import_list, first_import_line):
+ group_no = import_line.lineno - idx
+ group = grouped_imports.get(group_no, [])
+ group.append(import_line)
+ grouped_imports[group_no] = group
+
+ if len(grouped_imports) > 3:
+ print("(WARNING) %s: The file contains more than three import "
+ "groups! This is not valid according to the PEP8 "
+ "style guide. " % source_path)
+
+ # Divide grouped_imports into groupes based on PEP8 style guide
+ pep8_groups = {}
+ package_name = self.package.__name__.split(".")[0]
+ for key in grouped_imports:
+ module = self._import_name(grouped_imports[key][0]).split(".")[0]
+ if module.startswith(package_name):
+ group = pep8_groups.get('3rd_group', [])
+ pep8_groups['3rd_group'] = group + grouped_imports[key]
+ elif self.is_std_lib(module):
+ group = pep8_groups.get('1st_group', [])
+ pep8_groups['1st_group'] = group + grouped_imports[key]
+ else:
+ group = pep8_groups.get('2nd_group', [])
+ pep8_groups['2nd_group'] = group + grouped_imports[key]
+
+ for node in pep8_groups.get('2nd_group', []):
+ if self._import_name(node) < DECORATOR_IMPORT:
+ continue
+ else:
+ line_no = node.lineno
+ import_snippet = IMPORT_LINE
+ break
+ else:
+ if pep8_groups.get('2nd_group', []):
+ line_no = pep8_groups['2nd_group'][-1].lineno + 1
+ import_snippet = IMPORT_LINE
+ elif pep8_groups.get('1st_group', []):
+ line_no = pep8_groups['1st_group'][-1].lineno + 1
+ import_snippet = '\n' + IMPORT_LINE
+ else:
+ line_no = pep8_groups['3rd_group'][0].lineno
+ import_snippet = IMPORT_LINE + '\n\n'
+
patcher.add_patch(source_path, import_snippet, line_no)
def get_tests(self):
diff --git a/tempest/lib/services/placement/__init__.py b/tempest/lib/services/placement/__init__.py
index 5c20c57..daeaeab 100644
--- a/tempest/lib/services/placement/__init__.py
+++ b/tempest/lib/services/placement/__init__.py
@@ -14,5 +14,7 @@
from tempest.lib.services.placement.placement_client import \
PlacementClient
+from tempest.lib.services.placement.resource_providers_client import \
+ ResourceProvidersClient
-__all__ = ['PlacementClient']
+__all__ = ['PlacementClient', 'ResourceProvidersClient']
diff --git a/tempest/lib/services/placement/resource_providers_client.py b/tempest/lib/services/placement/resource_providers_client.py
new file mode 100644
index 0000000..56f6409
--- /dev/null
+++ b/tempest/lib/services/placement/resource_providers_client.py
@@ -0,0 +1,82 @@
+# 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 oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib.services.placement import base_placement_client
+
+
+class ResourceProvidersClient(base_placement_client.BasePlacementClient):
+ """Client class for resource provider related methods
+
+ This client class aims to support read-only API operations for resource
+ providers. The following resources are supported:
+ * resource providers
+ * resource provider inventories
+ * resource provider aggregates
+ """
+
+ def list_resource_providers(self, **params):
+ """List resource providers.
+
+ For full list of available parameters, please refer to the official
+ API reference:
+ https://docs.openstack.org/api-ref/placement/#list-resource-providers
+ """
+ url = '/resource_providers'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_resource_provider(self, rp_uuid):
+ """Show resource provider.
+
+ For full list of available parameters, please refer to the official
+ API reference:
+ https://docs.openstack.org/api-ref/placement/#show-resource-provider
+ """
+ url = '/resource_providers/%s' % rp_uuid
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_resource_provider_inventories(self, rp_uuid):
+ """List resource provider inventories.
+
+ For full list of available parameters, please refer to the official
+ API reference:
+ https://docs.openstack.org/api-ref/placement/#list-resource-provider-inventories
+ """
+ url = '/resource_providers/%s/inventories' % rp_uuid
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_resource_provider_aggregates(self, rp_uuid):
+ """List resource provider aggregates.
+
+ For full list of available parameters, please refer to the official
+ API reference:
+ https://docs.openstack.org/api-ref/placement/#list-resource-provider-aggregates
+ """
+ url = '/resource_providers/%s/aggregates' % rp_uuid
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/lib/cmd/test_check_uuid.py b/tempest/tests/lib/cmd/test_check_uuid.py
index 28ebca1..428e047 100644
--- a/tempest/tests/lib/cmd/test_check_uuid.py
+++ b/tempest/tests/lib/cmd/test_check_uuid.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ast
import importlib
import os
import sys
@@ -95,6 +96,8 @@
class TestTestChecker(base.TestCase):
+ IMPORT_LINE = "from tempest.lib import decorators\n"
+
def _test_add_uuid_to_test(self, source_file):
class Fake_test_node():
lineno = 1
@@ -127,55 +130,69 @@
" pass")
self._test_add_uuid_to_test(source_file)
+ @staticmethod
+ def get_mocked_ast_object(lineno, col_offset, module, name, object_type):
+ ast_object = mock.Mock(spec=object_type)
+ name_obj = mock.Mock()
+ ast_object.lineno = lineno
+ ast_object.col_offset = col_offset
+ name_obj.name = name
+ ast_object.module = module
+ ast_object.names = [name_obj]
+
+ return ast_object
+
def test_add_import_for_test_uuid_no_tempest(self):
patcher = check_uuid.SourcePatcher()
checker = check_uuid.TestChecker(importlib.import_module('tempest'))
- fake_file = tempfile.NamedTemporaryFile("w+t")
+ fake_file = tempfile.NamedTemporaryFile("w+t", delete=False)
+ source_code = "from unittest import mock\n"
+ fake_file.write(source_code)
+ fake_file.close()
class Fake_src_parsed():
- body = ['test_node']
- checker._import_name = mock.Mock(return_value='fake_module')
+ body = [TestTestChecker.get_mocked_ast_object(
+ 1, 4, 'unittest', 'mock', ast.ImportFrom)]
- checker._add_import_for_test_uuid(patcher, Fake_src_parsed(),
+ checker._add_import_for_test_uuid(patcher, Fake_src_parsed,
fake_file.name)
- (patch_id, patch), = patcher.patches.items()
- self.assertEqual(patcher._quote('\n' + check_uuid.IMPORT_LINE + '\n'),
- patch)
- self.assertEqual('{%s:s}' % patch_id,
- patcher.source_files[fake_file.name])
+ patcher.apply_patches()
+
+ with open(fake_file.name, "r") as f:
+ expected_result = source_code + '\n' + TestTestChecker.IMPORT_LINE
+ self.assertTrue(expected_result == f.read())
def test_add_import_for_test_uuid_tempest(self):
patcher = check_uuid.SourcePatcher()
checker = check_uuid.TestChecker(importlib.import_module('tempest'))
fake_file = tempfile.NamedTemporaryFile("w+t", delete=False)
- test1 = (" def test_test():\n"
- " pass\n")
- test2 = (" def test_another_test():\n"
- " pass\n")
- source_code = test1 + test2
+ source_code = "from tempest import a_fake_module\n"
fake_file.write(source_code)
fake_file.close()
- def fake_import_name(node):
- return node.name
- checker._import_name = fake_import_name
+ class Fake_src_parsed:
+ body = [TestTestChecker.get_mocked_ast_object(
+ 1, 4, 'tempest', 'a_fake_module', ast.ImportFrom)]
- class Fake_node():
- def __init__(self, lineno, col_offset, name):
- self.lineno = lineno
- self.col_offset = col_offset
- self.name = name
-
- class Fake_src_parsed():
- body = [Fake_node(1, 4, 'tempest.a_fake_module'),
- Fake_node(3, 4, 'another_fake_module')]
-
- checker._add_import_for_test_uuid(patcher, Fake_src_parsed(),
+ checker._add_import_for_test_uuid(patcher, Fake_src_parsed,
fake_file.name)
- (patch_id, patch), = patcher.patches.items()
- self.assertEqual(patcher._quote(check_uuid.IMPORT_LINE + '\n'),
- patch)
- expected_source = patcher._quote(test1) + '{' + patch_id + ':s}' +\
- patcher._quote(test2)
- self.assertEqual(expected_source,
- patcher.source_files[fake_file.name])
+ patcher.apply_patches()
+
+ with open(fake_file.name, "r") as f:
+ expected_result = source_code + TestTestChecker.IMPORT_LINE
+ self.assertTrue(expected_result == f.read())
+
+ def test_add_import_no_import(self):
+ patcher = check_uuid.SourcePatcher()
+ patcher.add_patch = mock.Mock()
+ checker = check_uuid.TestChecker(importlib.import_module('tempest'))
+ fake_file = tempfile.NamedTemporaryFile("w+t", delete=False)
+ fake_file.close()
+
+ class Fake_src_parsed:
+ body = []
+
+ checker._add_import_for_test_uuid(patcher, Fake_src_parsed,
+ fake_file.name)
+
+ self.assertTrue(not patcher.add_patch.called)
diff --git a/tempest/tests/lib/services/identity/v3/test_endpoints_client.py b/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
index ca15dd1..0efc462 100644
--- a/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
@@ -54,12 +54,44 @@
}
FAKE_SERVICE_ID = "a4dc5060-f757-4662-b658-edd2aefbb41d"
+ FAKE_ENDPOINT_ID = "b335d394-cdb9-4519-b95d-160b7706e54ew"
+
+ FAKE_UPDATE_ENDPOINT = {
+ "endpoint": {
+ "id": "828384",
+ "interface": "internal",
+ "links": {
+ "self": "http://example.com/identity/v3/"
+ "endpoints/828384"
+ },
+ "region_id": "north",
+ "service_id": "686766",
+ "url": "http://example.com/identity/v3/"
+ "endpoints/828384"
+ }
+ }
+
+ FAKE_SHOW_ENDPOINT = {
+ "endpoint": {
+ "enabled": True,
+ "id": "01c3d5b92f7841ac83fb4b26173c12c7",
+ "interface": "admin",
+ "links": {
+ "self": "http://example.com/identity/v3/"
+ "endpoints/828384"
+ },
+ "region": "RegionOne",
+ "region_id": "RegionOne",
+ "service_id": "3b2d6ad7e02c4cde8498a547601f1b8f",
+ "url": "http://23.253.211.234:9696/"
+ }
+ }
def setUp(self):
super(TestEndpointsClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
- self.client = endpoints_client.EndPointsClient(fake_auth,
- 'identity', 'regionOne')
+ self.client = endpoints_client.EndPointsClient(
+ fake_auth, 'identity', 'regionOne')
def _test_create_endpoint(self, bytes_body=False):
self.check_service_client_function(
@@ -84,6 +116,38 @@
mock_args=[mock_args],
**params)
+ def _test_update_endpoint(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_endpoint,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_UPDATE_ENDPOINT,
+ bytes_body,
+ endpoint_id=self.FAKE_ENDPOINT_ID,
+ interface="public",
+ region_id="north",
+ url="http://example.com/identity/v3/endpoints/828384",
+ service_id=self.FAKE_SERVICE_ID)
+
+ def _test_show_endpoint(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_endpoint,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_SHOW_ENDPOINT,
+ bytes_body,
+ endpoint_id="3456")
+
+ def test_update_endpoint_with_str_body(self):
+ self._test_update_endpoint()
+
+ def test_update_endpoint_with_bytes_body(self):
+ self._test_update_endpoint(bytes_body=True)
+
+ def test_show_endpoint_with_str_body(self):
+ self._test_show_endpoint()
+
+ def test_show_endpoint_with_bytes_body(self):
+ self._test_show_endpoint(bytes_body=True)
+
def test_create_endpoint_with_str_body(self):
self._test_create_endpoint()
diff --git a/tempest/tests/lib/services/network/test_networks_client.py b/tempest/tests/lib/services/network/test_networks_client.py
index 078f4b0..17233bc 100644
--- a/tempest/tests/lib/services/network/test_networks_client.py
+++ b/tempest/tests/lib/services/network/test_networks_client.py
@@ -31,12 +31,17 @@
"nova"
],
"created_at": "2016-03-08T20:19:41",
+ "dns_domain": "my-domain.org.",
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
+ "ipv4_address_scope": None,
+ "ipv6_address_scope": None,
+ "l2_adjacency": False,
"mtu": 0,
"name": "net1",
"port_security_enabled": True,
"project_id": "4fd44f30292945e481c7b8a0c8908869",
"qos_policy_id": "6a8454ade84346f59e8d40665f878b2e",
+ "revision_number": 1,
"router:external": False,
"shared": False,
"status": "ACTIVE",
@@ -46,7 +51,8 @@
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
"updated_at": "2016-03-08T20:19:41",
"vlan_transparent": True,
- "description": ""
+ "description": "",
+ "is_default": False
},
{
"admin_state_up": True,
@@ -54,12 +60,18 @@
"availability_zones": [
"nova"
],
+ "created_at": "2016-03-08T20:19:41",
+ "dns_domain": "my-domain.org.",
"id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
+ "ipv4_address_scope": None,
+ "ipv6_address_scope": None,
+ "l2_adjacency": False,
"mtu": 0,
"name": "net2",
"port_security_enabled": True,
"project_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
"qos_policy_id": "bfdb6c39f71e4d44b1dfbda245c50819",
+ "revision_number": 3,
"router:external": False,
"shared": False,
"status": "ACTIVE",
@@ -69,7 +81,8 @@
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
"updated_at": "2016-03-08T20:19:41",
"vlan_transparent": False,
- "description": ""
+ "description": "",
+ "is_default": False
}
]
}
@@ -108,6 +121,7 @@
"alive": True,
"topic": "dhcp_agent",
"host": "osboxes",
+ "ha_state": None,
"agent_type": "DHCP agent",
"resource_versions": {},
"created_at": "2017-06-19 21:39:51",
diff --git a/tempest/tests/lib/services/placement/test_resource_providers_client.py b/tempest/tests/lib/services/placement/test_resource_providers_client.py
new file mode 100644
index 0000000..11aeaf2
--- /dev/null
+++ b/tempest/tests/lib/services/placement/test_resource_providers_client.py
@@ -0,0 +1,119 @@
+# 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 tempest.lib.services.placement import resource_providers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestResourceProvidersClient(base.BaseServiceTest):
+ FAKE_RESOURCE_PROVIDER_UUID = '3722a86e-a563-11e9-9abb-c3d41b6d3abf'
+ FAKE_ROOT_PROVIDER_UUID = '4a6a57c8-a563-11e9-914e-f3e0478fce53'
+ FAKE_RESOURCE_PROVIDER = {
+ 'generation': 0,
+ 'name': 'Ceph Storage Pool',
+ 'uuid': FAKE_RESOURCE_PROVIDER_UUID,
+ 'parent_provider_uuid': FAKE_ROOT_PROVIDER_UUID,
+ 'root_provider_uuid': FAKE_ROOT_PROVIDER_UUID
+ }
+
+ FAKE_RESOURCE_PROVIDERS = {
+ 'resource_providers': [FAKE_RESOURCE_PROVIDER]
+ }
+
+ FAKE_RESOURCE_PROVIDER_INVENTORIES = {
+ 'inventories': {
+ 'DISK_GB': {
+ 'allocation_ratio': 1.0,
+ 'max_unit': 35,
+ 'min_unit': 1,
+ 'reserved': 0,
+ 'step_size': 1,
+ 'total': 35
+ }
+ },
+ 'resource_provider_generation': 7
+ }
+
+ FAKE_AGGREGATE_UUID = '1166be40-a567-11e9-9f2a-53827f9311fa'
+ FAKE_RESOURCE_PROVIDER_AGGREGATES = {
+ 'aggregates': [FAKE_AGGREGATE_UUID]
+ }
+
+ def setUp(self):
+ super(TestResourceProvidersClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = resource_providers_client.ResourceProvidersClient(
+ fake_auth, 'placement', 'regionOne')
+
+ def _test_list_resource_providers(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_resource_providers,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_RESOURCE_PROVIDERS,
+ to_utf=bytes_body,
+ status=200
+ )
+
+ def test_list_resource_providers_with_bytes_body(self):
+ self._test_list_resource_providers()
+
+ def test_list_resource_providers_with_str_body(self):
+ self._test_list_resource_providers(bytes_body=True)
+
+ def _test_show_resource_provider(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_resource_provider,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_RESOURCE_PROVIDER,
+ to_utf=bytes_body,
+ status=200,
+ rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID
+ )
+
+ def test_show_resource_provider_with_str_body(self):
+ self._test_show_resource_provider()
+
+ def test_show_resource_provider_with_bytes_body(self):
+ self._test_show_resource_provider(bytes_body=True)
+
+ def _test_list_resource_provider_inventories(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_resource_provider_inventories,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_RESOURCE_PROVIDER_INVENTORIES,
+ to_utf=bytes_body,
+ status=200,
+ rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID
+ )
+
+ def test_list_resource_provider_inventories_with_str_body(self):
+ self._test_list_resource_provider_inventories()
+
+ def test_list_resource_provider_inventories_with_bytes_body(self):
+ self._test_list_resource_provider_inventories(bytes_body=True)
+
+ def _test_list_resource_provider_aggregates(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_resource_provider_aggregates,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_RESOURCE_PROVIDER_AGGREGATES,
+ to_utf=bytes_body,
+ status=200,
+ rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID
+ )
+
+ def test_list_resource_provider_aggregates_with_str_body(self):
+ self._test_list_resource_provider_aggregates()
+
+ def test_list_resource_provider_aggregates_with_bytes_body(self):
+ self._test_list_resource_provider_aggregates(bytes_body=True)