Merge "Add host validation after live migration"
diff --git a/.zuul.yaml b/.zuul.yaml
index d3cdab8..403c93d 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -698,8 +698,6 @@
irrelevant-files: *tempest-irrelevant-files
- neutron-grenade-multinode:
irrelevant-files: *tempest-irrelevant-files
- - neutron-grenade:
- irrelevant-files: *tempest-irrelevant-files
- grenade-py3:
irrelevant-files: *tempest-irrelevant-files
- devstack-plugin-ceph-tempest:
@@ -739,8 +737,6 @@
irrelevant-files: *tempest-irrelevant-files
- tempest-full:
irrelevant-files: *tempest-irrelevant-files
- - neutron-grenade:
- irrelevant-files: *tempest-irrelevant-files
- grenade-py3:
irrelevant-files: *tempest-irrelevant-files
- tempest-ipv6-only:
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
index 423214d..e51b90b 100644
--- a/doc/source/overview.rst
+++ b/doc/source/overview.rst
@@ -116,7 +116,7 @@
$ stestr run --black-regex '\[.*\bslow\b.*\]' '^tempest\.(api|scenario)'
will run the same set of tests as the default gate jobs. Or you can
- use `unittest`_ compatible test runners such as `testr`_, `pytest`_ etc.
+ use `unittest`_ compatible test runners such as `stestr`_, `pytest`_ etc.
Tox also contains several existing job configurations. For example::
@@ -130,7 +130,6 @@
to run the tests tagged as smoke.
.. _unittest: https://docs.python.org/3/library/unittest.html
-.. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
.. _stestr: https://stestr.readthedocs.org/en/latest/MANUAL.html
.. _pytest: https://docs.pytest.org/en/latest/
@@ -269,14 +268,14 @@
will have a configuration file already set up to work with your
DevStack installation.
-Tempest is not tied to any single test runner, but `testr`_ is the most commonly
+Tempest is not tied to any single test runner, but `stestr`_ is the most commonly
used tool. Also, the nosetests test runner is **not** recommended to run Tempest.
After setting up your configuration file, you can execute the set of Tempest
-tests by using ``testr`` ::
+tests by using ``stestr``. By default, ``stestr`` runs tests in parallel ::
- $ testr run --parallel
+ $ stestr run
To run one single test serially ::
- $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
+ $ stestr run --serial tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
diff --git a/releasenotes/notes/Extend-cleanup-CLI-to-delete-regions-9f1dbda2c8de12e2.yaml b/releasenotes/notes/Extend-cleanup-CLI-to-delete-regions-9f1dbda2c8de12e2.yaml
new file mode 100644
index 0000000..e2fc5b3
--- /dev/null
+++ b/releasenotes/notes/Extend-cleanup-CLI-to-delete-regions-9f1dbda2c8de12e2.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ tempest cleanup CLI is extended about region deletion. Until now, the
+ regions have been neglected by tempest cleanup. From now on, tempest
+ cleanup is able to delete leftover regions as well.
diff --git a/releasenotes/notes/subunit_describe_calls-ad7df689b9d63e3f.yaml b/releasenotes/notes/subunit_describe_calls-ad7df689b9d63e3f.yaml
new file mode 100644
index 0000000..e7fc3a0
--- /dev/null
+++ b/releasenotes/notes/subunit_describe_calls-ad7df689b9d63e3f.yaml
@@ -0,0 +1,8 @@
+---
+deprecations:
+ - |
+ Deprecated command for subunit-describe-calls
+
+features:
+ - |
+ Added new tempest subcommand for subunit-describe-calls
diff --git a/setup.cfg b/setup.cfg
index 5c1d24c..1e9b8e9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -36,6 +36,7 @@
subunit-describe-calls = tempest.cmd.subunit_describe_calls:entry_point
tempest.cm =
account-generator = tempest.cmd.account_generator:TempestAccountGenerator
+ subunit-describe-calls = tempest.cmd.subunit_describe_calls:TempestSubunitDescribeCalls
init = tempest.cmd.init:TempestInit
cleanup = tempest.cmd.cleanup:TempestCleanup
list-plugins = tempest.cmd.list_plugins:TempestListPlugins
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 56b5509..10121de 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -197,14 +197,14 @@
'Ports from multiple tenants are in the list resp')
port_ids = [port['id'] for port in ports]
fixed_ips = [port['fixed_ips'] for port in ports]
- port_ips = []
- for addr in fixed_ips:
- port_ips.extend([port['ip_address'] for port in addr])
-
port_net_ids = [port['network_id'] for port in ports]
self.assertIn(port_1['port']['id'], port_ids)
- self.assertIn(port_1_fixed_ip, port_ips)
self.assertIn(network['id'], port_net_ids)
+ # Check that every port has a fixed_ip that matches the query
+ for addr in fixed_ips:
+ port_ips = [port['ip_address'] for port in addr]
+ self.assertIn(port_1_fixed_ip, port_ips,
+ 'Port not matching IP filter found')
@decorators.idempotent_id('79895408-85d5-460d-94e7-9531c5fd9123')
@testtools.skipUnless(
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index b4fb5a5..c54b16b 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -136,7 +136,10 @@
self._init_admin_ids()
# available services
- self.project_services = cleanup_service.get_project_cleanup_services()
+ self.project_associated_services = (
+ cleanup_service.get_project_associated_cleanup_services())
+ self.resource_cleanup_services = (
+ cleanup_service.get_resource_cleanup_services())
self.global_services = cleanup_service.get_global_cleanup_services()
if parsed_args.init_saved_state:
@@ -180,6 +183,10 @@
svc = service(admin_mgr, **kwargs)
svc.run()
+ for service in self.resource_cleanup_services:
+ svc = service(self.admin_mgr, **kwargs)
+ svc.run()
+
if is_dry_run:
with open(DRY_RUN_JSON, 'w+') as f:
f.write(json.dumps(self.dry_run_data, sort_keys=True,
@@ -204,7 +211,7 @@
'is_save_state': False,
'project_id': project_id,
'got_exceptions': self.GOT_EXCEPTIONS}
- for service in self.project_services:
+ for service in self.project_associated_services:
svc = service(self.admin_mgr, **kwargs)
svc.run()
@@ -269,7 +276,11 @@
svc = service(admin_mgr, **kwargs)
svc.run()
- for service in self.project_services:
+ for service in self.project_associated_services:
+ svc = service(admin_mgr, **kwargs)
+ svc.run()
+
+ for service in self.resource_cleanup_services:
svc = service(admin_mgr, **kwargs)
svc.run()
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 8b625d0..2b35ebf 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -730,6 +730,44 @@
# begin global services
+class RegionService(BaseService):
+
+ def __init__(self, manager, **kwargs):
+ super(RegionService, self).__init__(kwargs)
+ self.client = manager.regions_client
+
+ def list(self):
+ client = self.client
+ regions = client.list_regions()
+ if not self.is_save_state:
+ regions = [region for region in regions['regions'] if region['id']
+ not in self.saved_state_json['regions'].keys()]
+ return regions
+ else:
+ return regions['regions']
+
+ def delete(self):
+ client = self.client
+ regions = self.list()
+ for region in regions:
+ try:
+ client.delete_region(region['id'])
+ except Exception:
+ LOG.exception("Delete Region %s exception.", region['id'])
+
+ def dry_run(self):
+ regions = self.list()
+ self.data['regions'] = {}
+ for region in regions:
+ self.data['regions'][region['id']] = region
+
+ def save_state(self):
+ regions = self.list()
+ self.data['regions'] = {}
+ for region in regions:
+ self.data['regions'][region['id']] = region
+
+
class FlavorService(BaseService):
def __init__(self, manager, **kwargs):
super(FlavorService, self).__init__(kwargs)
@@ -968,31 +1006,52 @@
self.data['domains'][domain['id']] = domain['name']
-def get_project_cleanup_services():
- project_services = []
+def get_project_associated_cleanup_services():
+ """Returns list of project service classes.
+
+ The list contains services whose resources need to be deleted prior,
+ the project they are associated with, deletion. The resources cannot be
+ most likely deleted after the project is deleted first.
+ """
+ project_associated_services = []
# TODO(gmann): Tempest should provide some plugin hook for cleanup
# script extension to plugin tests also.
if IS_NOVA:
- project_services.append(ServerService)
- project_services.append(KeyPairService)
- project_services.append(ServerGroupService)
- project_services.append(NovaQuotaService)
- if IS_NEUTRON:
- project_services.append(NetworkFloatingIpService)
- if utils.is_extension_enabled('metering', 'network'):
- project_services.append(NetworkMeteringLabelRuleService)
- project_services.append(NetworkMeteringLabelService)
- project_services.append(NetworkRouterService)
- project_services.append(NetworkPortService)
- project_services.append(NetworkSubnetService)
- project_services.append(NetworkService)
- project_services.append(NetworkSecGroupService)
- project_services.append(NetworkSubnetPoolsService)
+ project_associated_services.append(NovaQuotaService)
if IS_CINDER:
- project_services.append(SnapshotService)
- project_services.append(VolumeService)
- project_services.append(VolumeQuotaService)
- return project_services
+ project_associated_services.append(VolumeQuotaService)
+ return project_associated_services
+
+
+def get_resource_cleanup_services():
+ """Returns list of project related classes.
+
+ The list contains services whose resources are associated with a project,
+ however, their deletion is possible also after the project is deleted
+ first.
+ """
+ resource_cleanup_services = []
+ # TODO(gmann): Tempest should provide some plugin hook for cleanup
+ # script extension to plugin tests also.
+ if IS_NOVA:
+ resource_cleanup_services.append(ServerService)
+ resource_cleanup_services.append(KeyPairService)
+ resource_cleanup_services.append(ServerGroupService)
+ if IS_NEUTRON:
+ resource_cleanup_services.append(NetworkFloatingIpService)
+ if utils.is_extension_enabled('metering', 'network'):
+ resource_cleanup_services.append(NetworkMeteringLabelRuleService)
+ resource_cleanup_services.append(NetworkMeteringLabelService)
+ resource_cleanup_services.append(NetworkRouterService)
+ resource_cleanup_services.append(NetworkPortService)
+ resource_cleanup_services.append(NetworkSubnetService)
+ resource_cleanup_services.append(NetworkService)
+ resource_cleanup_services.append(NetworkSecGroupService)
+ resource_cleanup_services.append(NetworkSubnetPoolsService)
+ if IS_CINDER:
+ resource_cleanup_services.append(SnapshotService)
+ resource_cleanup_services.append(VolumeService)
+ return resource_cleanup_services
def get_global_cleanup_services():
@@ -1005,4 +1064,5 @@
global_services.append(ProjectService)
global_services.append(DomainService)
global_services.append(RoleService)
+ global_services.append(RegionService)
return global_services
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
index 081fa7a..e029538 100644
--- a/tempest/cmd/subunit_describe_calls.py
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -81,13 +81,19 @@
import os
import re
import sys
+import traceback
+from cliff.command import Command
from oslo_serialization import jsonutils as json
import subunit
import testtools
+DESCRIPTION = "Outputs all HTTP calls a given test made that were logged."
+
+
class UrlParser(testtools.TestResult):
+
uuid_re = re.compile(r'(^|[^0-9a-f])[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-'
'[0-9a-f]{4}-[0-9a-f]{12}([^0-9a-f]|$)')
id_re = re.compile(r'(^|[^0-9a-z])[0-9a-z]{8}[0-9a-z]{4}[0-9a-z]{4}'
@@ -241,33 +247,12 @@
class ArgumentParser(argparse.ArgumentParser):
+
def __init__(self):
- desc = "Outputs all HTTP calls a given test made that were logged."
+ desc = DESCRIPTION
super(ArgumentParser, self).__init__(description=desc)
-
self.prog = "subunit-describe-calls"
-
- self.add_argument(
- "-s", "--subunit", metavar="<subunit file>",
- nargs="?", type=argparse.FileType('rb'), default=sys.stdin,
- help="The path to the subunit output file.")
-
- self.add_argument(
- "-n", "--non-subunit-name", metavar="<non subunit name>",
- default="pythonlogging",
- help="The name used in subunit to describe the file contents.")
-
- self.add_argument(
- "-o", "--output-file", metavar="<output file>", default=None,
- help="The output file name for the json.")
-
- self.add_argument(
- "-p", "--ports", metavar="<ports file>", default=None,
- help="A JSON file describing the ports for each service.")
-
- self.add_argument(
- "-v", "--verbose", action='store_true', default=False,
- help="Add Request and Response header and body data to stdout.")
+ _parser_add_args(self)
def parse(stream, non_subunit_name, ports):
@@ -321,11 +306,63 @@
sys.stdout.write('\n')
-def entry_point():
- cl_args = ArgumentParser().parse_args()
+def entry_point(cl_args=None):
+ print('Running subunit_describe_calls ...')
+ if not cl_args:
+ print("Use of: 'subunit-describe-calls' is deprecated, "
+ "please use: 'tempest subunit-describe-calls'")
+ cl_args = ArgumentParser().parse_args()
parser = parse(cl_args.subunit, cl_args.non_subunit_name, cl_args.ports)
output(parser, cl_args.output_file, cl_args.verbose)
+def _parser_add_args(parser):
+ parser.add_argument(
+ "-s", "--subunit", metavar="<subunit file>",
+ nargs="?", type=argparse.FileType('rb'), default=sys.stdin,
+ help="The path to the subunit output file(default:stdin v1/v2 stream)"
+ )
+
+ parser.add_argument(
+ "-n", "--non-subunit-name", metavar="<non subunit name>",
+ default="pythonlogging",
+ help="The name used in subunit to describe the file contents."
+ )
+
+ parser.add_argument(
+ "-o", "--output-file", metavar="<output file>", default=None,
+ help="The output file name for the json."
+ )
+
+ parser.add_argument(
+ "-p", "--ports", metavar="<ports file>", default=None,
+ help="A JSON file describing the ports for each service."
+ )
+
+ parser.add_argument(
+ "-v", "--verbose", action='store_true', default=False,
+ help="Add Request and Response header and body data to stdout."
+ )
+
+
+class TempestSubunitDescribeCalls(Command):
+
+ def get_parser(self, prog_name):
+ parser = super(TempestSubunitDescribeCalls, self).get_parser(prog_name)
+ _parser_add_args(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ try:
+ entry_point(parsed_args)
+
+ except Exception:
+ traceback.print_exc()
+ raise
+
+ def get_description(self):
+ return DESCRIPTION
+
+
if __name__ == "__main__":
entry_point()
diff --git a/tempest/tests/cmd/test_cleanup_services.py b/tempest/tests/cmd/test_cleanup_services.py
index de0dbec..ae08d02 100644
--- a/tempest/tests/cmd/test_cleanup_services.py
+++ b/tempest/tests/cmd/test_cleanup_services.py
@@ -175,7 +175,8 @@
"ports": {u'aa74aa4v-741a': u'saved-port'},
"security_groups": {u'7q844add-3697': u'saved-sec-group'},
"subnets": {u'55ttda4a-2584': u'saved-subnet'},
- "subnetpools": {u'8acf64c1-43fc': u'saved-subnet-pool'}
+ "subnetpools": {u'8acf64c1-43fc': u'saved-subnet-pool'},
+ "regions": {u'RegionOne': {}}
}
# Mocked methods
get_method = 'tempest.lib.common.rest_client.RestClient.get'
@@ -1202,6 +1203,57 @@
# begin global services
+class TestRegionService(BaseCmdServiceTests):
+ service_class = 'RegionService'
+ service_name = 'regions'
+ response = {
+ "regions": [{
+ "parent_region_id": None,
+ "id": "RegionOne",
+ "links": {
+ "self":
+ "http://10.0.145.61:5000/v3/regions/RegionOne"
+ },
+ "description": ""
+ },
+ {
+ "parent_region_id": None,
+ "id": "RegionTwo",
+ "links": {
+ "self":
+ "http://10.0.145.61:5000/v3/regions/RegionTwo"
+ },
+ "description": ""
+ }],
+ "links": {
+ "self":
+ "http://10.0.145.61:5000/v3/regions",
+ "next": None,
+ "previous": None
+ }
+ }
+
+ def test_delete_pass(self):
+ delete_mock = [(self.get_method, self.response, 200),
+ (self.delete_method, None, 204),
+ (self.log_method, "exception", None)]
+ self._test_delete(delete_mock)
+
+ def test_delete_fail(self):
+ delete_mock = [(self.get_method, self.response, 200),
+ (self.delete_method, 'error', None),
+ (self.log_method, "exception", None)]
+ self._test_delete(delete_mock, fail=True)
+
+ def test_dry_run(self):
+ dry_mock = [(self.get_method, self.response, 200),
+ (self.delete_method, "delete", None)]
+ self._test_dry_run_true(dry_mock)
+
+ def test_save_state(self):
+ self._test_saved_state_true([(self.get_method, self.response, 200)])
+
+
class TestDomainService(BaseCmdServiceTests):
service_class = 'DomainService'
diff --git a/tempest/tests/lib/services/identity/v3/test_application_credentials_client.py b/tempest/tests/lib/services/identity/v3/test_application_credentials_client.py
index 9bf9b68..2774c44 100644
--- a/tempest/tests/lib/services/identity/v3/test_application_credentials_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_application_credentials_client.py
@@ -146,7 +146,7 @@
def test_list_application_credential_with_bytes_body(self):
self._test_list_app_creds(bytes_body=True)
- def test_delete_trust(self):
+ def test_delete_application_credential(self):
self.check_service_client_function(
self.client.delete_application_credential,
'tempest.lib.common.rest_client.RestClient.delete',
diff --git a/tempest/tests/lib/services/image/v2/test_image_members_client.py b/tempest/tests/lib/services/image/v2/test_image_members_client.py
index 703b6e1..2caa567 100644
--- a/tempest/tests/lib/services/image/v2/test_image_members_client.py
+++ b/tempest/tests/lib/services/image/v2/test_image_members_client.py
@@ -27,6 +27,28 @@
"schema": "/v2/schemas/member"
}
+ FAKE_LIST_IMAGE_MEMBERS = {
+ "members": [
+ {
+ "created_at": "2013-10-07T17:58:03Z",
+ "image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
+ "member_id": "123456789",
+ "schema": "/v2/schemas/member",
+ "status": "pending",
+ "updated_at": "2013-10-07T17:58:03Z"
+ },
+ {
+ "created_at": "2013-10-07T17:58:55Z",
+ "image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
+ "member_id": "987654321",
+ "schema": "/v2/schemas/member",
+ "status": "accepted",
+ "updated_at": "2013-10-08T12:08:55Z"
+ }
+ ],
+ "schema": "/v2/schemas/members"
+ }
+
def setUp(self):
super(TestImageMembersClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -34,6 +56,14 @@
'image',
'regionOne')
+ def _test_list_image_members(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_image_members,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_IMAGE_MEMBERS,
+ bytes_body,
+ image_id="dbc999e3-c52f-4200-bedd-3b18fe7f87fe")
+
def _test_show_image_member(self, bytes_body=False):
self.check_service_client_function(
self.client.show_image_member,
@@ -62,6 +92,12 @@
member_id="8989447062e04a818baf9e073fd04fa7",
schema="/v2/schemas/member2")
+ def test_list_image_members_with_str_body(self):
+ self._test_list_image_members()
+
+ def test_list_image_members_with_bytes_body(self):
+ self._test_list_image_members(bytes_body=True)
+
def test_show_image_member_with_str_body(self):
self._test_show_image_member()
diff --git a/tempest/tests/lib/services/volume/v3/test_groups_client.py b/tempest/tests/lib/services/volume/v3/test_groups_client.py
index 5a5ae88..8a2c4ea 100644
--- a/tempest/tests/lib/services/volume/v3/test_groups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_groups_client.py
@@ -59,11 +59,11 @@
"volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
"status": "available",
"availability_zone": "az1",
- "created_at": "20127-06-20T03:50:07Z"
+ "created_at": "2017-06-20T03:50:07Z"
}
}
- FAKE_LIST_GROUPS = {
+ FAKE_LIST_GROUP_DETAILS = {
"groups": [
{
"id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
@@ -100,6 +100,19 @@
]
}
+ FAKE_LIST_GROUPS = {
+ "groups": [
+ {
+ "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+ "name": "group-001",
+ },
+ {
+ "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+ "name": "group-002",
+ }
+ ]
+ }
+
def setUp(self):
super(TestGroupsClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -123,13 +136,21 @@
bytes_body,
group_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+ def _test_list_group_details(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_groups,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_GROUP_DETAILS,
+ bytes_body,
+ detail=True)
+
def _test_list_groups(self, bytes_body=False):
self.check_service_client_function(
self.client.list_groups,
'tempest.lib.common.rest_client.RestClient.get',
self.FAKE_LIST_GROUPS,
bytes_body,
- detail=True)
+ detail=False)
def test_create_group_with_str_body(self):
self._test_create_group()
@@ -143,6 +164,12 @@
def test_show_group_with_bytes_body(self):
self._test_show_group(bytes_body=True)
+ def test_list_group_details_with_str_body(self):
+ self._test_list_group_details()
+
+ def test_list_group_details_with_bytes_body(self):
+ self._test_list_group_details(bytes_body=True)
+
def test_list_groups_with_str_body(self):
self._test_list_groups()