Merge "Added Trove (database) version API tests"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index a1abe7f..28a4d1c 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -350,6 +350,10 @@
# API key to use when authenticating as admin. (string value)
#password=<None>
+# Domain name for authentication as admin (Keystone V3).The
+# same domain applies to user and project (string value)
+#domain_name=<None>
+
[compute-feature-enabled]
@@ -401,6 +405,14 @@
# as [nova.vnc]->vnc_enabled in nova.conf (boolean value)
#vnc_console=false
+# Enable Spice console. This configuration value should be
+# same as [nova.spice]->enabled in nova.conf (boolean value)
+#spice_console=false
+
+# Enable RDP console. This configuration value should be same
+# as [nova.rdp]->enabled in nova.conf (boolean value)
+#rdp_console=false
+
[dashboard]
@@ -520,6 +532,10 @@
# API key to use when authenticating. (string value)
#password=<None>
+# Domain name for authentication (Keystone V3).The same domain
+# applies to user and project (string value)
+#domain_name=<None>
+
# Username of alternate user to use for Nova API requests.
# (string value)
#alt_username=<None>
@@ -532,6 +548,10 @@
# (string value)
#alt_password=<None>
+# Alternate domain name for authentication (Keystone V3).The
+# same domain applies to user and project (string value)
+#alt_domain_name=<None>
+
# Administrative Username to use for Keystone API requests.
# (string value)
#admin_username=<None>
@@ -543,6 +563,10 @@
# API key to use when authenticating as admin. (string value)
#admin_password=<None>
+# Admin domain name for authentication (Keystone V3).The same
+# domain applies to user and project (string value)
+#admin_domain_name=<None>
+
[identity-feature-enabled]
diff --git a/requirements.txt b/requirements.txt
index 5e396c6..75a61e7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,7 +15,7 @@
python-heatclient>=0.2.3
python-ironicclient
python-saharaclient>=0.6.0
-python-swiftclient>=1.6
+python-swiftclient>=2.0.2
testresources>=0.2.4
keyring>=2.1
testrepository>=0.0.18
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index 3c06624..9555367 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -17,15 +17,15 @@
from tempest import test
-class AZAdminTestJSON(base.BaseV2ComputeAdminTest):
-
+class AZAdminV3Test(base.BaseComputeAdminTest):
"""
Tests Availability Zone API List
"""
+ _api_version = 3
@classmethod
def setUpClass(cls):
- super(AZAdminTestJSON, cls).setUpClass()
+ super(AZAdminV3Test, cls).setUpClass()
cls.client = cls.os_adm.availability_zone_client
@test.attr(type='gate')
@@ -44,5 +44,9 @@
self.assertTrue(len(availability_zone) > 0)
-class AZAdminTestXML(AZAdminTestJSON):
+class AZAdminV2TestJSON(AZAdminV3Test):
+ _api_version = 2
+
+
+class AZAdminV2TestXML(AZAdminV2TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index 193d415..3ba7314 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -32,15 +32,12 @@
msg = "FlavorExtraData extension not enabled."
raise cls.skipException(msg)
+ # Compute admin flavor client
cls.client = cls.os_adm.flavors_client
- admin_client = cls._get_identity_admin_client()
- cls.tenant = admin_client.get_tenant_by_name(cls.flavors_client.
- tenant_name)
- cls.tenant_id = cls.tenant['id']
- cls.adm_tenant = admin_client.get_tenant_by_name(cls.os_adm.
- flavors_client.
- tenant_name)
- cls.adm_tenant_id = cls.adm_tenant['id']
+ # Non admin tenant ID
+ cls.tenant_id = cls.flavors_client.tenant_id
+ # Compute admin tenant ID
+ cls.adm_tenant_id = cls.client.tenant_id
cls.flavor_name_prefix = 'test_flavor_access_'
cls.ram = 512
cls.vcpus = 1
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 8fe3331..73834e9 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -36,14 +36,7 @@
raise cls.skipException(msg)
cls.client = cls.os_adm.flavors_client
- admin_client = cls._get_identity_admin_client()
- cls.tenant = admin_client.get_tenant_by_name(cls.flavors_client.
- tenant_name)
- cls.tenant_id = cls.tenant['id']
- cls.adm_tenant = admin_client.get_tenant_by_name(cls.os_adm.
- flavors_client.
- tenant_name)
- cls.adm_tenant_id = cls.adm_tenant['id']
+ cls.tenant_id = cls.flavors_client.tenant_id
cls.flavor_name_prefix = 'test_flavor_access_'
cls.ram = 512
cls.vcpus = 1
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 32e0478..348666d 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -28,8 +28,7 @@
# NOTE(afazekas): these test cases should always create and use a new
# tenant most of them should be skipped if we can't do that
- cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
- 'tenantId')
+ cls.demo_tenant_id = cls.quotas_client.tenant_id
cls.default_quota_set = set(('injected_file_content_bytes',
'metadata_items', 'injected_files',
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index 5b2b5fd..e1dc685 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -33,8 +33,7 @@
# NOTE(afazekas): these test cases should always create and use a new
# tenant most of them should be skipped if we can't do that
- cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
- 'tenantId')
+ cls.demo_tenant_id = cls.client.tenant_id
@test.attr(type=['negative', 'gate'])
def test_update_quota_normal_user(self):
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 9fa07f6..8b3a0b5 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -37,10 +37,7 @@
cls.client = cls.os_adm.servers_client
cls.non_adm_client = cls.servers_client
cls.flavors_client = cls.os_adm.flavors_client
- cls.identity_client = cls._get_identity_admin_client()
- tenant = cls.identity_client.get_tenant_by_name(
- cls.client.tenant_name)
- cls.tenant_id = tenant['id']
+ cls.tenant_id = cls.client.tenant_id
cls.s1_name = data_utils.rand_name('server')
resp, server = cls.create_test_server(name=cls.s1_name,
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index 33cd6f3..f3a81d1 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -27,11 +27,7 @@
super(TenantUsagesTestJSON, cls).setUpClass()
cls.adm_client = cls.os_adm.tenant_usages_client
cls.client = cls.os.tenant_usages_client
- cls.identity_client = cls._get_identity_admin_client()
-
- resp, tenants = cls.identity_client.list_tenants()
- cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- cls.client.tenant_name][0]
+ cls.tenant_id = cls.client.tenant_id
# Create a server in the demo tenant
resp, server = cls.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
index a080f2e..d69c43c 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
@@ -52,11 +52,9 @@
params = {'start': self.end,
'end': self.start}
resp, tenants = self.identity_client.list_tenants()
- tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- self.client.tenant_name][0]
self.assertRaises(exceptions.BadRequest,
self.adm_client.get_tenant_usage,
- tenant_id, params)
+ self.client.tenant_id, params)
@test.attr(type=['negative', 'gate'])
def test_list_usage_all_tenants_with_non_admin_user(self):
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index edeb2fc..44340c3 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -38,6 +38,8 @@
cls.set_network_resources()
super(BaseComputeTest, cls).setUpClass()
+ # TODO(andreaf) WE should care also for the alt_manager here
+ # but only once client lazy load in the manager is done
os = cls.get_client_manager()
cls.os = os
@@ -173,7 +175,6 @@
pass
except Exception:
LOG.exception('Exception raised deleting image %s' % image_id)
- pass
@classmethod
def clear_security_groups(cls):
@@ -188,7 +189,6 @@
LOG.info('Exception raised deleting security group %s',
sg['id'])
LOG.exception(exc)
- pass
@classmethod
def tearDownClass(cls):
@@ -320,7 +320,6 @@
cls.servers_client.wait_for_server_termination(server_id)
except Exception:
LOG.exception('Failed to delete server %s' % server_id)
- pass
resp, server = cls.create_test_server(wait_until='ACTIVE', **kwargs)
if cls._api_version == 2:
cls.password = server['adminPass']
@@ -342,68 +341,51 @@
_interface = "json"
-class BaseV2ComputeAdminTest(BaseV2ComputeTest):
- """Base test case class for Compute Admin V2 API tests."""
-
- @classmethod
- def setUpClass(cls):
- super(BaseV2ComputeAdminTest, cls).setUpClass()
- admin_username = CONF.compute_admin.username
- admin_password = CONF.compute_admin.password
- admin_tenant = CONF.compute_admin.tenant_name
- if not (admin_username and admin_password and admin_tenant):
- msg = ("Missing Compute Admin API credentials "
- "in configuration.")
- raise cls.skipException(msg)
- if (CONF.compute.allow_tenant_isolation or
- cls.force_tenant_isolation is True):
- creds = cls.isolated_creds.get_admin_creds()
- admin_username, admin_tenant_name, admin_password = creds
- cls.os_adm = clients.Manager(username=admin_username,
- password=admin_password,
- tenant_name=admin_tenant_name,
- interface=cls._interface)
- else:
- cls.os_adm = clients.ComputeAdminManager(interface=cls._interface)
-
-
class BaseV3ComputeTest(BaseComputeTest):
_api_version = 3
_interface = "json"
-class BaseV3ComputeAdminTest(BaseV3ComputeTest):
- """Base test case class for all Compute Admin API V3 tests."""
+class BaseComputeAdminTest(BaseComputeTest):
+ """Base test case class for Compute Admin API tests."""
+ _interface = "json"
@classmethod
def setUpClass(cls):
- super(BaseV3ComputeAdminTest, cls).setUpClass()
- admin_username = CONF.compute_admin.username
- admin_password = CONF.compute_admin.password
- admin_tenant = CONF.compute_admin.tenant_name
- if not (admin_username and admin_password and admin_tenant):
- msg = ("Missing Compute Admin API credentials "
- "in configuration.")
- raise cls.skipException(msg)
- if CONF.compute.allow_tenant_isolation:
+ super(BaseComputeAdminTest, cls).setUpClass()
+ if (CONF.compute.allow_tenant_isolation or
+ cls.force_tenant_isolation is True):
creds = cls.isolated_creds.get_admin_creds()
- admin_username, admin_tenant_name, admin_password = creds
- os_adm = clients.Manager(username=admin_username,
- password=admin_password,
- tenant_name=admin_tenant_name,
- interface=cls._interface)
+ cls.os_adm = clients.Manager(credentials=creds,
+ interface=cls._interface)
else:
- os_adm = clients.ComputeAdminManager(interface=cls._interface)
+ try:
+ cls.os_adm = clients.ComputeAdminManager(
+ interface=cls._interface)
+ except exceptions.InvalidCredentials:
+ msg = ("Missing Compute Admin API credentials "
+ "in configuration.")
+ raise cls.skipException(msg)
- cls.os_adm = os_adm
- cls.servers_admin_client = cls.os_adm.servers_v3_client
- cls.services_admin_client = cls.os_adm.services_v3_client
- cls.availability_zone_admin_client = \
- cls.os_adm.availability_zone_v3_client
- cls.hypervisor_admin_client = cls.os_adm.hypervisor_v3_client
- cls.flavors_admin_client = cls.os_adm.flavors_v3_client
- cls.aggregates_admin_client = cls.os_adm.aggregates_v3_client
- cls.hosts_admin_client = cls.os_adm.hosts_v3_client
- cls.quotas_admin_client = cls.os_adm.quotas_v3_client
- cls.agents_admin_client = cls.os_adm.agents_v3_client
- cls.migrations_admin_client = cls.os_adm.migrations_v3_client
+ if cls._api_version == 3:
+ cls.servers_admin_client = cls.os_adm.servers_v3_client
+ cls.services_admin_client = cls.os_adm.services_v3_client
+ cls.availability_zone_admin_client = \
+ cls.os_adm.availability_zone_v3_client
+ cls.hypervisor_admin_client = cls.os_adm.hypervisor_v3_client
+ cls.flavors_admin_client = cls.os_adm.flavors_v3_client
+ cls.aggregates_admin_client = cls.os_adm.aggregates_v3_client
+ cls.hosts_admin_client = cls.os_adm.hosts_v3_client
+ cls.quotas_admin_client = cls.os_adm.quotas_v3_client
+ cls.agents_admin_client = cls.os_adm.agents_v3_client
+ cls.migrations_admin_client = cls.os_adm.migrations_v3_client
+
+
+class BaseV2ComputeAdminTest(BaseComputeAdminTest):
+ """Base test case class for Compute Admin V2 API tests."""
+ _api_version = 2
+
+
+class BaseV3ComputeAdminTest(BaseComputeAdminTest):
+ """Base test case class for Compute Admin V3 API tests."""
+ _api_version = 3
diff --git a/tempest/api/compute/certificates/test_certificates.py b/tempest/api/compute/certificates/test_certificates.py
index f6cadf7..0f921c5 100644
--- a/tempest/api/compute/certificates/test_certificates.py
+++ b/tempest/api/compute/certificates/test_certificates.py
@@ -17,13 +17,14 @@
from tempest import test
-class CertificatesTestJSON(base.BaseV2ComputeTest):
+class CertificatesV3Test(base.BaseComputeTest):
+
+ _api_version = 3
@test.attr(type='gate')
def test_create_root_certificate(self):
# create certificates
resp, body = self.certificates_client.create_certificate()
- self.assertEqual(200, resp.status)
self.assertIn('data', body)
self.assertIn('private_key', body)
@@ -36,5 +37,9 @@
self.assertIn('private_key', body)
-class CertificatesTestXML(CertificatesTestJSON):
+class CertificatesV2TestJSON(CertificatesV3Test):
+ _api_version = 2
+
+
+class CertificatesV2TestXML(CertificatesV2TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index 6e202f6..bfebb5e 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -17,11 +17,15 @@
from tempest import test
-class FlavorsTestJSON(base.BaseV2ComputeTest):
+class FlavorsV3Test(base.BaseComputeTest):
+
+ _api_version = 3
+ _min_disk = 'min_disk'
+ _min_ram = 'min_ram'
@classmethod
def setUpClass(cls):
- super(FlavorsTestJSON, cls).setUpClass()
+ super(FlavorsV3Test, cls).setUpClass()
cls.client = cls.flavors_client
@test.attr(type='smoke')
@@ -89,7 +93,7 @@
flavors = sorted(flavors, key=lambda k: k['disk'])
flavor_id = flavors[0]['id']
- params = {'minDisk': flavors[0]['disk'] + 1}
+ params = {self._min_disk: flavors[0]['disk'] + 1}
resp, flavors = self.client.list_flavors_with_detail(params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
@@ -100,7 +104,7 @@
flavors = sorted(flavors, key=lambda k: k['ram'])
flavor_id = flavors[0]['id']
- params = {'minRam': flavors[0]['ram'] + 1}
+ params = {self._min_ram: flavors[0]['ram'] + 1}
resp, flavors = self.client.list_flavors_with_detail(params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
@@ -111,7 +115,7 @@
flavors = sorted(flavors, key=lambda k: k['disk'])
flavor_id = flavors[0]['id']
- params = {'minDisk': flavors[0]['disk'] + 1}
+ params = {self._min_disk: flavors[0]['disk'] + 1}
resp, flavors = self.client.list_flavors(params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
@@ -122,10 +126,17 @@
flavors = sorted(flavors, key=lambda k: k['ram'])
flavor_id = flavors[0]['id']
- params = {'minRam': flavors[0]['ram'] + 1}
+ params = {self._min_ram: flavors[0]['ram'] + 1}
resp, flavors = self.client.list_flavors(params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
-class FlavorsTestXML(FlavorsTestJSON):
+class FlavorsV2TestJSON(FlavorsV3Test):
+
+ _api_version = 2
+ _min_disk = 'minDisk'
+ _min_ram = 'minRam'
+
+
+class FlavorsV2TestXML(FlavorsV2TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 67fafed..01979c0 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -18,16 +18,17 @@
from tempest import test
-class KeyPairsTestJSON(base.BaseV2ComputeTest):
+class KeyPairsV3Test(base.BaseComputeTest):
+
+ _api_version = 3
@classmethod
def setUpClass(cls):
- super(KeyPairsTestJSON, cls).setUpClass()
+ super(KeyPairsV3Test, cls).setUpClass()
cls.client = cls.keypairs_client
def _delete_keypair(self, keypair_name):
resp, _ = self.client.delete_keypair(keypair_name)
- self.assertEqual(202, resp.status)
def _create_keypair(self, keypair_name, pub_key=None):
resp, body = self.client.create_keypair(keypair_name, pub_key)
@@ -46,7 +47,6 @@
# as the keypair dicts from list API doesn't have them.
keypair.pop('private_key')
keypair.pop('user_id')
- self.assertEqual(200, resp.status)
key_list.append(keypair)
# Fetch all keypairs and verify the list
# has all created keypairs
@@ -69,7 +69,6 @@
# Keypair should be created, verified and deleted
k_name = data_utils.rand_name('keypair-')
resp, keypair = self._create_keypair(k_name)
- self.assertEqual(200, resp.status)
private_key = keypair['private_key']
key_name = keypair['name']
self.assertEqual(key_name, k_name,
@@ -108,7 +107,6 @@
"XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
"snSA8wzBx3A/8y9Pp1B nova@ubuntu")
resp, keypair = self._create_keypair(k_name, pub_key)
- self.assertEqual(200, resp.status)
self.assertFalse('private_key' in keypair,
"Field private_key is not empty!")
key_name = keypair['name']
@@ -117,5 +115,9 @@
"to the requested name!")
-class KeyPairsTestXML(KeyPairsTestJSON):
+class KeyPairsV2TestJSON(KeyPairsV3Test):
+ _api_version = 2
+
+
+class KeyPairsV2TestXML(KeyPairsV2TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 8b69c78..b55833c 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -26,10 +26,7 @@
super(ServerMetadataNegativeTestJSON, cls).setUpClass()
cls.client = cls.servers_client
cls.quotas = cls.quotas_client
- cls.admin_client = cls._get_identity_admin_client()
- resp, tenants = cls.admin_client.list_tenants()
- cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- cls.client.tenant_name][0]
+ cls.tenant_id = cls.client.tenant_id
resp, server = cls.create_test_server(meta={}, wait_until='ACTIVE')
cls.server_id = server['id']
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index c87f24e..375ddf8 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -43,10 +43,7 @@
if CONF.compute.allow_tenant_isolation:
creds = cls.isolated_creds.get_alt_creds()
- username, tenant_name, password = creds
- cls.alt_manager = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name)
+ cls.alt_manager = clients.Manager(credentials=creds)
else:
# Use the alt_XXX credentials in the config file
cls.alt_manager = clients.AltManager()
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 4db8c56..dc85e76 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -23,13 +23,8 @@
def setUpClass(cls):
super(QuotasTestJSON, cls).setUpClass()
cls.client = cls.quotas_client
- cls.admin_client = cls._get_identity_admin_client()
- resp, tenants = cls.admin_client.list_tenants()
- cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- cls.client.tenant_name][0]
- resp, users = cls.admin_client.list_users_for_tenant(cls.tenant_id)
- cls.user_id = [user['id'] for user in users if user['name'] ==
- cls.client.user][0]
+ cls.tenant_id = cls.client.tenant_id
+ cls.user_id = cls.client.user_id
cls.default_quota_set = set(('injected_file_content_bytes',
'metadata_items', 'injected_files',
'ram', 'floating_ips',
diff --git a/tempest/api/compute/v3/admin/test_availability_zone.py b/tempest/api/compute/v3/admin/test_availability_zone.py
deleted file mode 100644
index 9ca8953..0000000
--- a/tempest/api/compute/v3/admin/test_availability_zone.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2013 NEC Corporation
-# 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 tempest.api.compute import base
-from tempest.test import attr
-
-
-class AZAdminV3Test(base.BaseV3ComputeAdminTest):
-
- """
- Tests Availability Zone API List
- """
-
- @classmethod
- def setUpClass(cls):
- super(AZAdminV3Test, cls).setUpClass()
- cls.client = cls.availability_zone_admin_client
-
- @attr(type='gate')
- def test_get_availability_zone_list(self):
- # List of availability zone
- resp, availability_zone = self.client.get_availability_zone_list()
- self.assertEqual(200, resp.status)
- self.assertTrue(len(availability_zone) > 0)
-
- @attr(type='gate')
- def test_get_availability_zone_list_detail(self):
- # List of availability zones and available services
- resp, availability_zone = \
- self.client.get_availability_zone_list_detail()
- self.assertEqual(200, resp.status)
- self.assertTrue(len(availability_zone) > 0)
diff --git a/tempest/api/compute/v3/admin/test_flavors_access.py b/tempest/api/compute/v3/admin/test_flavors_access.py
index 03305ff..c641bf6 100644
--- a/tempest/api/compute/v3/admin/test_flavors_access.py
+++ b/tempest/api/compute/v3/admin/test_flavors_access.py
@@ -31,12 +31,8 @@
cls.client = cls.flavors_admin_client
admin_client = cls._get_identity_admin_client()
- cls.tenant = admin_client.get_tenant_by_name(cls.flavors_client.
- tenant_name)
- cls.tenant_id = cls.tenant['id']
- cls.adm_tenant = admin_client.get_tenant_by_name(
- cls.flavors_admin_client.tenant_name)
- cls.adm_tenant_id = cls.adm_tenant['id']
+ cls.tenant_id = cls.client.tenant_id
+ cls.adm_tenant_id = admin_client.tenant_id
cls.flavor_name_prefix = 'test_flavor_access_'
cls.ram = 512
cls.vcpus = 1
diff --git a/tempest/api/compute/v3/admin/test_flavors_access_negative.py b/tempest/api/compute/v3/admin/test_flavors_access_negative.py
index 334d124..02ecb24 100644
--- a/tempest/api/compute/v3/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/v3/admin/test_flavors_access_negative.py
@@ -33,13 +33,7 @@
super(FlavorsAccessNegativeV3Test, cls).setUpClass()
cls.client = cls.flavors_admin_client
- admin_client = cls._get_identity_admin_client()
- cls.tenant = admin_client.get_tenant_by_name(cls.flavors_client.
- tenant_name)
- cls.tenant_id = cls.tenant['id']
- cls.adm_tenant = admin_client.get_tenant_by_name(
- cls.flavors_admin_client.tenant_name)
- cls.adm_tenant_id = cls.adm_tenant['id']
+ cls.tenant_id = cls.client.tenant_id
cls.flavor_name_prefix = 'test_flavor_access_'
cls.ram = 512
cls.vcpus = 1
diff --git a/tempest/api/compute/v3/admin/test_quotas.py b/tempest/api/compute/v3/admin/test_quotas.py
index b70e254..19c31fe 100644
--- a/tempest/api/compute/v3/admin/test_quotas.py
+++ b/tempest/api/compute/v3/admin/test_quotas.py
@@ -32,8 +32,7 @@
# NOTE(afazekas): these test cases should always create and use a new
# tenant most of them should be skipped if we can't do that
- cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
- 'tenantId')
+ cls.demo_tenant_id = cls.client.tenant_id
cls.default_quota_set = set(('metadata_items',
'ram', 'floating_ips',
diff --git a/tempest/api/compute/v3/admin/test_quotas_negative.py b/tempest/api/compute/v3/admin/test_quotas_negative.py
index d138e80..307462f 100644
--- a/tempest/api/compute/v3/admin/test_quotas_negative.py
+++ b/tempest/api/compute/v3/admin/test_quotas_negative.py
@@ -30,8 +30,7 @@
# NOTE(afazekas): these test cases should always create and use a new
# tenant most of them should be skipped if we can't do that
- cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
- 'tenantId')
+ cls.demo_tenant_id = cls.isolated_creds.get_primary_creds().tenant_id
# TODO(afazekas): Add dedicated tenant to the skiped quota tests
# it can be moved into the setUpClass as well
diff --git a/tempest/api/compute/v3/admin/test_servers_negative.py b/tempest/api/compute/v3/admin/test_servers_negative.py
index fba4cd1..a46da47 100644
--- a/tempest/api/compute/v3/admin/test_servers_negative.py
+++ b/tempest/api/compute/v3/admin/test_servers_negative.py
@@ -37,10 +37,7 @@
cls.client = cls.servers_admin_client
cls.non_adm_client = cls.servers_client
cls.flavors_client = cls.flavors_admin_client
- cls.identity_client = cls._get_identity_admin_client()
- tenant = cls.identity_client.get_tenant_by_name(
- cls.client.tenant_name)
- cls.tenant_id = tenant['id']
+ cls.tenant_id = cls.client.tenant_id
cls.s1_name = data_utils.rand_name('server')
resp, server = cls.create_test_server(name=cls.s1_name,
diff --git a/tempest/api/compute/v3/certificates/__init__.py b/tempest/api/compute/v3/certificates/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/compute/v3/certificates/__init__.py
+++ /dev/null
diff --git a/tempest/api/compute/v3/certificates/test_certificates.py b/tempest/api/compute/v3/certificates/test_certificates.py
deleted file mode 100644
index 0ba44cb..0000000
--- a/tempest/api/compute/v3/certificates/test_certificates.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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 tempest.api.compute import base
-from tempest.test import attr
-
-
-class CertificatesV3Test(base.BaseV3ComputeTest):
-
- @attr(type='gate')
- def test_create_root_certificate(self):
- # create certificates
- resp, body = self.certificates_client.create_certificate()
- self.assertEqual(201, resp.status)
- self.assertIn('data', body)
- self.assertIn('private_key', body)
-
- @attr(type='gate')
- def test_get_root_certificate(self):
- # get the root certificate
- resp, body = self.certificates_client.get_certificate('root')
- self.assertEqual(200, resp.status)
- self.assertIn('data', body)
- self.assertIn('private_key', body)
diff --git a/tempest/api/compute/v3/flavors/test_flavors.py b/tempest/api/compute/v3/flavors/test_flavors.py
deleted file mode 100644
index a0bbba6..0000000
--- a/tempest/api/compute/v3/flavors/test_flavors.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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 tempest.api.compute import base
-from tempest import test
-
-
-class FlavorsV3Test(base.BaseV3ComputeTest):
-
- @classmethod
- def setUpClass(cls):
- super(FlavorsV3Test, cls).setUpClass()
- cls.client = cls.flavors_client
-
- @test.attr(type='smoke')
- def test_list_flavors(self):
- # List of all flavors should contain the expected flavor
- resp, flavors = self.client.list_flavors()
- resp, flavor = self.client.get_flavor_details(self.flavor_ref)
- flavor_min_detail = {'id': flavor['id'], 'links': flavor['links'],
- 'name': flavor['name']}
- self.assertIn(flavor_min_detail, flavors)
-
- @test.attr(type='smoke')
- def test_list_flavors_with_detail(self):
- # Detailed list of all flavors should contain the expected flavor
- resp, flavors = self.client.list_flavors_with_detail()
- resp, flavor = self.client.get_flavor_details(self.flavor_ref)
- self.assertIn(flavor, flavors)
-
- @test.attr(type='smoke')
- def test_get_flavor(self):
- # The expected flavor details should be returned
- resp, flavor = self.client.get_flavor_details(self.flavor_ref)
- self.assertEqual(self.flavor_ref, flavor['id'])
-
- @test.attr(type='gate')
- def test_list_flavors_limit_results(self):
- # Only the expected number of flavors should be returned
- params = {'limit': 1}
- resp, flavors = self.client.list_flavors(params)
- self.assertEqual(1, len(flavors))
-
- @test.attr(type='gate')
- def test_list_flavors_detailed_limit_results(self):
- # Only the expected number of flavors (detailed) should be returned
- params = {'limit': 1}
- resp, flavors = self.client.list_flavors_with_detail(params)
- self.assertEqual(1, len(flavors))
-
- @test.attr(type='gate')
- def test_list_flavors_using_marker(self):
- # The list of flavors should start from the provided marker
- resp, flavors = self.client.list_flavors()
- flavor_id = flavors[0]['id']
-
- params = {'marker': flavor_id}
- resp, flavors = self.client.list_flavors(params)
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
- 'The list of flavors did not start after the marker.')
-
- @test.attr(type='gate')
- def test_list_flavors_detailed_using_marker(self):
- # The list of flavors should start from the provided marker
- resp, flavors = self.client.list_flavors_with_detail()
- flavor_id = flavors[0]['id']
-
- params = {'marker': flavor_id}
- resp, flavors = self.client.list_flavors_with_detail(params)
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
- 'The list of flavors did not start after the marker.')
-
- @test.attr(type='gate')
- def test_list_flavors_detailed_filter_by_min_disk(self):
- # The detailed list of flavors should be filtered by disk space
- resp, flavors = self.client.list_flavors_with_detail()
- flavors = sorted(flavors, key=lambda k: k['disk'])
- flavor_id = flavors[0]['id']
-
- params = {'min_disk': flavors[0]['disk'] + 1}
- resp, flavors = self.client.list_flavors_with_detail(params)
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
-
- @test.attr(type='gate')
- def test_list_flavors_detailed_filter_by_min_ram(self):
- # The detailed list of flavors should be filtered by RAM
- resp, flavors = self.client.list_flavors_with_detail()
- flavors = sorted(flavors, key=lambda k: k['ram'])
- flavor_id = flavors[0]['id']
-
- params = {'min_ram': flavors[0]['ram'] + 1}
- resp, flavors = self.client.list_flavors_with_detail(params)
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
-
- @test.attr(type='gate')
- def test_list_flavors_filter_by_min_disk(self):
- # The list of flavors should be filtered by disk space
- resp, flavors = self.client.list_flavors_with_detail()
- flavors = sorted(flavors, key=lambda k: k['disk'])
- flavor_id = flavors[0]['id']
-
- params = {'min_disk': flavors[0]['disk'] + 1}
- resp, flavors = self.client.list_flavors(params)
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
-
- @test.attr(type='gate')
- def test_list_flavors_filter_by_min_ram(self):
- # The list of flavors should be filtered by RAM
- resp, flavors = self.client.list_flavors_with_detail()
- flavors = sorted(flavors, key=lambda k: k['ram'])
- flavor_id = flavors[0]['id']
-
- params = {'min_ram': flavors[0]['ram'] + 1}
- resp, flavors = self.client.list_flavors(params)
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
diff --git a/tempest/api/compute/v3/keypairs/test_keypairs.py b/tempest/api/compute/v3/keypairs/test_keypairs.py
deleted file mode 100644
index 668a295..0000000
--- a/tempest/api/compute/v3/keypairs/test_keypairs.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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 tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest import test
-
-
-class KeyPairsV3Test(base.BaseV3ComputeTest):
-
- @classmethod
- def setUpClass(cls):
- super(KeyPairsV3Test, cls).setUpClass()
- cls.client = cls.keypairs_client
-
- def _delete_keypair(self, keypair_name):
- resp, _ = self.client.delete_keypair(keypair_name)
- self.assertEqual(204, resp.status)
-
- def _create_keypair(self, keypair_name, pub_key=None):
- resp, body = self.client.create_keypair(keypair_name, pub_key)
- self.addCleanup(self._delete_keypair, keypair_name)
- return resp, body
-
- @test.attr(type='gate')
- def test_keypairs_create_list_delete(self):
- # Keypairs created should be available in the response list
- # Create 3 keypairs
- key_list = list()
- for i in range(3):
- k_name = data_utils.rand_name('keypair-')
- resp, keypair = self._create_keypair(k_name)
- # Need to pop these keys so that our compare doesn't fail later,
- # as the keypair dicts from list API doesn't have them.
- keypair.pop('private_key')
- keypair.pop('user_id')
- self.assertEqual(201, resp.status)
- key_list.append(keypair)
- # Fetch all keypairs and verify the list
- # has all created keypairs
- resp, fetched_list = self.client.list_keypairs()
- self.assertEqual(200, resp.status)
- # We need to remove the extra 'keypair' element in the
- # returned dict. See comment in keypairs_client.list_keypairs()
- new_list = list()
- for keypair in fetched_list:
- new_list.append(keypair['keypair'])
- fetched_list = new_list
- # Now check if all the created keypairs are in the fetched list
- missing_kps = [kp for kp in key_list if kp not in fetched_list]
- self.assertFalse(missing_kps,
- "Failed to find keypairs %s in fetched list"
- % ', '.join(m_key['name'] for m_key in missing_kps))
-
- @test.attr(type='gate')
- def test_keypair_create_delete(self):
- # Keypair should be created, verified and deleted
- k_name = data_utils.rand_name('keypair-')
- resp, keypair = self._create_keypair(k_name)
- self.assertEqual(201, resp.status)
- private_key = keypair['private_key']
- key_name = keypair['name']
- self.assertEqual(key_name, k_name,
- "The created keypair name is not equal "
- "to the requested name")
- self.assertTrue(private_key is not None,
- "Field private_key is empty or not found.")
-
- @test.attr(type='gate')
- def test_get_keypair_detail(self):
- # Keypair should be created, Got details by name and deleted
- k_name = data_utils.rand_name('keypair-')
- resp, keypair = self._create_keypair(k_name)
- resp, keypair_detail = self.client.get_keypair(k_name)
- self.assertEqual(200, resp.status)
- self.assertIn('name', keypair_detail)
- self.assertIn('public_key', keypair_detail)
- self.assertEqual(keypair_detail['name'], k_name,
- "The created keypair name is not equal "
- "to requested name")
- public_key = keypair_detail['public_key']
- self.assertTrue(public_key is not None,
- "Field public_key is empty or not found.")
-
- @test.attr(type='gate')
- def test_keypair_create_with_pub_key(self):
- # Keypair should be created with a given public key
- k_name = data_utils.rand_name('keypair-')
- pub_key = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCs"
- "Ne3/1ILNCqFyfYWDeTKLD6jEXC2OQHLmietMWW+/vd"
- "aZq7KZEwO0jhglaFjU1mpqq4Gz5RX156sCTNM9vRbw"
- "KAxfsdF9laBYVsex3m3Wmui3uYrKyumsoJn2g9GNnG1P"
- "I1mrVjZ61i0GY3khna+wzlTpCCmy5HNlrmbj3XLqBUpip"
- "TOXmsnr4sChzC53KCd8LXuwc1i/CZPvF+3XipvAgFSE53pCt"
- "LOeB1kYMOBaiUPLQTWXR3JpckqFIQwhIH0zoHlJvZE8hh90"
- "XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
- "snSA8wzBx3A/8y9Pp1B nova@ubuntu")
- resp, keypair = self._create_keypair(k_name, pub_key)
- self.assertEqual(201, resp.status)
- self.assertFalse('private_key' in keypair,
- "Field private_key is not empty!")
- key_name = keypair['name']
- self.assertEqual(key_name, k_name,
- "The created keypair name is not equal "
- "to the requested name!")
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 5b35e2a..721fe42 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -14,6 +14,7 @@
# under the License.
import testtools
+import urlparse
from tempest.api.compute import base
from tempest.common.utils import data_utils
@@ -420,6 +421,12 @@
self.assertEqual(202, resp.status)
self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+ def _validate_url(self, url):
+ valid_scheme = ['http', 'https']
+ parsed_url = urlparse.urlparse(url)
+ self.assertNotEqual('None', parsed_url.hostname)
+ self.assertIn(parsed_url.scheme, valid_scheme)
+
@testtools.skipUnless(CONF.compute_feature_enabled.vnc_console,
'VNC Console feature is disabled')
@test.attr(type='gate')
@@ -429,6 +436,35 @@
for console_type in console_types:
resp, body = self.servers_client.get_vnc_console(self.server_id,
console_type)
- self.assertEqual(200, resp.status)
+ self.assertEqual(
+ 200, resp.status,
+ "Failed to get Console Type: %s" % (console_type))
self.assertEqual(console_type, body['type'])
self.assertNotEqual('', body['url'])
+ self._validate_url(body['url'])
+
+ @testtools.skipUnless(CONF.compute_feature_enabled.spice_console,
+ 'Spice Console feature is disabled.')
+ @test.attr(type='gate')
+ def test_get_spice_console(self):
+ # Get the Spice console of type "spice-html5"
+ console_type = 'spice-html5'
+ resp, body = self.servers_client.get_spice_console(self.server_id,
+ console_type)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(console_type, body['type'])
+ self.assertNotEqual('', body['url'])
+ self._validate_url(body['url'])
+
+ @testtools.skipUnless(CONF.compute_feature_enabled.rdp_console,
+ 'RDP Console feature is disabled.')
+ @test.attr(type='gate')
+ def test_get_rdp_console(self):
+ # Get the RDP console of type "rdp-html5"
+ console_type = 'rdp-html5'
+ resp, body = self.servers_client.get_rdp_console(self.server_id,
+ console_type)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(console_type, body['type'])
+ self.assertNotEqual('', body['url'])
+ self._validate_url(body['url'])
diff --git a/tempest/api/compute/v3/servers/test_server_metadata.py b/tempest/api/compute/v3/servers/test_server_metadata.py
index 0e4ef07..298cd3c 100644
--- a/tempest/api/compute/v3/servers/test_server_metadata.py
+++ b/tempest/api/compute/v3/servers/test_server_metadata.py
@@ -24,10 +24,7 @@
super(ServerMetadataV3Test, cls).setUpClass()
cls.client = cls.servers_client
cls.quotas = cls.quotas_client
- cls.admin_client = cls._get_identity_admin_client()
- resp, tenants = cls.admin_client.list_tenants()
- cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- cls.client.tenant_name][0]
+ cls.tenant_id = cls.client.tenant_id
resp, server = cls.create_test_server(meta={}, wait_until='ACTIVE')
cls.server_id = server['id']
diff --git a/tempest/api/compute/v3/servers/test_server_metadata_negative.py b/tempest/api/compute/v3/servers/test_server_metadata_negative.py
index ec2bc8c..f746be3 100644
--- a/tempest/api/compute/v3/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/v3/servers/test_server_metadata_negative.py
@@ -25,10 +25,7 @@
super(ServerMetadataV3NegativeTest, cls).setUpClass()
cls.client = cls.servers_client
cls.quotas = cls.quotas_client
- cls.admin_client = cls._get_identity_admin_client()
- resp, tenants = cls.admin_client.list_tenants()
- cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- cls.client.tenant_name][0]
+ cls.tenant_id = cls.client.tenant_id
resp, server = cls.create_test_server(meta={}, wait_until='ACTIVE')
cls.server_id = server['id']
diff --git a/tempest/api/compute/v3/test_quotas.py b/tempest/api/compute/v3/test_quotas.py
index 3fe62e9..62a7556 100644
--- a/tempest/api/compute/v3/test_quotas.py
+++ b/tempest/api/compute/v3/test_quotas.py
@@ -23,13 +23,8 @@
def setUpClass(cls):
super(QuotasV3Test, cls).setUpClass()
cls.client = cls.quotas_client
- cls.admin_client = cls._get_identity_admin_client()
- resp, tenants = cls.admin_client.list_tenants()
- cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- cls.client.tenant_name][0]
- resp, users = cls.admin_client.list_users_for_tenant(cls.tenant_id)
- cls.user_id = [user['id'] for user in users if user['name'] ==
- cls.client.user][0]
+ cls.tenant_id = cls.client.tenant_id
+ cls.user_id = cls.client.user_id
cls.default_quota_set = set(('metadata_items',
'ram', 'floating_ips',
'fixed_ips', 'key_pairs',
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index fc313f2..74444d7 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -38,6 +38,7 @@
# add lists for watched resources
cls._node_group_templates = []
cls._cluster_templates = []
+ cls._data_sources = []
@classmethod
def tearDownClass(cls):
@@ -45,6 +46,8 @@
cls.client.delete_cluster_template)
cls.cleanup_resources(getattr(cls, '_node_group_templates', []),
cls.client.delete_node_group_template)
+ cls.cleanup_resources(getattr(cls, '_data_sources', []),
+ cls.client.delete_data_source)
cls.clear_isolated_creds()
super(BaseDataProcessingTest, cls).tearDownClass()
@@ -96,3 +99,17 @@
cls._cluster_templates.append(body['id'])
return resp, body
+
+ @classmethod
+ def create_data_source(cls, name, type, url, **kwargs):
+ """Creates watched data source with specified params.
+
+ It supports passing additional params using kwargs and returns created
+ object. All resources created in this method will be automatically
+ removed in tearDownClass method.
+ """
+ resp, body = cls.client.create_data_source(name, type, url, **kwargs)
+ # store id of created data source
+ cls._data_sources.append(body['id'])
+
+ return resp, body
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index cae20ad..8e3a7d1 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -13,6 +13,7 @@
import datetime
import re
from tempest.api.identity import base
+from tempest import auth
from tempest import clients
from tempest.common.utils import data_utils
from tempest import config
@@ -88,10 +89,13 @@
self.assertIsNotNone(self.trustee_user_id)
# Initialize a new client with the trustor credentials
- os = clients.Manager(username=self.trustor_username,
- password=self.trustor_password,
- tenant_name=self.trustor_project_name,
- interface=self._interface)
+ creds = auth.get_credentials(
+ username=self.trustor_username,
+ password=self.trustor_password,
+ tenant_name=self.trustor_project_name)
+ os = clients.Manager(
+ credentials=creds,
+ interface=self._interface)
self.trustor_client = os.identity_v3_client
def cleanup_user_and_roles(self):
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index a5bf248..e4e74c1 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -14,6 +14,7 @@
# under the License.
+from tempest import auth
from tempest import clients
from tempest.common.utils import data_utils
from tempest import config
@@ -120,6 +121,14 @@
self.projects = []
self.v3_roles = []
+ @property
+ def test_credentials(self):
+ return auth.get_credentials(username=self.test_user,
+ user_id=self.user['id'],
+ password=self.test_password,
+ tenant_name=self.test_tenant,
+ tenant_id=self.tenant['id'])
+
def setup_test_user(self):
"""Set up a test user."""
self.setup_test_tenant()
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index e439238..e31e635 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -42,11 +42,7 @@
skip_msg = ("%s skipped as glance is not available" % cls.__name__)
raise cls.skipException(skip_msg)
if CONF.compute.allow_tenant_isolation:
- creds = cls.isolated_creds.get_primary_creds()
- username, tenant_name, password = creds
- cls.os = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name)
+ cls.os = clients.Manager(cls.isolated_creds.get_primary_creds())
else:
cls.os = clients.Manager()
@@ -96,19 +92,12 @@
def setUpClass(cls):
super(BaseV1ImageMembersTest, cls).setUpClass()
if CONF.compute.allow_tenant_isolation:
- creds = cls.isolated_creds.get_alt_creds()
- username, tenant_name, password = creds
- cls.os_alt = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name)
- cls.alt_tenant_id = cls.isolated_creds.get_alt_tenant()['id']
+ cls.os_alt = clients.Manager(cls.isolated_creds.get_alt_creds())
else:
cls.os_alt = clients.AltManager()
- identity_client = cls._get_identity_admin_client()
- cls.alt_tenant_id = identity_client.get_tenant_by_name(
- cls.os_alt.credentials['tenant_name'])['id']
cls.alt_img_cli = cls.os_alt.image_client
+ cls.alt_tenant_id = cls.alt_img_cli.tenant_id
def _create_image(self):
image_file = StringIO.StringIO('*' * 1024)
@@ -139,12 +128,8 @@
super(BaseV2MemberImageTest, cls).setUpClass()
if CONF.compute.allow_tenant_isolation:
creds = cls.isolated_creds.get_alt_creds()
- username, tenant_name, password = creds
- cls.os_alt = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name,
- interface=cls._interface)
- cls.alt_tenant_id = cls.isolated_creds.get_alt_tenant()['id']
+ cls.os_alt = clients.Manager(creds)
+ cls.alt_tenant_id = cls.isolated_creds.get_alt_creds().tenant_id
else:
cls.os_alt = clients.AltManager()
alt_tenant_name = cls.os_alt.credentials['tenant_name']
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 8466c7b..b90891b 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -290,6 +290,7 @@
return image_id
@test.attr(type='gate')
+ @test.services('compute')
def test_index_server_id(self):
# The images should contain images filtered by server id
resp, images = self.client.image_list_detail(
@@ -299,6 +300,7 @@
self.assertEqual(self.snapshot_set, result_set)
@test.attr(type='gate')
+ @test.services('compute')
def test_index_type(self):
# The list of servers should be filtered by image type
params = {'image_type': 'snapshot'}
@@ -309,6 +311,7 @@
self.assertIn(self.snapshot, result_set)
@test.attr(type='gate')
+ @test.services('compute')
def test_index_limit(self):
# Verify only the expected number of results are returned
resp, images = self.client.image_list_detail(limit=1)
@@ -317,6 +320,7 @@
self.assertEqual(1, len(images))
@test.attr(type='gate')
+ @test.services('compute')
def test_index_by_change_since(self):
# Verify an update image is returned
# Becoming ACTIVE will modify the updated time
diff --git a/tempest/api/network/admin/test_load_balancer_admin_actions.py b/tempest/api/network/admin/test_load_balancer_admin_actions.py
index bc7f1d6..fe4fc60 100644
--- a/tempest/api/network/admin/test_load_balancer_admin_actions.py
+++ b/tempest/api/network/admin/test_load_balancer_admin_actions.py
@@ -38,9 +38,7 @@
cls.force_tenant_isolation = True
manager = cls.get_client_manager()
cls.client = manager.network_client
- username, tenant_name, passwd = cls.isolated_creds.get_primary_creds()
- cls.tenant_id = cls.os_adm.identity_client.get_tenant_by_name(
- tenant_name)['id']
+ cls.tenant_id = cls.isolated_creds.get_primary_creds().tenant_id
cls.network = cls.create_network()
cls.subnet = cls.create_subnet(cls.network)
cls.pool = cls.create_pool(data_utils.rand_name('pool-'),
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 425d3f2..dcd9bff 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -145,7 +145,7 @@
return network
@classmethod
- def create_subnet(cls, network):
+ def create_subnet(cls, network, gateway=None):
"""Wrapper utility that returns a test subnet."""
# The cidr and mask_bits depend on the ip version.
if cls._ip_version == 4:
@@ -156,14 +156,19 @@
mask_bits = CONF.network.tenant_network_v6_mask_bits
# Find a cidr that is not in use yet and create a subnet with it
for subnet_cidr in cidr.subnet(mask_bits):
+ if not gateway:
+ gateway = str(netaddr.IPAddress(subnet_cidr) + 1)
try:
resp, body = cls.client.create_subnet(
network_id=network['id'],
cidr=str(subnet_cidr),
- ip_version=cls._ip_version)
+ ip_version=cls._ip_version,
+ gateway_ip=gateway)
break
except exceptions.BadRequest as e:
is_overlapping_cidr = 'overlaps with another subnet' in str(e)
+ # Unset gateway value if there is an overlapping subnet
+ gateway = None
if not is_overlapping_cidr:
raise
else:
@@ -294,7 +299,7 @@
def create_vpnservice(cls, subnet_id, router_id):
"""Wrapper utility that returns a test vpn service."""
resp, body = cls.client.create_vpnservice(
- subnet_id, router_id, admin_state_up=True,
+ subnet_id=subnet_id, router_id=router_id, admin_state_up=True,
name=data_utils.rand_name("vpnservice-"))
vpnservice = body['vpnservice']
cls.vpnservices.append(vpnservice)
@@ -303,7 +308,7 @@
@classmethod
def create_ikepolicy(cls, name):
"""Wrapper utility that returns a test ike policy."""
- resp, body = cls.client.create_ikepolicy(name)
+ resp, body = cls.client.create_ikepolicy(name=name)
ikepolicy = body['ikepolicy']
cls.ikepolicies.append(ikepolicy)
return ikepolicy
@@ -352,11 +357,7 @@
raise cls.skipException(msg)
if (CONF.compute.allow_tenant_isolation or
cls.force_tenant_isolation is True):
- creds = cls.isolated_creds.get_admin_creds()
- admin_username, admin_tenant_name, admin_password = creds
- cls.os_adm = clients.Manager(username=admin_username,
- password=admin_password,
- tenant_name=admin_tenant_name,
+ cls.os_adm = clients.Manager(cls.isolated_creds.get_admin_creds(),
interface=cls._interface)
else:
cls.os_adm = clients.ComputeAdminManager(interface=cls._interface)
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index de44f4d..660b376 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -216,6 +216,37 @@
# it from the list.
self.subnets.pop()
+ @test.attr(type='smoke')
+ def test_create_delete_subnet_with_gw(self):
+ gateway = '10.100.0.13'
+ name = data_utils.rand_name('network-')
+ resp, body = self.client.create_network(name=name)
+ self.assertEqual('201', resp['status'])
+ network = body['network']
+ net_id = network['id']
+ subnet = self.create_subnet(network, gateway)
+ # Verifies Subnet GW in IPv4
+ self.assertEqual(subnet['gateway_ip'], gateway)
+ # Delete network and subnet
+ resp, body = self.client.delete_network(net_id)
+ self.assertEqual('204', resp['status'])
+ self.subnets.pop()
+
+ @test.attr(type='smoke')
+ def test_create_delete_subnet_without_gw(self):
+ name = data_utils.rand_name('network-')
+ resp, body = self.client.create_network(name=name)
+ self.assertEqual('201', resp['status'])
+ network = body['network']
+ net_id = network['id']
+ subnet = self.create_subnet(network)
+ # Verifies Subnet GW in IPv4
+ self.assertEqual(subnet['gateway_ip'], '10.100.0.1')
+ # Delete network and subnet
+ resp, body = self.client.delete_network(net_id)
+ self.assertEqual('204', resp['status'])
+ self.subnets.pop()
+
class NetworksTestXML(NetworksTestJSON):
_interface = 'xml'
@@ -364,11 +395,41 @@
@classmethod
def setUpClass(cls):
- super(NetworksIpV6TestJSON, cls).setUpClass()
if not CONF.network_feature_enabled.ipv6:
- cls.tearDownClass()
skip_msg = "IPv6 Tests are disabled."
raise cls.skipException(skip_msg)
+ super(NetworksIpV6TestJSON, cls).setUpClass()
+
+ @test.attr(type='smoke')
+ def test_create_delete_subnet_with_gw(self):
+ gateway = '2003::2'
+ name = data_utils.rand_name('network-')
+ resp, body = self.client.create_network(name=name)
+ self.assertEqual('201', resp['status'])
+ network = body['network']
+ net_id = network['id']
+ subnet = self.create_subnet(network, gateway)
+ # Verifies Subnet GW in IPv6
+ self.assertEqual(subnet['gateway_ip'], gateway)
+ # Delete network and subnet
+ resp, body = self.client.delete_network(net_id)
+ self.assertEqual('204', resp['status'])
+ self.subnets.pop()
+
+ @test.attr(type='smoke')
+ def test_create_delete_subnet_without_gw(self):
+ name = data_utils.rand_name('network-')
+ resp, body = self.client.create_network(name=name)
+ self.assertEqual('201', resp['status'])
+ network = body['network']
+ net_id = network['id']
+ subnet = self.create_subnet(network)
+ # Verifies Subnet GW in IPv6
+ self.assertEqual(subnet['gateway_ip'], '2003::1')
+ # Delete network and subnet
+ resp, body = self.client.delete_network(net_id)
+ self.assertEqual('204', resp['status'])
+ self.subnets.pop()
class NetworksIpV6TestXML(NetworksIpV6TestJSON):
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index e924585..e6e6ea1 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -288,11 +288,10 @@
@classmethod
def setUpClass(cls):
- super(PortsAdminExtendedAttrsIpV6TestJSON, cls).setUpClass()
if not CONF.network_feature_enabled.ipv6:
- cls.tearDownClass()
skip_msg = "IPv6 Tests are disabled."
raise cls.skipException(skip_msg)
+ super(PortsAdminExtendedAttrsIpV6TestJSON, cls).setUpClass()
class PortsAdminExtendedAttrsIpV6TestXML(
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 4cc0338..7605b8a 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -262,10 +262,25 @@
self.addCleanup(
self._delete_extra_routes,
self.router['id'])
- # Update router extra route
+ # Update router extra route, second ip of the range is
+ # used as next hop
cidr = netaddr.IPNetwork(self.subnet['cidr'])
+ next_hop = str(cidr[2])
+ destination = str(self.subnet['cidr'])
resp, extra_route = self.client.update_extra_routes(
- self.router['id'], str(cidr[0]), str(self.subnet['cidr']))
+ self.router['id'], next_hop, destination)
+ self.assertEqual('200', resp['status'])
+ self.assertEqual(1, len(extra_route['router']['routes']))
+ self.assertEqual(destination,
+ extra_route['router']['routes'][0]['destination'])
+ self.assertEqual(next_hop,
+ extra_route['router']['routes'][0]['nexthop'])
+ resp, show_body = self.client.show_router(self.router['id'])
+ self.assertEqual('200', resp['status'])
+ self.assertEqual(destination,
+ show_body['router']['routes'][0]['destination'])
+ self.assertEqual(next_hop,
+ show_body['router']['routes'][0]['nexthop'])
def _delete_extra_routes(self, router_id):
resp, _ = self.client.delete_extra_routes(router_id)
diff --git a/tempest/api/network/test_vpnaas_extensions.py b/tempest/api/network/test_vpnaas_extensions.py
index 7edaaf8..a49e944 100644
--- a/tempest/api/network/test_vpnaas_extensions.py
+++ b/tempest/api/network/test_vpnaas_extensions.py
@@ -82,8 +82,8 @@
def test_create_update_delete_vpn_service(self):
# Creates a VPN service
name = data_utils.rand_name('vpn-service-')
- resp, body = self.client.create_vpnservice(self.subnet['id'],
- self.router['id'],
+ resp, body = self.client.create_vpnservice(subnet_id=self.subnet['id'],
+ router_id=self.router['id'],
name=name,
admin_state_up=True)
self.assertEqual('201', resp['status'])
@@ -134,7 +134,7 @@
# Creates a IKE policy
name = data_utils.rand_name('ike-policy-')
resp, body = (self.client.create_ikepolicy(
- name,
+ name=name,
ike_version="v1",
encryption_algorithm="aes-128",
auth_algorithm="sha1"))
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 45c895b..6b18182 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -38,23 +38,12 @@
cls.__name__, network_resources=cls.network_resources)
if CONF.compute.allow_tenant_isolation:
# Get isolated creds for normal user
- creds = cls.isolated_creds.get_primary_creds()
- username, tenant_name, password = creds
- cls.os = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name)
+ cls.os = clients.Manager(cls.isolated_creds.get_primary_creds())
# Get isolated creds for admin user
- admin_creds = cls.isolated_creds.get_admin_creds()
- admin_username, admin_tenant_name, admin_password = admin_creds
- cls.os_admin = clients.Manager(username=admin_username,
- password=admin_password,
- tenant_name=admin_tenant_name)
+ cls.os_admin = clients.Manager(
+ cls.isolated_creds.get_admin_creds())
# Get isolated creds for alt user
- alt_creds = cls.isolated_creds.get_alt_creds()
- alt_username, alt_tenant, alt_password = alt_creds
- cls.os_alt = clients.Manager(username=alt_username,
- password=alt_password,
- tenant_name=alt_tenant)
+ cls.os_alt = clients.Manager(cls.isolated_creds.get_alt_creds())
# Add isolated users to operator role so that they can create a
# container in swift.
cls._assign_member_role()
@@ -92,8 +81,8 @@
@classmethod
def _assign_member_role(cls):
- primary_user = cls.isolated_creds.get_primary_user()
- alt_user = cls.isolated_creds.get_alt_user()
+ primary_creds = cls.isolated_creds.get_primary_creds()
+ alt_creds = cls.isolated_creds.get_alt_creds()
swift_role = CONF.object_storage.operator_role
try:
resp, roles = cls.os_admin.identity_client.list_roles()
@@ -101,9 +90,9 @@
except StopIteration:
msg = "No role named %s found" % swift_role
raise exceptions.NotFound(msg)
- for user in [primary_user, alt_user]:
- cls.os_admin.identity_client.assign_user_role(user['tenantId'],
- user['id'],
+ for creds in [primary_creds, alt_creds]:
+ cls.os_admin.identity_client.assign_user_role(creds.tenant_id,
+ creds.user_id,
role['id'])
@classmethod
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index c1f468b..19e3068 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -35,10 +35,7 @@
cls.data.setup_test_user()
- cls.os_reselleradmin = clients.Manager(
- cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
+ cls.os_reselleradmin = clients.Manager(cls.data.test_credentials)
# Retrieve the ResellerAdmin role id
reseller_role_id = None
@@ -50,15 +47,11 @@
msg = "No ResellerAdmin role found"
raise exceptions.NotFound(msg)
- # Retrieve the ResellerAdmin tenant id
- _, users = cls.os_admin.identity_client.get_users()
- reseller_user_id = next(usr['id'] for usr in users if usr['name']
- == cls.data.test_user)
+ # Retrieve the ResellerAdmin user id
+ reseller_user_id = cls.data.test_credentials.user_id
# Retrieve the ResellerAdmin tenant id
- _, tenants = cls.os_admin.identity_client.list_tenants()
- reseller_tenant_id = next(tnt['id'] for tnt in tenants if tnt['name']
- == cls.data.test_tenant)
+ reseller_tenant_id = cls.data.test_credentials.tenant_id
# Assign the newly created user the appropriate ResellerAdmin role
cls.os_admin.identity_client.assign_user_role(
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 4677f97..6afd381 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -35,10 +35,7 @@
cls.data.setup_test_user()
- cls.os_reselleradmin = clients.Manager(
- cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
+ cls.os_reselleradmin = clients.Manager(cls.data.test_credentials)
# Retrieve the ResellerAdmin role id
reseller_role_id = None
@@ -51,14 +48,10 @@
raise exceptions.NotFound(msg)
# Retrieve the ResellerAdmin tenant id
- _, users = cls.os_admin.identity_client.get_users()
- reseller_user_id = next(usr['id'] for usr in users if usr['name']
- == cls.data.test_user)
+ reseller_user_id = cls.data.test_credentials.user_id
# Retrieve the ResellerAdmin tenant id
- _, tenants = cls.os_admin.identity_client.list_tenants()
- reseller_tenant_id = next(tnt['id'] for tnt in tenants if tnt['name']
- == cls.data.test_tenant)
+ reseller_tenant_id = cls.data.test_credentials.tenant_id
# Assign the newly created user the appropriate ResellerAdmin role
cls.os_admin.identity_client.assign_user_role(
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 7fb0604..d615374 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -67,9 +67,7 @@
self.data.setup_test_user()
os_test_user = clients.Manager(
- self.data.test_user,
- self.data.test_password,
- self.data.test_tenant)
+ self.data.test_credentials)
# Retrieve the id of an operator role of object storage
test_role_id = None
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index 71eaab5..d5f8649 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -28,9 +28,7 @@
# create user
self.data.setup_test_user()
- test_os = clients.Manager(self.data.test_user,
- self.data.test_password,
- self.data.test_tenant)
+ test_os = clients.Manager(self.data.test_credentials)
test_auth_provider = test_os.auth_provider
# Get auth for the test user
test_auth_provider.auth_data
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index c865ee1..fc51504 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -24,9 +24,7 @@
def setUpClass(cls):
super(ObjectTestACLs, cls).setUpClass()
cls.data.setup_test_user()
- test_os = clients.Manager(cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
+ test_os = clients.Manager(cls.data.test_credentials)
cls.test_auth_data = test_os.auth_provider.auth_data
@classmethod
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index 547bf87..ca53876 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -26,9 +26,7 @@
def setUpClass(cls):
super(ObjectACLsNegativeTest, cls).setUpClass()
cls.data.setup_test_user()
- test_os = clients.Manager(cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
+ test_os = clients.Manager(cls.data.test_credentials)
cls.test_auth_data = test_os.auth_provider.auth_data
@classmethod
diff --git a/tempest/api/object_storage/test_crossdomain.py b/tempest/api/object_storage/test_crossdomain.py
index 4f399b4..d1541b9 100644
--- a/tempest/api/object_storage/test_crossdomain.py
+++ b/tempest/api/object_storage/test_crossdomain.py
@@ -29,10 +29,7 @@
# endpoint and test the healthcheck feature.
cls.data.setup_test_user()
- cls.os_test_user = clients.Manager(
- cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
+ cls.os_test_user = clients.Manager(cls.data.test_credentials)
cls.xml_start = '<?xml version="1.0"?>\n' \
'<!DOCTYPE cross-domain-policy SYSTEM ' \
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
index 60b8dc1..7f088de 100644
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
@@ -70,13 +70,13 @@
output_map[outputs['output_key']] = outputs['output_value']
#Test that first key generated public and private keys
self.assertTrue('KeyPair_PublicKey' in output_map)
- self.assertTrue("Generated by" in output_map['KeyPair_PublicKey'])
+ self.assertTrue("Generated" in output_map['KeyPair_PublicKey'])
self.assertTrue('KeyPair_PrivateKey' in output_map)
self.assertTrue('-----BEGIN' in output_map['KeyPair_PrivateKey'])
#Test that second key generated public key, and private key is not
#in the output due to save_private_key = false
self.assertTrue('KeyPairDontSavePrivate_PublicKey' in output_map)
- self.assertTrue('Generated by' in
+ self.assertTrue('Generated' in
output_map['KeyPairDontSavePrivate_PublicKey'])
self.assertTrue(u'KeyPairDontSavePrivate_PrivateKey' in output_map)
private_key = output_map['KeyPairDontSavePrivate_PrivateKey']
diff --git a/tempest/api/telemetry/test_telemetry_alarming_api.py b/tempest/api/telemetry/test_telemetry_alarming_api.py
index a59d3ae..3472b31 100644
--- a/tempest/api/telemetry/test_telemetry_alarming_api.py
+++ b/tempest/api/telemetry/test_telemetry_alarming_api.py
@@ -11,6 +11,7 @@
# under the License.
from tempest.api.telemetry import base
+from tempest.common.utils import data_utils
from tempest import exceptions
from tempest.test import attr
@@ -18,46 +19,96 @@
class TelemetryAlarmingAPITestJSON(base.BaseTelemetryTest):
_interface = 'json'
+ @classmethod
+ def setUpClass(cls):
+ super(TelemetryAlarmingAPITestJSON, cls).setUpClass()
+ cls.rule = {'meter_name': 'cpu_util',
+ 'comparison_operator': 'gt',
+ 'threshold': 80.0,
+ 'period': 70}
+ for i in range(2):
+ cls.create_alarm(threshold_rule=cls.rule)
+
@attr(type="gate")
def test_alarm_list(self):
- # Create an alarm to verify in the list of alarms
- created_alarm_ids = list()
- fetched_ids = list()
- rules = {'meter_name': 'cpu_util',
- 'comparison_operator': 'gt',
- 'threshold': 80.0,
- 'period': 70}
- for i in range(3):
- resp, body = self.create_alarm(threshold_rule=rules)
- created_alarm_ids.append(body['alarm_id'])
-
# List alarms
resp, alarm_list = self.telemetry_client.list_alarms()
- self.assertEqual(int(resp['status']), 200)
+ self.assertEqual(200, resp.status)
# Verify created alarm in the list
fetched_ids = [a['alarm_id'] for a in alarm_list]
- missing_alarms = [a for a in created_alarm_ids if a not in fetched_ids]
+ missing_alarms = [a for a in self.alarm_ids if a not in fetched_ids]
self.assertEqual(0, len(missing_alarms),
"Failed to find the following created alarm(s)"
" in a fetched list: %s" %
', '.join(str(a) for a in missing_alarms))
@attr(type="gate")
- def test_create_alarm(self):
- rules = {'meter_name': 'cpu_util',
- 'comparison_operator': 'gt',
- 'threshold': 80.0,
- 'period': 70}
- resp, body = self.create_alarm(threshold_rule=rules)
- self.alarm_id = body['alarm_id']
- self.assertEqual(int(resp['status']), 201)
- self.assertDictContainsSubset(rules, body['threshold_rule'])
- resp, body = self.telemetry_client.get_alarm(self.alarm_id)
- self.assertEqual(int(resp['status']), 200)
- self.assertDictContainsSubset(rules, body['threshold_rule'])
- resp, _ = self.telemetry_client.delete_alarm(self.alarm_id)
- self.assertEqual(int(resp['status']), 204)
+ def test_create_update_get_delete_alarm(self):
+ # Create an alarm
+ alarm_name = data_utils.rand_name('telemetry_alarm')
+ resp, body = self.telemetry_client.create_alarm(
+ name=alarm_name, type='threshold', threshold_rule=self.rule)
+ self.assertEqual(201, resp.status)
+ self.assertEqual(alarm_name, body['name'])
+ alarm_id = body['alarm_id']
+ self.assertDictContainsSubset(self.rule, body['threshold_rule'])
+ # Update alarm with new rule and new name
+ new_rule = {'meter_name': 'cpu',
+ 'comparison_operator': 'eq',
+ 'threshold': 70.0,
+ 'period': 60}
+ alarm_name = data_utils.rand_name('telemetry-alarm-update')
+ resp, body = self.telemetry_client.update_alarm(
+ alarm_id,
+ threshold_rule=new_rule,
+ name=alarm_name,
+ type='threshold')
+ self.assertEqual(200, resp.status)
+ self.assertEqual(alarm_name, body['name'])
+ self.assertDictContainsSubset(new_rule, body['threshold_rule'])
+ # Get and verify details of an alarm after update
+ resp, body = self.telemetry_client.get_alarm(alarm_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(alarm_name, body['name'])
+ self.assertDictContainsSubset(new_rule, body['threshold_rule'])
+ # Delete alarm and verify if deleted
+ resp, _ = self.telemetry_client.delete_alarm(alarm_id)
+ self.assertEqual(204, resp.status)
self.assertRaises(exceptions.NotFound,
- self.telemetry_client.get_alarm,
- self.alarm_id)
+ self.telemetry_client.get_alarm, alarm_id)
+
+ @attr(type="gate")
+ def test_set_get_alarm_state(self):
+ alarm_states = ['ok', 'alarm', 'insufficient data']
+ _, alarm = self.create_alarm(threshold_rule=self.rule)
+ # Set alarm state and verify
+ new_state =\
+ [elem for elem in alarm_states if elem != alarm['state']][0]
+ resp, state = self.telemetry_client.alarm_set_state(alarm['alarm_id'],
+ new_state)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(new_state, state)
+ # Get alarm state and verify
+ resp, state = self.telemetry_client.alarm_get_state(alarm['alarm_id'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(new_state, state)
+
+ @attr(type="gate")
+ def test_create_delete_alarm_with_combination_rule(self):
+ rule = {"alarm_ids": self.alarm_ids,
+ "operator": "or"}
+ # Verifies alarm create
+ alarm_name = data_utils.rand_name('combination_alarm')
+ resp, body = self.telemetry_client.create_alarm(name=alarm_name,
+ combination_rule=rule,
+ type='combination')
+ self.assertEqual(201, resp.status)
+ self.assertEqual(alarm_name, body['name'])
+ alarm_id = body['alarm_id']
+ self.assertDictContainsSubset(rule, body['combination_rule'])
+ # Verify alarm delete
+ resp, _ = self.telemetry_client.delete_alarm(alarm_id)
+ self.assertEqual(204, resp.status)
+ self.assertRaises(exceptions.NotFound,
+ self.telemetry_client.get_alarm, alarm_id)
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 2949d56..531e145 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -29,8 +29,7 @@
def setUpClass(cls):
super(VolumeQuotasAdminTestJSON, cls).setUpClass()
cls.admin_volume_client = cls.os_adm.volumes_client
- cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
- 'tenantId')
+ cls.demo_tenant_id = cls.isolated_creds.get_primary_creds().tenant_id
@test.attr(type='gate')
def test_list_quotas(self):
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 292f8ed..ab88b90 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -26,8 +26,8 @@
@test.safe_setup
def setUpClass(cls):
super(VolumeQuotasNegativeTestJSON, cls).setUpClass()
- demo_user = cls.isolated_creds.get_primary_user()
- cls.demo_tenant_id = demo_user.get('tenantId')
+ demo_user = cls.isolated_creds.get_primary_creds()
+ cls.demo_tenant_id = demo_user.tenant_id
cls.shared_quota_set = {'gigabytes': 3, 'volumes': 1, 'snapshots': 1}
# NOTE(gfidente): no need to restore original quota set
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index ff616fc..4d11d24 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -136,11 +136,7 @@
"in configuration.")
raise cls.skipException(msg)
if CONF.compute.allow_tenant_isolation:
- creds = cls.isolated_creds.get_admin_creds()
- admin_username, admin_tenant_name, admin_password = creds
- cls.os_adm = clients.Manager(username=admin_username,
- password=admin_password,
- tenant_name=admin_tenant_name,
+ cls.os_adm = clients.Manager(cls.isolated_creds.get_admin_creds(),
interface=cls._interface)
else:
cls.os_adm = clients.AdminManager(interface=cls._interface)
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 55a72c1..82d1364 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -32,32 +32,18 @@
# Add another tenant to test volume-transfer
if CONF.compute.allow_tenant_isolation:
- creds = cls.isolated_creds.get_alt_creds()
- username, tenant_name, password = creds
- cls.os_alt = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name,
+ cls.os_alt = clients.Manager(cls.isolated_creds.get_alt_creds(),
interface=cls._interface)
- cls.alt_tenant_id = cls.isolated_creds.get_alt_tenant()['id']
-
# Add admin tenant to cleanup resources
- adm_creds = cls.isolated_creds.get_admin_creds()
- admin_username, admin_tenant_name, admin_password = adm_creds
- cls.os_adm = clients.Manager(username=admin_username,
- password=admin_password,
- tenant_name=admin_tenant_name,
+ cls.os_adm = clients.Manager(cls.isolated_creds.get_admin_creds(),
interface=cls._interface)
else:
cls.os_alt = clients.AltManager()
- alt_tenant_name = cls.os_alt.credentials['tenant_name']
- identity_client = cls._get_identity_admin_client()
- _, tenants = identity_client.list_tenants()
- cls.alt_tenant_id = [tnt['id'] for tnt in tenants
- if tnt['name'] == alt_tenant_name][0]
cls.os_adm = clients.ComputeAdminManager(interface=cls._interface)
cls.client = cls.volumes_client
cls.alt_client = cls.os_alt.volumes_client
+ cls.alt_tenant_id = cls.alt_client.tenant_id
cls.adm_client = cls.os_adm.volumes_client
def _delete_volume(self, volume_id):
diff --git a/tempest/api_schema/compute/aggregates.py b/tempest/api_schema/compute/aggregates.py
index a3ab3c8..9393a16 100644
--- a/tempest/api_schema/compute/aggregates.py
+++ b/tempest/api_schema/compute/aggregates.py
@@ -64,3 +64,23 @@
'updated_at'] = {
'type': 'string'
}
+
+common_create_aggregate = {
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'aggregate': aggregate
+ },
+ 'required': ['aggregate']
+ }
+}
+# create-aggregate api doesn't have 'hosts' and 'metadata' attributes.
+del common_create_aggregate['response_body']['properties']['aggregate'][
+ 'properties']['hosts']
+del common_create_aggregate['response_body']['properties']['aggregate'][
+ 'properties']['metadata']
+common_create_aggregate['response_body']['properties']['aggregate'][
+ 'required'] = ['availability_zone', 'created_at', 'deleted', 'deleted_at',
+ 'id', 'name', 'updated_at']
+
+aggregate_add_remove_host = get_aggregate
diff --git a/tempest/api_schema/compute/hosts.py b/tempest/api_schema/compute/hosts.py
index a9d6567..2596c27 100644
--- a/tempest/api_schema/compute/hosts.py
+++ b/tempest/api_schema/compute/hosts.py
@@ -73,3 +73,13 @@
'required': ['host']
}
}
+
+update_host_common = {
+ 'type': 'object',
+ 'properties': {
+ 'host': {'type': 'string'},
+ 'maintenance_mode': {'enum': ['on_maintenance', 'off_maintenance']},
+ 'status': {'enum': ['enabled', 'disabled']}
+ },
+ 'required': ['host', 'maintenance_mode', 'status']
+}
diff --git a/tempest/api_schema/compute/interfaces.py b/tempest/api_schema/compute/interfaces.py
index 1e15c18..79a8f42 100644
--- a/tempest/api_schema/compute/interfaces.py
+++ b/tempest/api_schema/compute/interfaces.py
@@ -12,6 +12,36 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.api_schema.compute import parameter_types
+
delete_interface = {
'status_code': [202]
}
+
+interface_common_info = {
+ 'type': 'object',
+ 'properties': {
+ 'port_state': {'type': 'string'},
+ 'fixed_ips': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'subnet_id': {
+ 'type': 'string',
+ 'format': 'uuid'
+ },
+ 'ip_address': {
+ 'type': 'string',
+ 'format': 'ipv4'
+ }
+ },
+ 'required': ['subnet_id', 'ip_address']
+ }
+ },
+ 'port_id': {'type': 'string', 'format': 'uuid'},
+ 'net_id': {'type': 'string', 'format': 'uuid'},
+ 'mac_addr': parameter_types.mac_address
+ },
+ 'required': ['port_state', 'fixed_ips', 'port_id', 'net_id', 'mac_addr']
+}
diff --git a/tempest/api_schema/compute/parameter_types.py b/tempest/api_schema/compute/parameter_types.py
index 95d5b92..4a1dfdd 100644
--- a/tempest/api_schema/compute/parameter_types.py
+++ b/tempest/api_schema/compute/parameter_types.py
@@ -31,3 +31,37 @@
'type': 'string',
'pattern': '(?:[a-f0-9]{2}:){5}[a-f0-9]{2}'
}
+
+access_ip_v4 = {
+ 'type': 'string',
+ 'anyOf': [{'format': 'ipv4'}, {'enum': ['']}]
+}
+
+access_ip_v6 = {
+ 'type': 'string',
+ 'anyOf': [{'format': 'ipv6'}, {'enum': ['']}]
+}
+
+addresses = {
+ 'type': 'object',
+ 'patternProperties': {
+ # NOTE: Here is for 'private' or something.
+ '^[a-zA-Z0-9-_.]+$': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'version': {'type': 'integer'},
+ 'addr': {
+ 'type': 'string',
+ 'anyOf': [
+ {'format': 'ipv4'},
+ {'format': 'ipv6'}
+ ]
+ }
+ },
+ 'required': ['version', 'addr']
+ }
+ }
+ }
+}
diff --git a/tempest/api_schema/compute/quotas.py b/tempest/api_schema/compute/quotas.py
new file mode 100644
index 0000000..f49771e
--- /dev/null
+++ b/tempest/api_schema/compute/quotas.py
@@ -0,0 +1,41 @@
+# Copyright 2014 NEC Corporation. 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.
+
+common_quota_set = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'quota_set': {
+ 'type': 'object',
+ 'properties': {
+ 'instances': {'type': 'integer'},
+ 'cores': {'type': 'integer'},
+ 'ram': {'type': 'integer'},
+ 'floating_ips': {'type': 'integer'},
+ 'fixed_ips': {'type': 'integer'},
+ 'metadata_items': {'type': 'integer'},
+ 'key_pairs': {'type': 'integer'},
+ 'security_groups': {'type': 'integer'},
+ 'security_group_rules': {'type': 'integer'}
+ },
+ 'required': ['instances', 'cores', 'ram',
+ 'floating_ips', 'fixed_ips',
+ 'metadata_items', 'key_pairs',
+ 'security_groups', 'security_group_rules']
+ }
+ },
+ 'required': ['quota_set']
+ }
+}
diff --git a/tempest/api_schema/compute/servers.py b/tempest/api_schema/compute/servers.py
index 24cdedd..e11f047 100644
--- a/tempest/api_schema/compute/servers.py
+++ b/tempest/api_schema/compute/servers.py
@@ -14,6 +14,8 @@
import copy
+from tempest.api_schema.compute import parameter_types
+
get_password = {
'status_code': [200],
'response_body': {
@@ -46,6 +48,50 @@
}
}
+base_update_server = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server': {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': ['integer', 'string']},
+ 'name': {'type': 'string'},
+ 'status': {'type': 'string'},
+ 'image': {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': ['integer', 'string']},
+ 'links': parameter_types.links
+ },
+ 'required': ['id', 'links']
+ },
+ 'flavor': {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': ['integer', 'string']},
+ 'links': parameter_types.links
+ },
+ 'required': ['id', 'links']
+ },
+ 'user_id': {'type': 'string'},
+ 'tenant_id': {'type': 'string'},
+ 'created': {'type': 'string'},
+ 'updated': {'type': 'string'},
+ 'progress': {'type': 'integer'},
+ 'metadata': {'type': 'object'},
+ 'links': parameter_types.links,
+ 'addresses': parameter_types.addresses,
+ },
+ 'required': ['id', 'name', 'status', 'image', 'flavor',
+ 'user_id', 'tenant_id', 'created', 'updated',
+ 'progress', 'metadata', 'links', 'addresses']
+ }
+ }
+ }
+}
+
delete_server = {
'status_code': [204],
}
@@ -71,3 +117,25 @@
delete_server_metadata_item = {
'status_code': [204]
}
+
+list_servers = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'servers': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': 'string'},
+ 'links': parameter_types.links,
+ 'name': {'type': 'string'}
+ },
+ 'required': ['id', 'links', 'name']
+ }
+ }
+ },
+ 'required': ['servers']
+ }
+}
diff --git a/tempest/api_schema/compute/v2/aggregates.py b/tempest/api_schema/compute/v2/aggregates.py
index de3e12b..bc36044 100644
--- a/tempest/api_schema/compute/v2/aggregates.py
+++ b/tempest/api_schema/compute/v2/aggregates.py
@@ -12,6 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
+
+from tempest.api_schema.compute import aggregates
+
delete_aggregate = {
'status_code': [200]
}
+
+create_aggregate = copy.deepcopy(aggregates.common_create_aggregate)
+# V2 API's response status_code is 200
+create_aggregate['status_code'] = [200]
diff --git a/tempest/api_schema/compute/v2/hosts.py b/tempest/api_schema/compute/v2/hosts.py
index 9ec8848..86efadf 100644
--- a/tempest/api_schema/compute/v2/hosts.py
+++ b/tempest/api_schema/compute/v2/hosts.py
@@ -35,3 +35,8 @@
reboot_host['response_body']['properties']['power_action'] = {
'enum': ['reboot']
}
+
+update_host = {
+ 'status_code': [200],
+ 'response_body': hosts.update_host_common
+}
diff --git a/tempest/api_schema/compute/v2/interfaces.py b/tempest/api_schema/compute/v2/interfaces.py
new file mode 100644
index 0000000..7fca791
--- /dev/null
+++ b/tempest/api_schema/compute/v2/interfaces.py
@@ -0,0 +1,29 @@
+# Copyright 2014 NEC Corporation. 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 tempest.api_schema.compute import interfaces as common_schema
+
+list_interfaces = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'interfaceAttachments': {
+ 'type': 'array',
+ 'items': common_schema.interface_common_info
+ }
+ },
+ 'required': ['interfaceAttachments']
+ }
+}
diff --git a/tempest/api_schema/compute/v2/quotas.py b/tempest/api_schema/compute/v2/quotas.py
index 17dc4dd..31c0458 100644
--- a/tempest/api_schema/compute/v2/quotas.py
+++ b/tempest/api_schema/compute/v2/quotas.py
@@ -12,39 +12,36 @@
# License for the specific language governing permissions and limitations
# under the License.
-quota_set = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'quota_set': {
- 'type': 'object',
- 'properties': {
- 'id': {'type': 'string'},
- 'instances': {'type': 'integer'},
- 'cores': {'type': 'integer'},
- 'ram': {'type': 'integer'},
- 'floating_ips': {'type': 'integer'},
- 'fixed_ips': {'type': 'integer'},
- 'metadata_items': {'type': 'integer'},
- 'injected_files': {'type': 'integer'},
- 'injected_file_content_bytes': {'type': 'integer'},
- 'injected_file_path_bytes': {'type': 'integer'},
- 'key_pairs': {'type': 'integer'},
- 'security_groups': {'type': 'integer'},
- 'security_group_rules': {'type': 'integer'}
- },
- 'required': ['id', 'instances', 'cores', 'ram',
- 'floating_ips', 'fixed_ips',
- 'metadata_items', 'injected_files',
- 'injected_file_content_bytes',
- 'injected_file_path_bytes', 'key_pairs',
- 'security_groups', 'security_group_rules']
- }
- },
- 'required': ['quota_set']
- }
-}
+import copy
+
+from tempest.api_schema.compute import quotas
+
+quota_set = copy.deepcopy(quotas.common_quota_set)
+quota_set['response_body']['properties']['quota_set']['properties'][
+ 'id'] = {'type': 'string'}
+quota_set['response_body']['properties']['quota_set']['properties'][
+ 'injected_files'] = {'type': 'integer'}
+quota_set['response_body']['properties']['quota_set']['properties'][
+ 'injected_file_content_bytes'] = {'type': 'integer'}
+quota_set['response_body']['properties']['quota_set']['properties'][
+ 'injected_file_path_bytes'] = {'type': 'integer'}
+quota_set['response_body']['properties']['quota_set']['required'].extend([
+ 'id',
+ 'injected_files',
+ 'injected_file_content_bytes',
+ 'injected_file_path_bytes'])
+
+quota_set_update = copy.deepcopy(quotas.common_quota_set)
+quota_set_update['response_body']['properties']['quota_set']['properties'][
+ 'injected_files'] = {'type': 'integer'}
+quota_set_update['response_body']['properties']['quota_set']['properties'][
+ 'injected_file_content_bytes'] = {'type': 'integer'}
+quota_set_update['response_body']['properties']['quota_set']['properties'][
+ 'injected_file_path_bytes'] = {'type': 'integer'}
+quota_set_update['response_body']['properties']['quota_set'][
+ 'required'].extend(['injected_files',
+ 'injected_file_content_bytes',
+ 'injected_file_path_bytes'])
delete_quota = {
'status_code': [202]
diff --git a/tempest/api_schema/compute/v2/servers.py b/tempest/api_schema/compute/v2/servers.py
index 5be51e1..e90f436 100644
--- a/tempest/api_schema/compute/v2/servers.py
+++ b/tempest/api_schema/compute/v2/servers.py
@@ -12,7 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
+
from tempest.api_schema.compute import parameter_types
+from tempest.api_schema.compute import servers
create_server = {
'status_code': [202],
@@ -43,6 +46,20 @@
}
}
+update_server = copy.deepcopy(servers.base_update_server)
+update_server['response_body']['properties']['server']['properties'].update({
+ 'hostId': {'type': 'string'},
+ 'OS-DCF:diskConfig': {'type': 'string'},
+ 'accessIPv4': parameter_types.access_ip_v4,
+ 'accessIPv6': parameter_types.access_ip_v6
+})
+update_server['response_body']['properties']['server']['required'].append(
+ # NOTE: OS-DCF:diskConfig and accessIPv4/v6 are API
+ # extensions, and some environments return a response
+ # without these attributes. So they are not 'required'.
+ 'hostId'
+)
+
list_virtual_interfaces = {
'status_code': [200],
'response_body': {
@@ -106,3 +123,8 @@
'required': ['meta']
}
}
+
+list_addresses_by_network = {
+ 'status_code': [200],
+ 'response_body': parameter_types.addresses
+}
diff --git a/tempest/api_schema/compute/v2/tenant_usages.py b/tempest/api_schema/compute/v2/tenant_usages.py
new file mode 100644
index 0000000..0b824a1
--- /dev/null
+++ b/tempest/api_schema/compute/v2/tenant_usages.py
@@ -0,0 +1,92 @@
+# Copyright 2014 NEC Corporation. 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 copy
+
+_server_usages = {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'ended_at': {
+ 'oneOf': [
+ {'type': 'string'},
+ {'type': 'null'}
+ ]
+ },
+ 'flavor': {'type': 'string'},
+ 'hours': {'type': 'number'},
+ 'instance_id': {'type': 'string'},
+ 'local_gb': {'type': 'integer'},
+ 'memory_mb': {'type': 'integer'},
+ 'name': {'type': 'string'},
+ 'started_at': {'type': 'string'},
+ 'state': {'type': 'string'},
+ 'tenant_id': {'type': 'string'},
+ 'uptime': {'type': 'integer'},
+ 'vcpus': {'type': 'integer'},
+ },
+ 'required': ['ended_at', 'flavor', 'hours', 'instance_id', 'local_gb',
+ 'memory_mb', 'name', 'started_at', 'state', 'tenant_id',
+ 'uptime', 'vcpus']
+ }
+}
+
+_tenant_usage_list = {
+ 'type': 'object',
+ 'properties': {
+ 'server_usages': _server_usages,
+ 'start': {'type': 'string'},
+ 'stop': {'type': 'string'},
+ 'tenant_id': {'type': 'string'},
+ 'total_hours': {'type': 'number'},
+ 'total_local_gb_usage': {'type': 'number'},
+ 'total_memory_mb_usage': {'type': 'number'},
+ 'total_vcpus_usage': {'type': 'number'},
+ },
+ 'required': ['start', 'stop', 'tenant_id',
+ 'total_hours', 'total_local_gb_usage',
+ 'total_memory_mb_usage', 'total_vcpus_usage']
+}
+
+# 'required' of get_tenant is different from list_tenant's.
+_tenant_usage_get = copy.deepcopy(_tenant_usage_list)
+_tenant_usage_get['required'] = ['server_usages', 'start', 'stop', 'tenant_id',
+ 'total_hours', 'total_local_gb_usage',
+ 'total_memory_mb_usage', 'total_vcpus_usage']
+
+list_tenant = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'tenant_usages': {
+ 'type': 'array',
+ 'items': _tenant_usage_list
+ }
+ },
+ 'required': ['tenant_usages']
+ }
+}
+
+get_tenant = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'tenant_usage': _tenant_usage_get
+ },
+ 'required': ['tenant_usage']
+ }
+}
diff --git a/tempest/api_schema/compute/v3/aggregates.py b/tempest/api_schema/compute/v3/aggregates.py
index 358e455..0272641 100644
--- a/tempest/api_schema/compute/v3/aggregates.py
+++ b/tempest/api_schema/compute/v3/aggregates.py
@@ -12,6 +12,18 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
+
+from tempest.api_schema.compute import aggregates
+
delete_aggregate = {
'status_code': [204]
}
+
+create_aggregate = copy.deepcopy(aggregates.common_create_aggregate)
+# V3 API's response status_code is 201
+create_aggregate['status_code'] = [201]
+
+aggregate_add_remove_host = copy.deepcopy(aggregates.aggregate_add_remove_host)
+# V3 API's response status_code is 202
+aggregate_add_remove_host['status_code'] = [202]
diff --git a/tempest/api_schema/compute/v3/hosts.py b/tempest/api_schema/compute/v3/hosts.py
index 575a6e2..eb689d1 100644
--- a/tempest/api_schema/compute/v3/hosts.py
+++ b/tempest/api_schema/compute/v3/hosts.py
@@ -16,7 +16,6 @@
from tempest.api_schema.compute import hosts
-
startup_host = {
'status_code': [200],
'response_body': {
@@ -41,3 +40,14 @@
reboot_host['response_body']['properties']['power_action'] = {
'enum': ['reboot']
}
+
+update_host = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'host': hosts.update_host_common
+ },
+ 'required': ['host']
+ }
+}
diff --git a/tempest/api_schema/compute/v3/interfaces.py b/tempest/api_schema/compute/v3/interfaces.py
new file mode 100644
index 0000000..5e1cee2
--- /dev/null
+++ b/tempest/api_schema/compute/v3/interfaces.py
@@ -0,0 +1,29 @@
+# Copyright 2014 NEC Corporation. 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 tempest.api_schema.compute import interfaces as common_schema
+
+list_interfaces = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'interface_attachments': {
+ 'type': 'array',
+ 'items': common_schema.interface_common_info
+ }
+ },
+ 'required': ['interface_attachments']
+ }
+}
diff --git a/tempest/api_schema/compute/v3/quotas.py b/tempest/api_schema/compute/v3/quotas.py
index aec1e80..a3212ed 100644
--- a/tempest/api_schema/compute/v3/quotas.py
+++ b/tempest/api_schema/compute/v3/quotas.py
@@ -12,34 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
-quota_set = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'quota_set': {
- 'type': 'object',
- 'properties': {
- 'id': {'type': 'string'},
- 'instances': {'type': 'integer'},
- 'cores': {'type': 'integer'},
- 'ram': {'type': 'integer'},
- 'floating_ips': {'type': 'integer'},
- 'fixed_ips': {'type': 'integer'},
- 'metadata_items': {'type': 'integer'},
- 'key_pairs': {'type': 'integer'},
- 'security_groups': {'type': 'integer'},
- 'security_group_rules': {'type': 'integer'}
- },
- 'required': ['id', 'instances', 'cores', 'ram',
- 'floating_ips', 'fixed_ips',
- 'metadata_items', 'key_pairs',
- 'security_groups', 'security_group_rules']
- }
- },
- 'required': ['quota_set']
- }
-}
+import copy
+
+from tempest.api_schema.compute import quotas
+
+quota_set = copy.deepcopy(quotas.common_quota_set)
+quota_set['response_body']['properties']['quota_set']['properties'][
+ 'id'] = {'type': 'string'}
+quota_set['response_body']['properties']['quota_set'][
+ 'required'].extend(['id'])
quota_common_info = {
'type': 'object',
@@ -51,34 +32,27 @@
'required': ['reserved', 'limit', 'in_use']
}
-quota_set_detail = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'quota_set': {
- 'type': 'object',
- 'properties': {
- 'id': {'type': 'string'},
- 'instances': quota_common_info,
- 'cores': quota_common_info,
- 'ram': quota_common_info,
- 'floating_ips': quota_common_info,
- 'fixed_ips': quota_common_info,
- 'metadata_items': quota_common_info,
- 'key_pairs': quota_common_info,
- 'security_groups': quota_common_info,
- 'security_group_rules': quota_common_info
- },
- 'required': ['id', 'instances', 'cores', 'ram',
- 'floating_ips', 'fixed_ips',
- 'metadata_items', 'key_pairs',
- 'security_groups', 'security_group_rules']
- }
- },
- 'required': ['quota_set']
- }
-}
+quota_set_detail = copy.deepcopy(quotas.common_quota_set)
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'id'] = {'type': 'string'}
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'instances'] = quota_common_info
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'cores'] = quota_common_info
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'ram'] = quota_common_info
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'floating_ips'] = quota_common_info
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'fixed_ips'] = quota_common_info
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'metadata_items'] = quota_common_info
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'key_pairs'] = quota_common_info
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'security_groups'] = quota_common_info
+quota_set_detail['response_body']['properties']['quota_set']['properties'][
+ 'security_group_rules'] = quota_common_info
delete_quota = {
'status_code': [204]
diff --git a/tempest/api_schema/compute/v3/servers.py b/tempest/api_schema/compute/v3/servers.py
index cace7d2..956b5ad 100644
--- a/tempest/api_schema/compute/v3/servers.py
+++ b/tempest/api_schema/compute/v3/servers.py
@@ -13,6 +13,7 @@
# under the License.
import copy
+
from tempest.api_schema.compute import parameter_types
from tempest.api_schema.compute import servers
@@ -31,8 +32,8 @@
'os-security-groups:security_groups': {'type': 'array'},
'links': parameter_types.links,
'admin_password': {'type': 'string'},
- 'os-access-ips:access_ip_v4': {'type': 'string'},
- 'os-access-ips:access_ip_v6': {'type': 'string'}
+ 'os-access-ips:access_ip_v4': parameter_types.access_ip_v4,
+ 'os-access-ips:access_ip_v6': parameter_types.access_ip_v6
},
# NOTE: os-access-ips:access_ip_v4/v6 are API extension,
# and some environments return a response without these
@@ -45,8 +46,38 @@
}
}
+addresses_v3 = copy.deepcopy(parameter_types.addresses)
+addresses_v3['patternProperties']['^[a-zA-Z0-9-_.]+$']['items'][
+ 'properties'].update({
+ 'type': {'type': 'string'},
+ 'mac_addr': {'type': 'string'}
+ })
+addresses_v3['patternProperties']['^[a-zA-Z0-9-_.]+$']['items'][
+ 'required'].extend(
+ ['type', 'mac_addr']
+ )
+
+update_server = copy.deepcopy(servers.base_update_server)
+update_server['response_body']['properties']['server']['properties'].update({
+ 'addresses': addresses_v3,
+ 'host_id': {'type': 'string'},
+ 'os-access-ips:access_ip_v4': parameter_types.access_ip_v4,
+ 'os-access-ips:access_ip_v6': parameter_types.access_ip_v6
+})
+update_server['response_body']['properties']['server']['required'].append(
+ # NOTE: os-access-ips:access_ip_v4/v6 are API extension,
+ # and some environments return a response without these
+ # attributes. So they are not 'required'.
+ 'host_id'
+)
+
attach_detach_volume = {
'status_code': [202]
}
set_get_server_metadata_item = copy.deepcopy(servers.set_server_metadata)
+
+list_addresses_by_network = {
+ 'status_code': [200],
+ 'response_body': addresses_v3
+}
diff --git a/tempest/auth.py b/tempest/auth.py
index ac8cbd1..9c51edb 100644
--- a/tempest/auth.py
+++ b/tempest/auth.py
@@ -80,6 +80,17 @@
def _get_auth(self):
raise NotImplementedError
+ def _fill_credentials(self, auth_data_body):
+ raise NotImplementedError
+
+ def fill_credentials(self):
+ """
+ Fill credentials object with data from auth
+ """
+ auth_data = self.get_auth()
+ self._fill_credentials(auth_data[1])
+ return self.credentials
+
@classmethod
def check_credentials(cls, credentials):
"""
@@ -89,20 +100,35 @@
@property
def auth_data(self):
- if self.cache is None or self.is_expired(self.cache):
- self.cache = self._get_auth()
- return self.cache
+ return self.get_auth()
@auth_data.deleter
def auth_data(self):
self.clear_auth()
+ def get_auth(self):
+ """
+ Returns auth from cache if available, else auth first
+ """
+ if self.cache is None or self.is_expired(self.cache):
+ self.set_auth()
+ return self.cache
+
+ def set_auth(self):
+ """
+ Forces setting auth, ignores cache if it exists.
+ Refills credentials
+ """
+ self.cache = self._get_auth()
+ self._fill_credentials(self.cache[1])
+
def clear_auth(self):
"""
Can be called to clear the access cache so that next request
will fetch a new token and base_url.
"""
self.cache = None
+ self.credentials.reset()
def is_expired(self, auth_data):
raise NotImplementedError
@@ -244,6 +270,18 @@
else:
raise NotImplementedError
+ def _fill_credentials(self, auth_data_body):
+ tenant = auth_data_body['token']['tenant']
+ user = auth_data_body['user']
+ if self.credentials.tenant_name is None:
+ self.credentials.tenant_name = tenant['name']
+ if self.credentials.tenant_id is None:
+ self.credentials.tenant_id = tenant['id']
+ if self.credentials.username is None:
+ self.credentials.username = user['name']
+ if self.credentials.user_id is None:
+ self.credentials.user_id = user['id']
+
def base_url(self, filters, auth_data=None):
"""
Filters can be:
@@ -300,19 +338,6 @@
EXPIRY_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
- def _convert_credentials(self, credentials):
- # For V3 do not convert as V3 Credentials are not defined yet
- return credentials
-
- @classmethod
- def check_credentials(cls, credentials, scoped=True):
- # tenant_name is optional if not scoped
- valid = 'username' in credentials and 'password' in credentials \
- and 'domain_name' in credentials
- if scoped:
- valid = valid and 'tenant_name' in credentials
- return valid
-
def _auth_client(self):
if self.client_type == 'tempest':
if self.interface == 'json':
@@ -325,14 +350,47 @@
def _auth_params(self):
if self.client_type == 'tempest':
return dict(
- user=self.credentials['username'],
- password=self.credentials['password'],
- tenant=self.credentials['tenant_name'],
- domain=self.credentials['domain_name'],
+ user=self.credentials.username,
+ password=self.credentials.password,
+ tenant=self.credentials.tenant_name,
+ domain=self.credentials.user_domain_name,
auth_data=True)
else:
raise NotImplementedError
+ def _fill_credentials(self, auth_data_body):
+ # project or domain, depending on the scope
+ project = auth_data_body.get('project', None)
+ domain = auth_data_body.get('domain', None)
+ # user is always there
+ user = auth_data_body['user']
+ # Set project fields
+ if project is not None:
+ if self.credentials.project_name is None:
+ self.credentials.project_name = project['name']
+ if self.credentials.project_id is None:
+ self.credentials.project_id = project['id']
+ if self.credentials.project_domain_id is None:
+ self.credentials.project_domain_id = project['domain']['id']
+ if self.credentials.project_domain_name is None:
+ self.credentials.project_domain_name = \
+ project['domain']['name']
+ # Set domain fields
+ if domain is not None:
+ if self.credentials.domain_id is None:
+ self.credentials.domain_id = domain['id']
+ if self.credentials.domain_name is None:
+ self.credentials.domain_name = domain['name']
+ # Set user fields
+ if self.credentials.username is None:
+ self.credentials.username = user['name']
+ if self.credentials.user_id is None:
+ self.credentials.user_id = user['id']
+ if self.credentials.user_domain_id is None:
+ self.credentials.user_domain_id = user['domain']['id']
+ if self.credentials.user_domain_name is None:
+ self.credentials.user_domain_name = user['domain']['name']
+
def base_url(self, filters, auth_data=None):
"""
Filters can be:
@@ -400,7 +458,15 @@
datetime.datetime.utcnow()
-def get_credentials(credential_type=None, **kwargs):
+def get_default_credentials(credential_type, fill_in=True):
+ """
+ Returns configured credentials of the specified type
+ based on the configured auth_version
+ """
+ return get_credentials(fill_in=fill_in, credential_type=credential_type)
+
+
+def get_credentials(credential_type=None, fill_in=True, **kwargs):
"""
Builds a credentials object based on the configured auth_version
@@ -420,12 +486,20 @@
"""
if CONF.identity.auth_version == 'v2':
credential_class = KeystoneV2Credentials
+ auth_provider_class = KeystoneV2AuthProvider
+ elif CONF.identity.auth_version == 'v3':
+ credential_class = KeystoneV3Credentials
+ auth_provider_class = KeystoneV3AuthProvider
else:
raise exceptions.InvalidConfiguration('Unsupported auth version')
if credential_type is not None:
creds = credential_class.get_default(credential_type)
else:
creds = credential_class(**kwargs)
+ # Fill in the credentials fields that were not specified
+ if fill_in:
+ auth_provider = auth_provider_class(creds)
+ creds = auth_provider.fill_credentials()
return creds
@@ -454,6 +528,7 @@
Additional attributes can still be set afterwards if tests need
to do so.
"""
+ self._initial = kwargs
self._apply_credentials(kwargs)
def _apply_credentials(self, attr):
@@ -514,6 +589,14 @@
def is_valid(self):
raise NotImplementedError
+ def reset(self):
+ # First delete all known attributes
+ for key in self.ATTRIBUTES:
+ if getattr(self, key) is not None:
+ delattr(self, key)
+ # Then re-apply initial setup
+ self._apply_credentials(self._initial)
+
class KeystoneV2Credentials(Credentials):
@@ -531,7 +614,7 @@
params[attr] = getattr(_section, attr)
else:
params[attr] = getattr(_section, prefix + "_" + attr)
- return KeystoneV2Credentials(**params)
+ return cls(**params)
def is_valid(self):
"""
@@ -539,3 +622,82 @@
Tenant is optional.
"""
return None not in (self.username, self.password)
+
+
+class KeystoneV3Credentials(KeystoneV2Credentials):
+ """
+ Credentials suitable for the Keystone Identity V3 API
+ """
+
+ CONF_ATTRIBUTES = ['domain_name', 'password', 'tenant_name', 'username']
+ ATTRIBUTES = ['project_domain_id', 'project_domain_name', 'project_id',
+ 'project_name', 'tenant_id', 'tenant_name', 'user_domain_id',
+ 'user_domain_name', 'user_id']
+ ATTRIBUTES.extend(CONF_ATTRIBUTES)
+
+ def __init__(self, **kwargs):
+ """
+ If domain is not specified, load the one configured for the
+ identity manager.
+ """
+ domain_fields = set(x for x in self.ATTRIBUTES if 'domain' in x)
+ if not domain_fields.intersection(kwargs.keys()):
+ kwargs['user_domain_name'] = CONF.identity.admin_domain_name
+ super(KeystoneV3Credentials, self).__init__(**kwargs)
+
+ def __setattr__(self, key, value):
+ parent = super(KeystoneV3Credentials, self)
+ # for tenant_* set both project and tenant
+ if key == 'tenant_id':
+ parent.__setattr__('project_id', value)
+ elif key == 'tenant_name':
+ parent.__setattr__('project_name', value)
+ # for project_* set both project and tenant
+ if key == 'project_id':
+ parent.__setattr__('tenant_id', value)
+ elif key == 'project_name':
+ parent.__setattr__('tenant_name', value)
+ # for *_domain_* set both user and project if not set yet
+ if key == 'user_domain_id':
+ if self.project_domain_id is None:
+ parent.__setattr__('project_domain_id', value)
+ if key == 'project_domain_id':
+ if self.user_domain_id is None:
+ parent.__setattr__('user_domain_id', value)
+ if key == 'user_domain_name':
+ if self.project_domain_name is None:
+ parent.__setattr__('project_domain_name', value)
+ if key == 'project_domain_name':
+ if self.user_domain_name is None:
+ parent.__setattr__('user_domain_name', value)
+ # support domain_name coming from config
+ if key == 'domain_name':
+ parent.__setattr__('user_domain_name', value)
+ parent.__setattr__('project_domain_name', value)
+ # finally trigger default behaviour for all attributes
+ parent.__setattr__(key, value)
+
+ def is_valid(self):
+ """
+ Valid combinations of v3 credentials (excluding token, scope)
+ - User id, password (optional domain)
+ - User name, password and its domain id/name
+ For the scope, valid combinations are:
+ - None
+ - Project id (optional domain)
+ - Project name and its domain id/name
+ """
+ valid_user_domain = any(
+ [self.user_domain_id is not None,
+ self.user_domain_name is not None])
+ valid_project_domain = any(
+ [self.project_domain_id is not None,
+ self.project_domain_name is not None])
+ valid_user = any(
+ [self.user_id is not None,
+ self.username is not None and valid_user_domain])
+ valid_project = any(
+ [self.project_name is None and self.project_id is None,
+ self.project_id is not None,
+ self.project_name is not None and valid_project_domain])
+ return all([self.password is not None, valid_user, valid_project])
diff --git a/tempest/clients.py b/tempest/clients.py
index 3ec442f..e50a0c3 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -16,6 +16,7 @@
import keystoneclient.exceptions
import keystoneclient.v2_0.client
+from tempest import auth
from tempest.common.rest_client import NegativeRestClient
from tempest import config
from tempest import exceptions
@@ -198,22 +199,12 @@
Top level manager for OpenStack tempest clients
"""
- def __init__(self, username=None, password=None, tenant_name=None,
- interface='json', service=None):
- """
- We allow overriding of the credentials used within the various
- client classes managed by the Manager object. Left as None, the
- standard username/password/tenant_name is used.
-
- :param username: Override of the username
- :param password: Override of the password
- :param tenant_name: Override of the tenant name
- """
+ def __init__(self, credentials=None, interface='json', service=None):
+ # Set interface and client type first
self.interface = interface
self.client_type = 'tempest'
# super cares for credentials validation
- super(Manager, self).__init__(
- username=username, password=password, tenant_name=tenant_name)
+ super(Manager, self).__init__(credentials=credentials)
if self.interface == 'xml':
self.certificates_client = CertificatesClientXML(
@@ -372,10 +363,10 @@
raise exceptions.InvalidConfiguration(msg)
# TODO(andreaf) EC2 client still do their auth, v2 only
- ec2_client_args = (self.credentials.get('username'),
- self.credentials.get('password'),
+ ec2_client_args = (self.credentials.username,
+ self.credentials.password,
CONF.identity.uri,
- self.credentials.get('tenant_name'))
+ self.credentials.tenant_name)
# common clients
self.account_client = AccountClient(self.auth_provider)
@@ -406,11 +397,10 @@
"""
def __init__(self, interface='json', service=None):
- super(AltManager, self).__init__(CONF.identity.alt_username,
- CONF.identity.alt_password,
- CONF.identity.alt_tenant_name,
- interface=interface,
- service=service)
+ super(AltManager, self).__init__(
+ credentials=auth.get_default_credentials('alt_user'),
+ interface=interface,
+ service=service)
class AdminManager(Manager):
@@ -421,11 +411,10 @@
"""
def __init__(self, interface='json', service=None):
- super(AdminManager, self).__init__(CONF.identity.admin_username,
- CONF.identity.admin_password,
- CONF.identity.admin_tenant_name,
- interface=interface,
- service=service)
+ super(AdminManager, self).__init__(
+ credentials=auth.get_default_credentials('identity_admin'),
+ interface=interface,
+ service=service)
class ComputeAdminManager(Manager):
@@ -437,11 +426,10 @@
def __init__(self, interface='json', service=None):
base = super(ComputeAdminManager, self)
- base.__init__(CONF.compute_admin.username,
- CONF.compute_admin.password,
- CONF.compute_admin.tenant_name,
- interface=interface,
- service=service)
+ base.__init__(
+ credentials=auth.get_default_credentials('compute_admin'),
+ interface=interface,
+ service=service)
class OfficialClientManager(manager.Manager):
@@ -456,47 +444,32 @@
IRONICCLIENT_VERSION = '1'
SAHARACLIENT_VERSION = '1.1'
- def __init__(self, username, password, tenant_name):
+ def __init__(self, credentials):
# FIXME(andreaf) Auth provider for client_type 'official' is
# not implemented yet, setting to 'tempest' for now.
self.client_type = 'tempest'
self.interface = None
# super cares for credentials validation
- super(OfficialClientManager, self).__init__(
- username=username, password=password, tenant_name=tenant_name)
+ super(OfficialClientManager, self).__init__(credentials=credentials)
self.baremetal_client = self._get_baremetal_client()
- self.compute_client = self._get_compute_client(username,
- password,
- tenant_name)
- self.identity_client = self._get_identity_client(username,
- password,
- tenant_name)
+ self.compute_client = self._get_compute_client(credentials)
+ self.identity_client = self._get_identity_client(credentials)
self.image_client = self._get_image_client()
self.network_client = self._get_network_client()
- self.volume_client = self._get_volume_client(username,
- password,
- tenant_name)
+ self.volume_client = self._get_volume_client(credentials)
self.object_storage_client = self._get_object_storage_client(
- username,
- password,
- tenant_name)
+ credentials)
self.orchestration_client = self._get_orchestration_client(
- username,
- password,
- tenant_name)
+ credentials)
self.data_processing_client = self._get_data_processing_client(
- username,
- password,
- tenant_name)
+ credentials)
def _get_roles(self):
- keystone_admin = self._get_identity_client(
- CONF.identity.admin_username,
- CONF.identity.admin_password,
- CONF.identity.admin_tenant_name)
+ admin_credentials = auth.get_default_credentials('identity_admin')
+ keystone_admin = self._get_identity_client(admin_credentials)
- username = self.credentials['username']
- tenant_name = self.credentials['tenant_name']
+ username = self.credentials.username
+ tenant_name = self.credentials.tenant_name
user_id = keystone_admin.users.find(name=username).id
tenant_id = keystone_admin.tenants.find(name=tenant_name).id
@@ -505,20 +478,20 @@
return [r.name for r in roles]
- def _get_compute_client(self, username, password, tenant_name):
+ def _get_compute_client(self, credentials):
# Novaclient will not execute operations for anyone but the
# identified user, so a new client needs to be created for
# each user that operations need to be performed for.
if not CONF.service_available.nova:
return None
import novaclient.client
- self._validate_credentials(username, password, tenant_name)
auth_url = CONF.identity.uri
dscv = CONF.identity.disable_ssl_certificate_validation
region = CONF.identity.region
- client_args = (username, password, tenant_name, auth_url)
+ client_args = (credentials.username, credentials.password,
+ credentials.tenant_name, auth_url)
# Create our default Nova client to use in testing
service_type = CONF.compute.catalog_type
@@ -546,7 +519,7 @@
return glanceclient.Client('1', endpoint=endpoint, token=token,
insecure=dscv)
- def _get_volume_client(self, username, password, tenant_name):
+ def _get_volume_client(self, credentials):
if not CONF.service_available.cinder:
return None
import cinderclient.client
@@ -555,25 +528,23 @@
endpoint_type = CONF.volume.endpoint_type
dscv = CONF.identity.disable_ssl_certificate_validation
return cinderclient.client.Client(self.CINDERCLIENT_VERSION,
- username,
- password,
- tenant_name,
+ credentials.username,
+ credentials.password,
+ credentials.tenant_name,
auth_url,
region_name=region,
endpoint_type=endpoint_type,
insecure=dscv,
http_log_debug=True)
- def _get_object_storage_client(self, username, password, tenant_name):
+ def _get_object_storage_client(self, credentials):
if not CONF.service_available.swift:
return None
import swiftclient
auth_url = CONF.identity.uri
# add current tenant to swift operator role group.
- keystone_admin = self._get_identity_client(
- CONF.identity.admin_username,
- CONF.identity.admin_password,
- CONF.identity.admin_tenant_name)
+ admin_credentials = auth.get_default_credentials('identity_admin')
+ keystone_admin = self._get_identity_client(admin_credentials)
# enable test user to operate swift by adding operator role to him.
roles = keystone_admin.roles.list()
@@ -590,26 +561,18 @@
endpoint_type = CONF.object_storage.endpoint_type
os_options = {'endpoint_type': endpoint_type}
- return swiftclient.Connection(auth_url, username, password,
- tenant_name=tenant_name,
+ return swiftclient.Connection(auth_url, credentials.username,
+ credentials.password,
+ tenant_name=credentials.tenant_name,
auth_version='2',
os_options=os_options)
- def _get_orchestration_client(self, username=None, password=None,
- tenant_name=None):
+ def _get_orchestration_client(self, credentials):
if not CONF.service_available.heat:
return None
import heatclient.client
- if not username:
- username = CONF.identity.admin_username
- if not password:
- password = CONF.identity.admin_password
- if not tenant_name:
- tenant_name = CONF.identity.tenant_name
- self._validate_credentials(username, password, tenant_name)
-
- keystone = self._get_identity_client(username, password, tenant_name)
+ keystone = self._get_identity_client(credentials)
region = CONF.identity.region
endpoint_type = CONF.orchestration.endpoint_type
token = keystone.auth_token
@@ -626,22 +589,22 @@
return heatclient.client.Client(self.HEATCLIENT_VERSION,
endpoint,
token=token,
- username=username,
- password=password)
+ username=credentials.username,
+ password=credentials.password)
- def _get_identity_client(self, username, password, tenant_name):
+ def _get_identity_client(self, credentials):
# This identity client is not intended to check the security
# of the identity service, so use admin credentials by default.
- self._validate_credentials(username, password, tenant_name)
auth_url = CONF.identity.uri
dscv = CONF.identity.disable_ssl_certificate_validation
- return keystoneclient.v2_0.client.Client(username=username,
- password=password,
- tenant_name=tenant_name,
- auth_url=auth_url,
- insecure=dscv)
+ return keystoneclient.v2_0.client.Client(
+ username=credentials.username,
+ password=credentials.password,
+ tenant_name=credentials.tenant_name,
+ auth_url=auth_url,
+ insecure=dscv)
def _get_baremetal_client(self):
# ironic client is currently intended to by used by admin users
@@ -658,9 +621,9 @@
service_type = CONF.baremetal.catalog_type
endpoint_type = CONF.baremetal.endpoint_type
creds = {
- 'os_username': self.credentials['username'],
- 'os_password': self.credentials['password'],
- 'os_tenant_name': self.credentials['tenant_name']
+ 'os_username': self.credentials.username,
+ 'os_password': self.credentials.password,
+ 'os_tenant_name': self.credentials.tenant_name
}
try:
@@ -684,41 +647,39 @@
if not CONF.service_available.neutron:
return None
import neutronclient.v2_0.client
- username = CONF.identity.admin_username
- password = CONF.identity.admin_password
- tenant_name = CONF.identity.admin_tenant_name
- self._validate_credentials(username, password, tenant_name)
+ credentials = auth.get_default_credentials('identity_admin')
auth_url = CONF.identity.uri
dscv = CONF.identity.disable_ssl_certificate_validation
endpoint_type = CONF.network.endpoint_type
- return neutronclient.v2_0.client.Client(username=username,
- password=password,
- tenant_name=tenant_name,
- endpoint_type=endpoint_type,
- auth_url=auth_url,
- insecure=dscv)
+ return neutronclient.v2_0.client.Client(
+ username=credentials.username,
+ password=credentials.password,
+ tenant_name=credentials.tenant_name,
+ endpoint_type=endpoint_type,
+ auth_url=auth_url,
+ insecure=dscv)
- def _get_data_processing_client(self, username, password, tenant_name):
+ def _get_data_processing_client(self, credentials):
if not CONF.service_available.sahara:
# Sahara isn't available
return None
import saharaclient.client
- self._validate_credentials(username, password, tenant_name)
-
endpoint_type = CONF.data_processing.endpoint_type
catalog_type = CONF.data_processing.catalog_type
auth_url = CONF.identity.uri
- client = saharaclient.client.Client(self.SAHARACLIENT_VERSION,
- username, password,
- project_name=tenant_name,
- endpoint_type=endpoint_type,
- service_type=catalog_type,
- auth_url=auth_url)
+ client = saharaclient.client.Client(
+ self.SAHARACLIENT_VERSION,
+ credentials.username,
+ credentials.password,
+ project_name=credentials.tenant_name,
+ endpoint_type=endpoint_type,
+ service_type=catalog_type,
+ auth_url=auth_url)
return client
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index c54a8e8..b4618ed 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -14,9 +14,7 @@
import netaddr
-import keystoneclient.v2_0.client as keystoneclient
-import neutronclient.v2_0.client as neutronclient
-
+from tempest import auth
from tempest import clients
from tempest.common.utils import data_utils
from tempest import config
@@ -42,24 +40,6 @@
self.identity_admin_client, self.network_admin_client = (
self._get_admin_clients())
- def _get_official_admin_clients(self):
- username = CONF.identity.admin_username
- password = CONF.identity.admin_password
- tenant_name = CONF.identity.admin_tenant_name
- auth_url = CONF.identity.uri
- dscv = CONF.identity.disable_ssl_certificate_validation
- identity_client = keystoneclient.Client(username=username,
- password=password,
- tenant_name=tenant_name,
- auth_url=auth_url,
- insecure=dscv)
- network_client = neutronclient.Client(username=username,
- password=password,
- tenant_name=tenant_name,
- auth_url=auth_url,
- insecure=dscv)
- return identity_client, network_client
-
def _get_admin_clients(self):
"""
Returns a tuple with instances of the following admin clients (in this
@@ -69,11 +49,11 @@
"""
if self.tempest_client:
os = clients.AdminManager(interface=self.interface)
- admin_clients = (os.identity_client,
- os.network_client,)
else:
- admin_clients = self._get_official_admin_clients()
- return admin_clients
+ os = clients.OfficialClientManager(
+ auth.get_default_credentials('identity_admin')
+ )
+ return os.identity_client, os.network_client
def _create_tenant(self, name, description):
if self.tempest_client:
@@ -185,22 +165,19 @@
self._assign_user_role(tenant['id'], user['id'], role['id'])
else:
self._assign_user_role(tenant.id, user.id, role.id)
- return user, tenant
+ return self._get_credentials(user, tenant)
- def _get_cred_names(self, user, tenant):
+ def _get_credentials(self, user, tenant):
if self.tempest_client:
- username = user.get('name')
- tenant_name = tenant.get('name')
+ user_get = user.get
+ tenant_get = tenant.get
else:
- username = user.name
- tenant_name = tenant.name
- return username, tenant_name
-
- def _get_tenant_id(self, tenant):
- if self.tempest_client:
- return tenant.get('id')
- else:
- return tenant.id
+ user_get = user.__dict__.get
+ tenant_get = tenant.__dict__.get
+ return auth.get_credentials(
+ username=user_get('name'), user_id=user_get('id'),
+ tenant_name=tenant_get('name'), tenant_id=tenant_get('id'),
+ password=self.password)
def _create_network_resources(self, tenant_id):
network = None
@@ -314,24 +291,6 @@
body = {'subnet_id': subnet_id}
self.network_admin_client.add_interface_router(router_id, body)
- def get_primary_tenant(self):
- return self.isolated_creds.get('primary')[1]
-
- def get_primary_user(self):
- return self.isolated_creds.get('primary')[0]
-
- def get_alt_tenant(self):
- return self.isolated_creds.get('alt')[1]
-
- def get_alt_user(self):
- return self.isolated_creds.get('alt')[0]
-
- def get_admin_tenant(self):
- return self.isolated_creds.get('admin')[1]
-
- def get_admin_user(self):
- return self.isolated_creds.get('admin')[0]
-
def get_primary_network(self):
return self.isolated_net_resources.get('primary')[0]
@@ -359,62 +318,33 @@
def get_alt_router(self):
return self.isolated_net_resources.get('alt')[2]
- def get_primary_creds(self):
- if self.isolated_creds.get('primary'):
- user, tenant = self.isolated_creds['primary']
- username, tenant_name = self._get_cred_names(user, tenant)
+ def get_credentials(self, credential_type):
+ if self.isolated_creds.get(credential_type):
+ credentials = self.isolated_creds[credential_type]
else:
- user, tenant = self._create_creds()
- username, tenant_name = self._get_cred_names(user, tenant)
- self.isolated_creds['primary'] = (user, tenant)
- LOG.info("Acquired isolated creds:\n user: %s, tenant: %s"
- % (username, tenant_name))
+ is_admin = (credential_type == 'admin')
+ credentials = self._create_creds(admin=is_admin)
+ self.isolated_creds[credential_type] = credentials
+ # Maintained until tests are ported
+ LOG.info("Acquired isolated creds:\n credentials: %s"
+ % credentials)
if CONF.service_available.neutron:
network, subnet, router = self._create_network_resources(
- self._get_tenant_id(tenant))
- self.isolated_net_resources['primary'] = (
+ credentials.tenant_id)
+ self.isolated_net_resources[credential_type] = (
network, subnet, router,)
LOG.info("Created isolated network resources for : \n"
- + " user: %s, tenant: %s" % (username, tenant_name))
- return username, tenant_name, self.password
+ + " credentials: %s" % credentials)
+ return credentials
+
+ def get_primary_creds(self):
+ return self.get_credentials('primary')
def get_admin_creds(self):
- if self.isolated_creds.get('admin'):
- user, tenant = self.isolated_creds['admin']
- username, tenant_name = self._get_cred_names(user, tenant)
- else:
- user, tenant = self._create_creds(admin=True)
- username, tenant_name = self._get_cred_names(user, tenant)
- self.isolated_creds['admin'] = (user, tenant)
- LOG.info("Acquired admin isolated creds:\n user: %s, tenant: %s"
- % (username, tenant_name))
- if CONF.service_available.neutron:
- network, subnet, router = self._create_network_resources(
- self._get_tenant_id(tenant))
- self.isolated_net_resources['admin'] = (
- network, subnet, router,)
- LOG.info("Created isolated network resources for : \n"
- + " user: %s, tenant: %s" % (username, tenant_name))
- return username, tenant_name, self.password
+ return self.get_credentials('admin')
def get_alt_creds(self):
- if self.isolated_creds.get('alt'):
- user, tenant = self.isolated_creds['alt']
- username, tenant_name = self._get_cred_names(user, tenant)
- else:
- user, tenant = self._create_creds()
- username, tenant_name = self._get_cred_names(user, tenant)
- self.isolated_creds['alt'] = (user, tenant)
- LOG.info("Acquired alt isolated creds:\n user: %s, tenant: %s"
- % (username, tenant_name))
- if CONF.service_available.neutron:
- network, subnet, router = self._create_network_resources(
- self._get_tenant_id(tenant))
- self.isolated_net_resources['alt'] = (
- network, subnet, router,)
- LOG.info("Created isolated network resources for : \n"
- + " user: %s, tenant: %s" % (username, tenant_name))
- return username, tenant_name, self.password
+ return self.get_credentials('alt')
def _clear_isolated_router(self, router_id, router_name):
net_client = self.network_admin_client
@@ -423,7 +353,6 @@
except exceptions.NotFound:
LOG.warn('router with name: %s not found for delete' %
router_name)
- pass
def _clear_isolated_subnet(self, subnet_id, subnet_name):
net_client = self.network_admin_client
@@ -432,7 +361,6 @@
except exceptions.NotFound:
LOG.warn('subnet with name: %s not found for delete' %
subnet_name)
- pass
def _clear_isolated_network(self, network_id, network_name):
net_client = self.network_admin_client
@@ -441,7 +369,6 @@
except exceptions.NotFound:
LOG.warn('network with name: %s not found for delete' %
network_name)
- pass
def _cleanup_ports(self, network_id):
# TODO(mlavalle) This method will be removed once patch
@@ -487,7 +414,6 @@
except exceptions.NotFound:
LOG.warn('router with name: %s not found for delete' %
router['name'])
- pass
self._clear_isolated_router(router['id'], router['name'])
if (not self.network_resources or
self.network_resources.get('network')):
@@ -505,29 +431,14 @@
if not self.isolated_creds:
return
self._clear_isolated_net_resources()
- for cred in self.isolated_creds:
- user, tenant = self.isolated_creds.get(cred)
+ for creds in self.isolated_creds.itervalues():
try:
- if self.tempest_client:
- self._delete_user(user['id'])
- else:
- self._delete_user(user.id)
+ self._delete_user(creds.user_id)
except exceptions.NotFound:
- if self.tempest_client:
- name = user['name']
- else:
- name = user.name
- LOG.warn("user with name: %s not found for delete" % name)
- pass
+ LOG.warn("user with name: %s not found for delete" %
+ creds.username)
try:
- if self.tempest_client:
- self._delete_tenant(tenant['id'])
- else:
- self._delete_tenant(tenant.id)
+ self._delete_tenant(creds.tenant_id)
except exceptions.NotFound:
- if self.tempest_client:
- name = tenant['name']
- else:
- name = tenant.name
- LOG.warn("tenant with name: %s not found for delete" % name)
- pass
+ LOG.warn("tenant with name: %s not found for delete" %
+ creds.tenant_name)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 8667445..10223a0 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -15,7 +15,6 @@
# under the License.
import collections
-import inspect
import json
from lxml import etree
import re
@@ -24,6 +23,7 @@
import jsonschema
from tempest.common import http
+from tempest.common.utils import misc as misc_utils
from tempest.common import xml_utils as common
from tempest import config
from tempest import exceptions
@@ -140,15 +140,23 @@
@property
def user(self):
- return self.auth_provider.credentials.get('username', None)
+ return self.auth_provider.credentials.username
+
+ @property
+ def user_id(self):
+ return self.auth_provider.credentials.user_id
@property
def tenant_name(self):
- return self.auth_provider.credentials.get('tenant_name', None)
+ return self.auth_provider.credentials.tenant_name
+
+ @property
+ def tenant_id(self):
+ return self.auth_provider.credentials.tenant_id
@property
def password(self):
- return self.auth_provider.credentials.get('password', None)
+ return self.auth_provider.credentials.password
@property
def base_url(self):
@@ -224,57 +232,6 @@
versions = map(lambda x: x['id'], body)
return resp, versions
- def _find_caller(self):
- """Find the caller class and test name.
-
- Because we know that the interesting things that call us are
- test_* methods, and various kinds of setUp / tearDown, we
- can look through the call stack to find appropriate methods,
- and the class we were in when those were called.
- """
- caller_name = None
- names = []
- frame = inspect.currentframe()
- is_cleanup = False
- # Start climbing the ladder until we hit a good method
- while True:
- try:
- frame = frame.f_back
- name = frame.f_code.co_name
- names.append(name)
- if re.search("^(test_|setUp|tearDown)", name):
- cname = ""
- if 'self' in frame.f_locals:
- cname = frame.f_locals['self'].__class__.__name__
- if 'cls' in frame.f_locals:
- cname = frame.f_locals['cls'].__name__
- caller_name = cname + ":" + name
- break
- elif re.search("^_run_cleanup", name):
- is_cleanup = True
- else:
- cname = ""
- if 'self' in frame.f_locals:
- cname = frame.f_locals['self'].__class__.__name__
- if 'cls' in frame.f_locals:
- cname = frame.f_locals['cls'].__name__
-
- # the fact that we are running cleanups is indicated pretty
- # deep in the stack, so if we see that we want to just
- # start looking for a real class name, and declare victory
- # once we do.
- if is_cleanup and cname:
- if not re.search("^RunTest", cname):
- caller_name = cname + ":_run_cleanups"
- break
- except Exception:
- break
- # prevents frame leaks
- del frame
- if caller_name is None:
- self.LOG.debug("Sane call name not found in %s" % names)
- return caller_name
-
def _get_request_id(self, resp):
for i in ('x-openstack-request-id', 'x-compute-request-id'):
if i in resp:
@@ -290,7 +247,7 @@
# we're going to just provide work around on who is actually
# providing timings by gracefully adding no content if they don't.
# Once we're down to 1 caller, clean this up.
- caller_name = self._find_caller()
+ caller_name = misc_utils.find_test_caller()
if secs:
secs = " %.3fs" % secs
self.LOG.info(
diff --git a/tempest/common/utils/misc.py b/tempest/common/utils/misc.py
index a0b0c0a..b9f411b 100644
--- a/tempest/common/utils/misc.py
+++ b/tempest/common/utils/misc.py
@@ -13,6 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+import inspect
+import re
+
+from tempest.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
def singleton(cls):
"""Simple wrapper for classes that should only have a single instance."""
@@ -23,3 +30,55 @@
instances[cls] = cls()
return instances[cls]
return getinstance
+
+
+def find_test_caller():
+ """Find the caller class and test name.
+
+ Because we know that the interesting things that call us are
+ test_* methods, and various kinds of setUp / tearDown, we
+ can look through the call stack to find appropriate methods,
+ and the class we were in when those were called.
+ """
+ caller_name = None
+ names = []
+ frame = inspect.currentframe()
+ is_cleanup = False
+ # Start climbing the ladder until we hit a good method
+ while True:
+ try:
+ frame = frame.f_back
+ name = frame.f_code.co_name
+ names.append(name)
+ if re.search("^(test_|setUp|tearDown)", name):
+ cname = ""
+ if 'self' in frame.f_locals:
+ cname = frame.f_locals['self'].__class__.__name__
+ if 'cls' in frame.f_locals:
+ cname = frame.f_locals['cls'].__name__
+ caller_name = cname + ":" + name
+ break
+ elif re.search("^_run_cleanup", name):
+ is_cleanup = True
+ else:
+ cname = ""
+ if 'self' in frame.f_locals:
+ cname = frame.f_locals['self'].__class__.__name__
+ if 'cls' in frame.f_locals:
+ cname = frame.f_locals['cls'].__name__
+
+ # the fact that we are running cleanups is indicated pretty
+ # deep in the stack, so if we see that we want to just
+ # start looking for a real class name, and declare victory
+ # once we do.
+ if is_cleanup and cname:
+ if not re.search("^RunTest", cname):
+ caller_name = cname + ":_run_cleanups"
+ break
+ except Exception:
+ break
+ # prevents frame leaks
+ del frame
+ if caller_name is None:
+ LOG.debug("Sane call name not found in %s" % names)
+ return caller_name
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 8e6b9fb..d52ed7c 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -13,6 +13,7 @@
import time
+from tempest.common.utils import misc as misc_utils
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
@@ -86,6 +87,9 @@
'timeout': timeout})
message += ' Current status: %s.' % server_status
message += ' Current task state: %s.' % task_state
+ caller = misc_utils.find_test_caller()
+ if caller:
+ message = '(%s) %s' % (caller, message)
raise exceptions.TimeoutException(message)
old_status = server_status
old_task_state = task_state
@@ -119,4 +123,7 @@
'status': status,
'timeout': client.build_timeout})
message += ' Current status: %s.' % image['status']
+ caller = misc_utils.find_test_caller()
+ if caller:
+ message = '(%s) %s' % (caller, message)
raise exceptions.TimeoutException(message)
diff --git a/tempest/config.py b/tempest/config.py
index f721ab8..f9be90d 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -72,6 +72,10 @@
default=None,
help="API key to use when authenticating.",
secret=True),
+ cfg.StrOpt('domain_name',
+ default=None,
+ help="Domain name for authentication (Keystone V3)."
+ "The same domain applies to user and project"),
cfg.StrOpt('alt_username',
default=None,
help="Username of alternate user to use for Nova API "
@@ -84,6 +88,10 @@
default=None,
help="API key to use when authenticating as alternate user.",
secret=True),
+ cfg.StrOpt('alt_domain_name',
+ default=None,
+ help="Alternate domain name for authentication (Keystone V3)."
+ "The same domain applies to user and project"),
cfg.StrOpt('admin_username',
default=None,
help="Administrative Username to use for "
@@ -96,6 +104,10 @@
default=None,
help="API key to use when authenticating as admin.",
secret=True),
+ cfg.StrOpt('admin_domain_name',
+ default=None,
+ help="Admin domain name for authentication (Keystone V3)."
+ "The same domain applies to user and project"),
]
identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
@@ -283,7 +295,15 @@
cfg.BoolOpt('vnc_console',
default=False,
help='Enable VNC console. This configuration value should '
- 'be same as [nova.vnc]->vnc_enabled in nova.conf')
+ 'be same as [nova.vnc]->vnc_enabled in nova.conf'),
+ cfg.BoolOpt('spice_console',
+ default=False,
+ help='Enable Spice console. This configuration value should '
+ 'be same as [nova.spice]->enabled in nova.conf'),
+ cfg.BoolOpt('rdp_console',
+ default=False,
+ help='Enable RDP console. This configuration value should '
+ 'be same as [nova.rdp]->enabled in nova.conf')
]
@@ -302,6 +322,10 @@
default=None,
help="API key to use when authenticating as admin.",
secret=True),
+ cfg.StrOpt('domain_name',
+ default=None,
+ help="Domain name for authentication as admin (Keystone V3)."
+ "The same domain applies to user and project"),
]
image_group = cfg.OptGroup(name='image',
@@ -999,6 +1023,13 @@
self.compute_admin.username = self.identity.admin_username
self.compute_admin.password = self.identity.admin_password
self.compute_admin.tenant_name = self.identity.admin_tenant_name
+ cfg.CONF.set_default('domain_name', self.identity.admin_domain_name,
+ group='identity')
+ cfg.CONF.set_default('alt_domain_name',
+ self.identity.admin_domain_name,
+ group='identity')
+ cfg.CONF.set_default('domain_name', self.identity.admin_domain_name,
+ group='compute-admin')
def __init__(self, parse_conf=True):
"""Initialize a configuration from a conf directory and conf file."""
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 2e2c5ec..4eb1cea 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -118,7 +118,7 @@
class StackResourceBuildErrorException(TempestException):
- message = ("Resource %(resource_name) in stack %(stack_identifier)s is "
+ message = ("Resource %(resource_name)s in stack %(stack_identifier)s is "
"in %(resource_status)s status due to "
"'%(resource_status_reason)s'")
diff --git a/tempest/manager.py b/tempest/manager.py
index 63235db..fb2842f 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -29,7 +29,7 @@
and a client object for a test case to use in performing actions.
"""
- def __init__(self, username=None, password=None, tenant_name=None):
+ def __init__(self, credentials=None):
"""
We allow overriding of the credentials used within the various
client classes managed by the Manager object. Left as None, the
@@ -38,29 +38,18 @@
:param credentials: Override of the credentials
"""
self.auth_version = CONF.identity.auth_version
- # FIXME(andreaf) Change Manager __init__ to accept a credentials dict
- if username is None or password is None:
- # Tenant None is a valid use case
- self.credentials = self.get_default_credentials()
+ if credentials is None:
+ self.credentials = auth.get_default_credentials('user')
else:
- self.credentials = dict(username=username, password=password,
- tenant_name=tenant_name)
- if self.auth_version == 'v3':
- self.credentials['domain_name'] = 'Default'
+ self.credentials = credentials
+ # Check if passed or default credentials are valid
+ if not self.credentials.is_valid():
+ raise exceptions.InvalidCredentials()
# Creates an auth provider for the credentials
self.auth_provider = self.get_auth_provider(self.credentials)
# FIXME(andreaf) unused
self.client_attr_names = []
- # we do this everywhere, have it be part of the super class
- def _validate_credentials(self, username, password, tenant_name):
- if None in (username, password, tenant_name):
- msg = ("Missing required credentials. "
- "username: %(u)s, password: %(p)s, "
- "tenant_name: %(t)s" %
- {'u': username, 'p': password, 't': tenant_name})
- raise exceptions.InvalidConfiguration(msg)
-
@classmethod
def get_auth_provider_class(cls, auth_version):
if auth_version == 'v2':
@@ -68,13 +57,6 @@
else:
return auth.KeystoneV3AuthProvider
- def get_default_credentials(self):
- return dict(
- username=CONF.identity.username,
- password=CONF.identity.password,
- tenant_name=CONF.identity.tenant_name
- )
-
def get_auth_provider(self, credentials):
if credentials is None:
raise exceptions.InvalidCredentials(
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 1e7ddb1..0ef34c6 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -24,6 +24,7 @@
from novaclient import exceptions as nova_exceptions
from tempest.api.network import common as net_common
+from tempest import auth
from tempest import clients
from tempest.common import isolated_creds
from tempest.common.utils import data_utils
@@ -65,10 +66,8 @@
cls.__name__, tempest_client=False,
network_resources=cls.network_resources)
- username, password, tenant_name = cls.credentials()
-
cls.manager = clients.OfficialClientManager(
- username, password, tenant_name)
+ credentials=cls.credentials())
cls.compute_client = cls.manager.compute_client
cls.image_client = cls.manager.image_client
cls.baremetal_client = cls.manager.baremetal_client
@@ -82,27 +81,27 @@
cls.os_resources = []
@classmethod
- def _get_credentials(cls, get_creds, prefix):
+ def _get_credentials(cls, get_creds, ctype):
if CONF.compute.allow_tenant_isolation:
- username, tenant_name, password = get_creds()
+ creds = get_creds()
else:
- username = getattr(CONF.identity, prefix + 'username')
- password = getattr(CONF.identity, prefix + 'password')
- tenant_name = getattr(CONF.identity, prefix + 'tenant_name')
- return username, password, tenant_name
+ creds = auth.get_default_credentials(ctype)
+ return creds
@classmethod
def credentials(cls):
- return cls._get_credentials(cls.isolated_creds.get_primary_creds, '')
+ return cls._get_credentials(cls.isolated_creds.get_primary_creds,
+ 'user')
@classmethod
def alt_credentials(cls):
- return cls._get_credentials(cls.isolated_creds.get_alt_creds, 'alt_')
+ return cls._get_credentials(cls.isolated_creds.get_alt_creds,
+ 'alt_user')
@classmethod
def admin_credentials(cls):
return cls._get_credentials(cls.isolated_creds.get_admin_creds,
- 'admin_')
+ 'identity_admin')
@staticmethod
def cleanup_resource(resource, test_name):
@@ -453,8 +452,8 @@
raise cls.skipException(msg)
# use an admin client manager for baremetal client
- username, password, tenant = cls.admin_credentials()
- manager = clients.OfficialClientManager(username, password, tenant)
+ admin_creds = cls.admin_credentials()
+ manager = clients.OfficialClientManager(credentials=admin_creds)
cls.baremetal_client = manager.baremetal_client
# allow any issues obtaining the node list to raise early
@@ -541,13 +540,7 @@
@classmethod
def setUpClass(cls):
super(NetworkScenarioTest, cls).setUpClass()
- if CONF.compute.allow_tenant_isolation:
- cls.tenant_id = cls.isolated_creds.get_primary_tenant().id
- else:
- cls.tenant_id = cls.manager._get_identity_client(
- CONF.identity.username,
- CONF.identity.password,
- CONF.identity.tenant_name).tenant_id
+ cls.tenant_id = cls.manager.identity_client.tenant_id
def _create_network(self, tenant_id, namestart='network-smoke-'):
name = data_utils.rand_name(namestart)
@@ -1053,10 +1046,10 @@
@classmethod
def credentials(cls):
- username = CONF.identity.admin_username
- password = CONF.identity.admin_password
- tenant_name = CONF.identity.tenant_name
- return username, password, tenant_name
+ admin_creds = auth.get_default_credentials('identity_admin')
+ creds = auth.get_default_credentials('user')
+ admin_creds.tenant_name = creds.tenant_name
+ return admin_creds
def _load_template(self, base_file, file_name):
filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index c76a117..4616b82 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -98,17 +98,10 @@
access point
"""
- def __init__(self, tenant_id, tenant_user, tenant_pass, tenant_name):
- self.manager = clients.OfficialClientManager(
- tenant_user,
- tenant_pass,
- tenant_name
- )
- self.keypair = None
- self.tenant_id = tenant_id
- self.tenant_name = tenant_name
- self.tenant_user = tenant_user
- self.tenant_pass = tenant_pass
+ def __init__(self, credentials):
+ self.manager = clients.OfficialClientManager(credentials)
+ # Credentials from manager are filled with both names and IDs
+ self.creds = self.manager.credentials
self.network = None
self.subnet = None
self.router = None
@@ -121,12 +114,14 @@
self.router = router
def _get_tenant_credentials(self):
- return self.tenant_user, self.tenant_pass, self.tenant_name
+ # FIXME(andreaf) Unused method
+ return self.creds
@classmethod
def check_preconditions(cls):
super(TestSecurityGroupsBasicOps, cls).check_preconditions()
- if (cls.alt_tenant_id is None) or (cls.tenant_id is cls.alt_tenant_id):
+ if (cls.alt_creds is None) or \
+ (cls.tenant_id is cls.alt_creds.tenant_id):
msg = 'No alt_tenant defined'
cls.enabled = False
raise cls.skipException(msg)
@@ -140,21 +135,20 @@
@classmethod
def setUpClass(cls):
super(TestSecurityGroupsBasicOps, cls).setUpClass()
- alt_creds = cls.alt_credentials()
- cls.alt_tenant_id = cls.manager._get_identity_client(
- *alt_creds
- ).tenant_id
+ cls.alt_creds = cls.alt_credentials()
+ cls.alt_manager = clients.OfficialClientManager(cls.alt_creds)
+ # Credentials from the manager are filled with both IDs and Names
+ cls.alt_creds = cls.alt_manager.credentials
cls.check_preconditions()
# TODO(mnewby) Consider looking up entities as needed instead
# of storing them as collections on the class.
cls.floating_ips = {}
cls.tenants = {}
- cls.primary_tenant = cls.TenantProperties(cls.tenant_id,
- *cls.credentials())
- cls.alt_tenant = cls.TenantProperties(cls.alt_tenant_id,
- *alt_creds)
+ creds = cls.credentials()
+ cls.primary_tenant = cls.TenantProperties(creds)
+ cls.alt_tenant = cls.TenantProperties(cls.alt_creds)
for tenant in [cls.primary_tenant, cls.alt_tenant]:
- cls.tenants[tenant.tenant_id] = tenant
+ cls.tenants[tenant.creds.tenant_id] = tenant
cls.floating_ip_access = not CONF.network.public_router_id
def cleanup_wrapper(self, resource):
@@ -175,14 +169,14 @@
def _create_tenant_security_groups(self, tenant):
access_sg = self._create_empty_security_group(
namestart='secgroup_access-',
- tenant_id=tenant.tenant_id
+ tenant_id=tenant.creds.tenant_id
)
self.addCleanup(self.cleanup_wrapper, access_sg)
# don't use default secgroup since it allows in-tenant traffic
def_sg = self._create_empty_security_group(
namestart='secgroup_general-',
- tenant_id=tenant.tenant_id
+ tenant_id=tenant.creds.tenant_id
)
self.addCleanup(self.cleanup_wrapper, def_sg)
tenant.security_groups.update(access=access_sg, default=def_sg)
@@ -239,7 +233,7 @@
],
'key_name': tenant.keypair.name,
'security_groups': security_groups,
- 'tenant_id': tenant.tenant_id
+ 'tenant_id': tenant.creds.tenant_id
}
server = self.create_server(name=name, create_kwargs=create_kwargs)
self.addCleanup(self.cleanup_wrapper, server)
@@ -248,7 +242,7 @@
def _create_tenant_servers(self, tenant, num=1):
for i in range(num):
name = 'server-{tenant}-gen-{num}-'.format(
- tenant=tenant.tenant_name,
+ tenant=tenant.creds.tenant_name,
num=i
)
name = data_utils.rand_name(name)
@@ -262,8 +256,8 @@
workaround ip namespace
"""
secgroups = [sg.name for sg in tenant.security_groups.values()]
- name = 'server-{tenant}-access_point-'.format(tenant=tenant.tenant_name
- )
+ name = 'server-{tenant}-access_point-'.format(
+ tenant=tenant.creds.tenant_name)
name = data_utils.rand_name(name)
server = self._create_server(name, tenant,
security_groups=secgroups)
@@ -277,7 +271,7 @@
self.floating_ips.setdefault(server, floating_ip)
def _create_tenant_network(self, tenant):
- network, subnet, router = self._create_networks(tenant.tenant_id)
+ network, subnet, router = self._create_networks(tenant.creds.tenant_id)
for r in [network, router, subnet]:
self.addCleanup(self.cleanup_wrapper, r)
tenant.set_network(network, subnet, router)
@@ -300,7 +294,7 @@
tenant_id = tenant_or_id
else:
tenant = tenant_or_id
- tenant_id = tenant.tenant_id
+ tenant_id = tenant.creds.tenant_id
self._set_compute_context(tenant)
self._create_tenant_keypairs(tenant_id)
self._create_tenant_network(tenant)
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
index 4c7b6d7..e2adb34 100644
--- a/tempest/scenario/utils.py
+++ b/tempest/scenario/utils.py
@@ -21,6 +21,7 @@
import testscenarios
import testtools
+from tempest import auth
from tempest import clients
from tempest.common.utils import misc
from tempest import config
@@ -39,9 +40,8 @@
self.non_ssh_image_pattern = \
CONF.input_scenario.non_ssh_image_regex
# Setup clients
- ocm = clients.OfficialClientManager(CONF.identity.username,
- CONF.identity.password,
- CONF.identity.tenant_name)
+ ocm = clients.OfficialClientManager(
+ auth.get_default_credentials('user'))
self.client = ocm.compute_client
def ssh_user(self, image_id):
@@ -99,9 +99,8 @@
digit=string.digits)
def __init__(self):
- ocm = clients.OfficialClientManager(CONF.identity.username,
- CONF.identity.password,
- CONF.identity.tenant_name)
+ ocm = clients.OfficialClientManager(
+ auth.get_default_credentials('user', fill_in=False))
self.client = ocm.compute_client
self.image_pattern = CONF.input_scenario.image_regex
self.flavor_pattern = CONF.input_scenario.flavor_regex
diff --git a/tempest/services/compute/json/aggregates_client.py b/tempest/services/compute/json/aggregates_client.py
index 5c0b5d3..71d6f63 100644
--- a/tempest/services/compute/json/aggregates_client.py
+++ b/tempest/services/compute/json/aggregates_client.py
@@ -50,6 +50,7 @@
resp, body = self.post('os-aggregates', post_body)
body = json.loads(body)
+ self.validate_response(v2_schema.create_aggregate, resp, body)
return resp, body['aggregate']
def update_aggregate(self, aggregate_id, name, availability_zone=None):
@@ -87,6 +88,7 @@
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
post_body)
body = json.loads(body)
+ self.validate_response(schema.aggregate_add_remove_host, resp, body)
return resp, body['aggregate']
def remove_host(self, aggregate_id, host):
@@ -98,6 +100,7 @@
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
post_body)
body = json.loads(body)
+ self.validate_response(schema.aggregate_add_remove_host, resp, body)
return resp, body['aggregate']
def set_metadata(self, aggregate_id, meta):
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index e148572..342f946 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -61,6 +61,7 @@
resp, body = self.put("os-hosts/%s" % str(hostname), request_body)
body = json.loads(body)
+ self.validate_response(v2_schema.update_host, resp, body)
return resp, body
def startup_host(self, hostname):
diff --git a/tempest/services/compute/json/interfaces_client.py b/tempest/services/compute/json/interfaces_client.py
index 2f165a2..8d51123 100644
--- a/tempest/services/compute/json/interfaces_client.py
+++ b/tempest/services/compute/json/interfaces_client.py
@@ -17,6 +17,7 @@
import time
from tempest.api_schema.compute import interfaces as common_schema
+from tempest.api_schema.compute.v2 import interfaces as schema
from tempest.common import rest_client
from tempest import config
from tempest import exceptions
@@ -33,6 +34,7 @@
def list_interfaces(self, server):
resp, body = self.get('servers/%s/os-interface' % server)
body = json.loads(body)
+ self.validate_response(schema.list_interfaces, resp, body)
return resp, body['interfaceAttachments']
def create_interface(self, server, port_id=None, network_id=None,
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index 9bddf2c..7e828d8 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -110,6 +110,7 @@
post_body)
body = json.loads(body)
+ self.validate_response(schema.quota_set_update, resp, body)
return resp, body['quota_set']
def delete_quota_set(self, tenant_id):
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 77cbf42..92cfc8e 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -126,6 +126,7 @@
post_body = json.dumps({'server': post_body})
resp, body = self.put("servers/%s" % str(server_id), post_body)
body = json.loads(body)
+ self.validate_response(schema.update_server, resp, body)
return resp, body['server']
def get_server(self, server_id):
@@ -149,6 +150,7 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(common_schema.list_servers, resp, body)
return resp, body
def list_servers_with_detail(self, params=None):
@@ -198,6 +200,7 @@
resp, body = self.get("servers/%s/ips/%s" %
(str(server_id), network_id))
body = json.loads(body)
+ self.validate_response(schema.list_addresses_by_network, resp, body)
return resp, body
def action(self, server_id, action_name, response_key,
diff --git a/tempest/services/compute/json/tenant_usages_client.py b/tempest/services/compute/json/tenant_usages_client.py
index f3a67dd..f8adae7 100644
--- a/tempest/services/compute/json/tenant_usages_client.py
+++ b/tempest/services/compute/json/tenant_usages_client.py
@@ -16,6 +16,7 @@
import json
import urllib
+from tempest.api_schema.compute.v2 import tenant_usages as schema
from tempest.common import rest_client
from tempest import config
@@ -35,6 +36,7 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(schema.list_tenant, resp, body)
return resp, body['tenant_usages'][0]
def get_tenant_usage(self, tenant_id, params=None):
@@ -44,4 +46,5 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(schema.get_tenant, resp, body)
return resp, body['tenant_usage']
diff --git a/tempest/services/compute/v3/json/aggregates_client.py b/tempest/services/compute/v3/json/aggregates_client.py
index 2487ee7..d9b7930 100644
--- a/tempest/services/compute/v3/json/aggregates_client.py
+++ b/tempest/services/compute/v3/json/aggregates_client.py
@@ -50,6 +50,7 @@
resp, body = self.post('os-aggregates', post_body)
body = json.loads(body)
+ self.validate_response(v3_schema.create_aggregate, resp, body)
return resp, body['aggregate']
def update_aggregate(self, aggregate_id, name, availability_zone=None):
@@ -87,6 +88,7 @@
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
post_body)
body = json.loads(body)
+ self.validate_response(v3_schema.aggregate_add_remove_host, resp, body)
return resp, body['aggregate']
def remove_host(self, aggregate_id, host):
@@ -98,6 +100,7 @@
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
post_body)
body = json.loads(body)
+ self.validate_response(v3_schema.aggregate_add_remove_host, resp, body)
return resp, body['aggregate']
def set_metadata(self, aggregate_id, meta):
diff --git a/tempest/services/compute/v3/json/hosts_client.py b/tempest/services/compute/v3/json/hosts_client.py
index 24d43d0..d2eb43d 100644
--- a/tempest/services/compute/v3/json/hosts_client.py
+++ b/tempest/services/compute/v3/json/hosts_client.py
@@ -61,6 +61,7 @@
resp, body = self.put("os-hosts/%s" % str(hostname), request_body)
body = json.loads(body)
+ self.validate_response(v3_schema.update_host, resp, body)
return resp, body
def startup_host(self, hostname):
diff --git a/tempest/services/compute/v3/json/interfaces_client.py b/tempest/services/compute/v3/json/interfaces_client.py
index 25c8db7..77b3179 100644
--- a/tempest/services/compute/v3/json/interfaces_client.py
+++ b/tempest/services/compute/v3/json/interfaces_client.py
@@ -17,6 +17,7 @@
import time
from tempest.api_schema.compute import interfaces as common_schema
+from tempest.api_schema.compute.v3 import interfaces as schema
from tempest.common import rest_client
from tempest import config
from tempest import exceptions
@@ -33,6 +34,7 @@
def list_interfaces(self, server):
resp, body = self.get('servers/%s/os-attach-interfaces' % server)
body = json.loads(body)
+ self.validate_response(schema.list_interfaces, resp, body)
return resp, body['interface_attachments']
def create_interface(self, server, port_id=None, network_id=None,
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index 49ee2ac..1990d39 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -126,6 +126,7 @@
post_body = json.dumps({'server': post_body})
resp, body = self.put("servers/%s" % str(server_id), post_body)
body = json.loads(body)
+ self.validate_response(schema.update_server, resp, body)
return resp, body['server']
def get_server(self, server_id):
@@ -149,6 +150,7 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(common_schema.list_servers, resp, body)
return resp, body
def list_servers_with_detail(self, params=None):
@@ -198,6 +200,7 @@
resp, body = self.get("servers/%s/ips/%s" %
(str(server_id), network_id))
body = json.loads(body)
+ self.validate_response(schema.list_addresses_by_network, resp, body)
return resp, body
def action(self, server_id, action_name, response_key, **kwargs):
@@ -467,3 +470,13 @@
def inject_network_info(self, server_id, **kwargs):
"""Inject the Network Info into server"""
return self.action(server_id, 'inject_network_info', None, **kwargs)
+
+ def get_spice_console(self, server_id, console_type):
+ """Get URL of Spice console."""
+ return self.action(server_id, "get_spice_console"
+ "console", type=console_type)
+
+ def get_rdp_console(self, server_id, console_type):
+ """Get URL of RDP console."""
+ return self.action(server_id, "get_rdp_console"
+ "console", type=console_type)
diff --git a/tempest/services/data_processing/v1_1/client.py b/tempest/services/data_processing/v1_1/client.py
index c7b5f93..194e300 100644
--- a/tempest/services/data_processing/v1_1/client.py
+++ b/tempest/services/data_processing/v1_1/client.py
@@ -128,3 +128,37 @@
uri = 'cluster-templates/%s' % tmpl_id
return self.delete(uri)
+
+ def list_data_sources(self):
+ """List all data sources for a user."""
+
+ uri = 'data-sources'
+ return self._request_and_parse(self.get, uri, 'data_sources')
+
+ def get_data_source(self, source_id):
+ """Returns the details of a single data source."""
+
+ uri = 'data-sources/%s' % source_id
+ return self._request_and_parse(self.get, uri, 'data_source')
+
+ def create_data_source(self, name, data_source_type, url, **kwargs):
+ """Creates data source with specified params.
+
+ It supports passing additional params using kwargs and returns created
+ object.
+ """
+ uri = 'data-sources'
+ body = kwargs.copy()
+ body.update({
+ 'name': name,
+ 'type': data_source_type,
+ 'url': url
+ })
+ return self._request_and_parse(self.post, uri, 'data_source',
+ body=json.dumps(body))
+
+ def delete_data_source(self, source_id):
+ """Deletes the specified data source by id."""
+
+ uri = 'data-sources/%s' % source_id
+ return self.delete(uri)
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index f9dd8ef..8e53b8d 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -165,21 +165,6 @@
resp, body = self.delete(uri)
return resp, body
- def create_vpnservice(self, subnet_id, router_id, **kwargs):
- post_body = {
- "vpnservice": {
- "subnet_id": subnet_id,
- "router_id": router_id
- }
- }
- for key, val in kwargs.items():
- post_body['vpnservice'][key] = val
- body = json.dumps(post_body)
- uri = '%s/vpn/vpnservices' % (self.uri_prefix)
- resp, body = self.post(uri, body)
- body = json.loads(body)
- return resp, body
-
def list_router_interfaces(self, uuid):
uri = '%s/ports?device_id=%s' % (self.uri_prefix, uuid)
resp, body = self.get(uri)
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index 50a1954..a9d4880 100644
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -257,38 +257,6 @@
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def create_vpnservice(self, subnet_id, router_id, **kwargs):
- uri = '%s/vpn/vpnservices' % (self.uri_prefix)
- vpnservice = common.Element("vpnservice")
- p1 = common.Element("subnet_id", subnet_id)
- p2 = common.Element("router_id", router_id)
- vpnservice.append(p1)
- vpnservice.append(p2)
- common.deep_dict_to_xml(vpnservice, kwargs)
- resp, body = self.post(uri, str(common.Document(vpnservice)))
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def create_ikepolicy(self, name, **kwargs):
- uri = '%s/vpn/ikepolicies' % (self.uri_prefix)
- ikepolicy = common.Element("ikepolicy")
- p1 = common.Element("name", name)
- ikepolicy.append(p1)
- common.deep_dict_to_xml(ikepolicy, kwargs)
- resp, body = self.post(uri, str(common.Document(ikepolicy)))
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def create_ipsecpolicy(self, name, **kwargs):
- uri = '%s/vpn/ipsecpolicies' % (self.uri_prefix)
- ipsecpolicy = common.Element("ipsecpolicy")
- p1 = common.Element("name", name)
- ipsecpolicy.append(p1)
- common.deep_dict_to_xml(ipsecpolicy, kwargs)
- resp, body = self.post(uri, str(common.Document(ipsecpolicy)))
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
body = ET.fromstring(xml_returned_body)
diff --git a/tempest/services/telemetry/telemetry_client_base.py b/tempest/services/telemetry/telemetry_client_base.py
index 610f07b..a073f54 100644
--- a/tempest/services/telemetry/telemetry_client_base.py
+++ b/tempest/services/telemetry/telemetry_client_base.py
@@ -73,7 +73,10 @@
return resp, body
def put(self, uri, body):
- return self.rest_client.put(uri, body)
+ body = self.serialize(body)
+ resp, body = self.rest_client.put(uri, body)
+ body = self.deserialize(body)
+ return resp, body
def get(self, uri):
resp, body = self.rest_client.get(uri)
@@ -133,3 +136,15 @@
def create_alarm(self, **kwargs):
uri = "%s/alarms" % self.uri_prefix
return self.post(uri, kwargs)
+
+ def update_alarm(self, alarm_id, **kwargs):
+ uri = "%s/alarms/%s" % (self.uri_prefix, alarm_id)
+ return self.put(uri, kwargs)
+
+ def alarm_get_state(self, alarm_id):
+ uri = "%s/alarms/%s/state" % (self.uri_prefix, alarm_id)
+ return self.get(uri)
+
+ def alarm_set_state(self, alarm_id, state):
+ uri = "%s/alarms/%s/state" % (self.uri_prefix, alarm_id)
+ return self.put(uri, state)
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index 517cfd5..642108a 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -19,6 +19,7 @@
from six import moves
+from tempest import auth
from tempest import clients
from tempest.common import ssh
from tempest.common.utils import data_utils
@@ -147,9 +148,10 @@
password,
tenant['id'],
"email")
- manager = clients.Manager(username=username,
- password="pass",
- tenant_name=tenant_name)
+ creds = auth.get_credentials(username=username,
+ password=password,
+ tenant_name=tenant_name)
+ manager = clients.Manager(credentials=creds)
test_obj = importutils.import_class(test['action'])
test_run = test_obj(manager, max_runs, stop_on_error)
diff --git a/tempest/test.py b/tempest/test.py
index 254fffa..748a98c 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -307,26 +307,18 @@
cls.__name__, network_resources=cls.network_resources)
force_tenant_isolation = getattr(cls, 'force_tenant_isolation', None)
- if (CONF.compute.allow_tenant_isolation or
- force_tenant_isolation):
+ if CONF.compute.allow_tenant_isolation or force_tenant_isolation:
creds = cls.isolated_creds.get_primary_creds()
- username, tenant_name, password = creds
if getattr(cls, '_interface', None):
- os = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name,
+ os = clients.Manager(credentials=creds,
interface=cls._interface,
service=cls._service)
elif interface:
- os = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name,
+ os = clients.Manager(credentials=creds,
interface=interface,
service=cls._service)
else:
- os = clients.Manager(username=username,
- password=password,
- tenant_name=tenant_name,
+ os = clients.Manager(credentials=creds,
service=cls._service)
else:
if getattr(cls, '_interface', None):
diff --git a/tempest/tests/common/utils/test_misc.py b/tempest/tests/common/utils/test_misc.py
index b8c6184..aee9805 100644
--- a/tempest/tests/common/utils/test_misc.py
+++ b/tempest/tests/common/utils/test_misc.py
@@ -50,3 +50,39 @@
self.assertEqual(test, test2)
test3 = TestBar()
self.assertNotEqual(test, test3)
+
+ def test_find_test_caller_test_case(self):
+ # Calling it from here should give us the method we're in.
+ self.assertEqual('TestMisc:test_find_test_caller_test_case',
+ misc.find_test_caller())
+
+ def test_find_test_caller_setup_self(self):
+ def setUp(self):
+ return misc.find_test_caller()
+ self.assertEqual('TestMisc:setUp', setUp(self))
+
+ def test_find_test_caller_setup_no_self(self):
+ def setUp():
+ return misc.find_test_caller()
+ self.assertEqual(':setUp', setUp())
+
+ def test_find_test_caller_setupclass_cls(self):
+ def setUpClass(cls): # noqa
+ return misc.find_test_caller()
+ self.assertEqual('TestMisc:setUpClass', setUpClass(self.__class__))
+
+ def test_find_test_caller_teardown_self(self):
+ def tearDown(self):
+ return misc.find_test_caller()
+ self.assertEqual('TestMisc:tearDown', tearDown(self))
+
+ def test_find_test_caller_teardown_no_self(self):
+ def tearDown():
+ return misc.find_test_caller()
+ self.assertEqual(':tearDown', tearDown())
+
+ def test_find_test_caller_teardown_class(self):
+ def tearDownClass(cls):
+ return misc.find_test_caller()
+ self.assertEqual('TestMisc:tearDownClass',
+ tearDownClass(self.__class__))
diff --git a/tempest/tests/fake_auth_provider.py b/tempest/tests/fake_auth_provider.py
index ddffb4a..44c331e 100644
--- a/tempest/tests/fake_auth_provider.py
+++ b/tempest/tests/fake_auth_provider.py
@@ -16,6 +16,10 @@
from tempest.tests import fake_credentials
+def get_default_credentials(credential_type, fill_in=True):
+ return fake_credentials.FakeCredentials()
+
+
def get_credentials(credential_type=None, fill_in=True, **kwargs):
return fake_credentials.FakeCredentials()
diff --git a/tempest/tests/fake_credentials.py b/tempest/tests/fake_credentials.py
index a372973..48f67d2 100644
--- a/tempest/tests/fake_credentials.py
+++ b/tempest/tests/fake_credentials.py
@@ -31,3 +31,32 @@
tenant_name='fake_tenant_name'
)
super(FakeKeystoneV2Credentials, self).__init__(**creds)
+
+
+class FakeKeystoneV3Credentials(auth.KeystoneV3Credentials):
+ """
+ Fake credentials suitable for the Keystone Identity V3 API
+ """
+
+ def __init__(self):
+ creds = dict(
+ username='fake_username',
+ password='fake_password',
+ user_domain_name='fake_domain_name',
+ project_name='fake_tenant_name'
+ )
+ super(FakeKeystoneV3Credentials, self).__init__(**creds)
+
+
+class FakeKeystoneV3DomainCredentials(auth.KeystoneV3Credentials):
+ """
+ Fake credentials suitable for the Keystone Identity V3 API, with no scope
+ """
+
+ def __init__(self):
+ creds = dict(
+ username='fake_username',
+ password='fake_password',
+ user_domain_name='fake_domain_name'
+ )
+ super(FakeKeystoneV3DomainCredentials, self).__init__(**creds)
diff --git a/tempest/tests/fake_http.py b/tempest/tests/fake_http.py
index a09d5ba..7b878af 100644
--- a/tempest/tests/fake_http.py
+++ b/tempest/tests/fake_http.py
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
import httplib2
@@ -44,3 +45,29 @@
else:
msg = "unsupported return type %s" % self.return_type
raise TypeError(msg)
+
+
+class fake_httplib(object):
+ def __init__(self, headers, body=None,
+ version=1.0, status=200, reason="Ok"):
+ """
+ :param headers: dict representing HTTP response headers
+ :param body: file-like object
+ :param version: HTTP Version
+ :param status: Response status code
+ :param reason: Status code related message.
+ """
+ self.body = body
+ self.status = status
+ self.reason = reason
+ self.version = version
+ self.headers = headers
+
+ def getheaders(self):
+ return copy.deepcopy(self.headers).items()
+
+ def getheader(self, key, default):
+ return self.headers.get(key, default)
+
+ def read(self, amt):
+ return self.body.read(amt)
diff --git a/tempest/tests/fake_identity.py b/tempest/tests/fake_identity.py
index 058c9c2..1900fc9 100644
--- a/tempest/tests/fake_identity.py
+++ b/tempest/tests/fake_identity.py
@@ -113,7 +113,7 @@
"expires_at": "2020-01-01T00:00:10.000123Z",
"project": {
"domain": {
- "id": "fake_id",
+ "id": "fake_domain_id",
"name": "fake"
},
"id": "project_id",
@@ -121,7 +121,7 @@
},
"user": {
"domain": {
- "id": "domain_id",
+ "id": "fake_domain_id",
"name": "domain_name"
},
"id": "fake_user_id",
diff --git a/tempest/tests/test_auth.py b/tempest/tests/test_auth.py
index 7b5b4d6..03333be 100644
--- a/tempest/tests/test_auth.py
+++ b/tempest/tests/test_auth.py
@@ -47,6 +47,8 @@
self.stubs.Set(http.ClosingHttp, 'request', self.fake_http.request)
self.stubs.Set(auth, 'get_credentials',
fake_auth_provider.get_credentials)
+ self.stubs.Set(auth, 'get_default_credentials',
+ fake_auth_provider.get_default_credentials)
self.auth_provider = self._auth(self.credentials)
@@ -107,6 +109,10 @@
self.assertIsNone(self.auth_provider.alt_part)
self.assertIsNone(self.auth_provider.alt_auth_data)
+ def test_fill_credentials(self):
+ self.assertRaises(NotImplementedError,
+ self.auth_provider.fill_credentials)
+
class TestKeystoneV2AuthProvider(BaseAuthTestsSetUp):
_endpoints = fake_identity.IDENTITY_V2_RESPONSE['access']['serviceCatalog']
@@ -131,6 +137,13 @@
def _get_token_from_fake_identity(self):
return fake_identity.TOKEN
+ def _get_from_fake_identity(self, attr):
+ access = fake_identity.IDENTITY_V2_RESPONSE['access']
+ if attr == 'user_id':
+ return access['user']['id']
+ elif attr == 'tenant_id':
+ return access['token']['tenant']['id']
+
def _test_request_helper(self, filters, expected):
url, headers, body = self.auth_provider.auth_request('GET',
self.target_url,
@@ -218,6 +231,13 @@
del cred[attr]
self.assertFalse(self.auth_provider.check_credentials(cred))
+ def test_fill_credentials(self):
+ self.auth_provider.fill_credentials()
+ creds = self.auth_provider.credentials
+ for attr in ['user_id', 'tenant_id']:
+ self.assertEqual(self._get_from_fake_identity(attr),
+ getattr(creds, attr))
+
def _test_base_url_helper(self, expected_url, filters,
auth_data=None):
@@ -318,12 +338,7 @@
class TestKeystoneV3AuthProvider(TestKeystoneV2AuthProvider):
_endpoints = fake_identity.IDENTITY_V3_RESPONSE['token']['catalog']
_auth_provider_class = auth.KeystoneV3AuthProvider
- credentials = {
- 'username': 'fake_user',
- 'password': 'fake_pwd',
- 'tenant_name': 'fake_tenant',
- 'domain_name': 'fake_domain_name',
- }
+ credentials = fake_credentials.FakeKeystoneV3Credentials()
def setUp(self):
super(TestKeystoneV3AuthProvider, self).setUp()
@@ -343,10 +358,44 @@
access['expires_at'] = date_as_string
return token, access
- def test_check_credentials_missing_tenant_name(self):
- cred = copy.copy(self.credentials)
- del cred['domain_name']
- self.assertFalse(self.auth_provider.check_credentials(cred))
+ def _get_from_fake_identity(self, attr):
+ token = fake_identity.IDENTITY_V3_RESPONSE['token']
+ if attr == 'user_id':
+ return token['user']['id']
+ elif attr == 'project_id':
+ return token['project']['id']
+ elif attr == 'user_domain_id':
+ return token['user']['domain']['id']
+ elif attr == 'project_domain_id':
+ return token['project']['domain']['id']
+
+ def test_check_credentials_missing_attribute(self):
+ # reset credentials to fresh ones
+ self.credentials.reset()
+ for attr in ['username', 'password', 'user_domain_name',
+ 'project_domain_name']:
+ cred = copy.copy(self.credentials)
+ del cred[attr]
+ self.assertFalse(self.auth_provider.check_credentials(cred),
+ "Credentials should be invalid without %s" % attr)
+
+ def test_check_domain_credentials_missing_attribute(self):
+ # reset credentials to fresh ones
+ self.credentials.reset()
+ domain_creds = fake_credentials.FakeKeystoneV3DomainCredentials()
+ for attr in ['username', 'password', 'user_domain_name']:
+ cred = copy.copy(domain_creds)
+ del cred[attr]
+ self.assertFalse(self.auth_provider.check_credentials(cred),
+ "Credentials should be invalid without %s" % attr)
+
+ def test_fill_credentials(self):
+ self.auth_provider.fill_credentials()
+ creds = self.auth_provider.credentials
+ for attr in ['user_id', 'project_id', 'user_domain_id',
+ 'project_domain_id']:
+ self.assertEqual(self._get_from_fake_identity(attr),
+ getattr(creds, attr))
# Overwrites v2 test
def test_base_url_to_get_admin_endpoint(self):
diff --git a/tempest/tests/test_credentials.py b/tempest/tests/test_credentials.py
index 86600fa..9da5f92 100644
--- a/tempest/tests/test_credentials.py
+++ b/tempest/tests/test_credentials.py
@@ -13,6 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
+
+from oslo.config import cfg
+
from tempest import auth
from tempest.common import http
from tempest.common import tempest_fixtures as fixtures
@@ -40,6 +44,10 @@
self.useFixture(fake_config.ConfigFixture())
self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ def test_create(self):
+ creds = self._get_credentials()
+ self.assertEqual(self.attributes, creds._initial)
+
def test_create_invalid_attr(self):
self.assertRaises(exceptions.InvalidCredentials,
self._get_credentials,
@@ -77,19 +85,44 @@
self.stubs.Set(http.ClosingHttp, 'request', self.identity_response)
self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
- def _verify_credentials(self, credentials_class, creds_dict):
- creds = auth.get_credentials(**creds_dict)
- # Check the right version of credentials has been returned
- self.assertIsInstance(creds, credentials_class)
- # Check the id attributes are filled in
- attributes = [x for x in creds.ATTRIBUTES if (
- '_id' in x and x != 'domain_id')]
- for attr in attributes:
- self.assertIsNone(getattr(creds, attr))
+ def _verify_credentials(self, credentials_class, filled=True,
+ creds_dict=None):
+
+ def _check(credentials):
+ # Check the right version of credentials has been returned
+ self.assertIsInstance(credentials, credentials_class)
+ # Check the id attributes are filled in
+ attributes = [x for x in credentials.ATTRIBUTES if (
+ '_id' in x and x != 'domain_id')]
+ for attr in attributes:
+ if filled:
+ self.assertIsNotNone(getattr(credentials, attr))
+ else:
+ self.assertIsNone(getattr(credentials, attr))
+
+ if creds_dict is None:
+ for ctype in auth.Credentials.TYPES:
+ creds = auth.get_default_credentials(credential_type=ctype,
+ fill_in=filled)
+ _check(creds)
+ else:
+ creds = auth.get_credentials(fill_in=filled, **creds_dict)
+ _check(creds)
+
+ def test_get_default_credentials(self):
+ self.useFixture(fixtures.LockFixture('auth_version'))
+ self._verify_credentials(credentials_class=self.credentials_class)
def test_get_credentials(self):
self.useFixture(fixtures.LockFixture('auth_version'))
- self._verify_credentials(self.credentials_class, self.attributes)
+ self._verify_credentials(credentials_class=self.credentials_class,
+ creds_dict=self.attributes)
+
+ def test_get_credentials_not_filled(self):
+ self.useFixture(fixtures.LockFixture('auth_version'))
+ self._verify_credentials(credentials_class=self.credentials_class,
+ filled=False,
+ creds_dict=self.attributes)
def test_is_valid(self):
creds = self._get_credentials()
@@ -110,3 +143,87 @@
# Default configuration values related to credentials
# are defined as fake_* in fake_config.py
self.assertEqual(getattr(creds, attr), 'fake_' + attr)
+
+ def test_reset_all_attributes(self):
+ creds = self._get_credentials()
+ initial_creds = copy.deepcopy(creds)
+ set_attr = creds.__dict__.keys()
+ missing_attr = set(creds.ATTRIBUTES).difference(set_attr)
+ # Set all unset attributes, then reset
+ for attr in missing_attr:
+ setattr(creds, attr, 'fake' + attr)
+ creds.reset()
+ # Check reset credentials are same as initial ones
+ self.assertEqual(creds, initial_creds)
+
+ def test_reset_single_attribute(self):
+ creds = self._get_credentials()
+ initial_creds = copy.deepcopy(creds)
+ set_attr = creds.__dict__.keys()
+ missing_attr = set(creds.ATTRIBUTES).difference(set_attr)
+ # Set one unset attributes, then reset
+ for attr in missing_attr:
+ setattr(creds, attr, 'fake' + attr)
+ creds.reset()
+ # Check reset credentials are same as initial ones
+ self.assertEqual(creds, initial_creds)
+
+
+class KeystoneV3CredentialsTests(KeystoneV2CredentialsTests):
+ attributes = {
+ 'username': 'fake_username',
+ 'password': 'fake_password',
+ 'project_name': 'fake_project_name',
+ 'user_domain_name': 'fake_domain_name'
+ }
+
+ credentials_class = auth.KeystoneV3Credentials
+ identity_response = fake_identity._fake_v3_response
+
+ def setUp(self):
+ super(KeystoneV3CredentialsTests, self).setUp()
+ # Additional config items reset by cfg fixture after each test
+ cfg.CONF.set_default('auth_version', 'v3', group='identity')
+ # Identity group items
+ for prefix in ['', 'alt_', 'admin_']:
+ cfg.CONF.set_default(prefix + 'domain_name', 'fake_domain_name',
+ group='identity')
+ # Compute Admin group items
+ cfg.CONF.set_default('domain_name', 'fake_domain_name',
+ group='compute-admin')
+
+ def test_default(self):
+ self.useFixture(fixtures.LockFixture('auth_version'))
+ for ctype in self.credentials_class.TYPES:
+ creds = self.credentials_class.get_default(credentials_type=ctype)
+ for attr in self.attributes.keys():
+ if attr == 'project_name':
+ config_value = 'fake_tenant_name'
+ elif attr == 'user_domain_name':
+ config_value = 'fake_domain_name'
+ else:
+ config_value = 'fake_' + attr
+ self.assertEqual(getattr(creds, attr), config_value)
+
+ def test_synced_attributes(self):
+ attributes = self.attributes
+ # Create V3 credentials with tenant instead of project, and user_domain
+ for attr in ['project_id', 'user_domain_id']:
+ attributes[attr] = 'fake_' + attr
+ creds = self._get_credentials(attributes)
+ self.assertEqual(creds.project_name, creds.tenant_name)
+ self.assertEqual(creds.project_id, creds.tenant_id)
+ self.assertEqual(creds.user_domain_name, creds.project_domain_name)
+ self.assertEqual(creds.user_domain_id, creds.project_domain_id)
+ # Replace user_domain with project_domain
+ del attributes['user_domain_name']
+ del attributes['user_domain_id']
+ del attributes['project_name']
+ del attributes['project_id']
+ for attr in ['project_domain_name', 'project_domain_id',
+ 'tenant_name', 'tenant_id']:
+ attributes[attr] = 'fake_' + attr
+ self.assertEqual(creds.tenant_name, creds.project_name)
+ self.assertEqual(creds.tenant_id, creds.project_id)
+ self.assertEqual(creds.project_domain_name, creds.user_domain_name)
+ self.assertEqual(creds.project_domain_id, creds.user_domain_id)
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
new file mode 100644
index 0000000..bb2df43
--- /dev/null
+++ b/tempest/tests/test_glance_http.py
@@ -0,0 +1,200 @@
+# Copyright 2014 IBM 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 httplib
+import json
+import mock
+import six
+import socket
+
+from tempest.common import glance_http
+from tempest import exceptions
+from tempest.openstack.common.fixture import mockpatch
+from tempest.tests import base
+from tempest.tests import fake_auth_provider
+from tempest.tests import fake_http
+
+
+class TestGlanceHTTPClient(base.TestCase):
+
+ def setUp(self):
+ super(TestGlanceHTTPClient, self).setUp()
+ self.fake_http = fake_http.fake_httplib2(return_type=200)
+ # NOTE(maurosr): using http here implies that we will be using httplib
+ # directly. With https glance_client would use an httpS version, but
+ # the real backend would still be httplib anyway and since we mock it
+ # that there is no reason to care.
+ self.endpoint = 'http://fake_url.com'
+ self.fake_auth = fake_auth_provider.FakeAuthProvider()
+
+ self.fake_auth.base_url = mock.MagicMock(return_value=self.endpoint)
+
+ self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
+ 'request',
+ side_effect=self.fake_http.request(self.endpoint)[1]))
+ self.client = glance_http.HTTPClient(self.fake_auth, {})
+
+ def _set_response_fixture(self, header, status, resp_body):
+ resp = fake_http.fake_httplib(header, status=status,
+ body=six.StringIO(resp_body))
+ self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
+ 'getresponse',
+ return_value=resp))
+ return resp
+
+ def test_json_request_without_content_type_header(self):
+ self._set_response_fixture({}, 200, 'fake_response_body')
+ resp, body = self.client.json_request('GET', '/images')
+ self.assertEqual(200, resp.status)
+ self.assertIsNone(body)
+
+ def test_json_request_with_xml_content_type_header(self):
+ self._set_response_fixture({'content-type': 'application/xml'},
+ 200, 'fake_response_body')
+ resp, body = self.client.json_request('GET', '/images')
+ self.assertEqual(200, resp.status)
+ self.assertIsNone(body)
+
+ def test_json_request_with_content_type_header(self):
+ self._set_response_fixture({'content-type': 'application/json'},
+ 200, 'fake_response_body')
+ resp, body = self.client.json_request('GET', '/images')
+ self.assertEqual(200, resp.status)
+ self.assertEqual('fake_response_body', body)
+
+ def test_json_request_fails_to_json_loads(self):
+ self._set_response_fixture({'content-type': 'application/json'},
+ 200, 'fake_response_body')
+ self.useFixture(mockpatch.PatchObject(json, 'loads',
+ side_effect=ValueError()))
+ resp, body = self.client.json_request('GET', '/images')
+ self.assertEqual(200, resp.status)
+ self.assertEqual(body, 'fake_response_body')
+
+ def test_json_request_socket_timeout(self):
+ self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
+ 'request',
+ side_effect=socket.timeout()))
+ self.assertRaises(exceptions.TimeoutException,
+ self.client.json_request, 'GET', '/images')
+
+ def test_json_request_endpoint_not_found(self):
+ self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
+ 'request',
+ side_effect=socket.gaierror()))
+ self.assertRaises(exceptions.EndpointNotFound,
+ self.client.json_request, 'GET', '/images')
+
+ def test_raw_request(self):
+ self._set_response_fixture({}, 200, 'fake_response_body')
+ resp, body = self.client.raw_request('GET', '/images')
+ self.assertEqual(200, resp.status)
+ self.assertEqual('fake_response_body', body.read())
+
+ def test_raw_request_with_response_chunked(self):
+ self._set_response_fixture({}, 200, 'fake_response_body')
+ self.useFixture(mockpatch.PatchObject(glance_http,
+ 'CHUNKSIZE', 1))
+ resp, body = self.client.raw_request('GET', '/images')
+ self.assertEqual(200, resp.status)
+ self.assertEqual('fake_response_body', body.read())
+
+ def test_raw_request_chunked(self):
+ self.useFixture(mockpatch.PatchObject(glance_http,
+ 'CHUNKSIZE', 1))
+ self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
+ 'endheaders'))
+ self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
+ 'send'))
+
+ self._set_response_fixture({}, 200, 'fake_response_body')
+ req_body = six.StringIO('fake_request_body')
+ resp, body = self.client.raw_request('PUT', '/images', body=req_body)
+ self.assertEqual(200, resp.status)
+ self.assertEqual('fake_response_body', body.read())
+ httplib.HTTPConnection.send.assert_call_count(req_body.len)
+
+ def test_get_connection_class_for_https(self):
+ conn_class = self.client.get_connection_class('https')
+ self.assertEqual(glance_http.VerifiedHTTPSConnection, conn_class)
+
+ def test_get_connection_class_for_http(self):
+ conn_class = (self.client.get_connection_class('http'))
+ self.assertEqual(httplib.HTTPConnection, conn_class)
+
+ def test_get_connection_http(self):
+ self.assertTrue(isinstance(self.client.get_connection(),
+ httplib.HTTPConnection))
+
+ def test_get_connection_https(self):
+ endpoint = 'https://fake_url.com'
+ self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
+ self.client = glance_http.HTTPClient(self.fake_auth, {})
+ self.assertTrue(isinstance(self.client.get_connection(),
+ glance_http.VerifiedHTTPSConnection))
+
+ def test_get_connection_url_not_fount(self):
+ self.useFixture(mockpatch.PatchObject(self.client, 'connection_class',
+ side_effect=httplib.InvalidURL()
+ ))
+ self.assertRaises(exceptions.EndpointNotFound,
+ self.client.get_connection)
+
+ def test_get_connection_kwargs_default_for_http(self):
+ kwargs = self.client.get_connection_kwargs('http')
+ self.assertEqual(600, kwargs['timeout'])
+ self.assertEqual(1, len(kwargs.keys()))
+
+ def test_get_connection_kwargs_set_timeout_for_http(self):
+ kwargs = self.client.get_connection_kwargs('http', timeout=10,
+ cacert='foo')
+ self.assertEqual(10, kwargs['timeout'])
+ # nothing more than timeout is evaluated for http connections
+ self.assertEqual(1, len(kwargs.keys()))
+
+ def test_get_connection_kwargs_default_for_https(self):
+ kwargs = self.client.get_connection_kwargs('https')
+ self.assertEqual(600, kwargs['timeout'])
+ self.assertEqual(None, kwargs['cacert'])
+ self.assertEqual(None, kwargs['cert_file'])
+ self.assertEqual(None, kwargs['key_file'])
+ self.assertEqual(False, kwargs['insecure'])
+ self.assertEqual(True, kwargs['ssl_compression'])
+ self.assertEqual(6, len(kwargs.keys()))
+
+ def test_get_connection_kwargs_set_params_for_https(self):
+ kwargs = self.client.get_connection_kwargs('https', timeout=10,
+ cacert='foo',
+ cert_file='/foo/bar.cert',
+ key_file='/foo/key.pem',
+ insecure=True,
+ ssl_compression=False)
+ self.assertEqual(10, kwargs['timeout'])
+ self.assertEqual('foo', kwargs['cacert'])
+ self.assertEqual('/foo/bar.cert', kwargs['cert_file'])
+ self.assertEqual('/foo/key.pem', kwargs['key_file'])
+ self.assertEqual(True, kwargs['insecure'])
+ self.assertEqual(False, kwargs['ssl_compression'])
+ self.assertEqual(6, len(kwargs.keys()))
+
+
+class TestResponseBodyIterator(base.TestCase):
+
+ def test_iter_default_chunk_size_64k(self):
+ resp = fake_http.fake_httplib({}, six.StringIO(
+ 'X' * (glance_http.CHUNKSIZE + 1)))
+ iterator = glance_http.ResponseBodyIterator(resp)
+ chunks = list(iterator)
+ self.assertEqual(chunks, ['X' * glance_http.CHUNKSIZE, 'X'])
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index f584cb9..ab81836 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -17,6 +17,36 @@
class HackingTestCase(base.TestCase):
+ """
+ This class tests the hacking checks in tempest.hacking.checks by passing
+ strings to the check methods like the pep8/flake8 parser would. The parser
+ loops over each line in the file and then passes the parameters to the
+ check method. The parameter names in the check method dictate what type of
+ object is passed to the check method. The parameter types are::
+
+ logical_line: A processed line with the following modifications:
+ - Multi-line statements converted to a single line.
+ - Stripped left and right.
+ - Contents of strings replaced with "xxx" of same length.
+ - Comments removed.
+ physical_line: Raw line of text from the input file.
+ lines: a list of the raw lines from the input file
+ tokens: the tokens that contribute to this logical line
+ line_number: line number in the input file
+ total_lines: number of lines in the input file
+ blank_lines: blank lines before this one
+ indent_char: indentation character in this file (" " or "\t")
+ indent_level: indentation (with tabs expanded to multiples of 8)
+ previous_indent_level: indentation on previous line
+ previous_logical: previous logical line
+ filename: Path of the file being run through pep8
+
+ When running a test on a check method the return will be False/None if
+ there is no violation in the sample input. If there is an error a tuple is
+ returned with a position in the line, and a message. So to check the result
+ just assertTrue if the check is expected to fail and assertFalse if it
+ should pass.
+ """
def test_no_setupclass_for_unit_tests(self):
self.assertTrue(checks.no_setupclass_for_unit_tests(
" def setUpClass(cls):", './tempest/tests/fake_test.py'))
@@ -24,3 +54,42 @@
" def setUpClass(cls): # noqa", './tempest/tests/fake_test.py'))
self.assertFalse(checks.no_setupclass_for_unit_tests(
" def setUpClass(cls):", './tempest/api/fake_test.py'))
+
+ def test_import_no_clients_in_api(self):
+ for client in checks.PYTHON_CLIENTS:
+ string = "import " + client + "client"
+ self.assertTrue(checks.import_no_clients_in_api(
+ string, './tempest/api/fake_test.py'))
+ self.assertFalse(checks.import_no_clients_in_api(
+ string, './tempest/scenario/fake_test.py'))
+
+ def test_scenario_tests_need_service_tags(self):
+ self.assertFalse(checks.scenario_tests_need_service_tags(
+ 'def test_fake:', './tempest/scenario/test_fake.py',
+ "@test.services('compute')"))
+ self.assertFalse(checks.scenario_tests_need_service_tags(
+ 'def test_fake_test:', './tempest/api/compute/test_fake.py',
+ "@test.services('image')"))
+ self.assertTrue(checks.scenario_tests_need_service_tags(
+ 'def test_fake_test:', './tempest/scenario/test_fake.py',
+ '\n'))
+
+ def test_no_vi_headers(self):
+ # NOTE(mtreinish) The lines parameter is used only for finding the
+ # line location in the file. So these tests just pass a list of an
+ # arbitrary length to use for verifying the check function.
+ self.assertTrue(checks.no_vi_headers(
+ '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 1, range(250)))
+ self.assertTrue(checks.no_vi_headers(
+ '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 249, range(250)))
+ self.assertFalse(checks.no_vi_headers(
+ '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 149, range(250)))
+
+ def test_service_tags_not_in_module_path(self):
+ self.assertTrue(checks.service_tags_not_in_module_path(
+ "@test.services('compute')", './tempest/api/compute/fake_test.py'))
+ self.assertFalse(checks.service_tags_not_in_module_path(
+ "@test.services('compute')",
+ './tempest/scenario/compute/fake_test.py'))
+ self.assertFalse(checks.service_tags_not_in_module_path(
+ "@test.services('compute')", './tempest/api/image/fake_test.py'))
diff --git a/tempest/tests/test_tenant_isolation.py b/tempest/tests/test_tenant_isolation.py
index ae2e57d..9292dcb 100644
--- a/tempest/tests/test_tenant_isolation.py
+++ b/tempest/tests/test_tenant_isolation.py
@@ -17,6 +17,8 @@
import neutronclient.v2_0.client as neutronclient
from oslo.config import cfg
+from tempest import clients
+from tempest.common import http
from tempest.common import isolated_creds
from tempest import config
from tempest import exceptions
@@ -27,6 +29,8 @@
from tempest.services.network.xml import network_client as xml_network_client
from tempest.tests import base
from tempest.tests import fake_config
+from tempest.tests import fake_http
+from tempest.tests import fake_identity
class TestTenantIsolation(base.TestCase):
@@ -35,6 +39,9 @@
super(TestTenantIsolation, self).setUp()
self.useFixture(fake_config.ConfigFixture())
self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ self.fake_http = fake_http.fake_httplib2(return_type=200)
+ self.stubs.Set(http.ClosingHttp, 'request',
+ fake_identity._fake_v2_response)
def test_tempest_client(self):
iso_creds = isolated_creds.IsolatedCreds('test class')
@@ -46,6 +53,12 @@
def test_official_client(self):
self.useFixture(mockpatch.PatchObject(keystoneclient.Client,
'authenticate'))
+ self.useFixture(mockpatch.PatchObject(clients.OfficialClientManager,
+ '_get_image_client'))
+ self.useFixture(mockpatch.PatchObject(clients.OfficialClientManager,
+ '_get_object_storage_client'))
+ self.useFixture(mockpatch.PatchObject(clients.OfficialClientManager,
+ '_get_orchestration_client'))
iso_creds = isolated_creds.IsolatedCreds('test class',
tempest_client=False)
self.assertTrue(isinstance(iso_creds.identity_admin_client,
@@ -108,14 +121,12 @@
password='fake_password')
self._mock_tenant_create('1234', 'fake_prim_tenant')
self._mock_user_create('1234', 'fake_prim_user')
- username, tenant_name, password = iso_creds.get_primary_creds()
- self.assertEqual(username, 'fake_prim_user')
- self.assertEqual(tenant_name, 'fake_prim_tenant')
- # Verify helper methods
- tenant = iso_creds.get_primary_tenant()
- user = iso_creds.get_primary_user()
- self.assertEqual(tenant['id'], '1234')
- self.assertEqual(user['id'], '1234')
+ primary_creds = iso_creds.get_primary_creds()
+ self.assertEqual(primary_creds.username, 'fake_prim_user')
+ self.assertEqual(primary_creds.tenant_name, 'fake_prim_tenant')
+ # Verify IDs
+ self.assertEqual(primary_creds.tenant_id, '1234')
+ self.assertEqual(primary_creds.user_id, '1234')
@patch('tempest.common.rest_client.RestClient')
def test_admin_creds(self, MockRestClient):
@@ -136,15 +147,13 @@
self.addCleanup(user_mock.stop)
with patch.object(json_iden_client.IdentityClientJSON,
'assign_user_role') as user_mock:
- username, tenant_name, password = iso_creds.get_admin_creds()
+ admin_creds = iso_creds.get_admin_creds()
user_mock.assert_called_once_with('1234', '1234', '1234')
- self.assertEqual(username, 'fake_admin_user')
- self.assertEqual(tenant_name, 'fake_admin_tenant')
- # Verify helper methods
- tenant = iso_creds.get_admin_tenant()
- user = iso_creds.get_admin_user()
- self.assertEqual(tenant['id'], '1234')
- self.assertEqual(user['id'], '1234')
+ self.assertEqual(admin_creds.username, 'fake_admin_user')
+ self.assertEqual(admin_creds.tenant_name, 'fake_admin_tenant')
+ # Verify IDs
+ self.assertEqual(admin_creds.tenant_id, '1234')
+ self.assertEqual(admin_creds.user_id, '1234')
@patch('tempest.common.rest_client.RestClient')
def test_all_cred_cleanup(self, MockRestClient):
@@ -153,12 +162,12 @@
password='fake_password')
tenant_fix = self._mock_tenant_create('1234', 'fake_prim_tenant')
user_fix = self._mock_user_create('1234', 'fake_prim_user')
- username, tenant_name, password = iso_creds.get_primary_creds()
+ iso_creds.get_primary_creds()
tenant_fix.cleanUp()
user_fix.cleanUp()
tenant_fix = self._mock_tenant_create('12345', 'fake_alt_tenant')
user_fix = self._mock_user_create('12345', 'fake_alt_user')
- alt_username, alt_tenant, alt_password = iso_creds.get_alt_creds()
+ iso_creds.get_alt_creds()
tenant_fix.cleanUp()
user_fix.cleanUp()
tenant_fix = self._mock_tenant_create('123456', 'fake_admin_tenant')
@@ -170,8 +179,7 @@
[{'id': '123456', 'name': 'admin'}])))
with patch.object(json_iden_client.IdentityClientJSON,
'assign_user_role'):
- admin_username, admin_tenant, admin_pass = \
- iso_creds.get_admin_creds()
+ iso_creds.get_admin_creds()
user_mock = self.patch(
'tempest.services.identity.json.identity_client.'
'IdentityClientJSON.delete_user')
@@ -201,14 +209,12 @@
password='fake_password')
self._mock_user_create('1234', 'fake_alt_user')
self._mock_tenant_create('1234', 'fake_alt_tenant')
- username, tenant_name, password = iso_creds.get_alt_creds()
- self.assertEqual(username, 'fake_alt_user')
- self.assertEqual(tenant_name, 'fake_alt_tenant')
- # Verify helper methods
- tenant = iso_creds.get_alt_tenant()
- user = iso_creds.get_alt_user()
- self.assertEqual(tenant['id'], '1234')
- self.assertEqual(user['id'], '1234')
+ alt_creds = iso_creds.get_alt_creds()
+ self.assertEqual(alt_creds.username, 'fake_alt_user')
+ self.assertEqual(alt_creds.tenant_name, 'fake_alt_tenant')
+ # Verify IDs
+ self.assertEqual(alt_creds.tenant_id, '1234')
+ self.assertEqual(alt_creds.user_id, '1234')
@patch('tempest.common.rest_client.RestClient')
def test_network_creation(self, MockRestClient):
@@ -222,7 +228,7 @@
router_interface_mock = self.patch(
'tempest.services.network.json.network_client.NetworkClientJSON.'
'add_router_interface_with_subnet_id')
- username, tenant_name, password = iso_creds.get_primary_creds()
+ iso_creds.get_primary_creds()
router_interface_mock.called_once_with('1234', '1234')
network = iso_creds.get_primary_network()
subnet = iso_creds.get_primary_subnet()
@@ -247,7 +253,7 @@
router_interface_mock = self.patch(
'tempest.services.network.json.network_client.NetworkClientJSON.'
'add_router_interface_with_subnet_id')
- username, tenant_name, password = iso_creds.get_primary_creds()
+ iso_creds.get_primary_creds()
router_interface_mock.called_once_with('1234', '1234')
router_interface_mock.reset_mock()
tenant_fix.cleanUp()
@@ -262,7 +268,7 @@
subnet_fix = self._mock_subnet_create(iso_creds, '12345',
'fake_alt_subnet')
router_fix = self._mock_router_create('12345', 'fake_alt_router')
- alt_username, alt_tenant_name, password = iso_creds.get_alt_creds()
+ iso_creds.get_alt_creds()
router_interface_mock.called_once_with('12345', '12345')
router_interface_mock.reset_mock()
tenant_fix.cleanUp()
@@ -285,7 +291,7 @@
[{'id': '123456', 'name': 'admin'}])))
with patch.object(json_iden_client.IdentityClientJSON,
'assign_user_role'):
- admin_user, admin_tenant, password = iso_creds.get_admin_creds()
+ iso_creds.get_admin_creds()
self.patch('tempest.services.identity.json.identity_client.'
'IdentityClientJSON.delete_user')
self.patch('tempest.services.identity.json.identity_client.'
@@ -348,7 +354,7 @@
router_interface_mock = self.patch(
'tempest.services.network.json.network_client.NetworkClientJSON.'
'add_router_interface_with_subnet_id')
- username, tenant_name, password = iso_creds.get_alt_creds()
+ iso_creds.get_alt_creds()
router_interface_mock.called_once_with('1234', '1234')
network = iso_creds.get_alt_network()
subnet = iso_creds.get_alt_subnet()
@@ -379,7 +385,7 @@
[{'id': '123456', 'name': 'admin'}])))
with patch.object(json_iden_client.IdentityClientJSON,
'assign_user_role'):
- username, tenant_name, password = iso_creds.get_admin_creds()
+ iso_creds.get_admin_creds()
router_interface_mock.called_once_with('1234', '1234')
network = iso_creds.get_admin_network()
subnet = iso_creds.get_admin_subnet()
@@ -414,7 +420,7 @@
'delete_router')
router_mock = router.start()
- username, tenant_name, password = iso_creds.get_primary_creds()
+ iso_creds.get_primary_creds()
self.assertEqual(net_mock.mock_calls, [])
self.assertEqual(subnet_mock.mock_calls, [])
self.assertEqual(router_mock.mock_calls, [])
diff --git a/tempest/tests/test_wrappers.py b/tempest/tests/test_wrappers.py
index f6ed445..bba4012 100644
--- a/tempest/tests/test_wrappers.py
+++ b/tempest/tests/test_wrappers.py
@@ -14,6 +14,7 @@
import os
import shutil
+import StringIO
import subprocess
import tempfile
@@ -33,6 +34,7 @@
# Setup Test files
self.testr_conf_file = os.path.join(self.directory, '.testr.conf')
self.setup_cfg_file = os.path.join(self.directory, 'setup.cfg')
+ self.subunit_trace = os.path.join(self.directory, 'subunit-trace.py')
self.passing_file = os.path.join(self.test_dir, 'test_passing.py')
self.failing_file = os.path.join(self.test_dir, 'test_failing.py')
self.init_file = os.path.join(self.test_dir, '__init__.py')
@@ -43,55 +45,48 @@
shutil.copy('setup.py', self.setup_py)
shutil.copy('tempest/tests/files/setup.cfg', self.setup_cfg_file)
shutil.copy('tempest/tests/files/__init__.py', self.init_file)
+ shutil.copy('tools/subunit-trace.py', self.subunit_trace)
+ # copy over the pretty_tox scripts
+ shutil.copy('tools/pretty_tox.sh',
+ os.path.join(self.directory, 'pretty_tox.sh'))
+ shutil.copy('tools/pretty_tox_serial.sh',
+ os.path.join(self.directory, 'pretty_tox_serial.sh'))
+
+ self.stdout = StringIO.StringIO()
+ self.stderr = StringIO.StringIO()
+ # Change directory, run wrapper and check result
+ self.addCleanup(os.chdir, os.path.abspath(os.curdir))
+ os.chdir(self.directory)
+
+ def assertRunExit(self, cmd, expected):
+ p = subprocess.Popen(
+ "bash %s" % cmd, shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ # wait in the general case is dangerous, however the amount of
+ # data coming back on those pipes is small enough it shouldn't be
+ # a problem.
+ p.wait()
+
+ self.assertEqual(
+ p.returncode, expected,
+ "Stdout: %s; Stderr: %s" % (p.stdout, p.stderr))
def test_pretty_tox(self):
- # Copy wrapper script and requirements:
- pretty_tox = os.path.join(self.directory, 'pretty_tox.sh')
- shutil.copy('tools/pretty_tox.sh', pretty_tox)
- # Change directory, run wrapper and check result
- self.addCleanup(os.chdir, os.path.abspath(os.curdir))
- os.chdir(self.directory)
# Git init is required for the pbr testr command. pbr requires a git
# version or an sdist to work. so make the test directory a git repo
# too.
- subprocess.call(['git', 'init'])
- exit_code = subprocess.call('bash pretty_tox.sh tests.passing',
- shell=True, stdout=DEVNULL, stderr=DEVNULL)
- self.assertEqual(exit_code, 0)
+ subprocess.call(['git', 'init'], stderr=DEVNULL)
+ self.assertRunExit('pretty_tox.sh tests.passing', 0)
def test_pretty_tox_fails(self):
- # Copy wrapper script and requirements:
- pretty_tox = os.path.join(self.directory, 'pretty_tox.sh')
- shutil.copy('tools/pretty_tox.sh', pretty_tox)
- # Change directory, run wrapper and check result
- self.addCleanup(os.chdir, os.path.abspath(os.curdir))
- os.chdir(self.directory)
# Git init is required for the pbr testr command. pbr requires a git
# version or an sdist to work. so make the test directory a git repo
# too.
- subprocess.call(['git', 'init'])
- exit_code = subprocess.call('bash pretty_tox.sh', shell=True,
- stdout=DEVNULL, stderr=DEVNULL)
- self.assertEqual(exit_code, 1)
+ subprocess.call(['git', 'init'], stderr=DEVNULL)
+ self.assertRunExit('pretty_tox.sh', 1)
def test_pretty_tox_serial(self):
- # Copy wrapper script and requirements:
- pretty_tox = os.path.join(self.directory, 'pretty_tox_serial.sh')
- shutil.copy('tools/pretty_tox_serial.sh', pretty_tox)
- # Change directory, run wrapper and check result
- self.addCleanup(os.chdir, os.path.abspath(os.curdir))
- os.chdir(self.directory)
- exit_code = subprocess.call('bash pretty_tox_serial.sh tests.passing',
- shell=True, stdout=DEVNULL, stderr=DEVNULL)
- self.assertEqual(exit_code, 0)
+ self.assertRunExit('pretty_tox_serial.sh tests.passing', 0)
def test_pretty_tox_serial_fails(self):
- # Copy wrapper script and requirements:
- pretty_tox = os.path.join(self.directory, 'pretty_tox_serial.sh')
- shutil.copy('tools/pretty_tox_serial.sh', pretty_tox)
- # Change directory, run wrapper and check result
- self.addCleanup(os.chdir, os.path.abspath(os.curdir))
- os.chdir(self.directory)
- exit_code = subprocess.call('bash pretty_tox_serial.sh', shell=True,
- stdout=DEVNULL, stderr=DEVNULL)
- self.assertEqual(exit_code, 1)
+ self.assertRunExit('pretty_tox_serial.sh', 1)
diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py
index 46822e3..743b59d 100644
--- a/tools/install_venv_common.py
+++ b/tools/install_venv_common.py
@@ -101,7 +101,6 @@
print('done.')
else:
print("venv already exists...")
- pass
def pip_install(self, *args):
self.run_command(['tools/with_venv.sh',
diff --git a/tools/pretty_tox.sh b/tools/pretty_tox.sh
index 07c35a0..f3c88f3 100755
--- a/tools/pretty_tox.sh
+++ b/tools/pretty_tox.sh
@@ -3,4 +3,4 @@
set -o pipefail
TESTRARGS=$1
-python setup.py testr --slowest --testr-args="--subunit $TESTRARGS" | subunit2pyunit
+python setup.py testr --slowest --testr-args="--subunit $TESTRARGS" | $(dirname $0)/subunit-trace.py
diff --git a/tools/pretty_tox_serial.sh b/tools/pretty_tox_serial.sh
index 42ce760..1634b8e 100755
--- a/tools/pretty_tox_serial.sh
+++ b/tools/pretty_tox_serial.sh
@@ -7,7 +7,7 @@
if [ ! -d .testrepository ]; then
testr init
fi
-testr run --subunit $TESTRARGS | subunit2pyunit
+testr run --subunit $TESTRARGS | $(dirname $0)/subunit-trace.py
retval=$?
testr slowest
exit $retval
diff --git a/tools/subunit-trace.py b/tools/subunit-trace.py
new file mode 100755
index 0000000..7bb88a4
--- /dev/null
+++ b/tools/subunit-trace.py
@@ -0,0 +1,268 @@
+#!/usr/bin/env python
+
+# Copyright 2014 Hewlett-Packard Development Company, L.P.
+# Copyright 2014 Samsung Electronics
+# 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.
+
+"""Trace a subunit stream in reasonable detail and high accuracy."""
+
+import functools
+import re
+import sys
+
+import mimeparse
+import subunit
+import testtools
+
+DAY_SECONDS = 60 * 60 * 24
+FAILS = []
+RESULTS = {}
+
+
+class Starts(testtools.StreamResult):
+
+ def __init__(self, output):
+ super(Starts, self).__init__()
+ self._output = output
+
+ def startTestRun(self):
+ self._neednewline = False
+ self._emitted = set()
+
+ def status(self, test_id=None, test_status=None, test_tags=None,
+ runnable=True, file_name=None, file_bytes=None, eof=False,
+ mime_type=None, route_code=None, timestamp=None):
+ super(Starts, self).status(
+ test_id, test_status,
+ test_tags=test_tags, runnable=runnable, file_name=file_name,
+ file_bytes=file_bytes, eof=eof, mime_type=mime_type,
+ route_code=route_code, timestamp=timestamp)
+ if not test_id:
+ if not file_bytes:
+ return
+ if not mime_type or mime_type == 'test/plain;charset=utf8':
+ mime_type = 'text/plain; charset=utf-8'
+ primary, sub, parameters = mimeparse.parse_mime_type(mime_type)
+ content_type = testtools.content_type.ContentType(
+ primary, sub, parameters)
+ content = testtools.content.Content(
+ content_type, lambda: [file_bytes])
+ text = content.as_text()
+ if text and text[-1] not in '\r\n':
+ self._neednewline = True
+ self._output.write(text)
+ elif test_status == 'inprogress' and test_id not in self._emitted:
+ if self._neednewline:
+ self._neednewline = False
+ self._output.write('\n')
+ worker = ''
+ for tag in test_tags or ():
+ if tag.startswith('worker-'):
+ worker = '(' + tag[7:] + ') '
+ if timestamp:
+ timestr = timestamp.isoformat()
+ else:
+ timestr = ''
+ self._output.write('%s: %s%s [start]\n' %
+ (timestr, worker, test_id))
+ self._emitted.add(test_id)
+
+
+def cleanup_test_name(name, strip_tags=True, strip_scenarios=False):
+ """Clean up the test name for display.
+
+ By default we strip out the tags in the test because they don't help us
+ in identifying the test that is run to it's result.
+
+ Make it possible to strip out the testscenarios information (not to
+ be confused with tempest scenarios) however that's often needed to
+ indentify generated negative tests.
+ """
+ if strip_tags:
+ tags_start = name.find('[')
+ tags_end = name.find(']')
+ if tags_start > 0 and tags_end > tags_start:
+ newname = name[:tags_start]
+ newname += name[tags_end + 1:]
+ name = newname
+
+ if strip_scenarios:
+ tags_start = name.find('(')
+ tags_end = name.find(')')
+ if tags_start > 0 and tags_end > tags_start:
+ newname = name[:tags_start]
+ newname += name[tags_end + 1:]
+ name = newname
+
+ return name
+
+
+def get_duration(timestamps):
+ start, end = timestamps
+ if not start or not end:
+ duration = ''
+ else:
+ delta = end - start
+ duration = '%d.%06ds' % (
+ delta.days * DAY_SECONDS + delta.seconds, delta.microseconds)
+ return duration
+
+
+def find_worker(test):
+ for tag in test['tags']:
+ if tag.startswith('worker-'):
+ return int(tag[7:])
+ return 'NaN'
+
+
+# Print out stdout/stderr if it exists, always
+def print_attachments(stream, test, all_channels=False):
+ """Print out subunit attachments.
+
+ Print out subunit attachments that contain content. This
+ runs in 2 modes, one for successes where we print out just stdout
+ and stderr, and an override that dumps all the attachments.
+ """
+ channels = ('stdout', 'stderr')
+ for name, detail in test['details'].items():
+ # NOTE(sdague): the subunit names are a little crazy, and actually
+ # are in the form pythonlogging:'' (with the colon and quotes)
+ name = name.split(':')[0]
+ if detail.content_type.type == 'test':
+ detail.content_type.type = 'text'
+ if (all_channels or name in channels) and detail.as_text():
+ title = "Captured %s:" % name
+ stream.write("\n%s\n%s\n" % (title, ('~' * len(title))))
+ # indent attachment lines 4 spaces to make them visually
+ # offset
+ for line in detail.as_text().split('\n'):
+ stream.write(" %s\n" % line)
+
+
+def show_outcome(stream, test):
+ global RESULTS
+ status = test['status']
+ # TODO(sdague): ask lifeless why on this?
+ if status == 'exists':
+ return
+
+ worker = find_worker(test)
+ name = cleanup_test_name(test['id'])
+ duration = get_duration(test['timestamps'])
+
+ if worker not in RESULTS:
+ RESULTS[worker] = []
+ RESULTS[worker].append(test)
+
+ # don't count the end of the return code as a fail
+ if name == 'process-returncode':
+ return
+
+ if status == 'success':
+ stream.write('{%s} %s [%s] ... ok\n' % (
+ worker, name, duration))
+ print_attachments(stream, test)
+ elif status == 'fail':
+ FAILS.append(test)
+ stream.write('{%s} %s [%s] ... FAILED\n' % (
+ worker, name, duration))
+ print_attachments(stream, test, all_channels=True)
+ elif status == 'skip':
+ stream.write('{%s} %s ... SKIPPED: %s\n' % (
+ worker, name, test['details']['reason'].as_text()))
+ else:
+ stream.write('{%s} %s [%s] ... %s\n' % (
+ worker, name, duration, test['status']))
+ print_attachments(stream, test, all_channels=True)
+
+ stream.flush()
+
+
+def print_fails(stream):
+ """Print summary failure report.
+
+ Currently unused, however there remains debate on inline vs. at end
+ reporting, so leave the utility function for later use.
+ """
+ if not FAILS:
+ return
+ stream.write("\n==============================\n")
+ stream.write("Failed %s tests - output below:" % len(FAILS))
+ stream.write("\n==============================\n")
+ for f in FAILS:
+ stream.write("\n%s\n" % f['id'])
+ stream.write("%s\n" % ('-' * len(f['id'])))
+ print_attachments(stream, f, all_channels=True)
+ stream.write('\n')
+
+
+def count_tests(key, value):
+ count = 0
+ for k, v in RESULTS.items():
+ for item in v:
+ if key in item:
+ if re.search(value, item[key]):
+ count += 1
+ return count
+
+
+def worker_stats(worker):
+ tests = RESULTS[worker]
+ num_tests = len(tests)
+ delta = tests[-1]['timestamps'][1] - tests[0]['timestamps'][0]
+ return num_tests, delta
+
+
+def print_summary(stream):
+ stream.write("\n======\nTotals\n======\n")
+ stream.write("Run: %s\n" % count_tests('status', '.*'))
+ stream.write(" - Passed: %s\n" % count_tests('status', 'success'))
+ stream.write(" - Skipped: %s\n" % count_tests('status', 'skip'))
+ stream.write(" - Failed: %s\n" % count_tests('status', 'fail'))
+
+ # we could have no results, especially as we filter out the process-codes
+ if RESULTS:
+ stream.write("\n==============\nWorker Balance\n==============\n")
+
+ for w in range(max(RESULTS.keys()) + 1):
+ if w not in RESULTS:
+ stream.write(
+ " - WARNING: missing Worker %s! "
+ "Race in testr accounting.\n" % w)
+ else:
+ num, time = worker_stats(w)
+ stream.write(" - Worker %s (%s tests) => %ss\n" %
+ (w, num, time))
+
+
+def main():
+ stream = subunit.ByteStreamToStreamResult(
+ sys.stdin, non_subunit_name='stdout')
+ starts = Starts(sys.stdout)
+ outcomes = testtools.StreamToDict(
+ functools.partial(show_outcome, sys.stdout))
+ summary = testtools.StreamSummary()
+ result = testtools.CopyStreamResult([starts, outcomes, summary])
+ result.startTestRun()
+ try:
+ stream.run(result)
+ finally:
+ result.stopTestRun()
+ print_summary(sys.stdout)
+ return (0 if summary.wasSuccessful() else 1)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/tempest_auto_config.py b/tools/tempest_auto_config.py
deleted file mode 100644
index 5b8d05b..0000000
--- a/tools/tempest_auto_config.py
+++ /dev/null
@@ -1,395 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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.
-#
-# This script aims to configure an initial OpenStack environment with all the
-# necessary configurations for tempest's run using nothing but OpenStack's
-# native API.
-# That includes, creating users, tenants, registering images (cirros),
-# configuring neutron and so on.
-#
-# ASSUMPTION: this script is run by an admin user as it is meant to configure
-# the OpenStack environment prior to actual use.
-
-# Config
-import ConfigParser
-import os
-import tarfile
-import urllib2
-
-# Default client libs
-import glanceclient as glance_client
-import keystoneclient.v2_0.client as keystone_client
-
-# Import OpenStack exceptions
-import glanceclient.exc as glance_exception
-import keystoneclient.exceptions as keystone_exception
-
-
-TEMPEST_TEMP_DIR = os.getenv("TEMPEST_TEMP_DIR", "/tmp").rstrip('/')
-TEMPEST_ROOT_DIR = os.getenv("TEMPEST_ROOT_DIR", os.getenv("HOME")).rstrip('/')
-
-# Environment variables override defaults
-TEMPEST_CONFIG_DIR = os.getenv("TEMPEST_CONFIG_DIR",
- "%s%s" % (TEMPEST_ROOT_DIR, "/etc")).rstrip('/')
-TEMPEST_CONFIG_FILE = os.getenv("TEMPEST_CONFIG_FILE",
- "%s%s" % (TEMPEST_CONFIG_DIR, "/tempest.conf"))
-TEMPEST_CONFIG_SAMPLE = os.getenv("TEMPEST_CONFIG_SAMPLE",
- "%s%s" % (TEMPEST_CONFIG_DIR,
- "/tempest.conf.sample"))
-# Image references
-IMAGE_DOWNLOAD_CHUNK_SIZE = 8 * 1024
-IMAGE_UEC_SOURCE_URL = os.getenv("IMAGE_UEC_SOURCE_URL",
- "http://download.cirros-cloud.net/0.3.1/"
- "cirros-0.3.1-x86_64-uec.tar.gz")
-TEMPEST_IMAGE_ID = os.getenv('IMAGE_ID')
-TEMPEST_IMAGE_ID_ALT = os.getenv('IMAGE_ID_ALT')
-IMAGE_STATUS_ACTIVE = 'active'
-
-
-class ClientManager(object):
- """
- Manager that provides access to the official python clients for
- calling various OpenStack APIs.
- """
- def __init__(self):
- self.identity_client = None
- self.image_client = None
- self.network_client = None
- self.compute_client = None
- self.volume_client = None
-
- def get_identity_client(self, **kwargs):
- """
- Returns the openstack identity python client
- :param username: a string representing the username
- :param password: a string representing the user's password
- :param tenant_name: a string representing the tenant name of the user
- :param auth_url: a string representing the auth url of the identity
- :param insecure: True if we wish to disable ssl certificate validation,
- False otherwise
- :returns an instance of openstack identity python client
- """
- if not self.identity_client:
- self.identity_client = keystone_client.Client(**kwargs)
-
- return self.identity_client
-
- def get_image_client(self, version="1", *args, **kwargs):
- """
- This method returns OpenStack glance python client
- :param version: a string representing the version of the glance client
- to use.
- :param string endpoint: A user-supplied endpoint URL for the glance
- service.
- :param string token: Token for authentication.
- :param integer timeout: Allows customization of the timeout for client
- http requests. (optional)
- :return: a Client object representing the glance client
- """
- if not self.image_client:
- self.image_client = glance_client.Client(version, *args, **kwargs)
-
- return self.image_client
-
-
-def get_tempest_config(path_to_config):
- """
- Gets the tempest configuration file as a ConfigParser object
- :param path_to_config: path to the config file
- :return: a ConfigParser object representing the tempest configuration file
- """
- # get the sample config file from the sample
- config = ConfigParser.ConfigParser()
- config.readfp(open(path_to_config))
-
- return config
-
-
-def update_config_admin_credentials(config, config_section):
- """
- Updates the tempest config with the admin credentials
- :param config: a ConfigParser object representing the tempest config file
- :param config_section: the section name where the admin credentials are
- """
- # Check if credentials are present, default uses the config credentials
- OS_USERNAME = os.getenv('OS_USERNAME',
- config.get(config_section, "admin_username"))
- OS_PASSWORD = os.getenv('OS_PASSWORD',
- config.get(config_section, "admin_password"))
- OS_TENANT_NAME = os.getenv('OS_TENANT_NAME',
- config.get(config_section, "admin_tenant_name"))
- OS_AUTH_URL = os.getenv('OS_AUTH_URL', config.get(config_section, "uri"))
-
- if not (OS_AUTH_URL and
- OS_USERNAME and
- OS_PASSWORD and
- OS_TENANT_NAME):
- raise Exception("Admin environment variables not found.")
-
- # TODO(tkammer): Add support for uri_v3
- config_identity_params = {'uri': OS_AUTH_URL,
- 'admin_username': OS_USERNAME,
- 'admin_password': OS_PASSWORD,
- 'admin_tenant_name': OS_TENANT_NAME}
-
- update_config_section_with_params(config,
- config_section,
- config_identity_params)
-
-
-def update_config_section_with_params(config, config_section, params):
- """
- Updates a given config object with given params
- :param config: a ConfigParser object representing the tempest config file
- :param config_section: the section we would like to update
- :param params: the parameters we wish to update for that section
- """
- for option, value in params.items():
- config.set(config_section, option, value)
-
-
-def get_identity_client_kwargs(config, config_section):
- """
- Get the required arguments for the identity python client
- :param config: a ConfigParser object representing the tempest config file
- :param config_section: the section name in the configuration where the
- arguments can be found
- :return: a dictionary representing the needed arguments for the identity
- client
- """
- username = config.get(config_section, 'admin_username')
- password = config.get(config_section, 'admin_password')
- tenant_name = config.get(config_section, 'admin_tenant_name')
- auth_url = config.get(config_section, 'uri')
- dscv = config.get(config_section, 'disable_ssl_certificate_validation')
- kwargs = {'username': username,
- 'password': password,
- 'tenant_name': tenant_name,
- 'auth_url': auth_url,
- 'insecure': dscv}
-
- return kwargs
-
-
-def create_user_with_tenant(identity_client, username, password, tenant_name):
- """
- Creates a user using a given identity client
- :param identity_client: openstack identity python client
- :param username: a string representing the username
- :param password: a string representing the user's password
- :param tenant_name: a string representing the tenant name of the user
- """
- # Try to create the necessary tenant
- tenant_id = None
- try:
- tenant_description = "Tenant for Tempest %s user" % username
- tenant = identity_client.tenants.create(tenant_name,
- tenant_description)
- tenant_id = tenant.id
- except keystone_exception.Conflict:
-
- # if already exist, use existing tenant
- tenant_list = identity_client.tenants.list()
- for tenant in tenant_list:
- if tenant.name == tenant_name:
- tenant_id = tenant.id
-
- # Try to create the user
- try:
- email = "%s@test.com" % username
- identity_client.users.create(name=username,
- password=password,
- email=email,
- tenant_id=tenant_id)
- except keystone_exception.Conflict:
-
- # if already exist, use existing user
- pass
-
-
-def create_users_and_tenants(identity_client,
- config,
- config_section):
- """
- Creates the two non admin users and tenants for tempest
- :param identity_client: openstack identity python client
- :param config: a ConfigParser object representing the tempest config file
- :param config_section: the section name of identity in the config
- """
- # Get the necessary params from the config file
- tenant_name = config.get(config_section, 'tenant_name')
- username = config.get(config_section, 'username')
- password = config.get(config_section, 'password')
-
- alt_tenant_name = config.get(config_section, 'alt_tenant_name')
- alt_username = config.get(config_section, 'alt_username')
- alt_password = config.get(config_section, 'alt_password')
-
- # Create the necessary users for the test runs
- create_user_with_tenant(identity_client, username, password, tenant_name)
- create_user_with_tenant(identity_client, alt_username, alt_password,
- alt_tenant_name)
-
-
-def get_image_client_kwargs(identity_client, config, config_section):
- """
- Get the required arguments for the image python client
- :param identity_client: openstack identity python client
- :param config: a ConfigParser object representing the tempest config file
- :param config_section: the section name of identity in the config
- :return: a dictionary representing the needed arguments for the image
- client
- """
-
- token = identity_client.auth_token
- endpoint = identity_client.\
- service_catalog.url_for(service_type='image', endpoint_type='publicURL'
- )
- dscv = config.get(config_section, 'disable_ssl_certificate_validation')
- kwargs = {'endpoint': endpoint,
- 'token': token,
- 'insecure': dscv}
-
- return kwargs
-
-
-def images_exist(image_client):
- """
- Checks whether the images ID's located in the environment variable are
- indeed registered
- :param image_client: the openstack python client representing the image
- client
- """
- exist = True
- if not TEMPEST_IMAGE_ID or not TEMPEST_IMAGE_ID_ALT:
- exist = False
- else:
- try:
- image_client.images.get(TEMPEST_IMAGE_ID)
- image_client.images.get(TEMPEST_IMAGE_ID_ALT)
- except glance_exception.HTTPNotFound:
- exist = False
-
- return exist
-
-
-def download_and_register_uec_images(image_client, download_url,
- download_folder):
- """
- Downloads and registered the UEC AKI/AMI/ARI images
- :param image_client:
- :param download_url: the url of the uec tar file
- :param download_folder: the destination folder we wish to save the file to
- """
- basename = os.path.basename(download_url)
- path = os.path.join(download_folder, basename)
-
- request = urllib2.urlopen(download_url)
-
- # First, download the file
- with open(path, "wb") as fp:
- while True:
- chunk = request.read(IMAGE_DOWNLOAD_CHUNK_SIZE)
- if not chunk:
- break
-
- fp.write(chunk)
-
- # Then extract and register images
- tar = tarfile.open(path, "r")
- for name in tar.getnames():
- file_obj = tar.extractfile(name)
- format = "aki"
-
- if file_obj.name.endswith(".img"):
- format = "ami"
-
- if file_obj.name.endswith("initrd"):
- format = "ari"
-
- # Register images in image client
- image_client.images.create(name=file_obj.name, disk_format=format,
- container_format=format, data=file_obj,
- is_public="true")
-
- tar.close()
-
-
-def create_images(image_client, config, config_section,
- download_url=IMAGE_UEC_SOURCE_URL,
- download_folder=TEMPEST_TEMP_DIR):
- """
- Creates images for tempest's use and registers the environment variables
- IMAGE_ID and IMAGE_ID_ALT with registered images
- :param image_client: OpenStack python image client
- :param config: a ConfigParser object representing the tempest config file
- :param config_section: the section name where the IMAGE ids are set
- :param download_url: the URL from which we should download the UEC tar
- :param download_folder: the place where we want to save the download file
- """
- if not images_exist(image_client):
- # Falls down to the default uec images
- download_and_register_uec_images(image_client, download_url,
- download_folder)
- image_ids = []
- for image in image_client.images.list():
- image_ids.append(image.id)
-
- os.environ["IMAGE_ID"] = image_ids[0]
- os.environ["IMAGE_ID_ALT"] = image_ids[1]
-
- params = {'image_ref': os.getenv("IMAGE_ID"),
- 'image_ref_alt': os.getenv("IMAGE_ID_ALT")}
-
- update_config_section_with_params(config, config_section, params)
-
-
-def main():
- """
- Main module to control the script
- """
- # Check if config file exists or fall to the default sample otherwise
- path_to_config = TEMPEST_CONFIG_SAMPLE
-
- if os.path.isfile(TEMPEST_CONFIG_FILE):
- path_to_config = TEMPEST_CONFIG_FILE
-
- config = get_tempest_config(path_to_config)
- update_config_admin_credentials(config, 'identity')
-
- client_manager = ClientManager()
-
- # Set the identity related info for tempest
- identity_client_kwargs = get_identity_client_kwargs(config,
- 'identity')
- identity_client = client_manager.get_identity_client(
- **identity_client_kwargs)
-
- # Create the necessary users and tenants for tempest run
- create_users_and_tenants(identity_client, config, 'identity')
-
- # Set the image related info for tempest
- image_client_kwargs = get_image_client_kwargs(identity_client,
- config,
- 'identity')
- image_client = client_manager.get_image_client(**image_client_kwargs)
-
- # Create the necessary users and tenants for tempest run
- create_images(image_client, config, 'compute')
-
- # TODO(tkammer): add network implementation
-
-if __name__ == "__main__":
- main()