Merge "Verify the response attributes of 'list_hosts'"
diff --git a/.gitignore b/.gitignore
index 8d2b281..28a9b9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,5 +15,5 @@
dist
build
.testrepository
-.coverage
+.coverage*
cover/
diff --git a/etc/whitelist.yaml b/etc/whitelist.yaml
deleted file mode 100644
index 2d8b741..0000000
--- a/etc/whitelist.yaml
+++ /dev/null
@@ -1,233 +0,0 @@
-n-cpu:
- - module: "nova.virt.libvirt.driver"
- message: "During wait destroy, instance disappeared"
- - module: "glanceclient.common.http"
- message: "Request returned failure status"
- - module: "nova.openstack.common.periodic_task"
- message: "Error during ComputeManager\\.update_available_resource: \
- 'NoneType' object is not iterable"
- - module: "nova.compute.manager"
- message: "Possibly task preempted"
- - module: "nova.openstack.common.rpc.amqp"
- message: "Exception during message handling"
- - module: "nova.network.api"
- message: "Failed storing info cache"
- - module: "nova.compute.manager"
- message: "Error while trying to clean up image"
- - module: "nova.virt.libvirt.driver"
- message: "Error injecting data into image.*\\(Unexpected error while \
- running command"
- - module: "nova.compute.manager"
- message: "Instance failed to spawn"
- - module: "nova.compute.manager"
- message: "Error: Unexpected error while running command"
- - module: "nova.virt.libvirt.driver"
- message: "Error from libvirt during destroy"
- - module: "nova.virt.libvirt.vif"
- message: "Failed while unplugging vif"
- - module: "nova.openstack.common.loopingcal"
- message: "in fixed duration looping call"
- - module: "nova.virt.libvirt.driver"
- message: "Getting disk size of instance"
- - module: "nova.virt.libvirt.driver"
- message: "No such file or directory: '/opt/stack/data/nova/instances"
- - module: "nova.virt.libvirt.driver"
- message: "Nova requires libvirt version 0\\.9\\.11 or greater"
- - module: "nova.compute.manager"
- message: "error during stop\\(\\) in sync_power_state"
- - module: "nova.compute.manager"
- message: "Instance failed network setup after 1 attempt"
- - module: "nova.compute.manager"
- message: "Periodic sync_power_state task had an error"
- - module: "nova.virt.driver"
- message: "Info cache for instance .* could not be found"
-
-g-api:
- - module: "glance.store.sheepdog"
- message: "Error in store configuration: Unexpected error while \
- running command"
- - module: "swiftclient"
- message: "Container HEAD failed: .*404 Not Found"
- - module: "glance.api.middleware.cache"
- message: "however the registry did not contain metadata for that image"
- - module: "oslo.messaging.notify._impl_messaging"
- message: ".*"
-
-ceilometer-acompute:
- - module: "ceilometer.compute.pollsters.disk"
- message: "Unable to read from monitor: Connection reset by peer"
- - module: "ceilometer.compute.pollsters.disk"
- message: "Requested operation is not valid: domain is not running"
- - module: "ceilometer.compute.pollsters.net"
- message: "Requested operation is not valid: domain is not running"
- - module: "ceilometer.compute.pollsters.disk"
- message: "Domain not found: no domain with matching uuid"
- - module: "ceilometer.compute.pollsters.net"
- message: "Domain not found: no domain with matching uuid"
- - module: "ceilometer.compute.pollsters.net"
- message: "No module named libvirt"
- - module: "ceilometer.compute.pollsters.net"
- message: "Unable to write to monitor: Broken pipe"
- - module: "ceilometer.compute.pollsters.cpu"
- message: "Domain not found: no domain with matching uuid"
- - module: "ceilometer.compute.pollsters.net"
- message: ".*"
- - module: "ceilometer.compute.pollsters.disk"
- message: ".*"
-
-ceilometer-acentral:
- - module: "ceilometer.central.manager"
- message: "403 Forbidden"
- - module: "ceilometer.central.manager"
- message: "get_samples\\(\\) got an unexpected keyword argument 'resources'"
-
-ceilometer-alarm-evaluator:
- - module: "ceilometer.alarm.service"
- message: "alarm evaluation cycle failed"
- - module: "ceilometer.alarm.evaluator.threshold"
- message: ".*"
-
-ceilometer-api:
- - module: "wsme.api"
- message: ".*"
-
-h-api:
- - module: "root"
- message: "Returning 400 to user: The server could not comply with \
- the request since it is either malformed or otherwise incorrect"
- - module: "root"
- message: "Unexpected error occurred serving API: Request limit \
- exceeded: Template exceeds maximum allowed size"
- - module: "root"
- message: "Unexpected error occurred serving API: The Stack \
- .*could not be found"
-
-h-eng:
- - module: "heat.openstack.common.rpc.amqp"
- message: "Exception during message handling"
- - module: "heat.openstack.common.rpc.common"
- message: "The Stack .* could not be found"
-
-n-api:
- - module: "glanceclient.common.http"
- message: "Request returned failure status"
- - module: "nova.api.openstack"
- message: "Caught error: Quota exceeded for"
- - module: "nova.compute.api"
- message: "ServerDiskConfigTest"
- - module: "nova.compute.api"
- message: "ServersTest"
- - module: "nova.compute.api"
- message: "\\{u'kernel_id'.*u'ramdisk_id':"
- - module: "nova.api.openstack.wsgi"
- message: "takes exactly 4 arguments"
- - module: "nova.api.openstack"
- message: "Caught error: Instance .* could not be found"
- - module: "nova.api.metadata.handler"
- message: "Failed to get metadata for instance id:"
-
-n-cond:
- - module: "nova.notifications"
- message: "Failed to send state update notification"
- - module: "nova.openstack.common.rpc.amqp"
- message: "Exception during message handling"
- - module: "nova.openstack.common.rpc.common"
- message: "but the actual state is deleting to caller"
- - module: "nova.openstack.common.rpc.common"
- message: "Traceback \\(most recent call last"
- - module: "nova.openstack.common.threadgroup"
- message: "Service with host .* topic conductor exists."
-
-n-sch:
- - module: "nova.scheduler.filter_scheduler"
- message: "Error from last host: "
-
-n-net:
- - module: "nova.openstack.common.rpc.amqp"
- message: "Exception during message handling"
- - module: "nova.openstack.common.rpc.common"
- message: "'NoneType' object has no attribute '__getitem__'"
- - module: "nova.openstack.common.rpc.common"
- message: "Instance .* could not be found"
-
-c-api:
- - module: "cinder.api.middleware.fault"
- message: "Caught error: Volume .* could not be found"
- - module: "cinder.api.middleware.fault"
- message: "Caught error: Snapshot .* could not be found"
- - module: "cinder.api.openstack.wsgi"
- message: "argument must be a string or a number, not 'NoneType'"
- - module: "cinder.volume.api"
- message: "Volume status must be available to reserve"
-
-c-vol:
- - module: "cinder.brick.iscsi.iscsi"
- message: "Failed to create iscsi target for volume id"
- - module: "cinder.brick.local_dev.lvm"
- message: "stat failed: No such file or directory"
- - module: "cinder.brick.local_dev.lvm"
- message: "LV stack-volumes.*in use: not deactivating"
- - module: "cinder.brick.local_dev.lvm"
- message: "Can't remove open logical volume"
-
-ceilometer-collector:
- - module: "stevedore.extension"
- message: ".*"
- - module: "ceilometer.collector.dispatcher.database"
- message: "duplicate key value violates unique constraint"
- - module: "ceilometer.collector.dispatcher.database"
- message: "Failed to record metering data: QueuePool limit"
- - module: "ceilometer.dispatcher.database"
- message: "\\(DataError\\) integer out of range"
- - module: "ceilometer.collector.dispatcher.database"
- message: "Failed to record metering data: .* integer out of range"
- - module: "ceilometer.collector.dispatcher.database"
- message: "Failed to record metering data: .* integer out of range"
- - module: "ceilometer.openstack.common.db.sqlalchemy.session"
- message: "DB exception wrapped"
-
-q-agt:
- - module: "neutron.agent.linux.ovs_lib"
- message: "Unable to execute.*Exception:"
-
-q-dhcp:
- - module: "neutron.common.legacy"
- message: "Skipping unknown group key: firewall_driver"
- - module: "neutron.agent.dhcp_agent"
- message: "Unable to enable dhcp"
- - module: "neutron.agent.dhcp_agent"
- message: "Network .* RPC info call failed"
-
-q-l3:
- - module: "neutron.common.legacy"
- message: "Skipping unknown group key: firewall_driver"
- - module: "neutron.agent.l3_agent"
- message: "Failed synchronizing routers"
-
-q-vpn:
- - module: "neutron.common.legacy"
- message: "Skipping unknown group key: firewall_driver"
-
-q-lbaas:
- - module: "neutron.common.legacy"
- message: "Skipping unknown group key: firewall_driver"
- - module: "neutron.services.loadbalancer.drivers.haproxy.agent_manager"
- message: "Error upating stats"
- - module: "neutron.services.loadbalancer.drivers.haproxy.agent_manager"
- message: "Unable to destroy device for pool"
-
-q-svc:
- - module: "neutron.common.legacy"
- message: "Skipping unknown group key: firewall_driver"
- - module: "neutron.openstack.common.rpc.amqp"
- message: "Exception during message handling"
- - module: "neutron.openstack.common.rpc.common"
- message: "(Network|Pool|Subnet|Agent|Port) .* could not be found"
- - module: "neutron.api.v2.resource"
- message: ".* failed"
- - module: ".*"
- message: ".*"
-
-s-proxy:
- - module: "proxy-server"
- message: "Timeout talking to memcached"
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 40a4df7..77431bb 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -27,6 +27,7 @@
_host_key = 'OS-EXT-SRV-ATTR:host'
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ServersAdminTestJSON, cls).setUpClass()
cls.client = cls.os_adm.servers_client
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index c825fb9..768cc11 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -26,6 +26,7 @@
force_tenant_isolation = True
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ListServersNegativeTestJSON, cls).setUpClass()
cls.client = cls.servers_client
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 48f2e14..093e9e2 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -21,6 +21,7 @@
class ServerRescueTestJSON(base.BaseV2ComputeTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
cls.set_network_resources(network=True, subnet=True, router=True)
super(ServerRescueTestJSON, cls).setUpClass()
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index e027567..ef45585 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -22,6 +22,7 @@
class ServerRescueNegativeTestJSON(base.BaseV2ComputeTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
cls.set_network_resources(network=True, subnet=True, router=True)
super(ServerRescueNegativeTestJSON, cls).setUpClass()
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 230d433..4db8c56 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -27,6 +27,9 @@
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.default_quota_set = set(('injected_file_content_bytes',
'metadata_items', 'injected_files',
'ram', 'floating_ips',
@@ -45,6 +48,14 @@
sorted(quota_set.keys()))
self.assertEqual(quota_set['id'], self.tenant_id)
+ # get the quota set using user id
+ resp, quota_set = self.client.get_quota_set(self.tenant_id,
+ self.user_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(sorted(expected_quota_set),
+ sorted(quota_set.keys()))
+ self.assertEqual(quota_set['id'], self.tenant_id)
+
@test.attr(type='smoke')
def test_get_default_quotas(self):
# User can get the default quota set for it's tenant
diff --git a/tempest/api/compute/v3/admin/test_quotas.py b/tempest/api/compute/v3/admin/test_quotas.py
index 536891c..917c115 100644
--- a/tempest/api/compute/v3/admin/test_quotas.py
+++ b/tempest/api/compute/v3/admin/test_quotas.py
@@ -109,3 +109,26 @@
resp, quota_set = self.adm_client.get_quota_set(tenant_id)
self.assertEqual(200, resp.status)
self.assertEqual(quota_set['ram'], 5120)
+
+ @test.attr(type='gate')
+ def test_delete_quota(self):
+ # Admin can delete the resource quota set for a tenant
+ tenant_name = data_utils.rand_name('cpu_quota_tenant_')
+ tenant_desc = tenant_name + '-desc'
+ identity_client = self.os_adm.identity_client
+ _, tenant = identity_client.create_tenant(name=tenant_name,
+ description=tenant_desc)
+ tenant_id = tenant['id']
+ self.addCleanup(identity_client.delete_tenant, tenant_id)
+ resp, quota_set_default = self.adm_client.get_quota_set(tenant_id)
+ self.assertEqual(200, resp.status)
+ ram_default = quota_set_default['ram']
+
+ self.adm_client.update_quota_set(tenant_id, ram='5120')
+ self.assertEqual(200, resp.status)
+ resp, _ = self.adm_client.delete_quota_set(tenant_id)
+ self.assertEqual(204, resp.status)
+
+ resp, quota_set_new = self.adm_client.get_quota_set(tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(ram_default, quota_set_new['ram'])
diff --git a/tempest/api/compute/v3/admin/test_servers.py b/tempest/api/compute/v3/admin/test_servers.py
index fb8afe4..579a535 100644
--- a/tempest/api/compute/v3/admin/test_servers.py
+++ b/tempest/api/compute/v3/admin/test_servers.py
@@ -27,6 +27,7 @@
_host_key = 'os-extended-server-attributes:host'
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ServersAdminV3Test, cls).setUpClass()
cls.client = cls.servers_admin_client
diff --git a/tempest/api/compute/v3/servers/test_create_server.py b/tempest/api/compute/v3/servers/test_create_server.py
index 7e9aaf2..14a4338 100644
--- a/tempest/api/compute/v3/servers/test_create_server.py
+++ b/tempest/api/compute/v3/servers/test_create_server.py
@@ -92,14 +92,6 @@
@testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
@test.attr(type='gate')
- def test_can_log_into_created_server(self):
- # Check that the user can authenticate with the generated password
- linux_client = remote_client.RemoteClient(self.server,
- self.ssh_user, self.password)
- self.assertTrue(linux_client.can_authenticate())
-
- @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
- @test.attr(type='gate')
def test_verify_created_server_vcpus(self):
# Verify that the number of vcpus reported by the instance matches
# the amount stated by the flavor
diff --git a/tempest/api/compute/v3/servers/test_list_servers_negative.py b/tempest/api/compute/v3/servers/test_list_servers_negative.py
index 92f44fe..9cbc4e0 100644
--- a/tempest/api/compute/v3/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_list_servers_negative.py
@@ -19,13 +19,14 @@
from tempest.api.compute import base
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ListServersNegativeV3Test(base.BaseV3ComputeTest):
force_tenant_isolation = True
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ListServersNegativeV3Test, cls).setUpClass()
cls.client = cls.servers_client
@@ -51,7 +52,7 @@
ignore_error=True)
cls.deleted_fixtures.append(srv)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_with_a_deleted_server(self):
# Verify deleted servers do not show by default in list servers
# List servers and verify server not returned
@@ -63,7 +64,7 @@
self.assertEqual('200', resp['status'])
self.assertEqual([], actual)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_by_non_existing_image(self):
# Listing servers for a non existing image returns empty list
non_existing_image = '1234abcd-zzz0-aaa9-ppp3-0987654abcde'
@@ -72,7 +73,7 @@
self.assertEqual('200', resp['status'])
self.assertEqual([], servers)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_by_non_existing_flavor(self):
# Listing servers by non existing flavor returns empty list
non_existing_flavor = 1234
@@ -81,7 +82,7 @@
self.assertEqual('200', resp['status'])
self.assertEqual([], servers)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_by_non_existing_server_name(self):
# Listing servers for a non existent server name returns empty list
non_existing_name = 'junk_server_1234'
@@ -90,7 +91,7 @@
self.assertEqual('200', resp['status'])
self.assertEqual([], servers)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_status_non_existing(self):
# Return an empty list when invalid status is specified
non_existing_status = 'BALONEY'
@@ -99,33 +100,33 @@
self.assertEqual('200', resp['status'])
self.assertEqual([], servers)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_servers_by_limits(self):
# List servers by specifying limits
resp, body = self.client.list_servers({'limit': 1})
self.assertEqual('200', resp['status'])
self.assertEqual(1, len([x for x in body['servers'] if 'id' in x]))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_by_limits_greater_than_actual_count(self):
# List servers by specifying a greater value for limit
resp, body = self.client.list_servers({'limit': 100})
self.assertEqual('200', resp['status'])
self.assertEqual(len(self.existing_fixtures), len(body['servers']))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_by_limits_pass_string(self):
# Return an error if a string value is passed for limit
self.assertRaises(exceptions.BadRequest, self.client.list_servers,
{'limit': 'testing'})
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_by_limits_pass_negative_value(self):
# Return an error if a negative value for limit is passed
self.assertRaises(exceptions.BadRequest, self.client.list_servers,
{'limit': -1})
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_servers_by_changes_since(self):
# Servers are listed by specifying changes-since date
changes_since = {'changes_since': self.start_time.isoformat()}
@@ -138,13 +139,13 @@
"Number of servers %d is wrong in %s" %
(num_expected, body['servers']))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_by_changes_since_invalid_date(self):
# Return an error when invalid date format is passed
self.assertRaises(exceptions.BadRequest, self.client.list_servers,
{'changes_since': '2011/01/01'})
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_by_changes_since_future_date(self):
# Return an empty list when a date in the future is passed
changes_since = {'changes_since': '2051-01-01T12:34:00Z'}
@@ -152,7 +153,7 @@
self.assertEqual('200', resp['status'])
self.assertEqual(0, len(body['servers']))
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_servers_detail_server_is_deleted(self):
# Server details are not listed for a deleted server
deleted_ids = [s['id'] for s in self.deleted_fixtures]
diff --git a/tempest/api/compute/v3/servers/test_server_rescue_negative.py b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
index 6e09376..6bb441c 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
@@ -22,6 +22,7 @@
class ServerRescueNegativeV3Test(base.BaseV3ComputeTest):
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(ServerRescueNegativeV3Test, cls).setUpClass()
cls.device = 'vdf'
diff --git a/tempest/api/compute/v3/test_quotas.py b/tempest/api/compute/v3/test_quotas.py
index b53d9be..3fe62e9 100644
--- a/tempest/api/compute/v3/test_quotas.py
+++ b/tempest/api/compute/v3/test_quotas.py
@@ -27,6 +27,9 @@
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.default_quota_set = set(('metadata_items',
'ram', 'floating_ips',
'fixed_ips', 'key_pairs',
@@ -43,6 +46,14 @@
sorted(quota_set.keys()))
self.assertEqual(quota_set['id'], self.tenant_id)
+ # get the quota set using user id
+ resp, quota_set = self.client.get_quota_set(self.tenant_id,
+ self.user_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(sorted(expected_quota_set),
+ sorted(quota_set.keys()))
+ self.assertEqual(quota_set['id'], self.tenant_id)
+
@test.attr(type='smoke')
def test_get_default_quotas(self):
# User can get the default quota set for it's tenant
diff --git a/tempest/api/identity/admin/test_tokens.py b/tempest/api/identity/admin/test_tokens.py
index 533f374..c931bcf 100644
--- a/tempest/api/identity/admin/test_tokens.py
+++ b/tempest/api/identity/admin/test_tokens.py
@@ -56,6 +56,49 @@
resp, body = self.client.delete_token(token_id)
self.assertEqual(resp['status'], '204')
+ @attr(type='gate')
+ def test_rescope_token(self):
+ """An unscoped token can be requested, that token can be used to
+ request a scoped token.
+ """
+
+ # Create a user.
+ user_name = data_utils.rand_name(name='user-')
+ user_password = data_utils.rand_name(name='pass-')
+ tenant_id = None # No default tenant so will get unscoped token.
+ email = ''
+ resp, user = self.client.create_user(user_name, user_password,
+ tenant_id, email)
+ self.assertEqual(200, resp.status)
+ self.data.users.append(user)
+
+ # Create a tenant.
+ tenant_name = data_utils.rand_name(name='tenant-')
+ resp, tenant = self.client.create_tenant(tenant_name)
+ self.assertEqual(200, resp.status)
+ self.data.tenants.append(tenant)
+
+ # Create a role
+ role_name = data_utils.rand_name(name='role-')
+ resp, role = self.client.create_role(role_name)
+ self.assertEqual(200, resp.status)
+ self.data.roles.append(role)
+
+ # Grant the user the role on the tenant.
+ resp, _ = self.client.assign_user_role(tenant['id'], user['id'],
+ role['id'])
+ self.assertEqual(200, resp.status)
+
+ # Get an unscoped token.
+ rsp, body = self.token_client.auth(user_name, user_password)
+ self.assertEqual(200, resp.status)
+
+ token_id = body['token']['id']
+
+ # Use the unscoped token to get a scoped token.
+ rsp, body = self.token_client.auth_token(token_id, tenant=tenant_name)
+ self.assertEqual(200, resp.status)
+
class TokensTestXML(TokensTestJSON):
_interface = 'xml'
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 05b704f..0e4d66b 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -102,6 +102,7 @@
self.client.create_endpoint(self.service_id, interface1,
url1, region=region1,
enabled=True)
+ self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
# Creating service so as update endpoint with new service ID
s_name = data_utils.rand_name('service-')
s_type = data_utils.rand_name('type--')
@@ -126,7 +127,6 @@
self.assertEqual(url2, endpoint['url'])
self.assertEqual(region2, endpoint['region'])
self.assertEqual('false', str(endpoint['enabled']).lower())
- self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
class EndPointsTestXML(EndPointsTestJSON):
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
new file mode 100644
index 0000000..28615a4
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -0,0 +1,94 @@
+
+# Copyright 2013 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.
+
+
+from tempest.api.identity import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class EndpointsNegativeTestJSON(base.BaseIdentityV3AdminTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(EndpointsNegativeTestJSON, cls).setUpClass()
+ cls.identity_client = cls.client
+ cls.client = cls.endpoints_client
+ cls.service_ids = list()
+ s_name = data_utils.rand_name('service-')
+ s_type = data_utils.rand_name('type--')
+ s_description = data_utils.rand_name('description-')
+ resp, cls.service_data = (
+ cls.service_client.create_service(s_name, s_type,
+ description=s_description))
+ cls.service_id = cls.service_data['id']
+ cls.service_ids.append(cls.service_id)
+
+ @classmethod
+ def tearDownClass(cls):
+ for s in cls.service_ids:
+ cls.service_client.delete_service(s)
+ super(EndpointsNegativeTestJSON, cls).tearDownClass()
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_enabled_False(self):
+ # Enabled should be a boolean, not a string like 'False'
+ interface = 'public'
+ url = data_utils.rand_name('url')
+ region = data_utils.rand_name('region')
+ self.assertRaises(exceptions.BadRequest, self.client.create_endpoint,
+ self.service_id, interface, url, region=region,
+ force_enabled='False')
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_enabled_True(self):
+ # Enabled should be a boolean, not a string like 'True'
+ interface = 'public'
+ url = data_utils.rand_name('url')
+ region = data_utils.rand_name('region')
+ self.assertRaises(exceptions.BadRequest, self.client.create_endpoint,
+ self.service_id, interface, url, region=region,
+ force_enabled='True')
+
+ def _assert_update_raises_bad_request(self, enabled):
+
+ # Create an endpoint
+ region1 = data_utils.rand_name('region')
+ url1 = data_utils.rand_name('url')
+ interface1 = 'public'
+ resp, endpoint_for_update = (
+ self.client.create_endpoint(self.service_id, interface1,
+ url1, region=region1, enabled=True))
+ self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
+
+ self.assertRaises(exceptions.BadRequest, self.client.update_endpoint,
+ endpoint_for_update['id'], force_enabled=enabled)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_with_enabled_False(self):
+ # Enabled should be a boolean, not a string like 'False'
+ self._assert_update_raises_bad_request('False')
+
+ @attr(type=['negative', 'gate'])
+ def test_update_with_enabled_True(self):
+ # Enabled should be a boolean, not a string like 'True'
+ self._assert_update_raises_bad_request('True')
+
+
+class EndpointsNegativeTestXML(EndpointsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
index ecd992a..0e601d1 100644
--- a/tempest/api/network/admin/test_dhcp_agent_scheduler.py
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -26,7 +26,7 @@
msg = "dhcp_agent_scheduler extension not enabled."
raise cls.skipException(msg)
# Create a network and make sure it will be hosted by a
- # dhcp agent.
+ # dhcp agent: this is done by creating a regular port
cls.network = cls.create_network()
cls.subnet = cls.create_subnet(cls.network)
cls.cidr = cls.subnet['cidr']
@@ -60,6 +60,9 @@
@test.attr(type='smoke')
def test_remove_network_from_dhcp_agent(self):
+ # The agent is now bound to the network, we can free the port
+ self.client.delete_port(self.port['id'])
+ self.ports.remove(self.port)
resp, body = self.admin_client.list_dhcp_agent_hosting_network(
self.network['id'])
agents = body['agents']
diff --git a/tempest/api/network/admin/test_load_balancer_admin_actions.py b/tempest/api/network/admin/test_load_balancer_admin_actions.py
new file mode 100644
index 0000000..34a8e32
--- /dev/null
+++ b/tempest/api/network/admin/test_load_balancer_admin_actions.py
@@ -0,0 +1,94 @@
+# Copyright 2014 Mirantis.inc
+# 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.network import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class LoadBalancerAdminTestJSON(base.BaseAdminNetworkTest):
+ _interface = 'json'
+
+ """
+ Test admin actions for load balancer.
+
+ Create VIP for another tenant
+ Create health monitor for another tenant
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(LoadBalancerAdminTestJSON, cls).setUpClass()
+ if not test.is_extension_enabled('lbaas', 'network'):
+ msg = "lbaas extension not enabled."
+ raise cls.skipException(msg)
+ 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.network = cls.create_network()
+ cls.subnet = cls.create_subnet(cls.network)
+
+ @test.attr(type='smoke')
+ def test_create_vip_as_admin_for_another_tenant(self):
+ name = data_utils.rand_name('vip-')
+ resp, body = self.admin_client.create_pool(
+ name=data_utils.rand_name('pool-'), lb_method="ROUND_ROBIN",
+ protocol="HTTP", subnet_id=self.subnet['id'],
+ tenant_id=self.tenant_id)
+ self.assertEqual('201', resp['status'])
+ pool = body['pool']
+ self.addCleanup(self.admin_client.delete_pool, pool['id'])
+ resp, body = self.admin_client.create_vip(name=name,
+ protocol="HTTP",
+ protocol_port=80,
+ subnet_id=self.subnet['id'],
+ pool_id=pool['id'],
+ tenant_id=self.tenant_id)
+ self.assertEqual('201', resp['status'])
+ vip = body['vip']
+ self.addCleanup(self.admin_client.delete_vip, vip['id'])
+ self.assertIsNotNone(vip['id'])
+ self.assertEqual(self.tenant_id, vip['tenant_id'])
+ resp, body = self.client.show_vip(vip['id'])
+ self.assertEqual('200', resp['status'])
+ show_vip = body['vip']
+ self.assertEqual(vip['id'], show_vip['id'])
+ self.assertEqual(vip['name'], show_vip['name'])
+
+ @test.attr(type='smoke')
+ def test_create_health_monitor_as_admin_for_another_tenant(self):
+ resp, body = (
+ self.admin_client.create_health_monitor(delay=4,
+ max_retries=3,
+ type="TCP",
+ timeout=1,
+ tenant_id=self.tenant_id))
+ self.assertEqual('201', resp['status'])
+ health_monitor = body['health_monitor']
+ self.addCleanup(self.admin_client.delete_health_monitor,
+ health_monitor['id'])
+ self.assertIsNotNone(health_monitor['id'])
+ self.assertEqual(self.tenant_id, health_monitor['tenant_id'])
+ resp, body = self.client.show_health_monitor(health_monitor['id'])
+ self.assertEqual('200', resp['status'])
+ show_health_monitor = body['health_monitor']
+ self.assertEqual(health_monitor['id'], show_health_monitor['id'])
+
+
+class LoadBalancerAdminTestXML(LoadBalancerAdminTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/network/test_load_balancer.py b/tempest/api/network/test_load_balancer.py
index 695dbf8..792d61d 100644
--- a/tempest/api/network/test_load_balancer.py
+++ b/tempest/api/network/test_load_balancer.py
@@ -108,6 +108,7 @@
def test_create_update_delete_pool_vip(self):
# Creates a vip
name = data_utils.rand_name('vip-')
+ address = self.subnet['allocation_pools'][0]['end']
resp, body = self.client.create_pool(
name=data_utils.rand_name("pool-"),
lb_method='ROUND_ROBIN',
@@ -118,16 +119,36 @@
protocol="HTTP",
protocol_port=80,
subnet_id=self.subnet['id'],
- pool_id=pool['id'])
+ pool_id=pool['id'],
+ address=address)
self.assertEqual('201', resp['status'])
vip = body['vip']
vip_id = vip['id']
+ # Confirm VIP's address correctness with a show
+ resp, body = self.client.show_vip(vip_id)
+ self.assertEqual('200', resp['status'])
+ vip = body['vip']
+ self.assertEqual(address, vip['address'])
# Verification of vip update
new_name = "New_vip"
- resp, body = self.client.update_vip(vip_id, name=new_name)
+ new_description = "New description"
+ persistence_type = "HTTP_COOKIE"
+ update_data = {"session_persistence": {
+ "type": persistence_type}}
+ resp, body = self.client.update_vip(vip_id,
+ name=new_name,
+ description=new_description,
+ connection_limit=10,
+ admin_state_up=False,
+ **update_data)
self.assertEqual('200', resp['status'])
updated_vip = body['vip']
- self.assertEqual(updated_vip['name'], new_name)
+ self.assertEqual(new_name, updated_vip['name'])
+ self.assertEqual(new_description, updated_vip['description'])
+ self.assertEqual(10, updated_vip['connection_limit'])
+ self.assertFalse(updated_vip['admin_state_up'])
+ self.assertEqual(persistence_type,
+ updated_vip['session_persistence']['type'])
# Verification of vip delete
resp, body = self.client.delete_vip(vip['id'])
self.assertEqual('204', resp['status'])
@@ -274,6 +295,40 @@
self.assertEqual('204', resp['status'])
@test.attr(type='smoke')
+ def test_create_health_monitor_http_type(self):
+ hm_type = "HTTP"
+ resp, body = self.client.create_health_monitor(delay=4,
+ max_retries=3,
+ type=hm_type,
+ timeout=1)
+ self.assertEqual('201', resp['status'])
+ health_monitor = body['health_monitor']
+ self.addCleanup(self.client.delete_health_monitor,
+ health_monitor['id'])
+ self.assertEqual(hm_type, health_monitor['type'])
+
+ @test.attr(type='smoke')
+ def test_update_health_monitor_http_method(self):
+ resp, body = self.client.create_health_monitor(delay=4,
+ max_retries=3,
+ type="HTTP",
+ timeout=1)
+ self.assertEqual('201', resp['status'])
+ health_monitor = body['health_monitor']
+ self.addCleanup(self.client.delete_health_monitor,
+ health_monitor['id'])
+ resp, body = (self.client.update_health_monitor
+ (health_monitor['id'],
+ http_method="POST",
+ url_path="/home/user",
+ expected_codes="290"))
+ self.assertEqual('200', resp['status'])
+ updated_health_monitor = body['health_monitor']
+ self.assertEqual("POST", updated_health_monitor['http_method'])
+ self.assertEqual("/home/user", updated_health_monitor['url_path'])
+ self.assertEqual("290", updated_health_monitor['expected_codes'])
+
+ @test.attr(type='smoke')
def test_show_health_monitor(self):
# Verifies the details of a health_monitor
resp, body = self.client.show_health_monitor(self.health_monitor['id'])
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 88e7238..70fb00a 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -67,7 +67,6 @@
cls.name = cls.network['name']
cls.subnet = cls.create_subnet(cls.network)
cls.cidr = cls.subnet['cidr']
- cls.port = cls.create_port(cls.network)
@attr(type='smoke')
def test_create_update_delete_network_subnet(self):
@@ -184,90 +183,6 @@
self.assertEqual(len(subnet), 1)
self.assertIn('id', subnet)
- @attr(type='smoke')
- def test_create_update_delete_port(self):
- # Verify port creation
- resp, body = self.client.create_port(network_id=self.network['id'])
- self.assertEqual('201', resp['status'])
- port = body['port']
- self.assertTrue(port['admin_state_up'])
- # Verify port update
- new_name = "New_Port"
- resp, body = self.client.update_port(
- port['id'],
- name=new_name,
- admin_state_up=False)
- self.assertEqual('200', resp['status'])
- updated_port = body['port']
- self.assertEqual(updated_port['name'], new_name)
- self.assertFalse(updated_port['admin_state_up'])
- # Verify port deletion
- resp, body = self.client.delete_port(port['id'])
- self.assertEqual('204', resp['status'])
-
- @attr(type='smoke')
- def test_show_port(self):
- # Verify the details of port
- resp, body = self.client.show_port(self.port['id'])
- self.assertEqual('200', resp['status'])
- port = body['port']
- self.assertIn('id', port)
- self.assertEqual(port['id'], self.port['id'])
-
- @attr(type='smoke')
- def test_show_port_fields(self):
- # Verify specific fields of a port
- field_list = [('fields', 'id'), ]
- resp, body = self.client.show_port(self.port['id'],
- field_list=field_list)
- self.assertEqual('200', resp['status'])
- port = body['port']
- self.assertEqual(len(port), len(field_list))
- for label, field_name in field_list:
- self.assertEqual(port[field_name], self.port[field_name])
-
- @attr(type='smoke')
- def test_list_ports(self):
- # Verify the port exists in the list of all ports
- resp, body = self.client.list_ports()
- self.assertEqual('200', resp['status'])
- ports = [port['id'] for port in body['ports']
- if port['id'] == self.port['id']]
- self.assertNotEmpty(ports, "Created port not found in the list")
-
- @attr(type='smoke')
- def test_port_list_filter_by_router_id(self):
- # Create a router
- network = self.create_network()
- self.create_subnet(network)
- router = self.create_router(data_utils.rand_name('router-'))
- resp, port = self.client.create_port(network_id=network['id'])
- # Add router interface to port created above
- resp, interface = self.client.add_router_interface_with_port_id(
- router['id'], port['port']['id'])
- self.addCleanup(self.client.remove_router_interface_with_port_id,
- router['id'], port['port']['id'])
- # List ports filtered by router_id
- resp, port_list = self.client.list_ports(
- device_id=router['id'])
- self.assertEqual('200', resp['status'])
- ports = port_list['ports']
- self.assertEqual(len(ports), 1)
- self.assertEqual(ports[0]['id'], port['port']['id'])
- self.assertEqual(ports[0]['device_id'], router['id'])
-
- @attr(type='smoke')
- def test_list_ports_fields(self):
- # Verify specific fields of ports
- resp, body = self.client.list_ports(fields='id')
- self.assertEqual('200', resp['status'])
- ports = body['ports']
- self.assertNotEmpty(ports, "Port list returned is empty")
- # Asserting the fields returned are correct
- for port in ports:
- self.assertEqual(len(port), 1)
- self.assertIn('id', port)
-
class NetworksTestXML(NetworksTestJSON):
_interface = 'xml'
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
new file mode 100644
index 0000000..fbb25a8
--- /dev/null
+++ b/tempest/api/network/test_ports.py
@@ -0,0 +1,249 @@
+# Copyright 2014 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.
+
+import socket
+
+from tempest.api.network import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class PortsTestJSON(base.BaseNetworkTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(PortsTestJSON, cls).setUpClass()
+ cls.network = cls.create_network()
+ cls.port = cls.create_port(cls.network)
+
+ def _delete_port(self, port_id):
+ resp, body = self.client.delete_port(port_id)
+ self.assertEqual('204', resp['status'])
+ resp, body = self.client.list_ports()
+ self.assertEqual('200', resp['status'])
+ ports_list = body['ports']
+ self.assertFalse(port_id in [n['id'] for n in ports_list])
+
+ @test.attr(type='smoke')
+ def test_create_update_delete_port(self):
+ # Verify port creation
+ resp, body = self.client.create_port(network_id=self.network['id'])
+ self.assertEqual('201', resp['status'])
+ port = body['port']
+ self.assertTrue(port['admin_state_up'])
+ # Verify port update
+ new_name = "New_Port"
+ resp, body = self.client.update_port(
+ port['id'],
+ name=new_name,
+ admin_state_up=False)
+ self.assertEqual('200', resp['status'])
+ updated_port = body['port']
+ self.assertEqual(updated_port['name'], new_name)
+ self.assertFalse(updated_port['admin_state_up'])
+ # Verify port deletion
+ resp, body = self.client.delete_port(port['id'])
+ self.assertEqual('204', resp['status'])
+
+ @test.attr(type='smoke')
+ def test_show_port(self):
+ # Verify the details of port
+ resp, body = self.client.show_port(self.port['id'])
+ self.assertEqual('200', resp['status'])
+ port = body['port']
+ self.assertIn('id', port)
+ self.assertEqual(port['id'], self.port['id'])
+ self.assertEqual(self.port['admin_state_up'], port['admin_state_up'])
+ self.assertEqual(self.port['device_id'], port['device_id'])
+ self.assertEqual(self.port['device_owner'], port['device_owner'])
+ self.assertEqual(self.port['mac_address'], port['mac_address'])
+ self.assertEqual(self.port['name'], port['name'])
+ self.assertEqual(self.port['security_groups'],
+ port['security_groups'])
+ self.assertEqual(self.port['network_id'], port['network_id'])
+ self.assertEqual(self.port['security_groups'],
+ port['security_groups'])
+
+ @test.attr(type='smoke')
+ def test_show_port_fields(self):
+ # Verify specific fields of a port
+ field_list = [('fields', 'id'), ]
+ resp, body = self.client.show_port(self.port['id'],
+ field_list=field_list)
+ self.assertEqual('200', resp['status'])
+ port = body['port']
+ self.assertEqual(len(port), len(field_list))
+ for label, field_name in field_list:
+ self.assertEqual(port[field_name], self.port[field_name])
+
+ @test.attr(type='smoke')
+ def test_list_ports(self):
+ # Verify the port exists in the list of all ports
+ resp, body = self.client.list_ports()
+ self.assertEqual('200', resp['status'])
+ ports = [port['id'] for port in body['ports']
+ if port['id'] == self.port['id']]
+ self.assertNotEmpty(ports, "Created port not found in the list")
+
+ @test.attr(type='smoke')
+ def test_port_list_filter_by_router_id(self):
+ # Create a router
+ network = self.create_network()
+ self.create_subnet(network)
+ router = self.create_router(data_utils.rand_name('router-'))
+ resp, port = self.client.create_port(network_id=network['id'])
+ # Add router interface to port created above
+ resp, interface = self.client.add_router_interface_with_port_id(
+ router['id'], port['port']['id'])
+ self.addCleanup(self.client.remove_router_interface_with_port_id,
+ router['id'], port['port']['id'])
+ # List ports filtered by router_id
+ resp, port_list = self.client.list_ports(
+ device_id=router['id'])
+ self.assertEqual('200', resp['status'])
+ ports = port_list['ports']
+ self.assertEqual(len(ports), 1)
+ self.assertEqual(ports[0]['id'], port['port']['id'])
+ self.assertEqual(ports[0]['device_id'], router['id'])
+
+ @test.attr(type='smoke')
+ def test_list_ports_fields(self):
+ # Verify specific fields of ports
+ resp, body = self.client.list_ports(fields='id')
+ self.assertEqual('200', resp['status'])
+ ports = body['ports']
+ self.assertNotEmpty(ports, "Port list returned is empty")
+ # Asserting the fields returned are correct
+ for port in ports:
+ self.assertEqual(len(port), 1)
+ self.assertIn('id', port)
+
+
+class PortsTestXML(PortsTestJSON):
+ _interface = 'xml'
+
+
+class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(PortsAdminExtendedAttrsTestJSON, cls).setUpClass()
+ cls.identity_client = cls._get_identity_admin_client()
+ cls.tenant = cls.identity_client.get_tenant_by_name(
+ CONF.identity.tenant_name)
+ cls.network = cls.create_network()
+ cls.host_id = socket.gethostname()
+
+ @test.attr(type='smoke')
+ def test_create_port_binding_ext_attr(self):
+ post_body = {"network_id": self.network['id'],
+ "binding:host_id": self.host_id}
+ resp, body = self.admin_client.create_port(**post_body)
+ self.assertEqual('201', resp['status'])
+ port = body['port']
+ self.addCleanup(self.admin_client.delete_port, port['id'])
+ host_id = port['binding:host_id']
+ self.assertIsNotNone(host_id)
+ self.assertEqual(self.host_id, host_id)
+
+ @test.attr(type='smoke')
+ def test_update_port_binding_ext_attr(self):
+ post_body = {"network_id": self.network['id']}
+ resp, body = self.admin_client.create_port(**post_body)
+ self.assertEqual('201', resp['status'])
+ port = body['port']
+ self.addCleanup(self.admin_client.delete_port, port['id'])
+ update_body = {"binding:host_id": self.host_id}
+ resp, body = self.admin_client.update_port(port['id'], **update_body)
+ self.assertEqual('200', resp['status'])
+ updated_port = body['port']
+ host_id = updated_port['binding:host_id']
+ self.assertIsNotNone(host_id)
+ self.assertEqual(self.host_id, host_id)
+
+ @test.attr(type='smoke')
+ def test_list_ports_binding_ext_attr(self):
+ resp, body = self.admin_client.list_ports(
+ **{'tenant_id': self.tenant['id']})
+ self.assertEqual('200', resp['status'])
+ ports_list = body['ports']
+ for port in ports_list:
+ vif_type = port['binding:vif_type']
+ self.assertIsNotNone(vif_type)
+ vif_details = port['binding:vif_details']['port_filter']
+ self.assertIsNotNone(vif_details)
+
+ @test.attr(type='smoke')
+ def test_show_port_binding_ext_attr(self):
+ resp, body = self.admin_client.create_port(
+ network_id=self.network['id'])
+ self.assertEqual('201', resp['status'])
+ port = body['port']
+ self.addCleanup(self.admin_client.delete_port, port['id'])
+ resp, body = self.admin_client.show_port(port['id'])
+ self.assertEqual('200', resp['status'])
+ show_port = body['port']
+ self.assertEqual(port['binding:host_id'],
+ show_port['binding:host_id'])
+ self.assertEqual(port['binding:vif_type'],
+ show_port['binding:vif_type'])
+ self.assertEqual(port['binding:vif_details'],
+ show_port['binding:vif_details'])
+
+
+class PortsAdminExtendedAttrsTestXML(PortsAdminExtendedAttrsTestJSON):
+ _interface = 'xml'
+
+
+class PortsIpV6TestJSON(PortsTestJSON):
+ _ip_version = 6
+ _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+ _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
+
+ @classmethod
+ def setUpClass(cls):
+ super(PortsIpV6TestJSON, cls).setUpClass()
+ if not CONF.network_feature_enabled.ipv6:
+ cls.tearDownClass()
+ skip_msg = "IPv6 Tests are disabled."
+ raise cls.skipException(skip_msg)
+
+
+class PortsIpV6TestXML(PortsIpV6TestJSON):
+ _interface = 'xml'
+
+
+class PortsAdminExtendedAttrsIpV6TestJSON(PortsAdminExtendedAttrsTestJSON):
+ _ip_version = 6
+ _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+ _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
+
+ @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)
+
+
+class PortsAdminExtendedAttrsIpV6TestXML(
+ PortsAdminExtendedAttrsIpV6TestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index b14adc0..a3098a5 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -68,7 +68,7 @@
# Retrieve a ResellerAdmin auth data and use it to set a quota
# on the client's account
cls.reselleradmin_auth_data = \
- cls.os_reselleradmin.get_auth_provider().auth_data
+ cls.os_reselleradmin.auth_provider.auth_data
def setUp(self):
super(AccountQuotasTest, self).setUp()
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 402cd90..7648ea1 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -68,7 +68,7 @@
# Retrieve a ResellerAdmin auth data and use it to set a quota
# on the client's account
cls.reselleradmin_auth_data = \
- cls.os_reselleradmin.get_auth_provider().auth_data
+ cls.os_reselleradmin.auth_provider.auth_data
def setUp(self):
super(AccountQuotasNegativeTest, self).setUp()
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index ea93aa3..71eaab5 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -31,7 +31,7 @@
test_os = clients.Manager(self.data.test_user,
self.data.test_password,
self.data.test_tenant)
- test_auth_provider = test_os.get_auth_provider()
+ 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 085ef51..c865ee1 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -27,7 +27,7 @@
test_os = clients.Manager(cls.data.test_user,
cls.data.test_password,
cls.data.test_tenant)
- cls.test_auth_data = test_os.get_auth_provider().auth_data
+ cls.test_auth_data = test_os.auth_provider.auth_data
@classmethod
def tearDownClass(cls):
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index a5a0950..547bf87 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -29,7 +29,7 @@
test_os = clients.Manager(cls.data.test_user,
cls.data.test_password,
cls.data.test_tenant)
- cls.test_auth_data = test_os.get_auth_provider().auth_data
+ cls.test_auth_data = test_os.auth_provider.auth_data
@classmethod
def tearDownClass(cls):
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index e140ad0..594c703 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -22,6 +22,7 @@
_interface = "json"
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(SnapshotsActionsTest, cls).setUpClass()
cls.client = cls.snapshots_client
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 742f7e1..cf308f5 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -65,7 +65,10 @@
self.addCleanup(self.quotas_client.update_quota_set,
self.demo_tenant_id, **default_quota_set)
self.assertEqual(200, resp.status)
- self.assertEqual(new_quota_set, quota_set)
+ # test that the specific values we set are actually in
+ # the final result. There is nothing here that ensures there
+ # would be no other values in there.
+ self.assertDictContainsSubset(new_quota_set, quota_set)
@test.attr(type='gate')
def test_show_quota_usage(self):
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 8183999..ee1d09a 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -116,3 +116,35 @@
self.assertEqual(extra_specs, fetched_volume_type['extra_specs'],
'The fetched Volume_type is different '
'from the created Volume_type')
+
+ @test.attr(type='smoke')
+ def test_volume_type_encryption_create_get(self):
+ # Create/get encryption type.
+ provider = "LuksEncryptor"
+ control_location = "front-end"
+ name = data_utils.rand_name("volume-type-")
+ resp, body = self.client.create_volume_type(name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self._delete_volume_type, body['id'])
+ resp, encryption_type = self.client.create_encryption_type(
+ body['id'], provider=provider,
+ control_location=control_location)
+ self.assertEqual(200, resp.status)
+ self.assertIn('volume_type_id', encryption_type)
+ self.assertEqual(provider, encryption_type['provider'],
+ "The created encryption_type provider is not equal "
+ "to the requested provider")
+ self.assertEqual(control_location, encryption_type['control_location'],
+ "The created encryption_type control_location is not "
+ "equal to the requested control_location")
+ resp, fetched_encryption_type = self.client.get_encryption_type(
+ encryption_type['volume_type_id'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(provider,
+ fetched_encryption_type['provider'],
+ 'The fetched encryption_type provider is different '
+ 'from the created encryption_type')
+ self.assertEqual(control_location,
+ fetched_encryption_type['control_location'],
+ 'The fetched encryption_type control_location is '
+ 'different from the created encryption_type')
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index aa00700..4496f18 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -22,6 +22,7 @@
_interface = "json"
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(VolumesActionsTest, cls).setUpClass()
cls.client = cls.volumes_client
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index 1493b37..d2c4ab7 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -21,6 +21,7 @@
_interface = "json"
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(SnapshotMetadataTest, cls).setUpClass()
cls.client = cls.snapshots_client
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index a22ad32..cfab0bd 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -25,6 +25,7 @@
_interface = "json"
@classmethod
+ @test.safe_setup
def setUpClass(cls):
super(VolumesActionsTest, cls).setUpClass()
cls.client = cls.volumes_client
@@ -44,7 +45,7 @@
def tearDownClass(cls):
# Delete the test instance
cls.servers_client.delete_server(cls.server['id'])
- cls.client.wait_for_resource_deletion(cls.server['id'])
+ cls.servers_client.wait_for_server_termination(cls.server['id'])
super(VolumesActionsTest, cls).tearDownClass()
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 2701e84..84c9501 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -71,8 +71,8 @@
resp, server = self.servers_client.create_server(server_name,
self.image_ref,
self.flavor_ref)
- self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
self.addCleanup(self.servers_client.delete_server, server['id'])
+ self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
mountpoint = '/dev/%s' % CONF.compute.volume_device_name
resp, body = self.volumes_client.attach_volume(
self.volume_origin['id'], server['id'], mountpoint)
diff --git a/tempest/api_schema/compute/v2/fixed_ips.py b/tempest/api_schema/compute/v2/fixed_ips.py
new file mode 100644
index 0000000..a6add04
--- /dev/null
+++ b/tempest/api_schema/compute/v2/fixed_ips.py
@@ -0,0 +1,36 @@
+# 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.
+
+fixed_ips = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'fixed_ip': {
+ 'type': 'object',
+ 'properties': {
+ 'address': {
+ 'type': 'string',
+ 'format': 'ip-address'
+ },
+ 'cidr': {'type': 'string'},
+ 'host': {'type': 'string'},
+ 'hostname': {'type': 'string'}
+ },
+ 'required': ['address', 'cidr', 'host', 'hostname']
+ }
+ },
+ 'required': ['fixed_ip']
+ }
+}
diff --git a/tempest/api_schema/compute/v2/floating_ips.py b/tempest/api_schema/compute/v2/floating_ips.py
new file mode 100644
index 0000000..61582ec
--- /dev/null
+++ b/tempest/api_schema/compute/v2/floating_ips.py
@@ -0,0 +1,46 @@
+# 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.
+
+list_floating_ips = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'floating_ips': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ # NOTE: Now the type of 'id' is integer, but
+ # here allows 'string' also because we will be
+ # able to change it to 'uuid' in the future.
+ 'id': {'type': ['integer', 'string']},
+ 'pool': {'type': ['string', 'null']},
+ 'instance_id': {'type': ['integer', 'string', 'null']},
+ 'ip': {
+ 'type': 'string',
+ 'format': 'ip-address'
+ },
+ 'fixed_ip': {
+ 'type': ['string', 'null'],
+ 'format': 'ip-address'
+ }
+ },
+ 'required': ['id', 'pool', 'instance_id', 'ip', 'fixed_ip']
+ }
+ }
+ },
+ 'required': ['floating_ips']
+ }
+}
diff --git a/tempest/api_schema/compute/v2/limits.py b/tempest/api_schema/compute/v2/limits.py
new file mode 100644
index 0000000..b9857f1
--- /dev/null
+++ b/tempest/api_schema/compute/v2/limits.py
@@ -0,0 +1,94 @@
+# 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.
+
+get_limit = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'limits': {
+ 'type': 'object',
+ 'properties': {
+ 'absolute': {
+ 'type': 'object',
+ 'properties': {
+ 'maxTotalRAMSize': {'type': 'integer'},
+ 'totalCoresUsed': {'type': 'integer'},
+ 'maxTotalInstances': {'type': 'integer'},
+ 'maxTotalFloatingIps': {'type': 'integer'},
+ 'totalSecurityGroupsUsed': {'type': 'integer'},
+ 'maxTotalCores': {'type': 'integer'},
+ 'totalFloatingIpsUsed': {'type': 'integer'},
+ 'maxSecurityGroups': {'type': 'integer'},
+ 'maxServerMeta': {'type': 'integer'},
+ 'maxPersonality': {'type': 'integer'},
+ 'maxImageMeta': {'type': 'integer'},
+ 'maxPersonalitySize': {'type': 'integer'},
+ 'maxSecurityGroupRules': {'type': 'integer'},
+ 'maxTotalKeypairs': {'type': 'integer'},
+ 'totalRAMUsed': {'type': 'integer'},
+ 'totalInstancesUsed': {'type': 'integer'}
+ },
+ 'required': ['maxImageMeta',
+ 'maxPersonality',
+ 'maxPersonalitySize',
+ 'maxSecurityGroupRules',
+ 'maxSecurityGroups',
+ 'maxServerMeta',
+ 'maxTotalCores',
+ 'maxTotalFloatingIps',
+ 'maxTotalInstances',
+ 'maxTotalKeypairs',
+ 'maxTotalRAMSize',
+ 'totalCoresUsed',
+ 'totalFloatingIpsUsed',
+ 'totalInstancesUsed',
+ 'totalRAMUsed',
+ 'totalSecurityGroupsUsed']
+ },
+ 'rate': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'limit': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'next-available':
+ {'type': 'string'},
+ 'remaining':
+ {'type': 'integer'},
+ 'unit':
+ {'type': 'string'},
+ 'value':
+ {'type': 'integer'},
+ 'verb':
+ {'type': 'string'}
+ }
+ }
+ },
+ 'regex': {'type': 'string'},
+ 'uri': {'type': 'string'}
+ }
+ }
+ }
+ },
+ 'required': ['absolute', 'rate']
+ }
+ },
+ 'required': ['limits']
+ }
+}
diff --git a/tempest/api_schema/compute/v2/servers.py b/tempest/api_schema/compute/v2/servers.py
new file mode 100644
index 0000000..7f06ca6
--- /dev/null
+++ b/tempest/api_schema/compute/v2/servers.py
@@ -0,0 +1,53 @@
+# 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.
+
+create_server = {
+ 'status_code': [202],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server': {
+ 'type': 'object',
+ 'properties': {
+ # NOTE: Now the type of 'id' is uuid, but here allows
+ # 'integer' also because old OpenStack uses 'integer'
+ # as a server id.
+ 'id': {'type': ['integer', 'string']},
+ 'security_groups': {'type': 'array'},
+ 'links': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'href': {
+ 'type': 'string',
+ 'format': 'uri'
+ },
+ 'rel': {'type': 'string'}
+ },
+ 'required': ['href', 'rel']
+ }
+ },
+ 'adminPass': {'type': 'string'},
+ 'OS-DCF:diskConfig': {'type': 'string'}
+ },
+ # NOTE: OS-DCF:diskConfig is API extension, and some
+ # environments return a response without the attribute.
+ # So it is not 'required'.
+ 'required': ['id', 'security_groups', 'links', 'adminPass']
+ }
+ },
+ 'required': ['server']
+ }
+}
diff --git a/tempest/api_schema/compute/v3/__init__.py b/tempest/api_schema/compute/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api_schema/compute/v3/__init__.py
diff --git a/tempest/api_schema/compute/v3/servers.py b/tempest/api_schema/compute/v3/servers.py
new file mode 100644
index 0000000..e69b25f
--- /dev/null
+++ b/tempest/api_schema/compute/v3/servers.py
@@ -0,0 +1,55 @@
+# 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.
+
+create_server = {
+ 'status_code': [202],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server': {
+ 'type': 'object',
+ 'properties': {
+ # NOTE: Now the type of 'id' is uuid, but here allows
+ # 'integer' also because old OpenStack uses 'integer'
+ # as a server id.
+ 'id': {'type': ['integer', 'string']},
+ 'os-security-groups:security_groups': {'type': 'array'},
+ 'links': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'href': {
+ 'type': 'string',
+ 'format': 'uri'
+ },
+ 'rel': {'type': 'string'}
+ },
+ 'required': ['href', 'rel']
+ }
+ },
+ 'admin_password': {'type': 'string'},
+ 'os-access-ips:access_ip_v4': {'type': 'string'},
+ 'os-access-ips:access_ip_v6': {'type': 'string'}
+ },
+ # 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'.
+ 'required': ['id', 'os-security-groups:security_groups',
+ 'links', 'admin_password']
+ }
+ },
+ 'required': ['server']
+ }
+}
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 810e6b2..932b151 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -42,6 +42,7 @@
def nova(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes nova command for the given action."""
+ flags += ' --endpoint-type %s' % CONF.compute.endpoint_type
return self.cmd_with_auth(
'nova', action, flags, params, admin, fail_ok)
@@ -58,33 +59,39 @@
def glance(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes glance command for the given action."""
+ flags += ' --os-endpoint-type %s' % CONF.image.endpoint_type
return self.cmd_with_auth(
'glance', action, flags, params, admin, fail_ok)
def ceilometer(self, action, flags='', params='', admin=True,
fail_ok=False):
"""Executes ceilometer command for the given action."""
+ flags += ' --os-endpoint-type %s' % CONF.telemetry.endpoint_type
return self.cmd_with_auth(
'ceilometer', action, flags, params, admin, fail_ok)
def heat(self, action, flags='', params='', admin=True,
fail_ok=False):
"""Executes heat command for the given action."""
+ flags += ' --os-endpoint-type %s' % CONF.orchestration.endpoint_type
return self.cmd_with_auth(
'heat', action, flags, params, admin, fail_ok)
def cinder(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes cinder command for the given action."""
+ flags += ' --endpoint-type %s' % CONF.volume.endpoint_type
return self.cmd_with_auth(
'cinder', action, flags, params, admin, fail_ok)
def neutron(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes neutron command for the given action."""
+ flags += ' --endpoint-type %s' % CONF.network.endpoint_type
return self.cmd_with_auth(
'neutron', action, flags, params, admin, fail_ok)
def sahara(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes sahara command for the given action."""
+ flags += ' --endpoint-type %s' % CONF.data_processing.endpoint_type
return self.cmd_with_auth(
# TODO (slukjanov): replace with sahara when new client released
'savanna', action, flags, params, admin, fail_ok)
@@ -94,7 +101,7 @@
"""Executes given command with auth attributes appended."""
# TODO(jogo) make admin=False work
creds = ('--os-username %s --os-tenant-name %s --os-password %s '
- '--os-auth-url %s ' %
+ '--os-auth-url %s' %
(CONF.identity.admin_username,
CONF.identity.admin_tenant_name,
CONF.identity.admin_password,
diff --git a/tempest/cli/simple_read_only/test_ceilometer.py b/tempest/cli/simple_read_only/test_ceilometer.py
index 0b6ae22..1d2822d 100644
--- a/tempest/cli/simple_read_only/test_ceilometer.py
+++ b/tempest/cli/simple_read_only/test_ceilometer.py
@@ -16,6 +16,7 @@
from tempest import cli
from tempest import config
from tempest.openstack.common import log as logging
+from tempest import test
CONF = config.CONF
@@ -41,6 +42,7 @@
def test_ceilometer_meter_list(self):
self.ceilometer('meter-list')
+ @test.attr(type='slow')
def test_ceilometer_resource_list(self):
self.ceilometer('resource-list')
diff --git a/tempest/exceptions/__init__.py b/tempest/exceptions/__init__.py
index 06dee71..485f532 100644
--- a/tempest/exceptions/__init__.py
+++ b/tempest/exceptions/__init__.py
@@ -20,6 +20,10 @@
message = "Invalid Configuration"
+class InvalidCredentials(base.TempestException):
+ message = "Invalid Credentials"
+
+
class InvalidHttpSuccessCode(base.RestClientException):
message = "The success code is different than the expected one"
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 55be60a..7f39905 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -15,7 +15,9 @@
import re
-PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron']
+PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
+ 'trove', 'ironic', 'savanna', 'heat', 'ceilometer',
+ 'marconi']
PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
TEST_DEFINITION = re.compile(r'^\s*def test.*')
diff --git a/tempest/manager.py b/tempest/manager.py
index 708447e..63235db 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -75,13 +75,12 @@
tenant_name=CONF.identity.tenant_name
)
- def get_auth_provider(self, credentials=None):
- auth_params = dict(client_type=getattr(self, 'client_type', None),
- interface=getattr(self, 'interface', None))
+ def get_auth_provider(self, credentials):
+ if credentials is None:
+ raise exceptions.InvalidCredentials(
+ 'Credentials must be specified')
auth_provider_class = self.get_auth_provider_class(self.auth_version)
- # If invalid / incomplete credentials are provided, use default ones
- if credentials is None or \
- not auth_provider_class.check_credentials(credentials):
- credentials = self.credentials
- auth_params['credentials'] = credentials
- return auth_provider_class(**auth_params)
+ return auth_provider_class(
+ client_type=getattr(self, 'client_type', None),
+ interface=getattr(self, 'interface', None),
+ credentials=credentials)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 4046cbd..e89ea70 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -169,3 +169,15 @@
# deletion operations to succeed
self._stop_instances([instance_2nd, instance_from_snapshot])
self._detach_volumes([volume_origin, volume])
+
+
+class TestVolumeBootPatternV2(TestVolumeBootPattern):
+ def _boot_instance_from_volume(self, vol_id, keypair):
+ bdms = [{'uuid': vol_id, 'source_type': 'volume',
+ 'destination_type': 'volume', 'boot_index': 0,
+ 'delete_on_termination': False}]
+ create_kwargs = {
+ 'block_device_mapping_v2': bdms,
+ 'key_name': keypair.name
+ }
+ return self.create_server(image='', create_kwargs=create_kwargs)
diff --git a/tempest/services/compute/json/fixed_ips_client.py b/tempest/services/compute/json/fixed_ips_client.py
index 8b2c6c9..5fdd564 100644
--- a/tempest/services/compute/json/fixed_ips_client.py
+++ b/tempest/services/compute/json/fixed_ips_client.py
@@ -15,6 +15,7 @@
import json
+from tempest.api_schema.compute.v2 import fixed_ips as schema
from tempest.common import rest_client
from tempest import config
@@ -31,6 +32,7 @@
url = "os-fixed-ips/%s" % (fixed_ip)
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(schema.fixed_ips, resp, body)
return resp, body['fixed_ip']
def reserve_fixed_ip(self, ip, body):
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 42487c3..2a7e25a 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -16,6 +16,7 @@
import json
import urllib
+from tempest.api_schema.compute.v2 import floating_ips as schema
from tempest.common import rest_client
from tempest import config
from tempest import exceptions
@@ -36,6 +37,7 @@
resp, body = self.get(url)
body = json.loads(body)
+ self.validate_response(schema.list_floating_ips, resp, body)
return resp, body['floating_ips']
def get_floating_ip_details(self, floating_ip_id):
diff --git a/tempest/services/compute/json/limits_client.py b/tempest/services/compute/json/limits_client.py
index 1493718..e503bef 100644
--- a/tempest/services/compute/json/limits_client.py
+++ b/tempest/services/compute/json/limits_client.py
@@ -15,6 +15,7 @@
import json
+from tempest.api_schema.compute.v2 import limits as schema
from tempest.common import rest_client
from tempest import config
@@ -30,11 +31,13 @@
def get_absolute_limits(self):
resp, body = self.get("limits")
body = json.loads(body)
+ self.validate_response(schema.get_limit, resp, body)
return resp, body['limits']['absolute']
def get_specific_absolute_limit(self, absolute_limit):
resp, body = self.get("limits")
body = json.loads(body)
+ self.validate_response(schema.get_limit, resp, body)
if absolute_limit not in body['limits']['absolute']:
return None
else:
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index c1ac3db..2fae927 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -27,10 +27,12 @@
super(QuotasClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
- def get_quota_set(self, tenant_id):
+ def get_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
url = 'os-quota-sets/%s' % str(tenant_id)
+ if user_id:
+ url += '?user_id=%s' % str(user_id)
resp, body = self.get(url)
body = json.loads(body)
return resp, body['quota_set']
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index ca0f114..4eba2b4 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -18,6 +18,7 @@
import time
import urllib
+from tempest.api_schema.compute.v2 import servers as schema
from tempest.common import rest_client
from tempest.common import waiters
from tempest import config
@@ -85,6 +86,7 @@
# with return reservation id set True
if 'reservation_id' in body:
return resp, body
+ self.validate_response(schema.create_server, resp, body)
return resp, body['server']
def update_server(self, server_id, name=None, meta=None, accessIPv4=None,
diff --git a/tempest/services/compute/v3/json/quotas_client.py b/tempest/services/compute/v3/json/quotas_client.py
index 32e31a3..ed92aae 100644
--- a/tempest/services/compute/v3/json/quotas_client.py
+++ b/tempest/services/compute/v3/json/quotas_client.py
@@ -27,10 +27,12 @@
super(QuotasV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
- def get_quota_set(self, tenant_id):
+ def get_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
url = 'os-quota-sets/%s' % str(tenant_id)
+ if user_id:
+ url += '?user_id=%s' % str(user_id)
resp, body = self.get(url)
body = json.loads(body)
return resp, body['quota_set']
@@ -96,3 +98,7 @@
body = json.loads(body)
return resp, body['quota_set']
+
+ def delete_quota_set(self, tenant_id):
+ """Delete the tenant's quota set."""
+ return self.delete('os-quota-sets/%s' % str(tenant_id))
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index 92eb09b..6f492d0 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -19,6 +19,7 @@
import time
import urllib
+from tempest.api_schema.compute.v3 import servers as schema
from tempest.common import rest_client
from tempest.common import waiters
from tempest import config
@@ -91,6 +92,7 @@
# with return reservation id set True
if 'servers_reservation' in body:
return resp, body['servers_reservation']
+ self.validate_response(schema.create_server, resp, body)
return resp, body['server']
def update_server(self, server_id, name=None, meta=None, access_ip_v4=None,
diff --git a/tempest/services/compute/xml/common.py b/tempest/services/compute/xml/common.py
index b29b932..b1bf789 100644
--- a/tempest/services/compute/xml/common.py
+++ b/tempest/services/compute/xml/common.py
@@ -19,6 +19,7 @@
XMLNS_V3 = "http://docs.openstack.org/compute/api/v1.1"
NEUTRON_NAMESPACES = {
+ 'binding': "http://docs.openstack.org/ext/binding/api/v1.0",
'router': "http://docs.openstack.org/ext/neutron/router/api/v1.0",
'provider': 'http://docs.openstack.org/ext/provider/api/v1.0',
}
diff --git a/tempest/services/compute/xml/quotas_client.py b/tempest/services/compute/xml/quotas_client.py
index 85e481c..911c476 100644
--- a/tempest/services/compute/xml/quotas_client.py
+++ b/tempest/services/compute/xml/quotas_client.py
@@ -44,10 +44,12 @@
return quota
- def get_quota_set(self, tenant_id):
+ def get_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
url = 'os-quota-sets/%s' % str(tenant_id)
+ if user_id:
+ url += '?user_id=%s' % str(user_id)
resp, body = self.get(url)
body = xml_to_json(etree.fromstring(body))
body = self._format_quota(body)
diff --git a/tempest/services/identity/json/identity_client.py b/tempest/services/identity/json/identity_client.py
index 9a31540..99b4036 100644
--- a/tempest/services/identity/json/identity_client.py
+++ b/tempest/services/identity/json/identity_client.py
@@ -134,9 +134,10 @@
post_body = {
'name': name,
'password': password,
- 'tenantId': tenant_id,
'email': email
}
+ if tenant_id is not None:
+ post_body['tenantId'] = tenant_id
if kwargs.get('enabled') is not None:
post_body['enabled'] = kwargs.get('enabled')
post_body = json.dumps({'user': post_body})
@@ -233,16 +234,36 @@
self.auth_url = auth_url
- def auth(self, user, password, tenant):
+ def auth(self, user, password, tenant=None):
creds = {
'auth': {
'passwordCredentials': {
'username': user,
'password': password,
},
- 'tenantName': tenant,
}
}
+
+ if tenant:
+ creds['auth']['tenantName'] = tenant
+
+ body = json.dumps(creds)
+ resp, body = self.post(self.auth_url, body=body)
+
+ return resp, body['access']
+
+ def auth_token(self, token_id, tenant=None):
+ creds = {
+ 'auth': {
+ 'token': {
+ 'id': token_id,
+ },
+ }
+ }
+
+ if tenant:
+ creds['auth']['tenantName'] = tenant
+
body = json.dumps(creds)
resp, body = self.post(self.auth_url, body=body)
diff --git a/tempest/services/identity/v3/json/endpoints_client.py b/tempest/services/identity/v3/json/endpoints_client.py
index c3c1e15..f7a894b 100644
--- a/tempest/services/identity/v3/json/endpoints_client.py
+++ b/tempest/services/identity/v3/json/endpoints_client.py
@@ -36,9 +36,17 @@
return resp, body['endpoints']
def create_endpoint(self, service_id, interface, url, **kwargs):
- """Create endpoint."""
+ """Create endpoint.
+
+ Normally this function wouldn't allow setting values that are not
+ allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+ """
region = kwargs.get('region', None)
- enabled = kwargs.get('enabled', None)
+ if 'force_enabled' in kwargs:
+ enabled = kwargs.get('force_enabled', None)
+ else:
+ enabled = kwargs.get('enabled', None)
post_body = {
'service_id': service_id,
'interface': interface,
@@ -52,8 +60,13 @@
return resp, body['endpoint']
def update_endpoint(self, endpoint_id, service_id=None, interface=None,
- url=None, region=None, enabled=None):
- """Updates an endpoint with given parameters."""
+ url=None, region=None, enabled=None, **kwargs):
+ """Updates an endpoint with given parameters.
+
+ Normally this function wouldn't allow setting values that are not
+ allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+ """
post_body = {}
if service_id is not None:
post_body['service_id'] = service_id
@@ -63,7 +76,9 @@
post_body['url'] = url
if region is not None:
post_body['region'] = region
- if enabled is not None:
+ if 'force_enabled' in kwargs:
+ post_body['enabled'] = kwargs['force_enabled']
+ elif enabled is not None:
post_body['enabled'] = enabled
post_body = json.dumps({'endpoint': post_body})
resp, body = self.patch('endpoints/%s' % endpoint_id, post_body)
diff --git a/tempest/services/identity/v3/xml/endpoints_client.py b/tempest/services/identity/v3/xml/endpoints_client.py
index cc9aa65..a1f9811 100644
--- a/tempest/services/identity/v3/xml/endpoints_client.py
+++ b/tempest/services/identity/v3/xml/endpoints_client.py
@@ -62,11 +62,19 @@
return resp, body
def create_endpoint(self, service_id, interface, url, **kwargs):
- """Create endpoint."""
+ """Create endpoint.
+
+ Normally this function wouldn't allow setting values that are not
+ allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+ """
region = kwargs.get('region', None)
- enabled = kwargs.get('enabled', None)
- if enabled is not None:
- enabled = str(enabled).lower()
+ if 'force_enabled' in kwargs:
+ enabled = kwargs['force_enabled']
+ else:
+ enabled = kwargs.get('enabled', None)
+ if enabled is not None:
+ enabled = str(enabled).lower()
create_endpoint = common.Element("endpoint",
xmlns=XMLNS,
service_id=service_id,
@@ -79,8 +87,13 @@
return resp, body
def update_endpoint(self, endpoint_id, service_id=None, interface=None,
- url=None, region=None, enabled=None):
- """Updates an endpoint with given parameters."""
+ url=None, region=None, enabled=None, **kwargs):
+ """Updates an endpoint with given parameters.
+
+ Normally this function wouldn't allow setting values that are not
+ allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+ """
doc = common.Document()
endpoint = common.Element("endpoint")
doc.append(endpoint)
@@ -93,8 +106,12 @@
endpoint.add_attr("url", url)
if region:
endpoint.add_attr("region", region)
- if enabled is not None:
+
+ if 'force_enabled' in kwargs:
+ endpoint.add_attr("enabled", kwargs['force_enabled'])
+ elif enabled is not None:
endpoint.add_attr("enabled", str(enabled).lower())
+
resp, body = self.patch('endpoints/%s' % str(endpoint_id), str(doc))
body = self._parse_body(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/identity/xml/identity_client.py b/tempest/services/identity/xml/identity_client.py
index 50403fb..c5bf310 100644
--- a/tempest/services/identity/xml/identity_client.py
+++ b/tempest/services/identity/xml/identity_client.py
@@ -75,8 +75,9 @@
xmlns=XMLNS,
name=name,
password=password,
- tenantId=tenant_id,
email=email)
+ if tenant_id:
+ create_user.add_attr('tenantId', tenant_id)
if 'enabled' in kwargs:
create_user.add_attr('enabled', str(kwargs['enabled']).lower())
@@ -116,11 +117,24 @@
class TokenClientXML(identity_client.TokenClientJSON):
TYPE = "xml"
- def auth(self, user, password, tenant):
- passwordCreds = xml.Element("passwordCredentials",
+ def auth(self, user, password, tenant=None):
+ passwordCreds = xml.Element('passwordCredentials',
username=user,
password=password)
- auth = xml.Element("auth", tenantName=tenant)
+ auth_kwargs = {}
+ if tenant:
+ auth_kwargs['tenantName'] = tenant
+ auth = xml.Element('auth', **auth_kwargs)
auth.append(passwordCreds)
resp, body = self.post(self.auth_url, body=str(xml.Document(auth)))
return resp, body['access']
+
+ def auth_token(self, token_id, tenant=None):
+ tokenCreds = xml.Element('token', id=token_id)
+ auth_kwargs = {}
+ if tenant:
+ auth_kwargs['tenantName'] = tenant
+ auth = xml.Element('auth', **auth_kwargs)
+ auth.append(tokenCreds)
+ resp, body = self.post(self.auth_url, body=str(xml.Document(auth)))
+ return resp, body['access']
diff --git a/tempest/services/volume/json/admin/volume_types_client.py b/tempest/services/volume/json/admin/volume_types_client.py
index 5554362..c9c0582 100644
--- a/tempest/services/volume/json/admin/volume_types_client.py
+++ b/tempest/services/volume/json/admin/volume_types_client.py
@@ -122,3 +122,31 @@
resp, body = self.put(url, put_body)
body = json.loads(body)
return resp, body
+
+ def get_encryption_type(self, vol_type_id):
+ """
+ Get the volume encryption type for the specified volume type.
+ vol_type_id: Id of volume_type.
+ """
+ url = "/types/%s/encryption" % str(vol_type_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body
+
+ def create_encryption_type(self, vol_type_id, **kwargs):
+ """
+ Create a new encryption type for the specified volume type.
+
+ vol_type_id: Id of volume_type.
+ provider: Class providing encryption support.
+ cipher: Encryption algorithm/mode to use.
+ key_size: Size of the encryption key, in bits.
+ control_location: Notional service where encryption is performed.
+ """
+ url = "/types/%s/encryption" % str(vol_type_id)
+ post_body = {}
+ post_body.update(kwargs)
+ post_body = json.dumps({'encryption': post_body})
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ return resp, body['encryption']
diff --git a/tempest/test.py b/tempest/test.py
index 6290761..75eb6be 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -220,13 +220,11 @@
def validate_tearDownClass():
if at_exit_set:
- raise RuntimeError("tearDownClass does not call the super's "
- "tearDownClass in these classes: "
- + str(at_exit_set) + "\n"
- "If you see the exception, with another "
- "exception please do not report this one! "
- "If you are changing tempest code, make sure you "
- "are calling the super class's tearDownClass!")
+ LOG.error(
+ "tearDownClass does not call the super's "
+ "tearDownClass in these classes: \n"
+ + str(at_exit_set))
+
atexit.register(validate_tearDownClass)
diff --git a/tempest/tests/common/utils/test_misc.py b/tempest/tests/common/utils/test_misc.py
new file mode 100644
index 0000000..b8c6184
--- /dev/null
+++ b/tempest/tests/common/utils/test_misc.py
@@ -0,0 +1,52 @@
+# 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.common.utils import misc
+from tempest.tests import base
+
+
+@misc.singleton
+class TestFoo(object):
+
+ count = 0
+
+ def increment(self):
+ self.count += 1
+ return self.count
+
+
+@misc.singleton
+class TestBar(object):
+
+ count = 0
+
+ def increment(self):
+ self.count += 1
+ return self.count
+
+
+class TestMisc(base.TestCase):
+
+ def test_singleton(self):
+ test = TestFoo()
+ self.assertEqual(0, test.count)
+ self.assertEqual(1, test.increment())
+ test2 = TestFoo()
+ self.assertEqual(1, test.count)
+ self.assertEqual(1, test2.count)
+ self.assertEqual(test, test2)
+ test3 = TestBar()
+ self.assertNotEqual(test, test3)
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index e941606..8a8ebb0 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -12,49 +12,33 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo.config import cfg
-class FakeConfig(object):
+from tempest import config
+from tempest.openstack.common.fixture import config as conf_fixture
- class fake_compute(object):
- build_interval = 10
- build_timeout = 10
- class fake_identity(object):
- disable_ssl_certificate_validation = True
- catalog_type = 'identity'
- uri = 'http://fake_uri.com/auth'
- uri_v3 = 'http://fake_uri_v3.com/auth'
+class ConfigFixture(conf_fixture.Config):
- class fake_default_feature_enabled(object):
- api_extensions = ['all']
+ def __init__(self):
+ config.register_opts()
+ super(ConfigFixture, self).__init__()
- class fake_compute_feature_enabled(fake_default_feature_enabled):
- api_v3_extensions = ['all']
+ def setUp(self):
+ super(ConfigFixture, self).setUp()
+ self.conf.set_default('build_interval', 10, group='compute')
+ self.conf.set_default('build_timeout', 10, group='compute')
+ self.conf.set_default('disable_ssl_certificate_validation', True,
+ group='identity')
+ self.conf.set_default('uri', 'http://fake_uri.com/auth',
+ group='identity')
+ self.conf.set_default('uri_v3', 'http://fake_uri_v3.com/auth',
+ group='identity')
+ self.conf.set_default('neutron', True, group='service_available')
+ self.conf.set_default('heat', True, group='service_available')
- class fake_object_storage_discoverable_apis(object):
- discoverable_apis = ['all']
- class fake_service_available(object):
- nova = True
- glance = True
- cinder = True
- heat = True
- neutron = True
- swift = True
- horizon = True
-
- class fake_negative(object):
- test_generator = 'tempest.common.' \
- 'generator.negative_generator.NegativeTestGenerator'
-
- compute_feature_enabled = fake_compute_feature_enabled()
- volume_feature_enabled = fake_default_feature_enabled()
- network_feature_enabled = fake_default_feature_enabled()
- object_storage_feature_enabled = fake_object_storage_discoverable_apis()
-
- service_available = fake_service_available()
-
- compute = fake_compute()
- identity = fake_identity()
-
- negative = fake_negative()
+class FakePrivate(config.TempestConfigPrivate):
+ def __init__(self):
+ cfg.CONF([], default_config_files=[])
+ self._set_attrs()
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
index 27ddc95..7a1909a 100644
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -15,6 +15,7 @@
import mock
+from tempest import config
import tempest.test as test
from tempest.tests import base
from tempest.tests import fake_config
@@ -38,7 +39,8 @@
def setUp(self):
super(TestNegativeAutoTest, self).setUp()
- self.stubs.Set(test, 'CONF', fake_config.FakeConfig)
+ self.useFixture(fake_config.ConfigFixture())
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
def _check_prop_entries(self, result, entry):
entries = [a for a in result if entry in a[0]]
diff --git a/tempest/tests/test_auth.py b/tempest/tests/test_auth.py
index df04d65..b6e15bd 100644
--- a/tempest/tests/test_auth.py
+++ b/tempest/tests/test_auth.py
@@ -42,7 +42,8 @@
def setUp(self):
super(BaseAuthTestsSetUp, self).setUp()
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakeConfig)
+ 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', self.fake_http.request)
self.auth_provider = self._auth(self.credentials)
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index aa3c8fc..ebf0ca0 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -15,6 +15,9 @@
import testtools
+from oslo.config import cfg
+
+from tempest import config
from tempest import exceptions
from tempest.openstack.common.fixture import mockpatch
from tempest import test
@@ -25,7 +28,8 @@
class BaseDecoratorsTest(base.TestCase):
def setUp(self):
super(BaseDecoratorsTest, self).setUp()
- self.stubs.Set(test, 'CONF', fake_config.FakeConfig)
+ self.config_fixture = self.useFixture(fake_config.ConfigFixture())
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
class TestAttrDecorator(BaseDecoratorsTest):
@@ -191,10 +195,8 @@
class TestRequiresExtDecorator(BaseDecoratorsTest):
def setUp(self):
super(TestRequiresExtDecorator, self).setUp()
- self.fixture = self.useFixture(mockpatch.PatchObject(
- test.CONF.compute_feature_enabled,
- 'api_extensions',
- new=['enabled_ext', 'another_ext']))
+ cfg.CONF.set_default('api_extensions', ['enabled_ext', 'another_ext'],
+ 'compute-feature-enabled')
def _test_requires_ext_helper(self, expected_to_skip=True,
**decorator_args):
@@ -220,7 +222,7 @@
def test_requires_ext_decorator_with_all_ext_enabled(self):
# disable fixture so the default (all) is used.
- self.fixture.cleanUp()
+ self.config_fixture.cleanUp()
self._test_requires_ext_helper(expected_to_skip=False,
extension='random_ext',
service='compute')
diff --git a/tempest/tests/test_rest_client.py b/tempest/tests/test_rest_client.py
index 827b5c9..da9ab72 100644
--- a/tempest/tests/test_rest_client.py
+++ b/tempest/tests/test_rest_client.py
@@ -35,7 +35,8 @@
def setUp(self):
super(BaseRestClientTestClass, self).setUp()
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakeConfig)
+ self.useFixture(fake_config.ConfigFixture())
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
self.rest_client = rest_client.RestClient(
fake_auth_provider.FakeAuthProvider())
self.stubs.Set(httplib2.Http, 'request', self.fake_http.request)
@@ -254,7 +255,8 @@
def setUp(self):
super(TestRestClientErrorCheckerJSON, self).setUp()
- self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakeConfig)
+ self.useFixture(fake_config.ConfigFixture())
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
self.rest_client = rest_client.RestClient(
fake_auth_provider.FakeAuthProvider())
@@ -341,3 +343,44 @@
self.assertRaises(exceptions.InvalidContentType,
self.rest_client._error_checker,
**self.set_data("405", enc="fake_enc"))
+
+
+class TestRestClientUtils(BaseRestClientTestClass):
+
+ def _is_resource_deleted(self, resource_id):
+ if not isinstance(self.retry_pass, int):
+ return False
+ if self.retry_count >= self.retry_pass:
+ return True
+ self.retry_count = self.retry_count + 1
+ return False
+
+ def setUp(self):
+ self.fake_http = fake_http.fake_httplib2()
+ super(TestRestClientUtils, self).setUp()
+ self.retry_count = 0
+ self.retry_pass = None
+ self.original_deleted_method = self.rest_client.is_resource_deleted
+ self.rest_client.is_resource_deleted = self._is_resource_deleted
+
+ def test_wait_for_resource_deletion(self):
+ self.retry_pass = 2
+ # Ensure timeout long enough for loop execution to hit retry count
+ self.rest_client.build_timeout = 500
+ sleep_mock = self.patch('time.sleep')
+ self.rest_client.wait_for_resource_deletion('1234')
+ self.assertEqual(len(sleep_mock.mock_calls), 2)
+
+ def test_wait_for_resource_deletion_not_deleted(self):
+ self.patch('time.sleep')
+ # Set timeout to be very quick to force exception faster
+ self.rest_client.build_timeout = 1
+ self.assertRaises(exceptions.TimeoutException,
+ self.rest_client.wait_for_resource_deletion,
+ '1234')
+
+ def test_wait_for_deletion_with_unimplemented_deleted_method(self):
+ self.rest_client.is_resource_deleted = self.original_deleted_method
+ self.assertRaises(NotImplementedError,
+ self.rest_client.wait_for_resource_deletion,
+ '1234')
diff --git a/tempest/tests/test_tenant_isolation.py b/tempest/tests/test_tenant_isolation.py
new file mode 100644
index 0000000..2e50cfd
--- /dev/null
+++ b/tempest/tests/test_tenant_isolation.py
@@ -0,0 +1,336 @@
+# Copyright 2014 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# 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 keystoneclient.v2_0.client as keystoneclient
+from mock import patch
+import neutronclient.v2_0.client as neutronclient
+from oslo.config import cfg
+
+from tempest.common import isolated_creds
+from tempest import config
+from tempest.openstack.common.fixture import mockpatch
+from tempest.services.identity.json import identity_client as json_iden_client
+from tempest.services.identity.xml import identity_client as xml_iden_client
+from tempest.services.network.json import network_client as json_network_client
+from tempest.services.network.xml import network_client as xml_network_client
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class TestTenantIsolation(base.TestCase):
+
+ def setUp(self):
+ super(TestTenantIsolation, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+
+ def test_tempest_client(self):
+ iso_creds = isolated_creds.IsolatedCreds('test class')
+ self.assertTrue(isinstance(iso_creds.identity_admin_client,
+ json_iden_client.IdentityClientJSON))
+ self.assertTrue(isinstance(iso_creds.network_admin_client,
+ json_network_client.NetworkClientJSON))
+
+ def test_official_client(self):
+ self.useFixture(mockpatch.PatchObject(keystoneclient.Client,
+ 'authenticate'))
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ tempest_client=False)
+ self.assertTrue(isinstance(iso_creds.identity_admin_client,
+ keystoneclient.Client))
+ self.assertTrue(isinstance(iso_creds.network_admin_client,
+ neutronclient.Client))
+
+ def test_tempest_client_xml(self):
+ iso_creds = isolated_creds.IsolatedCreds('test class', interface='xml')
+ self.assertEqual(iso_creds.interface, 'xml')
+ self.assertTrue(isinstance(iso_creds.identity_admin_client,
+ xml_iden_client.IdentityClientXML))
+ self.assertTrue(isinstance(iso_creds.network_admin_client,
+ xml_network_client.NetworkClientXML))
+
+ def _mock_user_create(self, id, name):
+ user_fix = self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'create_user',
+ return_value=({'status': 200},
+ {'id': id, 'name': name})))
+ return user_fix
+
+ def _mock_tenant_create(self, id, name):
+ tenant_fix = self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'create_tenant',
+ return_value=({'status': 200},
+ {'id': id, 'name': name})))
+ return tenant_fix
+
+ def _mock_network_create(self, iso_creds, id, name):
+ net_fix = self.useFixture(mockpatch.PatchObject(
+ iso_creds.network_admin_client,
+ 'create_network',
+ return_value=({'status': 200},
+ {'network': {'id': id, 'name': name}})))
+ return net_fix
+
+ def _mock_subnet_create(self, iso_creds, id, name):
+ subnet_fix = self.useFixture(mockpatch.PatchObject(
+ iso_creds.network_admin_client,
+ 'create_subnet',
+ return_value=({'status': 200},
+ {'subnet': {'id': id, 'name': name}})))
+ return subnet_fix
+
+ def _mock_router_create(self, id, name):
+ router_fix = self.useFixture(mockpatch.PatchObject(
+ json_network_client.NetworkClientJSON,
+ 'create_router',
+ return_value=({'status': 200},
+ {'router': {'id': id, 'name': name}})))
+ return router_fix
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_primary_creds(self, MockRestClient):
+ cfg.CONF.set_default('neutron', False, 'service_available')
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ 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')
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_admin_creds(self, MockRestClient):
+ cfg.CONF.set_default('neutron', False, 'service_available')
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ self._mock_user_create('1234', 'fake_admin_user')
+ self._mock_tenant_create('1234', 'fake_admin_tenant')
+ self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'list_roles',
+ return_value=({'status': 200},
+ [{'id': '1234', 'name': 'admin'}])))
+
+ user_mock = patch.object(json_iden_client.IdentityClientJSON,
+ 'assign_user_role')
+ user_mock.start()
+ 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()
+ 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')
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_all_cred_cleanup(self, MockRestClient):
+ cfg.CONF.set_default('neutron', False, 'service_available')
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ 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()
+ 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()
+ tenant_fix.cleanUp()
+ user_fix.cleanUp()
+ tenant_fix = self._mock_tenant_create('123456', 'fake_admin_tenant')
+ user_fix = self._mock_user_create('123456', 'fake_admin_user')
+ self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'list_roles',
+ return_value=({'status': 200},
+ [{'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()
+ user_mock = self.patch(
+ 'tempest.services.identity.json.identity_client.'
+ 'IdentityClientJSON.delete_user')
+ tenant_mock = self.patch(
+ 'tempest.services.identity.json.identity_client.'
+ 'IdentityClientJSON.delete_tenant')
+ iso_creds.clear_isolated_creds()
+ # Verify user delete calls
+ calls = user_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
+ # Verify tenant delete calls
+ calls = tenant_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_alt_creds(self, MockRestClient):
+ cfg.CONF.set_default('neutron', False, 'service_available')
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ 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')
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_network_creation(self, MockRestClient):
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ self._mock_user_create('1234', 'fake_prim_user')
+ self._mock_tenant_create('1234', 'fake_prim_tenant')
+ self._mock_network_create(iso_creds, '1234', 'fake_net')
+ self._mock_subnet_create(iso_creds, '1234', 'fake_subnet')
+ self._mock_router_create('1234', 'fake_router')
+ 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()
+ router_interface_mock.called_once_with('1234', '1234')
+ network = iso_creds.get_primary_network()
+ subnet = iso_creds.get_primary_subnet()
+ router = iso_creds.get_primary_router()
+ self.assertEqual(network['id'], '1234')
+ self.assertEqual(network['name'], 'fake_net')
+ self.assertEqual(subnet['id'], '1234')
+ self.assertEqual(subnet['name'], 'fake_subnet')
+ self.assertEqual(router['id'], '1234')
+ self.assertEqual(router['name'], 'fake_router')
+
+ @patch('tempest.common.rest_client.RestClient')
+ def test_network_cleanup(self, MockRestClient):
+ iso_creds = isolated_creds.IsolatedCreds('test class',
+ password='fake_password')
+ # Create primary tenant and network
+ user_fix = self._mock_user_create('1234', 'fake_prim_user')
+ tenant_fix = self._mock_tenant_create('1234', 'fake_prim_tenant')
+ net_fix = self._mock_network_create(iso_creds, '1234', 'fake_net')
+ subnet_fix = self._mock_subnet_create(iso_creds, '1234', 'fake_subnet')
+ router_fix = self._mock_router_create('1234', 'fake_router')
+ 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()
+ router_interface_mock.called_once_with('1234', '1234')
+ router_interface_mock.reset_mock()
+ tenant_fix.cleanUp()
+ user_fix.cleanUp()
+ net_fix.cleanUp()
+ subnet_fix.cleanUp()
+ router_fix.cleanUp()
+ # Create alternate tenant and network
+ user_fix = self._mock_user_create('12345', 'fake_alt_user')
+ tenant_fix = self._mock_tenant_create('12345', 'fake_alt_tenant')
+ net_fix = self._mock_network_create(iso_creds, '12345', 'fake_alt_net')
+ 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()
+ router_interface_mock.called_once_with('12345', '12345')
+ router_interface_mock.reset_mock()
+ tenant_fix.cleanUp()
+ user_fix.cleanUp()
+ net_fix.cleanUp()
+ subnet_fix.cleanUp()
+ router_fix.cleanUp()
+ # Create admin tenant and networks
+ user_fix = self._mock_user_create('123456', 'fake_admin_user')
+ tenant_fix = self._mock_tenant_create('123456', 'fake_admin_tenant')
+ net_fix = self._mock_network_create(iso_creds, '123456',
+ 'fake_admin_net')
+ subnet_fix = self._mock_subnet_create(iso_creds, '123456',
+ 'fake_admin_subnet')
+ router_fix = self._mock_router_create('123456', 'fake_admin_router')
+ self.useFixture(mockpatch.PatchObject(
+ json_iden_client.IdentityClientJSON,
+ 'list_roles',
+ return_value=({'status': 200},
+ [{'id': '123456', 'name': 'admin'}])))
+ with patch.object(json_iden_client.IdentityClientJSON,
+ 'assign_user_role'):
+ admin_user, admin_tenant, password = iso_creds.get_admin_creds()
+ self.patch('tempest.services.identity.json.identity_client.'
+ 'IdentityClientJSON.delete_user')
+ self.patch('tempest.services.identity.json.identity_client.'
+ 'IdentityClientJSON.delete_tenant')
+ net = patch.object(iso_creds.network_admin_client,
+ 'delete_network')
+ net_mock = net.start()
+ subnet = patch.object(iso_creds.network_admin_client,
+ 'delete_subnet')
+ subnet_mock = subnet.start()
+ router = patch.object(iso_creds.network_admin_client,
+ 'delete_router')
+ router_mock = router.start()
+ remove_router_interface_mock = self.patch(
+ 'tempest.services.network.json.network_client.NetworkClientJSON.'
+ 'remove_router_interface_with_subnet_id')
+ port_list_mock = patch.object(iso_creds.network_admin_client,
+ 'list_ports', return_value=(
+ {'status': 200}, {'ports': []}))
+ port_list_mock.start()
+ iso_creds.clear_isolated_creds()
+ # Verify remove router interface calls
+ calls = remove_router_interface_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1], calls)
+ self.assertIn(('1234', '1234'), args)
+ self.assertIn(('12345', '12345'), args)
+ self.assertIn(('123456', '123456'), args)
+ # Verify network delete calls
+ calls = net_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
+ # Verify subnet delete calls
+ calls = subnet_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
+ # Verify router delete calls
+ calls = router_mock.mock_calls
+ self.assertEqual(len(calls), 3)
+ args = map(lambda x: x[1][0], calls)
+ self.assertIn('1234', args)
+ self.assertIn('12345', args)
+ self.assertIn('123456', args)
diff --git a/tools/check_logs.py b/tools/check_logs.py
index edf95a1..e28c230 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -30,9 +30,30 @@
dump_all_errors = True
# As logs are made clean, add to this set
-must_be_clean = set(['c-sch', 'g-reg', 'ceilometer-alarm-notifier',
- 'ceilometer-collector', 'horizon', 'n-crt', 'n-obj',
- 'q-vpn'])
+allowed_dirty = set([
+ 'c-api',
+ 'ceilometer-acentral',
+ 'ceilometer-acompute',
+ 'ceilometer-alarm-evaluator',
+ 'ceilometer-anotification',
+ 'ceilometer-api',
+ 'c-vol',
+ 'g-api',
+ 'h-api',
+ 'h-eng',
+ 'ir-cond',
+ 'n-api',
+ 'n-cpu',
+ 'n-net',
+ 'n-sch',
+ 'q-agt',
+ 'q-dhcp',
+ 'q-lbaas',
+ 'q-meta',
+ 'q-metering',
+ 'q-svc',
+ 'q-vpn',
+ 's-proxy'])
def process_files(file_specs, url_specs, whitelists):
@@ -69,12 +90,12 @@
break
if not whitelisted or dump_all_errors:
if print_log_name:
- print("Log File: %s" % name)
+ print("Log File Has Errors: %s" % name)
print_log_name = False
if not whitelisted:
had_errors = True
print("*** Not Whitelisted ***"),
- print(line)
+ print(line.rstrip())
return had_errors
@@ -135,8 +156,8 @@
return 0
failed = False
for log in logs_with_errors:
- if log in must_be_clean:
- print("FAILED: %s" % log)
+ if log not in allowed_dirty:
+ print("Log: %s not allowed to have ERRORS or TRACES" % log)
failed = True
if failed:
return 1
diff --git a/tools/verify_tempest_config.py b/tools/verify_tempest_config.py
index 4be812c..79e1fe3 100755
--- a/tools/verify_tempest_config.py
+++ b/tools/verify_tempest_config.py
@@ -63,6 +63,7 @@
'nova_v3': os.extensions_v3_client,
'cinder': os.volumes_extension_client,
'neutron': os.network_client,
+ 'swift': os.account_client,
}
if service not in extensions_client:
print('No tempest extensions client for %s' % service)
@@ -76,6 +77,7 @@
'nova_v3': CONF.compute_feature_enabled.api_v3_extensions,
'cinder': CONF.volume_feature_enabled.api_extensions,
'neutron': CONF.network_feature_enabled.api_extensions,
+ 'swift': CONF.object_storage_feature_enabled.discoverable_apis,
}
if service not in extensions_options:
print('No supported extensions list option for %s' % service)
@@ -93,6 +95,10 @@
# instead of name.
if service == 'neutron':
extensions = map(lambda x: x['alias'], resp['extensions'])
+ elif service == 'swift':
+ # Remove Swift general information from extensions list
+ resp.pop('swift')
+ extensions = resp.keys()
else:
extensions = map(lambda x: x['name'], resp['extensions'])
@@ -142,7 +148,7 @@
print('Running config verification...')
os = clients.ComputeAdminManager(interface='json')
results = {}
- for service in ['nova', 'nova_v3', 'cinder', 'neutron']:
+ for service in ['nova', 'nova_v3', 'cinder', 'neutron', 'swift']:
# TODO(mtreinish) make this a keystone endpoint check for available
# services
if not check_service_availability(service):