Merge "Add CLI tests for Neutron's metering agent"
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 85b03e6..ee262df 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -16,7 +16,6 @@
# under the License.
from tempest.api.compute import base
-from tempest import config
from tempest import exceptions
from tempest.test import attr
@@ -24,8 +23,6 @@
class FixedIPsTestJson(base.BaseComputeAdminTest):
_interface = 'json'
- CONF = config.TempestConfig()
-
@classmethod
def setUpClass(cls):
super(FixedIPsTestJson, cls).setUpClass()
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index e730d31..2029a2e 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -35,8 +35,11 @@
cls.client = cls.os_adm.servers_client
cls.flavors_client = cls.os_adm.flavors_client
- cls.admin_client = cls._get_identity_admin_client()
- tenant = cls.admin_client.get_tenant_by_name(
+ 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']
@@ -158,6 +161,24 @@
self.assertRaises(exceptions.NotFound,
self.client.reset_state, '999')
+ @attr(type='gate')
+ def test_get_server_diagnostics_by_admin(self):
+ # Retrieve server diagnostics by admin user
+ resp, diagnostic = self.client.get_server_diagnostics(self.s1_id)
+ self.assertEqual(200, resp.status)
+ basic_attrs = ['rx_packets', 'rx_errors', 'rx_drop',
+ 'tx_packets', 'tx_errors', 'tx_drop',
+ 'read_req', 'write_req', 'cpu', 'memory']
+ for key in basic_attrs:
+ self.assertIn(key, str(diagnostic.keys()))
+
+ @attr(type=['negative', 'gate'])
+ def test_get_server_diagnostics_by_non_admin(self):
+ # Non-admin user can not view server diagnostics according to policy
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_adm_client.get_server_diagnostics,
+ self.s1_id)
+
class ServersAdminTestXML(ServersAdminTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index 983258d..9dd2e27 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -37,7 +37,7 @@
blocks while the servers are being deleted.
"""
if len(servers):
- if not compute.MULTI_USER:
+ if not cls.config.compute.allow_tenant_isolation:
for srv in servers:
cls.client.wait_for_server_termination(srv['id'],
ignore_error=True)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index e9defe5..6f646b2 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -118,7 +118,8 @@
password = 'rebuildPassw0rd'
resp, rebuilt_server = self.client.rebuild(self.server_id,
self.image_ref_alt,
- name=new_name, meta=meta,
+ name=new_name,
+ metadata=meta,
personality=personality,
adminPass=password)
@@ -226,7 +227,8 @@
self.assertRaises(exceptions.NotFound,
self.client.rebuild,
999, self.image_ref_alt,
- name=new_name, meta=meta,
+ name=new_name,
+ metadata=meta,
personality=personality,
adminPass='rebuild')
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index 689ab29..66d35cb 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -19,7 +19,6 @@
from tempest.api.identity import base
from tempest.common.utils.data_utils import rand_name
-from tempest import exceptions
from tempest.test import attr
@@ -46,60 +45,19 @@
self.assertEqual('200', resp['status'])
self.assertEqual(self.alt_user, user['name'])
- @attr(type=['negative', 'gate'])
- def test_create_user_by_unauthorized_user(self):
- # Non-administrator should not be authorized to create a user
+ @attr(type='smoke')
+ def test_create_user_with_enabled(self):
+ # Create a user with enabled : False
self.data.setup_test_tenant()
- self.assertRaises(exceptions.Unauthorized,
- self.non_admin_client.create_user, self.alt_user,
- self.alt_password, self.data.tenant['id'],
- self.alt_email)
-
- @attr(type=['negative', 'gate'])
- def test_create_user_with_empty_name(self):
- # User with an empty name should not be created
- self.data.setup_test_tenant()
- self.assertRaises(exceptions.BadRequest, self.client.create_user, '',
- self.alt_password, self.data.tenant['id'],
- self.alt_email)
-
- @attr(type=['negative', 'gate'])
- def test_create_user_with_name_length_over_255(self):
- # Length of user name filed should be restricted to 255 characters
- self.data.setup_test_tenant()
- self.assertRaises(exceptions.BadRequest, self.client.create_user,
- 'a' * 256, self.alt_password,
- self.data.tenant['id'], self.alt_email)
-
- @attr(type=['negative', 'gate'])
- def test_create_user_with_duplicate_name(self):
- # Duplicate user should not be created
- self.data.setup_test_user()
- self.assertRaises(exceptions.Duplicate, self.client.create_user,
- self.data.test_user, self.data.test_password,
- self.data.tenant['id'], self.data.test_email)
-
- @attr(type=['negative', 'gate'])
- def test_create_user_for_non_existant_tenant(self):
- # Attempt to create a user in a non-existent tenant should fail
- self.assertRaises(exceptions.NotFound, self.client.create_user,
- self.alt_user, self.alt_password, '49ffgg99999',
- self.alt_email)
-
- @attr(type=['negative', 'gate'])
- def test_create_user_request_without_a_token(self):
- # Request to create a user without a valid token should fail
- self.data.setup_test_tenant()
- # Get the token of the current client
- token = self.client.get_auth()
- # Delete the token from database
- self.client.delete_token(token)
- self.assertRaises(exceptions.Unauthorized, self.client.create_user,
- self.alt_user, self.alt_password,
- self.data.tenant['id'], self.alt_email)
-
- # Unset the token to allow further tests to generate a new token
- self.client.clear_auth()
+ name = rand_name('test_user_')
+ resp, user = self.client.create_user(name, self.alt_password,
+ self.data.tenant['id'],
+ self.alt_email, enabled=False)
+ self.data.users.append(user)
+ self.assertEqual('200', resp['status'])
+ self.assertEqual(name, user['name'])
+ self.assertEqual('false', str(user['enabled']).lower())
+ self.assertEqual(self.alt_email, user['email'])
@attr(type='smoke')
def test_update_user(self):
@@ -141,20 +99,6 @@
resp, body = self.client.delete_user(user['id'])
self.assertEqual('204', resp['status'])
- @attr(type=['negative', 'gate'])
- def test_delete_users_by_unauthorized_user(self):
- # Non-administrator user should not be authorized to delete a user
- self.data.setup_test_user()
- self.assertRaises(exceptions.Unauthorized,
- self.non_admin_client.delete_user,
- self.data.user['id'])
-
- @attr(type=['negative', 'gate'])
- def test_delete_non_existant_user(self):
- # Attempt to delete a non-existent user should fail
- self.assertRaises(exceptions.NotFound, self.client.delete_user,
- 'junk12345123')
-
@attr(type='smoke')
def test_user_authentication(self):
# Valid user's token is authenticated
@@ -168,51 +112,6 @@
self.data.test_tenant)
self.assertEqual('200', resp['status'])
- @attr(type=['negative', 'gate'])
- def test_authentication_for_disabled_user(self):
- # Disabled user's token should not get authenticated
- self.data.setup_test_user()
- self.disable_user(self.data.test_user)
- self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
- self.data.test_user,
- self.data.test_password,
- self.data.test_tenant)
-
- @attr(type=['negative', 'gate'])
- def test_authentication_when_tenant_is_disabled(self):
- # User's token for a disabled tenant should not be authenticated
- self.data.setup_test_user()
- self.disable_tenant(self.data.test_tenant)
- self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
- self.data.test_user,
- self.data.test_password,
- self.data.test_tenant)
-
- @attr(type=['negative', 'gate'])
- def test_authentication_with_invalid_tenant(self):
- # User's token for an invalid tenant should not be authenticated
- self.data.setup_test_user()
- self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
- self.data.test_user,
- self.data.test_password,
- 'junktenant1234')
-
- @attr(type=['negative', 'gate'])
- def test_authentication_with_invalid_username(self):
- # Non-existent user's token should not get authenticated
- self.data.setup_test_user()
- self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
- 'junkuser123', self.data.test_password,
- self.data.test_tenant)
-
- @attr(type=['negative', 'gate'])
- def test_authentication_with_invalid_password(self):
- # User's token with invalid password should not be authenticated
- self.data.setup_test_user()
- self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
- self.data.test_user, 'junkpass1234',
- self.data.test_tenant)
-
@attr(type='gate')
def test_authentication_request_without_token(self):
# Request for token authentication with a valid token in header
@@ -239,21 +138,6 @@
Contains(self.data.test_user),
"Could not find %s" % self.data.test_user)
- @attr(type=['negative', 'gate'])
- def test_get_users_by_unauthorized_user(self):
- # Non-administrator user should not be authorized to get user list
- self.data.setup_test_user()
- self.assertRaises(exceptions.Unauthorized,
- self.non_admin_client.get_users)
-
- @attr(type=['negative', 'gate'])
- def test_get_users_request_without_token(self):
- # Request to get list of users without a valid token should fail
- token = self.client.get_auth()
- self.client.delete_token(token)
- self.assertRaises(exceptions.Unauthorized, self.client.get_users)
- self.client.clear_auth()
-
@attr(type='gate')
def test_list_users_for_tenant(self):
# Return a list of all users for a tenant
@@ -326,21 +210,6 @@
"Failed to find user %s in fetched list" %
', '.join(m_user for m_user in missing_users))
- @attr(type=['negative', 'gate'])
- def test_list_users_with_invalid_tenant(self):
- # Should not be able to return a list of all
- # users for a non-existent tenant
- # Assign invalid tenant ids
- invalid_id = list()
- invalid_id.append(rand_name('999'))
- invalid_id.append('alpha')
- invalid_id.append(rand_name("dddd@#%%^$"))
- invalid_id.append('!@#()$%^&*?<>{}[]')
- # List the users with invalid tenant id
- for invalid in invalid_id:
- self.assertRaises(exceptions.NotFound,
- self.client.list_users_for_tenant, invalid)
-
class UsersTestXML(UsersTestJSON):
_interface = 'xml'
diff --git a/tempest/api/identity/admin/test_users_negative.py b/tempest/api/identity/admin/test_users_negative.py
new file mode 100644
index 0000000..7cb9aef
--- /dev/null
+++ b/tempest/api/identity/admin/test_users_negative.py
@@ -0,0 +1,236 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.identity import base
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+from tempest.test import attr
+import uuid
+
+
+class UsersNegativeTestJSON(base.BaseIdentityAdminTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(UsersNegativeTestJSON, cls).setUpClass()
+ cls.alt_user = rand_name('test_user_')
+ cls.alt_password = rand_name('pass_')
+ cls.alt_email = cls.alt_user + '@testmail.tm'
+ cls.alt_tenant = rand_name('test_tenant_')
+ cls.alt_description = rand_name('desc_')
+
+ @attr(type=['negative', 'gate'])
+ def test_create_user_by_unauthorized_user(self):
+ # Non-administrator should not be authorized to create a user
+ self.data.setup_test_tenant()
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_admin_client.create_user, self.alt_user,
+ self.alt_password, self.data.tenant['id'],
+ self.alt_email)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_user_with_empty_name(self):
+ # User with an empty name should not be created
+ self.data.setup_test_tenant()
+ self.assertRaises(exceptions.BadRequest, self.client.create_user, '',
+ self.alt_password, self.data.tenant['id'],
+ self.alt_email)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_user_with_name_length_over_255(self):
+ # Length of user name filed should be restricted to 255 characters
+ self.data.setup_test_tenant()
+ self.assertRaises(exceptions.BadRequest, self.client.create_user,
+ 'a' * 256, self.alt_password,
+ self.data.tenant['id'], self.alt_email)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_user_with_duplicate_name(self):
+ # Duplicate user should not be created
+ self.data.setup_test_user()
+ self.assertRaises(exceptions.Duplicate, self.client.create_user,
+ self.data.test_user, self.data.test_password,
+ self.data.tenant['id'], self.data.test_email)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_user_for_non_existant_tenant(self):
+ # Attempt to create a user in a non-existent tenant should fail
+ self.assertRaises(exceptions.NotFound, self.client.create_user,
+ self.alt_user, self.alt_password, '49ffgg99999',
+ self.alt_email)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_user_request_without_a_token(self):
+ # Request to create a user without a valid token should fail
+ self.data.setup_test_tenant()
+ # Get the token of the current client
+ token = self.client.get_auth()
+ # Delete the token from database
+ self.client.delete_token(token)
+ self.assertRaises(exceptions.Unauthorized, self.client.create_user,
+ self.alt_user, self.alt_password,
+ self.data.tenant['id'], self.alt_email)
+
+ # Unset the token to allow further tests to generate a new token
+ self.client.clear_auth()
+
+ @attr(type=['negative', 'gate'])
+ def test_create_user_with_enabled_non_bool(self):
+ # Attempt to create a user with valid enabled para should fail
+ self.data.setup_test_tenant()
+ name = rand_name('test_user_')
+ self.assertRaises(exceptions.BadRequest, self.client.create_user,
+ name, self.alt_password,
+ self.data.tenant['id'],
+ self.alt_email, enabled=3)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_user_for_non_existant_user(self):
+ # Attempt to update a user non-existent user should fail
+ user_name = rand_name('user-')
+ non_existent_id = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.client.update_user,
+ non_existent_id, name=user_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_user_request_without_a_token(self):
+ # Request to update a user without a valid token should fail
+
+ # Get the token of the current client
+ token = self.client.get_auth()
+ # Delete the token from database
+ self.client.delete_token(token)
+ self.assertRaises(exceptions.Unauthorized, self.client.update_user,
+ self.alt_user)
+
+ # Unset the token to allow further tests to generate a new token
+ self.client.clear_auth()
+
+ @attr(type=['negative', 'gate'])
+ def test_update_user_by_unauthorized_user(self):
+ # Non-administrator should not be authorized to update user
+ self.data.setup_test_tenant()
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_admin_client.update_user, self.alt_user)
+
+ @attr(type=['negative', 'gate'])
+ def test_delete_users_by_unauthorized_user(self):
+ # Non-administrator user should not be authorized to delete a user
+ self.data.setup_test_user()
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_admin_client.delete_user,
+ self.data.user['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_delete_non_existant_user(self):
+ # Attempt to delete a non-existent user should fail
+ self.assertRaises(exceptions.NotFound, self.client.delete_user,
+ 'junk12345123')
+
+ @attr(type=['negative', 'gate'])
+ def test_delete_user_request_without_a_token(self):
+ # Request to delete a user without a valid token should fail
+
+ # Get the token of the current client
+ token = self.client.get_auth()
+ # Delete the token from database
+ self.client.delete_token(token)
+ self.assertRaises(exceptions.Unauthorized, self.client.delete_user,
+ self.alt_user)
+
+ # Unset the token to allow further tests to generate a new token
+ self.client.clear_auth()
+
+ @attr(type=['negative', 'gate'])
+ def test_authentication_for_disabled_user(self):
+ # Disabled user's token should not get authenticated
+ self.data.setup_test_user()
+ self.disable_user(self.data.test_user)
+ self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
+ self.data.test_user,
+ self.data.test_password,
+ self.data.test_tenant)
+
+ @attr(type=['negative', 'gate'])
+ def test_authentication_when_tenant_is_disabled(self):
+ # User's token for a disabled tenant should not be authenticated
+ self.data.setup_test_user()
+ self.disable_tenant(self.data.test_tenant)
+ self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
+ self.data.test_user,
+ self.data.test_password,
+ self.data.test_tenant)
+
+ @attr(type=['negative', 'gate'])
+ def test_authentication_with_invalid_tenant(self):
+ # User's token for an invalid tenant should not be authenticated
+ self.data.setup_test_user()
+ self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
+ self.data.test_user,
+ self.data.test_password,
+ 'junktenant1234')
+
+ @attr(type=['negative', 'gate'])
+ def test_authentication_with_invalid_username(self):
+ # Non-existent user's token should not get authenticated
+ self.data.setup_test_user()
+ self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
+ 'junkuser123', self.data.test_password,
+ self.data.test_tenant)
+
+ @attr(type=['negative', 'gate'])
+ def test_authentication_with_invalid_password(self):
+ # User's token with invalid password should not be authenticated
+ self.data.setup_test_user()
+ self.assertRaises(exceptions.Unauthorized, self.token_client.auth,
+ self.data.test_user, 'junkpass1234',
+ self.data.test_tenant)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_users_by_unauthorized_user(self):
+ # Non-administrator user should not be authorized to get user list
+ self.data.setup_test_user()
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_admin_client.get_users)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_users_request_without_token(self):
+ # Request to get list of users without a valid token should fail
+ token = self.client.get_auth()
+ self.client.delete_token(token)
+ self.assertRaises(exceptions.Unauthorized, self.client.get_users)
+ self.client.clear_auth()
+
+ @attr(type=['negative', 'gate'])
+ def test_list_users_with_invalid_tenant(self):
+ # Should not be able to return a list of all
+ # users for a non-existent tenant
+ # Assign invalid tenant ids
+ invalid_id = list()
+ invalid_id.append(rand_name('999'))
+ invalid_id.append('alpha')
+ invalid_id.append(rand_name("dddd@#%%^$"))
+ invalid_id.append('!@#()$%^&*?<>{}[]')
+ # List the users with invalid tenant id
+ for invalid in invalid_id:
+ self.assertRaises(exceptions.NotFound,
+ self.client.list_users_for_tenant, invalid)
+
+
+class UsersNegativeTestXML(UsersNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 32eecfb..2aaa71d 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack Foundation
+# Copyright 2013 IBM Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -16,6 +17,7 @@
# under the License.
from tempest.api.volume import base
+from tempest.common.utils.data_utils import rand_int_id
from tempest.common.utils.data_utils import rand_name
from tempest.openstack.common import log as logging
from tempest.test import attr
@@ -103,6 +105,66 @@
self.assertEqual(200, resp.status)
self.assertVolumesIn(fetched_list, self.volume_list)
+ @attr(type='gate')
+ def test_volume_list_by_name(self):
+ volume = self.volume_list[rand_int_id(0, 2)]
+ params = {'display_name': volume['display_name']}
+ resp, fetched_vol = self.client.list_volumes(params)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(1, len(fetched_vol), str(fetched_vol))
+ self.assertEqual(fetched_vol[0]['display_name'],
+ volume['display_name'])
+
+ @attr(type='gate')
+ def test_volume_list_details_by_name(self):
+ volume = self.volume_list[rand_int_id(0, 2)]
+ params = {'display_name': volume['display_name']}
+ resp, fetched_vol = self.client.list_volumes_with_detail(params)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(1, len(fetched_vol), str(fetched_vol))
+ self.assertEqual(fetched_vol[0]['display_name'],
+ volume['display_name'])
+
+ @attr(type='gate')
+ def test_volumes_list_by_status(self):
+ params = {'status': 'available'}
+ resp, fetched_list = self.client.list_volumes(params)
+ self.assertEqual(200, resp.status)
+ for volume in fetched_list:
+ self.assertEqual('available', volume['status'])
+ self.assertVolumesIn(fetched_list, self.volume_list)
+
+ @attr(type='gate')
+ def test_volumes_list_details_by_status(self):
+ params = {'status': 'available'}
+ resp, fetched_list = self.client.list_volumes_with_detail(params)
+ self.assertEqual(200, resp.status)
+ for volume in fetched_list:
+ self.assertEqual('available', volume['status'])
+ self.assertVolumesIn(fetched_list, self.volume_list)
+
+ @attr(type='gate')
+ def test_volumes_list_by_availability_zone(self):
+ volume = self.volume_list[rand_int_id(0, 2)]
+ zone = volume['availability_zone']
+ params = {'availability_zone': zone}
+ resp, fetched_list = self.client.list_volumes(params)
+ self.assertEqual(200, resp.status)
+ for volume in fetched_list:
+ self.assertEqual(zone, volume['availability_zone'])
+ self.assertVolumesIn(fetched_list, self.volume_list)
+
+ @attr(type='gate')
+ def test_volumes_list_details_by_availability_zone(self):
+ volume = self.volume_list[rand_int_id(0, 2)]
+ zone = volume['availability_zone']
+ params = {'availability_zone': zone}
+ resp, fetched_list = self.client.list_volumes_with_detail(params)
+ self.assertEqual(200, resp.status)
+ for volume in fetched_list:
+ self.assertEqual(zone, volume['availability_zone'])
+ self.assertVolumesIn(fetched_list, self.volume_list)
+
class VolumeListTestXML(VolumesListTest):
_interface = 'xml'
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index a48cea2..3e2b6ad 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -38,19 +38,19 @@
cls.client.wait_for_volume_status(cls.volume['id'], 'available')
cls.mountpoint = "/dev/vdc"
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_volume_get_nonexistant_volume_id(self):
# Should not be able to get a non-existant volume
self.assertRaises(exceptions.NotFound, self.client.get_volume,
str(uuid.uuid4()))
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_volume_delete_nonexistant_volume_id(self):
# Should not be able to delete a non-existant Volume
self.assertRaises(exceptions.NotFound, self.client.delete_volume,
str(uuid.uuid4()))
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_volume_with_invalid_size(self):
# Should not be able to create volume with invalid size
# in request
@@ -59,7 +59,7 @@
self.assertRaises(exceptions.BadRequest, self.client.create_volume,
size='#$%', display_name=v_name, metadata=metadata)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_volume_with_out_passing_size(self):
# Should not be able to create volume without passing size
# in request
@@ -68,7 +68,7 @@
self.assertRaises(exceptions.BadRequest, self.client.create_volume,
size='', display_name=v_name, metadata=metadata)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_volume_with_size_zero(self):
# Should not be able to create volume with size zero
v_name = rand_name('Volume-')
@@ -76,24 +76,56 @@
self.assertRaises(exceptions.BadRequest, self.client.create_volume,
size='0', display_name=v_name, metadata=metadata)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
+ def test_create_volume_with_size_negative(self):
+ # Should not be able to create volume with size negative
+ v_name = rand_name('Volume-')
+ metadata = {'Type': 'work'}
+ self.assertRaises(exceptions.BadRequest, self.client.create_volume,
+ size='-1', display_name=v_name, metadata=metadata)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_volume_with_nonexistant_volume_id(self):
+ v_name = rand_name('Volume-')
+ metadata = {'Type': 'work'}
+ self.assertRaises(exceptions.NotFound, self.client.update_volume,
+ volume_id=str(uuid.uuid4()), display_name=v_name,
+ metadata=metadata)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_volume_with_invalid_volume_id(self):
+ v_name = rand_name('Volume-')
+ metadata = {'Type': 'work'}
+ self.assertRaises(exceptions.NotFound, self.client.update_volume,
+ volume_id='#$%%&^&^', display_name=v_name,
+ metadata=metadata)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_volume_with_empty_volume_id(self):
+ v_name = rand_name('Volume-')
+ metadata = {'Type': 'work'}
+ self.assertRaises(exceptions.NotFound, self.client.update_volume,
+ volume_id='', display_name=v_name,
+ metadata=metadata)
+
+ @attr(type=['negative', 'gate'])
def test_get_invalid_volume_id(self):
# Should not be able to get volume with invalid id
self.assertRaises(exceptions.NotFound, self.client.get_volume,
'#$%%&^&^')
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_get_volume_without_passing_volume_id(self):
# Should not be able to get volume when empty ID is passed
self.assertRaises(exceptions.NotFound, self.client.get_volume, '')
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_delete_invalid_volume_id(self):
# Should not be able to delete volume when invalid ID is passed
self.assertRaises(exceptions.NotFound, self.client.delete_volume,
'!@#$%^&*()')
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_delete_volume_without_passing_volume_id(self):
# Should not be able to delete volume when empty ID is passed
self.assertRaises(exceptions.NotFound, self.client.delete_volume, '')
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
index 3ff997a..25157a4 100644
--- a/tempest/cli/simple_read_only/test_cinder.py
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -84,6 +84,24 @@
roles = self.parser.listing(self.cinder('list-extensions'))
self.assertTableStruct(roles, ['Name', 'Summary', 'Alias', 'Updated'])
+ def test_cinder_credentials(self):
+ self.cinder('credentials')
+
+ def test_cinder_availability_zone_list(self):
+ self.cinder('availability-zone-list')
+
+ def test_cinder_endpoints(self):
+ self.cinder('endpoints')
+
+ def test_cinder_service_list(self):
+ self.cinder('service-list')
+
+ def test_cinder_transfer_list(self):
+ self.cinder('transfer-list')
+
+ def test_cinder_bash_completion(self):
+ self.cinder('bash-completion')
+
def test_admin_help(self):
help_text = self.cinder('help')
lines = help_text.split('\n')
diff --git a/tempest/cli/simple_read_only/test_compute.py b/tempest/cli/simple_read_only/test_compute.py
index 9b358e6..94415f0 100644
--- a/tempest/cli/simple_read_only/test_compute.py
+++ b/tempest/cli/simple_read_only/test_compute.py
@@ -167,6 +167,10 @@
def test_admin_net_list(self):
self.nova('net-list')
+ def test_agent_list(self):
+ self.nova('agent-list')
+ self.nova('agent-list', flags='--debug')
+
# Optional arguments:
def test_admin_version(self):
diff --git a/tempest/cli/simple_read_only/test_compute_manage.py b/tempest/cli/simple_read_only/test_compute_manage.py
index 523c65f..9a33556 100644
--- a/tempest/cli/simple_read_only/test_compute_manage.py
+++ b/tempest/cli/simple_read_only/test_compute_manage.py
@@ -81,3 +81,11 @@
def test_db_version(self):
self.assertNotEqual("", self.nova_manage('db version'))
+
+ def test_cell_list(self):
+ # make sure command doesn't error out
+ self.nova_manage('cell list')
+
+ def test_host_list(self):
+ # make sure command doesn't error out
+ self.nova_manage('host list')
diff --git a/tempest/common/tempest_fixtures.py b/tempest/common/tempest_fixtures.py
index 081b271..ebc9ad3 100644
--- a/tempest/common/tempest_fixtures.py
+++ b/tempest/common/tempest_fixtures.py
@@ -15,16 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import fixtures
-
from tempest.openstack.common import lockutils
-class LockFixture(fixtures.Fixture):
+class LockFixture(lockutils.LockFixture):
def __init__(self, name):
- self.mgr = lockutils.lock(name, 'tempest-', True)
-
- def setUp(self):
- super(LockFixture, self).setUp()
- self.addCleanup(self.mgr.__exit__, None, None, None)
- self.mgr.__enter__()
+ super(LockFixture, self).__init__(name, 'tempest-')
diff --git a/tempest/config.py b/tempest/config.py
index af78381..ff0cddb 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -27,6 +27,12 @@
from tempest.openstack.common import log as logging
+def register_opt_group(conf, opt_group, options):
+ conf.register_group(opt_group)
+ for opt in options:
+ conf.register_opt(opt, group=opt_group.name)
+
+
identity_group = cfg.OptGroup(name='identity',
title="Keystone Configuration Options")
@@ -87,13 +93,6 @@
secret=True),
]
-
-def register_identity_opts(conf):
- conf.register_group(identity_group)
- for opt in IdentityGroup:
- conf.register_opt(opt, group='identity')
-
-
compute_group = cfg.OptGroup(name='compute',
title='Compute Service Options')
@@ -225,12 +224,6 @@
"an instance")
]
-
-def register_compute_opts(conf):
- conf.register_group(compute_group)
- for opt in ComputeGroup:
- conf.register_opt(opt, group='compute')
-
compute_admin_group = cfg.OptGroup(name='compute-admin',
title="Compute Admin Options")
@@ -248,12 +241,6 @@
secret=True),
]
-
-def register_compute_admin_opts(conf):
- conf.register_group(compute_admin_group)
- for opt in ComputeAdminGroup:
- conf.register_opt(opt, group='compute-admin')
-
image_group = cfg.OptGroup(name='image',
title="Image Service Options")
@@ -277,12 +264,6 @@
]
-def register_image_opts(conf):
- conf.register_group(image_group)
- for opt in ImageGroup:
- conf.register_opt(opt, group='image')
-
-
network_group = cfg.OptGroup(name='network',
title='Network Service Options')
@@ -316,12 +297,6 @@
"connectivity"),
]
-
-def register_network_opts(conf):
- conf.register_group(network_group)
- for opt in NetworkGroup:
- conf.register_opt(opt, group='network')
-
volume_group = cfg.OptGroup(name='volume',
title='Block Storage Options')
@@ -363,12 +338,6 @@
]
-def register_volume_opts(conf):
- conf.register_group(volume_group)
- for opt in VolumeGroup:
- conf.register_opt(opt, group='volume')
-
-
object_storage_group = cfg.OptGroup(name='object-storage',
title='Object Storage Service Options')
@@ -404,12 +373,6 @@
]
-def register_object_storage_opts(conf):
- conf.register_group(object_storage_group)
- for opt in ObjectStoreGroup:
- conf.register_opt(opt, group='object-storage')
-
-
orchestration_group = cfg.OptGroup(name='orchestration',
title='Orchestration Service Options')
@@ -452,12 +415,6 @@
]
-def register_orchestration_opts(conf):
- conf.register_group(orchestration_group)
- for opt in OrchestrationGroup:
- conf.register_opt(opt, group='orchestration')
-
-
dashboard_group = cfg.OptGroup(name="dashboard",
title="Dashboard options")
@@ -471,12 +428,6 @@
]
-def register_dashboard_opts(conf):
- conf.register_group(dashboard_group)
- for opt in DashboardGroup:
- conf.register_opt(opt, group='dashboard')
-
-
boto_group = cfg.OptGroup(name='boto',
title='EC2/S3 options')
BotoGroup = [
@@ -523,12 +474,6 @@
help="Status Change Test Interval"),
]
-
-def register_boto_opts(conf):
- conf.register_group(boto_group)
- for opt in BotoGroup:
- conf.register_opt(opt, group='boto')
-
stress_group = cfg.OptGroup(name='stress', title='Stress Test Options')
StressGroup = [
@@ -563,12 +508,6 @@
]
-def register_stress_opts(conf):
- conf.register_group(stress_group)
- for opt in StressGroup:
- conf.register_opt(opt, group='stress')
-
-
scenario_group = cfg.OptGroup(name='scenario', title='Scenario Test Options')
ScenarioGroup = [
@@ -596,12 +535,6 @@
]
-def register_scenario_opts(conf):
- conf.register_group(scenario_group)
- for opt in ScenarioGroup:
- conf.register_opt(opt, group='scenario')
-
-
service_available_group = cfg.OptGroup(name="service_available",
title="Available OpenStack Services")
@@ -629,12 +562,6 @@
help="Whether or not Horizon is expected to be available"),
]
-
-def register_service_available_opts(conf):
- conf.register_group(service_available_group)
- for opt in ServiceAvailableGroup:
- conf.register_opt(opt, group='service_available')
-
debug_group = cfg.OptGroup(name="debug",
title="Debug System")
@@ -645,12 +572,6 @@
]
-def register_debug_opts(conf):
- conf.register_group(debug_group)
- for opt in DebugGroup:
- conf.register_opt(opt, group='debug')
-
-
@singleton
class TempestConfig:
"""Provides OpenStack configuration information."""
@@ -689,20 +610,21 @@
LOG = logging.getLogger('tempest')
LOG.info("Using tempest config file %s" % path)
- register_compute_opts(cfg.CONF)
- register_identity_opts(cfg.CONF)
- register_image_opts(cfg.CONF)
- register_network_opts(cfg.CONF)
- register_volume_opts(cfg.CONF)
- register_object_storage_opts(cfg.CONF)
- register_orchestration_opts(cfg.CONF)
- register_dashboard_opts(cfg.CONF)
- register_boto_opts(cfg.CONF)
- register_compute_admin_opts(cfg.CONF)
- register_stress_opts(cfg.CONF)
- register_scenario_opts(cfg.CONF)
- register_service_available_opts(cfg.CONF)
- register_debug_opts(cfg.CONF)
+ register_opt_group(cfg.CONF, compute_group, ComputeGroup)
+ register_opt_group(cfg.CONF, identity_group, IdentityGroup)
+ register_opt_group(cfg.CONF, image_group, ImageGroup)
+ register_opt_group(cfg.CONF, network_group, NetworkGroup)
+ register_opt_group(cfg.CONF, volume_group, VolumeGroup)
+ register_opt_group(cfg.CONF, object_storage_group, ObjectStoreGroup)
+ register_opt_group(cfg.CONF, orchestration_group, OrchestrationGroup)
+ register_opt_group(cfg.CONF, dashboard_group, DashboardGroup)
+ register_opt_group(cfg.CONF, boto_group, BotoGroup)
+ register_opt_group(cfg.CONF, compute_admin_group, ComputeAdminGroup)
+ register_opt_group(cfg.CONF, stress_group, StressGroup)
+ register_opt_group(cfg.CONF, scenario_group, ScenarioGroup)
+ register_opt_group(cfg.CONF, service_available_group,
+ ServiceAvailableGroup)
+ register_opt_group(cfg.CONF, debug_group, DebugGroup)
self.compute = cfg.CONF.compute
self.identity = cfg.CONF.identity
self.images = cfg.CONF.image
diff --git a/tempest/openstack/common/lockutils.py b/tempest/openstack/common/lockutils.py
index 0abd1a7..a55fd94 100644
--- a/tempest/openstack/common/lockutils.py
+++ b/tempest/openstack/common/lockutils.py
@@ -24,6 +24,7 @@
import time
import weakref
+import fixtures
from oslo.config import cfg
from tempest.openstack.common import fileutils
@@ -275,3 +276,36 @@
"""
return functools.partial(synchronized, lock_file_prefix=lock_file_prefix)
+
+
+class LockFixture(fixtures.Fixture):
+ """External locking fixture.
+
+ This fixture is basically an alternative to the synchronized decorator with
+ the external flag so that tearDowns and addCleanups will be included in
+ the lock context for locking between tests. The fixture is recommended to
+ be the first line in a test method, like so::
+
+ def test_method(self):
+ self.useFixture(LockFixture)
+ ...
+
+ or the first line in setUp if all the test methods in the class are
+ required to be serialized. Something like::
+
+ class TestCase(testtools.testcase):
+ def setUp(self):
+ self.useFixture(LockFixture)
+ super(TestCase, self).setUp()
+ ...
+
+ This is because addCleanups are put on a LIFO queue that gets run after the
+ test method exits. (either by completing or raising an exception)
+ """
+ def __init__(self, name, lock_file_prefix=None):
+ self.mgr = lock(name, lock_file_prefix, True)
+
+ def setUp(self):
+ super(LockFixture, self).setUp()
+ self.addCleanup(self.mgr.__exit__, None, None, None)
+ self.mgr.__enter__()
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index c4b98d5..8ccc899 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -280,16 +280,23 @@
cls.os_resources.remove(thing)
del cls.resource_keys[key]
- def status_timeout(self, things, thing_id, expected_status):
+ def status_timeout(self, things, thing_id, expected_status,
+ error_status='ERROR',
+ not_found_exception=nova_exceptions.NotFound):
"""
Given a thing and an expected status, do a loop, sleeping
for a configurable amount of time, checking for the
expected status to show. At any time, if the returned
status of the thing is ERROR, fail out.
"""
- self._status_timeout(things, thing_id, expected_status=expected_status)
+ self._status_timeout(things, thing_id,
+ expected_status=expected_status,
+ error_status=error_status,
+ not_found_exception=not_found_exception)
- def delete_timeout(self, things, thing_id):
+ def delete_timeout(self, things, thing_id,
+ error_status='ERROR',
+ not_found_exception=nova_exceptions.NotFound):
"""
Given a thing, do a loop, sleeping
for a configurable amount of time, checking for the
@@ -298,13 +305,17 @@
"""
self._status_timeout(things,
thing_id,
- allow_notfound=True)
+ allow_notfound=True,
+ error_status=error_status,
+ not_found_exception=not_found_exception)
def _status_timeout(self,
things,
thing_id,
expected_status=None,
- allow_notfound=False):
+ allow_notfound=False,
+ error_status='ERROR',
+ not_found_exception=nova_exceptions.NotFound):
log_status = expected_status if expected_status else ''
if allow_notfound:
@@ -316,16 +327,16 @@
# for the singular resource to retrieve.
try:
thing = things.get(thing_id)
- except nova_exceptions.NotFound:
+ except not_found_exception:
if allow_notfound:
return True
else:
raise
new_status = thing.status
- if new_status == 'ERROR':
+ if new_status == error_status:
message = "%s failed to get to expected status. \
- In ERROR state." % (thing)
+ In %s state." % (thing, new_status)
raise exceptions.BuildErrorException(message)
elif new_status == expected_status and expected_status is not None:
return True # All good.
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 33b7adc..22c543b 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -24,7 +24,7 @@
LOG = logging.getLogger(__name__)
-class TestLargeOpsScenario(manager.OfficialClientTest):
+class TestLargeOpsScenario(manager.NetworkScenarioTest):
"""
Test large operations.
@@ -82,19 +82,19 @@
properties=properties)
def nova_boot(self):
- def delete(servers):
- [x.delete() for x in servers]
-
name = rand_name('scenario-server-')
client = self.compute_client
flavor_id = self.config.compute.flavor_ref
+ secgroup = self._create_security_group()
self.servers = client.servers.create(
name=name, image=self.image,
flavor=flavor_id,
- min_count=self.config.scenario.large_ops_number)
+ min_count=self.config.scenario.large_ops_number,
+ security_groups=[secgroup.name])
# needed because of bug 1199788
self.servers = [x for x in client.servers.list() if name in x.name]
- self.addCleanup(delete, self.servers)
+ for server in self.servers:
+ self.set_resource(server.name, server)
self._wait_for_server_status('ACTIVE')
@services('compute', 'image')
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 990c986..eda0ede 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -366,6 +366,11 @@
"""Unrescue the provided server."""
return self.action(server_id, 'unrescue', None)
+ def get_server_diagnostics(self, server_id):
+ """Get the usage data for a server."""
+ resp, body = self.get("servers/%s/diagnostics" % str(server_id))
+ return resp, json.loads(body)
+
def list_instance_actions(self, server_id):
"""List the provided server action."""
resp, body = self.get("servers/%s/os-instance-actions" %
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index c58c3ee..cb21c61 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -575,6 +575,13 @@
(server_id, volume_id), headers)
return resp, body
+ def get_server_diagnostics(self, server_id):
+ """Get the usage data for a server."""
+ resp, body = self.get("servers/%s/diagnostics" % server_id,
+ self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
def list_instance_actions(self, server_id):
"""List the provided server action."""
resp, body = self.get("servers/%s/os-instance-actions" % server_id,
diff --git a/tempest/services/identity/json/identity_client.py b/tempest/services/identity/json/identity_client.py
index 18132ed..94045b8 100644
--- a/tempest/services/identity/json/identity_client.py
+++ b/tempest/services/identity/json/identity_client.py
@@ -139,7 +139,7 @@
body = json.loads(body)
return resp, body['tenant']
- def create_user(self, name, password, tenant_id, email):
+ def create_user(self, name, password, tenant_id, email, **kwargs):
"""Create a user."""
post_body = {
'name': name,
@@ -147,6 +147,8 @@
'tenantId': tenant_id,
'email': email
}
+ if kwargs.get('enabled') is not None:
+ post_body['enabled'] = kwargs.get('enabled')
post_body = json.dumps({'user': post_body})
resp, body = self.post('users', post_body, self.headers)
body = json.loads(body)
diff --git a/tempest/services/identity/xml/identity_client.py b/tempest/services/identity/xml/identity_client.py
index 9d44826..9c0a72c 100644
--- a/tempest/services/identity/xml/identity_client.py
+++ b/tempest/services/identity/xml/identity_client.py
@@ -159,7 +159,7 @@
body = self._parse_body(etree.fromstring(body))
return resp, body
- def create_user(self, name, password, tenant_id, email):
+ def create_user(self, name, password, tenant_id, email, **kwargs):
"""Create a user."""
create_user = Element("user",
xmlns=XMLNS,
@@ -167,6 +167,9 @@
password=password,
tenantId=tenant_id,
email=email)
+ if 'enabled' in kwargs:
+ create_user.add_attr('enabled', str(kwargs['enabled']).lower())
+
resp, body = self.post('users', str(Document(create_user)),
self.headers)
body = self._parse_body(etree.fromstring(body))
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index 55404ce..b5cab68 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -196,7 +196,7 @@
if not logfiles:
continue
- if _has_error_in_logs(logfiles, computes):
+ if _has_error_in_logs(logfiles, computes, stop_on_error):
had_errors = True
break
diff --git a/tools/tempest_auto_config.py b/tools/tempest_auto_config.py
new file mode 100644
index 0000000..aef6a1f
--- /dev/null
+++ b/tools/tempest_auto_config.py
@@ -0,0 +1,234 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.
+
+# Config
+import ConfigParser
+import os
+
+# Default client libs
+import keystoneclient.v2_0.client as keystone_client
+
+# Import Openstack exceptions
+import keystoneclient.exceptions as keystone_exception
+
+
+DEFAULT_CONFIG_DIR = "%s/etc" % os.path.abspath(os.path.pardir)
+DEFAULT_CONFIG_FILE = "tempest.conf"
+DEFAULT_CONFIG_SAMPLE = "tempest.conf.sample"
+
+# Environment variables override defaults
+TEMPEST_CONFIG_DIR = os.environ.get('TEMPEST_CONFIG_DIR') or DEFAULT_CONFIG_DIR
+TEMPEST_CONFIG = os.environ.get('TEMPEST_CONFIG') or "%s/%s" % \
+ (TEMPEST_CONFIG_DIR, DEFAULT_CONFIG_FILE)
+TEMPEST_CONFIG_SAMPLE = os.environ.get('TEMPEST_CONFIG_SAMPLE') or "%s/%s" % \
+ (TEMPEST_CONFIG_DIR, DEFAULT_CONFIG_SAMPLE)
+
+# Admin credentials
+OS_USERNAME = os.environ.get('OS_USERNAME')
+OS_PASSWORD = os.environ.get('OS_PASSWORD')
+OS_TENANT_NAME = os.environ.get('OS_TENANT_NAME')
+OS_AUTH_URL = os.environ.get('OS_AUTH_URL')
+
+# Image references
+IMAGE_ID = os.environ.get('IMAGE_ID')
+IMAGE_ID_ALT = os.environ.get('IMAGE_ID_ALT')
+
+
+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 getTempestConfigSample():
+ """
+ Gets the tempest configuration file as a ConfigParser object
+ :return: the tempest configuration file
+ """
+ # get the sample config file from the sample
+ config_sample = ConfigParser.ConfigParser()
+ config_sample.readfp(open(TEMPEST_CONFIG_SAMPLE))
+
+ return config_sample
+
+
+def update_config_admin_credentials(config, config_section):
+ """
+ Updates the tempest config with the admin credentials
+ :param config: an object representing the tempest config file
+ :param config_section: the section name where the admin credentials are
+ """
+ # Check if credentials are present
+ 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, section, params):
+ """
+ Updates a given config object with given params
+ :param config: the object representing the config file of tempest
+ :param 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(section, option, value)
+
+
+def get_identity_client_kwargs(config, section_name):
+ """
+ Get the required arguments for the identity python client
+ :param config: the tempest configuration file
+ :param section_name: 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(section_name, 'admin_username')
+ password = config.get(section_name, 'admin_password')
+ tenant_name = config.get(section_name, 'admin_tenant_name')
+ auth_url = config.get(section_name, 'uri')
+ dscv = config.get(section_name, '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,
+ identity_section):
+ """
+ Creates the two non admin users and tenants for tempest
+ :param identity_client: openstack identity python client
+ :param config: tempest configuration file
+ :param identity_section: the section name of identity in the config
+ """
+ # Get the necessary params from the config file
+ tenant_name = config.get(identity_section, 'tenant_name')
+ username = config.get(identity_section, 'username')
+ password = config.get(identity_section, 'password')
+
+ alt_tenant_name = config.get(identity_section, 'alt_tenant_name')
+ alt_username = config.get(identity_section, 'alt_username')
+ alt_password = config.get(identity_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 main():
+ """
+ Main module to control the script
+ """
+ # TODO(tkammer): add support for existing config file
+ config_sample = getTempestConfigSample()
+ update_config_admin_credentials(config_sample, 'identity')
+
+ client_manager = ClientManager()
+
+ # Set the identity related info for tempest
+ identity_client_kwargs = get_identity_client_kwargs(config_sample,
+ '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_sample,
+ 'identity')
+
+ # TODO(tkammer): add image implementation
+
+if __name__ == "__main__":
+ main()