Merge "Firewall as a service API Test Enhancement"
diff --git a/HACKING.rst b/HACKING.rst
index e920634..7363e7f 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -111,9 +111,9 @@
Negative Tests
--------------
Newly added negative tests should use the negative test framework. First step
-is to create an interface description in a json file under `etc/schemas`.
-These descriptions consists of two important sections for the test
-(one of those is mandatory):
+is to create an interface description in a python file under
+`tempest/api_schema/request/`. These descriptions consists of two important
+sections for the test (one of those is mandatory):
- A resource (part of the URL of the request): Resources needed for a test
must be created in `setUpClass` and registered with `set_resource` e.g.:
@@ -126,21 +126,17 @@
load_tests = test.NegativeAutoTest.load_tests
- class SampeTestNegativeTestJSON(<your base class>, test.NegativeAutoTest):
- _interface = 'json'
+ @test.SimpleNegativeAutoTest
+ class SampleTestNegativeTestJSON(<your base class>, test.NegativeAutoTest):
_service = 'compute'
- _schema_file = <your Schema file>
+ _schema = <your schema file>
-Negative tests must be marked with a negative attribute::
-
- @test.attr(type=['negative', 'gate'])
- def test_get_console_output(self):
- self.execute(self._schema_file)
+The class decorator `SimpleNegativeAutoTest` will automatically generate test
+cases out of the given schema in the attribute `_schema`.
All negative tests should be added into a separate negative test file.
If such a file doesn't exist for the particular resource being tested a new
-test file should be added. Old XML based negative tests can be kept but should
-be renamed to `_xml.py`.
+test file should be added.
Test skips because of Known Bugs
--------------------------------
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 13ee8fe..3c6eb44 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -467,9 +467,6 @@
# [nova.vnc]->vnc_enabled in nova.conf (boolean value)
#vnc_console = false
-# If false skip all v2 api tests with xml (boolean value)
-#xml_api_v2 = true
-
[dashboard]
@@ -581,6 +578,10 @@
# (string value)
#auth_version = v2
+# Specify a CA bundle file to use in verifying a TLS (https) server
+# certificate. (string value)
+#ca_certificates_file = <None>
+
# Catalog type of the Identity service. (string value)
#catalog_type = identity
@@ -632,9 +633,6 @@
# (boolean value)
#trust = true
-# If false, skip all identity api tests with xml (boolean value)
-#xml_api = false
-
[image]
@@ -810,9 +808,6 @@
# attributes ipv6_ra_mode and ipv6_address_mode (boolean value)
#ipv6_subnet_attributes = false
-# If false, skip all network api tests with xml (boolean value)
-#xml_api = false
-
[object-storage]
diff --git a/requirements.txt b/requirements.txt
index e939c5c..ce5886e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,6 @@
httplib2>=0.7.5
jsonschema>=2.0.0,<3.0.0
testtools>=0.9.36,!=1.2.0
-lxml>=2.3
boto>=2.32.1
paramiko>=1.13.0
netaddr>=0.7.12
diff --git a/tempest/api/baremetal/admin/test_ports.py b/tempest/api/baremetal/admin/test_ports.py
index b3f9b7f..3392ab9 100644
--- a/tempest/api/baremetal/admin/test_ports.py
+++ b/tempest/api/baremetal/admin/test_ports.py
@@ -57,11 +57,13 @@
_, body = self.client.show_port(uuid)
self._assertExpected(port, body)
+ @test.skip_because(bug='1398350')
@test.attr(type='smoke')
def test_create_port_with_extra(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- extra = {'key': 'value'}
+ extra = {'str': 'value', 'int': 123, 'float': 0.123,
+ 'bool': True, 'list': [1, 2, 3], 'dict': {'foo': 'bar'}}
_, port = self.create_port(node_id=node_id, address=address,
extra=extra)
@@ -224,6 +226,7 @@
_, body = self.client.show_port(port['uuid'])
self.assertEqual(extra, body['extra'])
+ @test.skip_because(bug='1398350')
@test.attr(type='smoke')
def test_update_port_mixed_ops(self):
node_id = self.node['uuid']
@@ -234,7 +237,7 @@
extra=extra)
new_address = data_utils.rand_mac_address()
- new_extra = {'key1': 'new-value1', 'key3': 'new-value3'}
+ new_extra = {'key1': 0.123, 'key3': {'cat': 'meow'}}
patch = [{'path': '/address',
'op': 'replace',
diff --git a/tempest/api/baremetal/admin/test_ports_negative.py b/tempest/api/baremetal/admin/test_ports_negative.py
index ead3799..8080eb6 100644
--- a/tempest/api/baremetal/admin/test_ports_negative.py
+++ b/tempest/api/baremetal/admin/test_ports_negative.py
@@ -34,15 +34,6 @@
self.create_port, node_id=node_id, address=address)
@test.attr(type=['negative', 'smoke'])
- def test_create_port_malformed_extra(self):
- node_id = self.node['uuid']
- address = data_utils.rand_mac_address()
- extra = {'key': 0.123}
- self.assertRaises(exc.BadRequest,
- self.create_port, node_id=node_id,
- address=address, extra=extra)
-
- @test.attr(type=['negative', 'smoke'])
def test_create_port_nonexsistent_node_id(self):
node_id = str(data_utils.rand_uuid())
address = data_utils.rand_mac_address()
@@ -160,31 +151,6 @@
'value': new_address}])
@test.attr(type=['negative', 'smoke'])
- def test_update_port_add_malformed_extra(self):
- node_id = self.node['uuid']
- address = data_utils.rand_mac_address()
-
- _, port = self.create_port(node_id=node_id, address=address)
- port_id = port['uuid']
-
- self.assertRaises(exc.BadRequest, self.client.update_port, port_id,
- [{'path': '/extra/key', ' op': 'add',
- 'value': 0.123}])
-
- @test.attr(type=['negative', 'smoke'])
- def test_update_port_add_whole_malformed_extra(self):
- node_id = self.node['uuid']
- address = data_utils.rand_mac_address()
-
- _, port = self.create_port(node_id=node_id, address=address)
- port_id = port['uuid']
-
- self.assertRaises(exc.BadRequest, self.client.update_port, port_id,
- [{'path': '/extra',
- 'op': 'add',
- 'value': [1, 2, 3, 4, 'a']}])
-
- @test.attr(type=['negative', 'smoke'])
def test_update_port_add_nonexistent_property(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
@@ -257,37 +223,6 @@
self.client.update_port, port_id, patch)
@test.attr(type=['negative', 'smoke'])
- def test_update_port_replace_extra_item_with_malformed(self):
- node_id = self.node['uuid']
- address = data_utils.rand_mac_address()
- extra = {'key': 'value'}
-
- _, port = self.create_port(node_id=node_id, address=address,
- extra=extra)
- port_id = port['uuid']
-
- patch = [{'path': '/extra/key',
- 'op': 'replace',
- 'value': 0.123}]
- self.assertRaises(exc.BadRequest,
- self.client.update_port, port_id, patch)
-
- @test.attr(type=['negative', 'smoke'])
- def test_update_port_replace_whole_extra_with_malformed(self):
- node_id = self.node['uuid']
- address = data_utils.rand_mac_address()
-
- _, port = self.create_port(node_id=node_id, address=address)
- port_id = port['uuid']
-
- patch = [{'path': '/extra',
- 'op': 'replace',
- 'value': [1, 2, 3, 4, 'a']}]
-
- self.assertRaises(exc.BadRequest,
- self.client.update_port, port_id, patch)
-
- @test.attr(type=['negative', 'smoke'])
def test_update_port_replace_nonexistent_property(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
diff --git a/tempest/api/compute/admin/test_flavors_negative.py b/tempest/api/compute/admin/test_flavors_negative.py
index 5bc3d10..fb27360 100644
--- a/tempest/api/compute/admin/test_flavors_negative.py
+++ b/tempest/api/compute/admin/test_flavors_negative.py
@@ -18,9 +18,13 @@
from tempest.api.compute import base
from tempest.api_schema.request.compute.v2 import flavors
from tempest.common.utils import data_utils
+from tempest import config
from tempest import exceptions
from tempest import test
+
+CONF = config.CONF
+
load_tests = test.NegativeAutoTest.load_tests
@@ -106,5 +110,5 @@
class FlavorCreateNegativeTestJSON(base.BaseV2ComputeAdminTest,
test.NegativeAutoTest):
_interface = 'json'
- _service = 'compute'
+ _service = CONF.compute.catalog_type
_schema = flavors.flavor_create
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 2f53a0b..6a3ee44 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -38,9 +38,6 @@
def resource_setup(cls):
cls.set_network_resources()
super(BaseComputeTest, cls).resource_setup()
- if getattr(cls, '_interface', None) == 'xml' and cls._api_version == 2:
- if not CONF.compute_feature_enabled.xml_api_v2:
- raise cls.skipException('XML API is not enabled')
# TODO(andreaf) WE should care also for the alt_manager here
# but only once client lazy load in the manager is done
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index cae1ac4..83f8e19 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -15,23 +15,26 @@
from tempest.api.compute import base
from tempest.api_schema.request.compute.v2 import flavors
+from tempest import config
from tempest import test
+CONF = config.CONF
+
load_tests = test.NegativeAutoTest.load_tests
@test.SimpleNegativeAutoTest
class FlavorsListWithDetailsNegativeTestJSON(base.BaseV2ComputeTest,
test.NegativeAutoTest):
- _service = 'compute'
+ _service = CONF.compute.catalog_type
_schema = flavors.flavor_list
@test.SimpleNegativeAutoTest
class FlavorDetailsNegativeTestJSON(base.BaseV2ComputeTest,
test.NegativeAutoTest):
- _service = 'compute'
+ _service = CONF.compute.catalog_type
_schema = flavors.flavors_details
@classmethod
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index ad1ec70..8b074fd 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -40,7 +40,7 @@
for sz in [256, 257, 511, 1023]:
key = "k" * sz
meta = {key: 'data1'}
- self.assertRaises(exceptions.OverLimit,
+ self.assertRaises((exceptions.BadRequest, exceptions.OverLimit),
self.create_test_server,
meta=meta)
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 1a338bd..4e6dcda 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -219,7 +219,7 @@
# Pass really long metadata while creating a server
metadata = {'a': 'b' * 260}
- self.assertRaises(exceptions.OverLimit,
+ self.assertRaises((exceptions.BadRequest, exceptions.OverLimit),
self.create_test_server,
meta=metadata)
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
new file mode 100644
index 0000000..8ffd1ed
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -0,0 +1,84 @@
+# 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 import auth
+from tempest import clients
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class TestDefaultProjectId (base.BaseIdentityV3AdminTest):
+ _interface = 'json'
+
+ @classmethod
+ def resource_setup(cls):
+ cls.set_network_resources()
+ super(TestDefaultProjectId, cls).resource_setup()
+
+ def _delete_domain(self, domain_id):
+ # It is necessary to disable the domain before deleting,
+ # or else it would result in unauthorized error
+ self.client.update_domain(domain_id, enabled=False)
+ self.client.delete_domain(domain_id)
+
+ @test.attr(type='smoke')
+ def test_default_project_id(self):
+ # create a domain
+ dom_name = data_utils.rand_name('dom')
+ _, domain_body = self.client.create_domain(dom_name)
+ dom_id = domain_body['id']
+ self.addCleanup(self._delete_domain, dom_id)
+
+ # create a project in the domain
+ proj_name = data_utils.rand_name('proj')
+ _, proj_body = self.client.create_project(proj_name, domain_id=dom_id)
+ proj_id = proj_body['id']
+ self.addCleanup(self.client.delete_project, proj_id)
+ self.assertEqual(proj_body['domain_id'], dom_id,
+ "project " + proj_name +
+ "doesn't have domain id " + dom_id)
+
+ # create a user in the domain, with the previous project as his
+ # default project
+ user_name = data_utils.rand_name('user')
+ _, user_body = self.client.create_user(user_name, password=user_name,
+ domain_id=dom_id,
+ default_project_id=proj_id)
+ user_id = user_body['id']
+ self.addCleanup(self.client.delete_user, user_id)
+ self.assertEqual(user_body['domain_id'], dom_id,
+ "user " + user_name +
+ "doesn't have domain id " + dom_id)
+
+ # get roles and find the admin role
+ admin_role = self.get_role_by_name("admin")
+ admin_role_id = admin_role['id']
+
+ # grant the admin role to the user on his project
+ self.client.assign_user_role_on_project(proj_id, user_id,
+ admin_role_id)
+
+ # create a new client with user's credentials (NOTE: unscoped token!)
+ creds = auth.KeystoneV3Credentials(username=user_name,
+ password=user_name,
+ domain_name=dom_name)
+ auth_provider = auth.KeystoneV3AuthProvider(creds)
+ creds = auth_provider.fill_credentials()
+ admin_client = clients.Manager(interface=self._interface,
+ credentials=creds)
+
+ # verify the user's token and see that it is scoped to the project
+ token, auth_data = admin_client.auth_provider.get_auth()
+ _, result = admin_client.identity_v3_client.get_token(token)
+ self.assertEqual(result['project']['domain']['id'], dom_id)
+ self.assertEqual(result['project']['id'], proj_id)
diff --git a/tempest/api/identity/admin/v3/test_policies.py b/tempest/api/identity/admin/v3/test_policies.py
index 9ea61df..23df13d 100644
--- a/tempest/api/identity/admin/v3/test_policies.py
+++ b/tempest/api/identity/admin/v3/test_policies.py
@@ -32,13 +32,13 @@
for _ in range(3):
blob = data_utils.rand_name('BlobName-')
policy_type = data_utils.rand_name('PolicyType-')
- resp, policy = self.policy_client.create_policy(blob,
- policy_type)
+ policy = self.policy_client.create_policy(blob,
+ policy_type)
# Delete the Policy at the end of this method
self.addCleanup(self._delete_policy, policy['id'])
policy_ids.append(policy['id'])
# List and Verify Policies
- _, body = self.policy_client.list_policies()
+ body = self.policy_client.list_policies()
for p in body:
fetched_ids.append(p['id'])
missing_pols = [p for p in policy_ids if p not in fetched_ids]
@@ -49,7 +49,7 @@
# Test to update policy
blob = data_utils.rand_name('BlobName-')
policy_type = data_utils.rand_name('PolicyType-')
- _, policy = self.policy_client.create_policy(blob, policy_type)
+ policy = self.policy_client.create_policy(blob, policy_type)
self.addCleanup(self._delete_policy, policy['id'])
self.assertIn('id', policy)
self.assertIn('type', policy)
@@ -59,11 +59,11 @@
self.assertEqual(policy_type, policy['type'])
# Update policy
update_type = data_utils.rand_name('UpdatedPolicyType-')
- _, data = self.policy_client.update_policy(
+ data = self.policy_client.update_policy(
policy['id'], type=update_type)
self.assertIn('type', data)
# Assertion for updated value with fetched value
- _, fetched_policy = self.policy_client.get_policy(policy['id'])
+ fetched_policy = self.policy_client.get_policy(policy['id'])
self.assertIn('id', fetched_policy)
self.assertIn('blob', fetched_policy)
self.assertIn('type', fetched_policy)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 74baba6..76b6f17 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -15,7 +15,7 @@
import cStringIO as StringIO
from tempest import clients
-from tempest.common import isolated_creds
+from tempest.common import credentials
from tempest.common.utils import data_utils
from tempest import config
from tempest import exceptions
@@ -36,7 +36,7 @@
super(BaseImageTest, cls).resource_setup()
cls.created_images = []
cls._interface = 'json'
- cls.isolated_creds = isolated_creds.IsolatedCreds(
+ cls.isolated_creds = credentials.get_isolated_credentials(
cls.__name__, network_resources=cls.network_resources)
if not CONF.service_available.glance:
skip_msg = ("%s skipped as glance is not available" % cls.__name__)
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 4b5f107..b13dd22 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -62,9 +62,6 @@
super(BaseNetworkTest, cls).resource_setup()
if not CONF.service_available.neutron:
raise cls.skipException("Neutron support is required")
- if getattr(cls, '_interface', None) == 'xml':
- if not CONF.network_feature_enabled.xml_api:
- raise cls.skipException('XML API is not enabled')
if cls._ip_version == 6 and not CONF.network_feature_enabled.ipv6:
raise cls.skipException("IPv6 Tests are disabled.")
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index d30c7dc..a03e587 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -186,6 +186,23 @@
[data_utils.rand_name('secgroup'),
data_utils.rand_name('secgroup')])
+ @test.attr(type='smoke')
+ def test_create_show_delete_port_user_defined_mac(self):
+ # Create a port for a legal mac
+ _, body = self.client.create_port(network_id=self.network['id'])
+ old_port = body['port']
+ free_mac_address = old_port['mac_address']
+ self.client.delete_port(old_port['id'])
+ # Create a new port with user defined mac
+ _, body = self.client.create_port(network_id=self.network['id'],
+ mac_address=free_mac_address)
+ self.addCleanup(self.client.delete_port, body['port']['id'])
+ port = body['port']
+ _, body = self.client.show_port(port['id'])
+ show_port = body['port']
+ self.assertEqual(free_mac_address,
+ show_port['mac_address'])
+
class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
_interface = 'json'
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index 4626aae..b9e8666 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -138,6 +138,7 @@
# Create rule for icmp protocol with invalid ports
states = [(1, 256, 'Invalid value for ICMP code'),
+ (None, 6, 'ICMP type (port-range-min) is missing'),
(300, 1, 'Invalid value for ICMP type')]
for pmin, pmax, msg in states:
ex = self.assertRaises(
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 2e39cf9..7fabb7d 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -16,8 +16,8 @@
from tempest.api.identity import base
from tempest import clients
+from tempest.common import credentials
from tempest.common import custom_matchers
-from tempest.common import isolated_creds
from tempest import config
from tempest import exceptions
import tempest.test
@@ -34,7 +34,7 @@
if not CONF.service_available.swift:
skip_msg = ("%s skipped as swift is not available" % cls.__name__)
raise cls.skipException(skip_msg)
- cls.isolated_creds = isolated_creds.IsolatedCreds(
+ cls.isolated_creds = credentials.get_isolated_credentials(
cls.__name__, network_resources=cls.network_resources)
# Get isolated creds for normal user
cls.os = clients.Manager(cls.isolated_creds.get_primary_creds())
diff --git a/tempest/api/orchestration/stacks/templates/non_empty_stack.yaml b/tempest/api/orchestration/stacks/templates/non_empty_stack.yaml
index 8690941..4f9df91 100644
--- a/tempest/api/orchestration/stacks/templates/non_empty_stack.yaml
+++ b/tempest/api/orchestration/stacks/templates/non_empty_stack.yaml
@@ -7,6 +7,8 @@
Default: not_yet
image:
Type: String
+ flavor:
+ Type: String
Resources:
fluffy:
Type: AWS::AutoScaling::LaunchConfiguration
@@ -16,7 +18,7 @@
- Stinky
Properties:
ImageId: {Ref: image}
- InstanceType: not_used
+ InstanceType: {Ref: flavor}
UserData:
Fn::Replace:
- variable_a: {Ref: trigger}
diff --git a/tempest/api/orchestration/stacks/test_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
index 759cbbe..bf6c79c 100644
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ b/tempest/api/orchestration/stacks/test_non_empty_stack.py
@@ -31,13 +31,15 @@
template = cls.read_template('non_empty_stack')
image_id = (CONF.orchestration.image_ref or
cls._create_image()['id'])
+ flavor = CONF.orchestration.instance_type
# create the stack
cls.stack_identifier = cls.create_stack(
cls.stack_name,
template,
parameters={
'trigger': 'start',
- 'image': image_id
+ 'image': image_id,
+ 'flavor': flavor
})
cls.stack_id = cls.stack_identifier.split('/')[1]
cls.resource_name = 'fluffy'
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 0cc2471..50bab56 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -22,13 +22,13 @@
QUOTA_USAGE_KEYS = ['reserved', 'limit', 'in_use']
-class VolumeQuotasAdminTestJSON(base.BaseVolumeV1AdminTest):
+class BaseVolumeQuotasAdminV2TestJSON(base.BaseVolumeAdminTest):
_interface = "json"
force_tenant_isolation = True
@classmethod
def resource_setup(cls):
- super(VolumeQuotasAdminTestJSON, cls).resource_setup()
+ super(BaseVolumeQuotasAdminV2TestJSON, cls).resource_setup()
cls.demo_tenant_id = cls.isolated_creds.get_primary_creds().tenant_id
@test.attr(type='gate')
@@ -113,3 +113,7 @@
self.quotas_client.delete_quota_set(tenant_id)
_, quota_set_new = self.quotas_client.get_quota_set(tenant_id)
self.assertEqual(volume_default, quota_set_new['volumes'])
+
+
+class VolumeQuotasAdminV1TestJSON(BaseVolumeQuotasAdminV2TestJSON):
+ _api_version = 1
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 0fff561..c367ebb 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -18,13 +18,13 @@
from tempest import test
-class VolumeQuotasNegativeTestJSON(base.BaseVolumeV1AdminTest):
+class BaseVolumeQuotasNegativeV2TestJSON(base.BaseVolumeAdminTest):
_interface = "json"
force_tenant_isolation = True
@classmethod
def resource_setup(cls):
- super(VolumeQuotasNegativeTestJSON, cls).resource_setup()
+ super(BaseVolumeQuotasNegativeV2TestJSON, cls).resource_setup()
demo_user = cls.isolated_creds.get_primary_creds()
cls.demo_tenant_id = demo_user.tenant_id
cls.shared_quota_set = {'gigabytes': 3, 'volumes': 1, 'snapshots': 1}
@@ -76,3 +76,7 @@
self.assertRaises(exceptions.OverLimit,
self.snapshots_client.create_snapshot,
self.volume['id'])
+
+
+class VolumeQuotasNegativeV1TestJSON(BaseVolumeQuotasNegativeV2TestJSON):
+ _api_version = 1
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 7820148..fffc5cb 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -17,7 +17,7 @@
from tempest import test
-class VolumesServicesTestJSON(base.BaseVolumeV1AdminTest):
+class VolumesServicesV2TestJSON(base.BaseVolumeAdminTest):
"""
Tests Volume Services API.
volume service list requires admin privileges.
@@ -26,21 +26,20 @@
@classmethod
def resource_setup(cls):
- super(VolumesServicesTestJSON, cls).resource_setup()
- cls.client = cls.os_adm.volume_services_client
- _, cls.services = cls.client.list_services()
+ super(VolumesServicesV2TestJSON, cls).resource_setup()
+ _, cls.services = cls.admin_volume_services_client.list_services()
cls.host_name = cls.services[0]['host']
cls.binary_name = cls.services[0]['binary']
@test.attr(type='gate')
def test_list_services(self):
- _, services = self.client.list_services()
+ _, services = self.admin_volume_services_client.list_services()
self.assertNotEqual(0, len(services))
@test.attr(type='gate')
def test_get_service_by_service_binary_name(self):
params = {'binary': self.binary_name}
- _, services = self.client.list_services(params)
+ _, services = self.admin_volume_services_client.list_services(params)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(self.binary_name, service['binary'])
@@ -51,7 +50,7 @@
service['host'] == self.host_name]
params = {'host': self.host_name}
- _, services = self.client.list_services(params)
+ _, services = self.admin_volume_services_client.list_services(params)
# we could have a periodic job checkin between the 2 service
# lookups, so only compare binary lists.
@@ -65,7 +64,11 @@
def test_get_service_by_service_and_host_name(self):
params = {'host': self.host_name, 'binary': self.binary_name}
- _, services = self.client.list_services(params)
+ _, services = self.admin_volume_services_client.list_services(params)
self.assertEqual(1, len(services))
self.assertEqual(self.host_name, services[0]['host'])
self.assertEqual(self.binary_name, services[0]['binary'])
+
+
+class VolumesServicesV1TestJSON(VolumesServicesV2TestJSON):
+ _api_version = 1
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 5d99123..52e48f3 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -145,10 +145,6 @@
pass
-class BaseVolumeV1Test(BaseVolumeTest):
- _api_version = 1
-
-
class BaseVolumeAdminTest(BaseVolumeTest):
"""Base test case class for all Volume Admin API tests."""
@classmethod
@@ -165,28 +161,32 @@
cls.qos_specs = []
- cls.quotas_client = cls.os_adm.volume_quotas_client
-
if cls._api_version == 1:
if not CONF.volume_feature_enabled.api_v1:
msg = "Volume API v1 is disabled"
raise cls.skipException(msg)
cls.volume_qos_client = cls.os_adm.volume_qos_client
+ cls.admin_volume_services_client = \
+ cls.os_adm.volume_services_client
cls.volume_types_client = cls.os_adm.volume_types_client
cls.admin_volume_client = cls.os_adm.volumes_client
cls.hosts_client = cls.os_adm.volume_hosts_client
cls.admin_snapshots_client = cls.os_adm.snapshots_client
cls.backups_adm_client = cls.os_adm.backups_client
+ cls.quotas_client = cls.os_adm.volume_quotas_client
elif cls._api_version == 2:
if not CONF.volume_feature_enabled.api_v2:
msg = "Volume API v2 is disabled"
raise cls.skipException(msg)
cls.volume_qos_client = cls.os_adm.volume_qos_v2_client
+ cls.admin_volume_services_client = \
+ cls.os_adm.volume_services_v2_client
cls.volume_types_client = cls.os_adm.volume_types_v2_client
cls.admin_volume_client = cls.os_adm.volumes_v2_client
cls.hosts_client = cls.os_adm.volume_hosts_v2_client
cls.admin_snapshots_client = cls.os_adm.snapshots_v2_client
cls.backups_adm_client = cls.os_adm.backups_v2_client
+ cls.quotas_client = cls.os_adm.volume_quotas_v2_client
@classmethod
def resource_cleanup(cls):
@@ -218,7 +218,3 @@
except exceptions.NotFound:
# The qos_specs may have already been deleted which is OK.
pass
-
-
-class BaseVolumeV1AdminTest(BaseVolumeAdminTest):
- _api_version = 1
diff --git a/tempest/api_schema/response/compute/services.py b/tempest/api_schema/response/compute/services.py
index fc42b89..6f361ef 100644
--- a/tempest/api_schema/response/compute/services.py
+++ b/tempest/api_schema/response/compute/services.py
@@ -22,7 +22,8 @@
'items': {
'type': 'object',
'properties': {
- 'id': {'type': 'integer'},
+ 'id': {'type': ['integer', 'string'],
+ 'pattern': '^[a-zA-Z!]*@[0-9]+$'},
'zone': {'type': 'string'},
'host': {'type': 'string'},
'state': {'type': 'string'},
diff --git a/tempest/cli/simple_read_only/network/test_neutron.py b/tempest/cli/simple_read_only/network/test_neutron.py
index 6090882..6cf0640 100644
--- a/tempest/cli/simple_read_only/network/test_neutron.py
+++ b/tempest/cli/simple_read_only/network/test_neutron.py
@@ -198,6 +198,31 @@
'auth_mode', 'status'])
@test.attr(type='smoke')
+ @test.requires_ext(extension='fwaas', service='network')
+ def test_neutron_firewall_list(self):
+ firewall_list = self.parser.listing(self.neutron
+ ('firewall-list'))
+ self.assertTableStruct(firewall_list, ['id', 'name',
+ 'firewall_policy_id'])
+
+ @test.attr(type='smoke')
+ @test.requires_ext(extension='fwaas', service='network')
+ def test_neutron_firewall_policy_list(self):
+ firewall_policy = self.parser.listing(self.neutron
+ ('firewall-policy-list'))
+ self.assertTableStruct(firewall_policy, ['id', 'name',
+ 'firewall_rules'])
+
+ @test.attr(type='smoke')
+ @test.requires_ext(extension='fwaas', service='network')
+ def test_neutron_firewall_rule_list(self):
+ firewall_rule = self.parser.listing(self.neutron
+ ('firewall-rule-list'))
+ self.assertTableStruct(firewall_rule, ['id', 'name',
+ 'firewall_policy_id',
+ 'summary', 'enabled'])
+
+ @test.attr(type='smoke')
def test_neutron_help(self):
help_text = self.neutron('help')
lines = help_text.split('\n')
diff --git a/tempest/clients.py b/tempest/clients.py
index 486eef2..91dc5f7 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -138,6 +138,10 @@
from tempest.services.volume.json.volumes_client import VolumesClientJSON
from tempest.services.volume.v2.json.admin.volume_hosts_client import \
VolumeHostsV2ClientJSON
+from tempest.services.volume.v2.json.admin.volume_quotas_client import \
+ VolumeQuotasV2Client
+from tempest.services.volume.v2.json.admin.volume_services_client import \
+ VolumesServicesV2ClientJSON
from tempest.services.volume.v2.json.admin.volume_types_client import \
VolumeTypesV2ClientJSON
from tempest.services.volume.v2.json.availability_zone_client import \
@@ -287,6 +291,8 @@
self.volume_qos_client = QosSpecsClientJSON(self.auth_provider)
self.volume_qos_v2_client = QosSpecsV2ClientJSON(
self.auth_provider)
+ self.volume_services_v2_client = VolumesServicesV2ClientJSON(
+ self.auth_provider)
def _set_volume_json_clients(self):
self.backups_client = BackupsClientJSON(self.auth_provider)
@@ -302,6 +308,7 @@
self.volume_hosts_v2_client = VolumeHostsV2ClientJSON(
self.auth_provider)
self.volume_quotas_client = VolumeQuotasClientJSON(self.auth_provider)
+ self.volume_quotas_v2_client = VolumeQuotasV2Client(self.auth_provider)
self.volumes_extension_client = VolumeExtensionClientJSON(
self.auth_provider)
self.volumes_v2_extension_client = VolumeV2ExtensionClientJSON(
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index a305e42..f36ef56 100755
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -9,7 +9,7 @@
#
# 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
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
@@ -36,14 +36,14 @@
**NOTE**: The _tenants_to_clean array in dry-run.json lists the
tenants that cleanup will loop through and delete child objects, not
delete the tenant itself. This may differ from the tenants array as you
-can clean the tempest and alternate tempest tenants but not delete the
-tenants themselves. This is actually the default behavior.
+can clean the tempest and alternate tempest tenants but by default,
+cleanup deletes the objects in the tempest and alternate tempest tenants
+but does not delete those tenants unless the --delete-tempest-conf-objects
+flag is used to force their deletion.
**Normal mode**: running with no arguments, will query your deployment and
-build a list of objects to delete after filtering out out the objects
-found in saved_state.json and based on the
---preserve-tempest-conf-objects and
---delete-tempest-conf-objects flags.
+build a list of objects to delete after filtering out the objects found in
+saved_state.json and based on the --delete-tempest-conf-objects flag.
By default the tempest and alternate tempest users and tenants are not
deleted and the admin user specified in tempest.conf is never deleted.
@@ -84,7 +84,6 @@
# available services
self.tenant_services = cleanup_service.get_tenant_cleanup_services()
self.global_services = cleanup_service.get_global_cleanup_services()
- cleanup_service.init_conf()
def run(self):
opts = self.options
@@ -98,7 +97,7 @@
def _cleanup(self):
LOG.debug("Begin cleanup")
is_dry_run = self.options.dry_run
- is_preserve = self.options.preserve_tempest_conf_objects
+ is_preserve = not self.options.delete_tempest_conf_objects
is_save_state = False
if is_dry_run:
@@ -149,7 +148,7 @@
LOG.debug("Cleaning tenant: %s " % tenant['name'])
is_dry_run = self.options.dry_run
dry_run_data = self.dry_run_data
- is_preserve = self.options.preserve_tempest_conf_objects
+ is_preserve = not self.options.delete_tempest_conf_objects
tenant_id = tenant['id']
tenant_name = tenant['name']
tenant_data = None
@@ -194,23 +193,16 @@
dest='init_saved_state', default=False,
help="Creates JSON file: " + SAVED_STATE_JSON +
", representing the current state of your "
- "deployment, specifically objects types "
- "Tempest creates and destroys during a run. "
+ "deployment, specifically object types "
+ "tempest creates and destroys during a run. "
"You must run with this flag prior to "
- "executing cleanup.")
- parser.add_argument('--preserve-tempest-conf-objects',
- action="store_true",
- dest='preserve_tempest_conf_objects',
- default=True, help="Do not delete the "
- "tempest and alternate tempest users and "
- "tenants, so they may be used for future "
- "tempest runs. By default this is argument "
- "is true.")
+ "executing cleanup in normal mode, which is with "
+ "no arguments.")
parser.add_argument('--delete-tempest-conf-objects',
- action="store_false",
- dest='preserve_tempest_conf_objects',
+ action="store_true",
+ dest='delete_tempest_conf_objects',
default=False,
- help="Delete the tempest and "
+ help="Force deletion of the tempest and "
"alternate tempest users and tenants.")
parser.add_argument('--dry-run', action="store_true",
dest='dry_run', default=False,
@@ -291,6 +283,7 @@
def main():
+ cleanup_service.init_conf()
cleanup = Cleanup()
cleanup.run()
LOG.info('Cleanup finished!')
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 8adfbef..67843e6 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
# Copyright 2014 Dell Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -12,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest import clients
from tempest import config
from tempest.openstack.common import log as logging
from tempest import test
@@ -19,13 +22,14 @@
LOG = logging.getLogger(__name__)
CONF = config.CONF
-CONF_USERS = None
-CONF_TENANTS = None
-CONF_PUB_NETWORK = None
-CONF_PRIV_NETWORK_NAME = None
-CONF_PUB_ROUTER = None
CONF_FLAVORS = None
CONF_IMAGES = None
+CONF_NETWORKS = []
+CONF_PRIV_NETWORK_NAME = None
+CONF_PUB_NETWORK = None
+CONF_PUB_ROUTER = None
+CONF_TENANTS = None
+CONF_USERS = None
IS_CEILOMETER = None
IS_CINDER = None
@@ -36,14 +40,15 @@
def init_conf():
- global CONF_USERS
- global CONF_TENANTS
- global CONF_PUB_NETWORK
- global CONF_PRIV_NETWORK_NAME
- global CONF_PUB_ROUTER
global CONF_FLAVORS
global CONF_IMAGES
-
+ global CONF_NETWORKS
+ global CONF_PRIV_NETWORK
+ global CONF_PRIV_NETWORK_NAME
+ global CONF_PUB_NETWORK
+ global CONF_PUB_ROUTER
+ global CONF_TENANTS
+ global CONF_USERS
global IS_CEILOMETER
global IS_CINDER
global IS_GLANCE
@@ -51,17 +56,6 @@
global IS_NEUTRON
global IS_NOVA
- CONF_USERS = [CONF.identity.admin_username, CONF.identity.username,
- CONF.identity.alt_username]
- CONF_TENANTS = [CONF.identity.admin_tenant_name,
- CONF.identity.tenant_name,
- CONF.identity.alt_tenant_name]
- CONF_PUB_NETWORK = CONF.network.public_network_id
- CONF_PRIV_NETWORK_NAME = CONF.compute.fixed_network_name
- CONF_PUB_ROUTER = CONF.network.public_router_id
- CONF_FLAVORS = [CONF.compute.flavor_ref, CONF.compute.flavor_ref_alt]
- CONF_IMAGES = [CONF.compute.image_ref, CONF.compute.image_ref_alt]
-
IS_CEILOMETER = CONF.service_available.ceilometer
IS_CINDER = CONF.service_available.cinder
IS_GLANCE = CONF.service_available.glance
@@ -69,6 +63,38 @@
IS_NEUTRON = CONF.service_available.neutron
IS_NOVA = CONF.service_available.nova
+ CONF_FLAVORS = [CONF.compute.flavor_ref, CONF.compute.flavor_ref_alt]
+ CONF_IMAGES = [CONF.compute.image_ref, CONF.compute.image_ref_alt]
+ CONF_PRIV_NETWORK_NAME = CONF.compute.fixed_network_name
+ CONF_PUB_NETWORK = CONF.network.public_network_id
+ CONF_PUB_ROUTER = CONF.network.public_router_id
+ CONF_TENANTS = [CONF.identity.admin_tenant_name,
+ CONF.identity.tenant_name,
+ CONF.identity.alt_tenant_name]
+ CONF_USERS = [CONF.identity.admin_username, CONF.identity.username,
+ CONF.identity.alt_username]
+
+ if IS_NEUTRON:
+ CONF_PRIV_NETWORK = _get_priv_net_id(CONF.compute.fixed_network_name,
+ CONF.identity.tenant_name)
+ CONF_NETWORKS = [CONF_PUB_NETWORK, CONF_PRIV_NETWORK]
+
+
+def _get_priv_net_id(prv_net_name, tenant_name):
+ am = clients.AdminManager()
+ net_cl = am.network_client
+ id_cl = am.identity_client
+
+ _, networks = net_cl.list_networks()
+ tenant = id_cl.get_tenant_by_name(tenant_name)
+ t_id = tenant['id']
+ n_id = None
+ for net in networks['networks']:
+ if (net['tenant_id'] == t_id and net['name'] == prv_net_name):
+ n_id = net['id']
+ break
+ return n_id
+
class BaseService(object):
def __init__(self, kwargs):
@@ -84,11 +110,8 @@
or 'tenant_id' not in item_list[0]):
return item_list
- _filtered_list = []
- for item in item_list:
- if item['tenant_id'] == self.tenant_id:
- _filtered_list.append(item)
- return _filtered_list
+ return [item for item in item_list
+ if item['tenant_id'] == self.tenant_id]
def list(self):
pass
@@ -325,6 +348,13 @@
super(NetworkService, self).__init__(kwargs)
self.client = manager.network_client
+ def _filter_by_conf_networks(self, item_list):
+ if not item_list or not all(('network_id' in i for i in item_list)):
+ return item_list
+
+ return [item for item in item_list if item['network_id']
+ not in CONF_NETWORKS]
+
def list(self):
client = self.client
_, networks = client.list_networks()
@@ -332,8 +362,7 @@
# filter out networks declared in tempest.conf
if self.is_preserve:
networks = [network for network in networks
- if (network['name'] != CONF_PRIV_NETWORK_NAME
- and network['id'] != CONF_PUB_NETWORK)]
+ if network['id'] not in CONF_NETWORKS]
LOG.debug("List count, %s Networks" % networks)
return networks
@@ -527,7 +556,7 @@
for port in ports:
subid = port['fixed_ips'][0]['subnet_id']
client.remove_router_interface_with_subnet_id(rid, subid)
- client.delete_router(rid)
+ client.delete_router(rid)
except Exception as e:
LOG.exception("Delete Router exception: %s" % e)
pass
@@ -694,6 +723,8 @@
_, ports = client.list_ports()
ports = ports['ports']
ports = self._filter_by_tenant_id(ports)
+ if self.is_preserve:
+ ports = self._filter_by_conf_networks(ports)
LOG.debug("List count, %s Ports" % len(ports))
return ports
@@ -719,6 +750,8 @@
_, subnets = client.list_subnets()
subnets = subnets['subnets']
subnets = self._filter_by_tenant_id(subnets)
+ if self.is_preserve:
+ subnets = self._filter_by_conf_networks(subnets)
LOG.debug("List count, %s Subnets" % len(subnets))
return subnets
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 228e47c..1ce1e39 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -203,9 +203,8 @@
if 'overlaps with another subnet' not in str(e):
raise
else:
- e = exceptions.BuildErrorException()
- e.message = 'Available CIDR for subnet creation could not be found'
- raise e
+ message = 'Available CIDR for subnet creation could not be found'
+ raise Exception(message)
return resp_body['subnet']
def _create_router(self, router_name, tenant_id):
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index c9448a7..4c3905c 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -52,6 +52,40 @@
return text
+class ResponseBody(dict):
+ """Class that wraps an http response and dict body into a single value.
+
+ Callers that receive this object will normally use it as a dict but
+ can extract the response if needed.
+ """
+
+ def __init__(self, response, body=None):
+ body_data = body or {}
+ self.update(body_data)
+ self.response = response
+
+ def __str__(self):
+ body = super.__str__(self)
+ return "response: %s\nBody: %s" % (self.response, body)
+
+
+class ResponseBodyList(list):
+ """Class that wraps an http response and list body into a single value.
+
+ Callers that receive this object will normally use it as a list but
+ can extract the response if needed.
+ """
+
+ def __init__(self, response, body=None):
+ body_data = body or []
+ self.extend(body_data)
+ self.response = response
+
+ def __str__(self):
+ body = super.__str__(self)
+ return "response: %s\nBody: %s" % (self.response, body)
+
+
class RestClient(object):
TYPE = "json"
@@ -89,8 +123,9 @@
'retry-after', 'server',
'vary', 'www-authenticate'))
dscv = CONF.identity.disable_ssl_certificate_validation
+ ca_certs = CONF.identity.ca_certificates_file
self.http_obj = http.ClosingHttp(
- disable_ssl_certificate_validation=dscv)
+ disable_ssl_certificate_validation=dscv, ca_certs=ca_certs)
def _get_type(self):
return self.TYPE
diff --git a/tempest/config.py b/tempest/config.py
index 616a476..03346c9 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -71,6 +71,10 @@
cfg.BoolOpt('disable_ssl_certificate_validation',
default=False,
help="Set to True if using self-signed SSL certificates."),
+ cfg.StrOpt('ca_certificates_file',
+ default=None,
+ help='Specify a CA bundle file to use in verifying a '
+ 'TLS (https) server certificate.'),
cfg.StrOpt('uri',
help="Full URI of the OpenStack Identity API (Keystone), v2"),
cfg.StrOpt('uri_v3',
@@ -143,9 +147,6 @@
cfg.BoolOpt('api_v3',
default=True,
help='Is the v3 identity API enabled'),
- cfg.BoolOpt('xml_api',
- default=False,
- help='If false, skip all identity api tests with xml'),
]
compute_group = cfg.OptGroup(name='compute',
@@ -280,9 +281,6 @@
cfg.BoolOpt('api_v3',
default=False,
help="If false, skip all nova v3 tests."),
- cfg.BoolOpt('xml_api_v2',
- default=True,
- help="If false skip all v2 api tests with xml"),
cfg.BoolOpt('disk_config',
default=True,
help="If false, skip disk config tests"),
@@ -491,9 +489,6 @@
"the extended IPv6 attributes ipv6_ra_mode "
"and ipv6_address_mode"
),
- cfg.BoolOpt('xml_api',
- default=False,
- help='If false, skip all network api tests with xml')
]
messaging_group = cfg.OptGroup(name='messaging',
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 522aa43..551a27d 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -297,7 +297,19 @@
return secgroup
- def get_remote_client(self, server_or_ip, username=None, private_key=None):
+ def get_remote_client(self, server_or_ip, username=None, private_key=None,
+ log_console_of_servers=None):
+ """Get a SSH client to a remote server
+
+ @param server_or_ip a server object as returned by Tempest compute
+ client or an IP address to connect to
+ @param username name of the Linux account on the remote server
+ @param private_key the SSH private key to use
+ @param log_console_of_servers a list of server objects. Each server
+ in the list will have its console printed in the logs in case the
+ SSH connection failed to be established
+ @return a RemoteClient object
+ """
if isinstance(server_or_ip, six.string_types):
ip = server_or_ip
else:
@@ -312,9 +324,13 @@
pkey=private_key)
try:
linux_client.validate_authentication()
- except exceptions.SSHTimeout:
- LOG.exception('ssh connection to %s failed' % ip)
+ except Exception:
+ LOG.exception('Initializing SSH connection to %s failed' % ip)
debug.log_net_debug()
+ # If we don't explicitely set for which servers we want to
+ # log the console output then all the servers will be logged.
+ # See the definition of _log_console_output()
+ self._log_console_output(log_console_of_servers)
raise
return linux_client
diff --git a/tempest/scenario/orchestration/test_server_cfn_init.py b/tempest/scenario/orchestration/test_server_cfn_init.py
index ddfabe4..f09f00c 100644
--- a/tempest/scenario/orchestration/test_server_cfn_init.py
+++ b/tempest/scenario/orchestration/test_server_cfn_init.py
@@ -119,14 +119,8 @@
if self.keypair:
# Check that the user can authenticate with the generated
# keypair
- try:
- linux_client = self.get_remote_client(
- server_ip, username='ec2-user')
- linux_client.validate_authentication()
- except (exceptions.ServerUnreachable,
- exceptions.SSHTimeout) as e:
- self._log_console_output(servers=[server])
- raise e
+ self.get_remote_client(server_ip, username='ec2-user',
+ log_console_of_servers=[server])
@test.attr(type='slow')
@test.skip_because(bug='1374175')
diff --git a/tempest/scenario/test_dashboard_basic_ops.py b/tempest/scenario/test_dashboard_basic_ops.py
index 875a1d9..2014293 100644
--- a/tempest/scenario/test_dashboard_basic_ops.py
+++ b/tempest/scenario/test_dashboard_basic_ops.py
@@ -12,11 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
+import HTMLParser
import urllib
import urllib2
-from lxml import html
-
from tempest import config
from tempest.scenario import manager
from tempest import test
@@ -24,6 +23,30 @@
CONF = config.CONF
+class HorizonHTMLParser(HTMLParser.HTMLParser):
+ csrf_token = None
+ region = None
+
+ def _find_name(self, attrs, name):
+ for attrpair in attrs:
+ if attrpair[0] == 'name' and attrpair[1] == name:
+ return True
+ return False
+
+ def _find_value(self, attrs):
+ for attrpair in attrs:
+ if attrpair[0] == 'value':
+ return attrpair[1]
+ return None
+
+ def handle_starttag(self, tag, attrs):
+ if tag == 'input':
+ if self._find_name(attrs, 'csrfmiddlewaretoken'):
+ self.csrf_token = self._find_value(attrs)
+ if self._find_name(attrs, 'region'):
+ self.region = self._find_value(attrs)
+
+
class TestDashboardBasicOps(manager.ScenarioTest):
"""
@@ -42,17 +65,15 @@
def check_login_page(self):
response = urllib2.urlopen(CONF.dashboard.dashboard_url)
- self.assertIn("<h3>Log In</h3>", response.read())
+ self.assertIn("Log In", response.read())
def user_login(self):
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
response = self.opener.open(CONF.dashboard.dashboard_url).read()
# Grab the CSRF token and default region
- csrf_token = html.fromstring(response).xpath(
- '//input[@name="csrfmiddlewaretoken"]/@value')[0]
- region = html.fromstring(response).xpath(
- '//input[@name="region"]/@value')[0]
+ parser = HorizonHTMLParser()
+ parser.feed(response)
# Prepare login form request
req = urllib2.Request(CONF.dashboard.login_url)
@@ -60,8 +81,8 @@
req.add_header('Referer', CONF.dashboard.dashboard_url)
params = {'username': CONF.identity.username,
'password': CONF.identity.password,
- 'region': region,
- 'csrfmiddlewaretoken': csrf_token}
+ 'region': parser.region,
+ 'csrfmiddlewaretoken': parser.csrf_token}
self.opener.open(req, urllib.urlencode(params))
def check_home_page(self):
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 91b95a8..60fd2bd 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -12,6 +12,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
+from tempest_lib import exceptions
from tempest.common.utils import data_utils
from tempest import config
@@ -44,6 +45,22 @@
"instances")
cls.set_network_resources()
super(TestLargeOpsScenario, cls).resource_setup()
+ # list of cleanup calls to be executed in reverse order
+ cls._cleanup_resources = []
+
+ @classmethod
+ def resource_cleanup(cls):
+ while cls._cleanup_resources:
+ function, args, kwargs = cls._cleanup_resources.pop(-1)
+ try:
+ function(*args, **kwargs)
+ except exceptions.NotFound:
+ pass
+ super(TestLargeOpsScenario, cls).resource_cleanup()
+
+ @classmethod
+ def addCleanupClass(cls, function, *arguments, **keywordArguments):
+ cls._cleanup_resources.append((function, arguments, keywordArguments))
def _wait_for_server_status(self, status):
for server in self.servers:
@@ -54,13 +71,20 @@
def nova_boot(self):
name = data_utils.rand_name('scenario-server-')
flavor_id = CONF.compute.flavor_ref
- secgroup = self._create_security_group()
+ # Explicitly create secgroup to avoid cleanup at the end of testcases.
+ # Since no traffic is tested, we don't need to actually add rules to
+ # secgroup
+ _, secgroup = self.security_groups_client.create_security_group(
+ 'secgroup-%s' % name, 'secgroup-desc-%s' % name)
+ self.addCleanupClass(self.security_groups_client.delete_security_group,
+ secgroup['id'])
+
self.servers_client.create_server(
name,
self.image,
flavor_id,
min_count=CONF.scenario.large_ops_number,
- security_groups=[secgroup])
+ security_groups=[{'name': secgroup['name']}])
# needed because of bug 1199788
params = {'name': name}
_, server_list = self.servers_client.list_servers(params)
@@ -68,15 +92,12 @@
for server in self.servers:
# after deleting all servers - wait for all servers to clear
# before cleanup continues
- self.addCleanup(self.servers_client.wait_for_server_termination,
- server['id'])
+ self.addCleanupClass(self.servers_client.
+ wait_for_server_termination,
+ server['id'])
for server in self.servers:
- self.addCleanup_with_wait(
- waiter_callable=(self.servers_client.
- wait_for_server_termination),
- thing_id=server['id'], thing_id_param='server_id',
- cleanup_callable=self.delete_wrapper,
- cleanup_args=[self.servers_client.delete_server, server['id']])
+ self.addCleanupClass(self.servers_client.delete_server,
+ server['id'])
self._wait_for_server_status('ACTIVE')
def _large_ops_scenario(self):
diff --git a/tempest/scenario/test_load_balancer_basic.py b/tempest/scenario/test_load_balancer_basic.py
index d061406..4b2dacd 100644
--- a/tempest/scenario/test_load_balancer_basic.py
+++ b/tempest/scenario/test_load_balancer_basic.py
@@ -123,10 +123,10 @@
def _create_server(self, name):
keypair = self.create_keypair()
- security_groups = [self.security_group]
+ security_groups = [{'name': self.security_group['name']}]
create_kwargs = {
- 'nics': [
- {'net-id': self.network['id']},
+ 'networks': [
+ {'uuid': self.network['id']},
],
'key_name': keypair['name'],
'security_groups': security_groups,
@@ -170,9 +170,9 @@
private_key=private_key)
# Write a backend's response into a file
- resp = """echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n""" \
- """Connection: close\r\nContent-Type: text/html; """ \
- """charset=UTF-8\r\n\r\n%s"; cat >/dev/null"""
+ resp = ('echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n'
+ 'Connection: close\r\nContent-Type: text/html; '
+ 'charset=UTF-8\r\n\r\n%s"; cat >/dev/null')
with tempfile.NamedTemporaryFile() as script:
script.write(resp % server_name)
@@ -186,8 +186,9 @@
username, key.name)
# Start netcat
- start_server = """sudo nc -ll -p %(port)s -e sh """ \
- """/tmp/%(script)s &"""
+ start_server = ('while true; do '
+ 'sudo nc -l -p %(port)s -e sh /tmp/%(script)s; '
+ 'done &')
cmd = start_server % {'port': self.port1,
'script': 'script1'}
ssh_client.exec_command(cmd)
@@ -215,6 +216,8 @@
return False
except IOError:
return False
+ except urllib2.HTTPError:
+ return False
timeout = config.compute.ping_timeout
start = time.time()
while not try_connect(check_ip, port):
@@ -297,8 +300,13 @@
def _send_requests(self, vip_ip, servers):
counters = dict.fromkeys(servers, 0)
for i in range(self.num):
- server = urllib2.urlopen("http://{0}/".format(vip_ip)).read()
- counters[server] += 1
+ try:
+ server = urllib2.urlopen("http://{0}/".format(vip_ip)).read()
+ counters[server] += 1
+ # HTTP exception means fail of server, so don't increase counter
+ # of success and continue connection tries
+ except urllib2.HTTPError:
+ continue
# Assert that each member of the pool gets balanced at least once
for member, counter in counters.iteritems():
self.assertGreater(counter, 0, 'Member %s never balanced' % member)
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 59af6b3..16a65c9 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -14,7 +14,6 @@
# under the License.
from tempest.common import custom_matchers
-from tempest.common import debug
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
@@ -89,17 +88,6 @@
self.servers_client.reboot(self.server['id'], 'SOFT')
self._wait_for_server_status('ACTIVE')
- def ssh_to_server(self):
- try:
- self.linux_client = self.get_remote_client(self.floating_ip['ip'])
- except Exception as e:
- LOG.exception('ssh to server failed')
- self._log_console_output()
- # network debug is called as part of ssh init
- if not isinstance(e, test.exceptions.SSHTimeout):
- debug.log_net_debug()
- raise
-
def check_partitions(self):
# NOTE(andreaf) The device name may be different on different guest OS
partitions = self.linux_client.get_partitions()
@@ -147,7 +135,9 @@
self.floating_ip = self.create_floating_ip(self.server)
self.create_and_add_security_group()
- self.ssh_to_server()
+
+ self.linux_client = self.get_remote_client(self.floating_ip['ip'])
self.nova_reboot()
- self.ssh_to_server()
+
+ self.linux_client = self.get_remote_client(self.floating_ip['ip'])
self.check_partitions()
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index ad7f18c..61c710e 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -65,7 +65,7 @@
{'uuid': network.id},
],
'key_name': self.keypair['name'],
- 'security_groups': [security_group],
+ 'security_groups': [{'name': security_group['name']}],
}
server_name = data_utils.rand_name('server-smoke')
self.server = self.create_server(name=server_name,
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index bac955d..9618124 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -146,7 +146,7 @@
def _create_server(self, name, network):
keypair = self.create_keypair()
self.keypairs[keypair['name']] = keypair
- security_groups = [self.security_group]
+ security_groups = [{'name': self.security_group['name']}]
create_kwargs = {
'networks': [
{'uuid': network.id},
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 747850b..7e6805c 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -230,12 +230,13 @@
self._set_compute_context(tenant)
if security_groups is None:
security_groups = [tenant.security_groups['default']]
+ security_groups_names = [{'name': s['name']} for s in security_groups]
create_kwargs = {
'networks': [
{'uuid': tenant.network.id},
],
'key_name': tenant.keypair['name'],
- 'security_groups': security_groups,
+ 'security_groups': security_groups_names,
'tenant_id': tenant.creds.tenant_id
}
server = self.create_server(name=name, create_kwargs=create_kwargs)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index eb636f7..23743c5 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -68,7 +68,7 @@
def boot_instance(self):
# Create server with image and flavor from input scenario
- security_groups = [self.security_group]
+ security_groups = [{'name': self.security_group['name']}]
create_kwargs = {
'key_name': self.keypair['name'],
'security_groups': security_groups
@@ -88,15 +88,10 @@
self.floating_ips_client.associate_floating_ip_to_server(
floating_ip['ip'], self.instance['id'])
# Check ssh
- try:
- self.get_remote_client(
- server_or_ip=floating_ip['ip'],
- username=self.image_utils.ssh_user(self.image_ref),
- private_key=self.keypair['private_key'])
- except Exception:
- LOG.exception('ssh to server failed')
- self._log_console_output()
- raise
+ self.get_remote_client(
+ server_or_ip=floating_ip['ip'],
+ username=self.image_utils.ssh_user(self.image_ref),
+ private_key=self.keypair['private_key'])
@test.services('compute', 'network')
def test_server_basicops(self):
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
new file mode 100644
index 0000000..8882177
--- /dev/null
+++ b/tempest/scenario/test_shelve_instance.py
@@ -0,0 +1,97 @@
+# Copyright 2014 Scality
+# 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 testtools
+
+from tempest import config
+from tempest.openstack.common import log
+from tempest.scenario import manager
+from tempest import test
+
+CONF = config.CONF
+
+LOG = log.getLogger(__name__)
+
+
+class TestShelveInstance(manager.ScenarioTest):
+ """
+ This test shelves then unshelves a Nova instance
+ The following is the scenario outline:
+ * boot a instance and create a timestamp file in it
+ * shelve the instance
+ * unshelve the instance
+ * check the existence of the timestamp file in the unshelved instance
+
+ """
+
+ def _write_timestamp(self, server_or_ip):
+ ssh_client = self.get_remote_client(server_or_ip)
+ ssh_client.exec_command('date > /tmp/timestamp; sync')
+ self.timestamp = ssh_client.exec_command('cat /tmp/timestamp')
+
+ def _check_timestamp(self, server_or_ip):
+ ssh_client = self.get_remote_client(server_or_ip)
+ got_timestamp = ssh_client.exec_command('cat /tmp/timestamp')
+ self.assertEqual(self.timestamp, got_timestamp)
+
+ def _shelve_then_unshelve_server(self, server):
+ self.servers_client.shelve_server(server['id'])
+ offload_time = CONF.compute.shelved_offload_time
+ if offload_time >= 0:
+ self.servers_client.wait_for_server_status(
+ server['id'], 'SHELVED_OFFLOADED', extra_timeout=offload_time)
+ else:
+ self.servers_client.wait_for_server_status(server['id'], 'SHELVED')
+ self.servers_client.shelve_offload_server(server['id'])
+ self.servers_client.wait_for_server_status(server['id'],
+ 'SHELVED_OFFLOADED')
+ self.servers_client.unshelve_server(server['id'])
+ self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+
+ @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
+ 'Shelve is not available.')
+ @test.services('compute', 'network', 'image')
+ def test_shelve_instance(self):
+ self.keypair = self.create_keypair()
+
+ self.security_group = self._create_security_group()
+ security_groups = [{'name': self.security_group['name']}]
+
+ create_kwargs = {
+ 'key_name': self.keypair['name'],
+ 'security_groups': security_groups
+ }
+ server = self.create_server(image=CONF.compute.image_ref,
+ create_kwargs=create_kwargs)
+
+ if CONF.compute.use_floatingip_for_ssh:
+ _, floating_ip = self.floating_ips_client.create_floating_ip()
+ self.addCleanup(self.delete_wrapper,
+ self.floating_ips_client.delete_floating_ip,
+ floating_ip['id'])
+ self.floating_ips_client.associate_floating_ip_to_server(
+ floating_ip['ip'], server['id'])
+ self._write_timestamp(floating_ip['ip'])
+ else:
+ self._write_timestamp(server)
+
+ # Prevent bug #1257594 from coming back
+ # Unshelve used to boot the instance with the original image, not
+ # with the instance snapshot
+ self._shelve_then_unshelve_server(server)
+ if CONF.compute.use_floatingip_for_ssh:
+ self._check_timestamp(floating_ip['ip'])
+ else:
+ self._check_timestamp(server)
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 9a99da4..5cb7c99 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -37,7 +37,7 @@
"""
def _boot_image(self, image_id):
- security_groups = [self.security_group]
+ security_groups = [{'name': self.security_group['name']}]
create_kwargs = {
'key_name': self.keypair['name'],
'security_groups': security_groups
@@ -47,21 +47,13 @@
def _add_keypair(self):
self.keypair = self.create_keypair()
- def _ssh_to_server(self, server_or_ip):
- try:
- return self.get_remote_client(server_or_ip)
- except Exception:
- LOG.exception('Initializing SSH connection failed')
- self._log_console_output()
- raise
-
def _write_timestamp(self, server_or_ip):
- ssh_client = self._ssh_to_server(server_or_ip)
+ ssh_client = self.get_remote_client(server_or_ip)
ssh_client.exec_command('date > /tmp/timestamp; sync')
self.timestamp = ssh_client.exec_command('cat /tmp/timestamp')
def _check_timestamp(self, server_or_ip):
- ssh_client = self._ssh_to_server(server_or_ip)
+ ssh_client = self.get_remote_client(server_or_ip)
got_timestamp = ssh_client.exec_command('cat /tmp/timestamp')
self.assertEqual(self.timestamp, got_timestamp)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index ee2c737..cfc1d37 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -61,7 +61,7 @@
status)
def _boot_image(self, image_id):
- security_groups = [self.security_group]
+ security_groups = [{'name': self.security_group['name']}]
create_kwargs = {
'key_name': self.keypair['name'],
'security_groups': security_groups
diff --git a/tempest/scenario/test_swift_basic_ops.py b/tempest/scenario/test_swift_basic_ops.py
index fcb9505..312fbc6 100644
--- a/tempest/scenario/test_swift_basic_ops.py
+++ b/tempest/scenario/test_swift_basic_ops.py
@@ -65,7 +65,10 @@
obj_name, _ = self.upload_object_to_container(container_name)
obj_url = '%s/%s/%s' % (self.object_client.base_url,
container_name, obj_name)
- http_client = http.ClosingHttp()
+ dscv = CONF.identity.disable_ssl_certificate_validation
+ ca_certs = CONF.identity.ca_certificates_file
+ http_client = http.ClosingHttp(
+ disable_ssl_certificate_validation=dscv, ca_certs=ca_certs)
resp, _ = http_client.request(obj_url, 'GET')
self.assertEqual(resp.status, 401)
self.change_container_acl(container_name, '.r:*')
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index dd115e7..c584a6e 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -110,14 +110,8 @@
network_name_for_ssh = CONF.compute.network_for_ssh
ip = server.networks[network_name_for_ssh][0]
- try:
- return self.get_remote_client(
- ip,
- private_key=keypair['private_key'])
- except Exception:
- LOG.exception('ssh to server failed')
- self._log_console_output(servers=[server])
- raise
+ return self.get_remote_client(ip, private_key=keypair['private_key'],
+ log_console_of_servers=[server])
def _get_content(self, ssh_client):
return ssh_client.exec_command('cat /tmp/text')
diff --git a/tempest/services/botoclients.py b/tempest/services/botoclients.py
index 7af904b..f581e89 100644
--- a/tempest/services/botoclients.py
+++ b/tempest/services/botoclients.py
@@ -38,6 +38,7 @@
# FIXME(andreaf) replace credentials and auth_url with auth_provider
insecure_ssl = CONF.identity.disable_ssl_certificate_validation
+ ca_cert = CONF.identity.ca_certificates_file
self.connection_timeout = str(CONF.boto.http_socket_timeout)
self.num_retries = str(CONF.boto.num_retries)
@@ -46,7 +47,8 @@
"password": password,
"auth_url": auth_url,
"tenant_name": tenant_name,
- "insecure": insecure_ssl}
+ "insecure": insecure_ssl,
+ "cacert": ca_cert}
def _keystone_aws_get(self):
# FIXME(andreaf) Move EC2 credentials to AuthProvider
diff --git a/tempest/services/compute/json/volumes_extensions_client.py b/tempest/services/compute/json/volumes_extensions_client.py
index 309dc5b..b23b20b 100644
--- a/tempest/services/compute/json/volumes_extensions_client.py
+++ b/tempest/services/compute/json/volumes_extensions_client.py
@@ -73,10 +73,9 @@
metadata: A dictionary of values to be used as metadata.
"""
post_body = {
- 'size': size,
- 'display_name': kwargs.get('display_name'),
- 'metadata': kwargs.get('metadata'),
+ 'size': size
}
+ post_body.update(kwargs)
post_body = json.dumps({'volume': post_body})
resp, body = self.post('os-volumes', post_body)
@@ -93,7 +92,6 @@
def wait_for_volume_status(self, volume_id, status):
"""Waits for a Volume to reach a given status."""
resp, body = self.get_volume(volume_id)
- volume_name = body['displayName']
volume_status = body['status']
start = int(time.time())
@@ -107,7 +105,7 @@
if int(time.time()) - start >= self.build_timeout:
message = ('Volume %s failed to reach %s status within '
'the required time (%s s).' %
- (volume_name, status, self.build_timeout))
+ (volume_id, status, self.build_timeout))
raise exceptions.TimeoutException(message)
def is_resource_deleted(self, id):
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index 5ad416c..6ac4901 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -36,8 +36,10 @@
"""Creates a user."""
en = kwargs.get('enabled', True)
description = kwargs.get('description', None)
+ default_project_id = kwargs.get('default_project_id')
post_body = {
'project_id': project_id,
+ 'default_project_id': default_project_id,
'description': description,
'domain_id': domain_id,
'email': email,
@@ -57,6 +59,11 @@
email = kwargs.get('email', body['email'])
en = kwargs.get('enabled', body['enabled'])
project_id = kwargs.get('project_id', body['project_id'])
+ if 'default_project_id' in body.keys():
+ default_project_id = kwargs.get('default_project_id',
+ body['default_project_id'])
+ else:
+ default_project_id = kwargs.get('default_project_id')
description = kwargs.get('description', body['description'])
domain_id = kwargs.get('domain_id', body['domain_id'])
post_body = {
@@ -64,6 +71,7 @@
'email': email,
'enabled': en,
'project_id': project_id,
+ 'default_project_id': default_project_id,
'id': user_id,
'domain_id': domain_id,
'description': description
diff --git a/tempest/services/identity/v3/json/policy_client.py b/tempest/services/identity/v3/json/policy_client.py
index e093260..579243c 100644
--- a/tempest/services/identity/v3/json/policy_client.py
+++ b/tempest/services/identity/v3/json/policy_client.py
@@ -39,14 +39,14 @@
resp, body = self.post('policies', post_body)
self.expected_success(201, resp.status)
body = json.loads(body)
- return resp, body['policy']
+ return rest_client.ResponseBody(resp, body['policy'])
def list_policies(self):
"""Lists the policies."""
resp, body = self.get('policies')
self.expected_success(200, resp.status)
body = json.loads(body)
- return resp, body['policies']
+ return rest_client.ResponseBodyList(resp, body['policies'])
def get_policy(self, policy_id):
"""Lists out the given policy."""
@@ -54,7 +54,7 @@
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
- return resp, body['policy']
+ return rest_client.ResponseBody(resp, body['policy'])
def update_policy(self, policy_id, **kwargs):
"""Updates a policy."""
@@ -67,11 +67,11 @@
resp, body = self.patch(url, post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
- return resp, body['policy']
+ return rest_client.ResponseBody(resp, body['policy'])
def delete_policy(self, policy_id):
"""Deletes the policy."""
url = "policies/%s" % policy_id
resp, body = self.delete(url)
self.expected_success(204, resp.status)
- return resp, body
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index d0d32e5..d60c9d9 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -106,9 +106,10 @@
def _get_http(self):
dscv = CONF.identity.disable_ssl_certificate_validation
+ ca_certs = CONF.identity.ca_certificates_file
return glance_http.HTTPClient(auth_provider=self.auth_provider,
filters=self.filters,
- insecure=dscv)
+ insecure=dscv, ca_certs=ca_certs)
def _create_with_data(self, headers, data):
resp, body_iter = self.http.raw_request('POST', '/v1/images',
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 4865073..7421508 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -35,9 +35,10 @@
def _get_http(self):
dscv = CONF.identity.disable_ssl_certificate_validation
+ ca_certs = CONF.identity.ca_certificates_file
return glance_http.HTTPClient(auth_provider=self.auth_provider,
filters=self.filters,
- insecure=dscv)
+ insecure=dscv, ca_certs=ca_certs)
def _validate_schema(self, body, type='image'):
if type in ['image', 'images']:
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 4417e3b..a2044ef 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -181,7 +181,11 @@
def request(self, method, url, extra_headers=False, headers=None,
body=None):
"""A simple HTTP request interface."""
- self.http_obj = http.ClosingHttp()
+ dscv = CONF.identity.disable_ssl_certificate_validation
+ ca_certs = CONF.identity.ca_certificates_file
+ self.http_obj = http.ClosingHttp(
+ disable_ssl_certificate_validation=dscv,
+ ca_certs=ca_certs)
if headers is None:
headers = {}
elif extra_headers:
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 2231407..7a69fa8 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -197,8 +197,10 @@
body=None):
"""A simple HTTP request interface."""
dscv = CONF.identity.disable_ssl_certificate_validation
+ ca_certs = CONF.identity.ca_certificates_file
self.http_obj = http.ClosingHttp(
- disable_ssl_certificate_validation=dscv)
+ disable_ssl_certificate_validation=dscv,
+ ca_certs=ca_certs)
if headers is None:
headers = {}
elif extra_headers:
diff --git a/tempest/services/volume/json/admin/volume_quotas_client.py b/tempest/services/volume/json/admin/volume_quotas_client.py
index 90790e3..5b49040 100644
--- a/tempest/services/volume/json/admin/volume_quotas_client.py
+++ b/tempest/services/volume/json/admin/volume_quotas_client.py
@@ -23,7 +23,7 @@
CONF = config.CONF
-class VolumeQuotasClientJSON(rest_client.RestClient):
+class BaseVolumeQuotasClientJSON(rest_client.RestClient):
"""
Client class to send CRUD Volume Quotas API requests to a Cinder endpoint
"""
@@ -31,7 +31,7 @@
TYPE = "json"
def __init__(self, auth_provider):
- super(VolumeQuotasClientJSON, self).__init__(auth_provider)
+ super(BaseVolumeQuotasClientJSON, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.volume.build_interval
@@ -85,3 +85,9 @@
"""Delete the tenant's quota set."""
resp, body = self.delete('os-quota-sets/%s' % tenant_id)
self.expected_success(200, resp.status)
+
+
+class VolumeQuotasClientJSON(BaseVolumeQuotasClientJSON):
+ """
+ Client class to send CRUD Volume Type API V1 requests to a Cinder endpoint
+ """
diff --git a/tempest/services/volume/json/admin/volume_services_client.py b/tempest/services/volume/json/admin/volume_services_client.py
index c9b8bcc..88c6db0 100644
--- a/tempest/services/volume/json/admin/volume_services_client.py
+++ b/tempest/services/volume/json/admin/volume_services_client.py
@@ -22,10 +22,10 @@
CONF = config.CONF
-class VolumesServicesClientJSON(rest_client.RestClient):
+class BaseVolumesServicesClientJSON(rest_client.RestClient):
def __init__(self, auth_provider):
- super(VolumesServicesClientJSON, self).__init__(auth_provider)
+ super(BaseVolumesServicesClientJSON, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
def list_services(self, params=None):
@@ -37,3 +37,7 @@
body = json.loads(body)
self.expected_success(200, resp.status)
return resp, body['services']
+
+
+class VolumesServicesClientJSON(BaseVolumesServicesClientJSON):
+ """Volume V1 volume services client"""
diff --git a/tempest/services/volume/v2/json/admin/volume_quotas_client.py b/tempest/services/volume/v2/json/admin/volume_quotas_client.py
new file mode 100644
index 0000000..64f4f33
--- /dev/null
+++ b/tempest/services/volume/v2/json/admin/volume_quotas_client.py
@@ -0,0 +1,27 @@
+# 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.
+
+from tempest.services.volume.json.admin import volume_quotas_client
+
+
+class VolumeQuotasV2Client(volume_quotas_client.BaseVolumeQuotasClientJSON):
+ """
+ Client class to send CRUD Volume V2 API requests to a Cinder endpoint
+ """
+
+ def __init__(self, auth_provider):
+ super(VolumeQuotasV2Client, self).__init__(auth_provider)
+
+ self.api_version = "v2"
diff --git a/tempest/services/volume/v2/json/admin/volume_services_client.py b/tempest/services/volume/v2/json/admin/volume_services_client.py
new file mode 100644
index 0000000..dc3c8ea
--- /dev/null
+++ b/tempest/services/volume/v2/json/admin/volume_services_client.py
@@ -0,0 +1,26 @@
+# 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.
+
+from tempest.services.volume.json.admin import volume_services_client as vs_cli
+
+
+class VolumesServicesV2ClientJSON(vs_cli.BaseVolumesServicesClientJSON):
+ """
+ Client class to send CRUD Volume V2 API requests to a Cinder endpoint
+ """
+
+ def __init__(self, auth_provider):
+ super(VolumesServicesV2ClientJSON, self).__init__(auth_provider)
+ self.api_version = "v2"
diff --git a/tempest/test.py b/tempest/test.py
index 14cf3bb..7db0376 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -414,12 +414,8 @@
else:
standard_tests, module, loader = args
for test in testtools.iterate_tests(standard_tests):
- schema_file = getattr(test, '_schema_file', None)
schema = getattr(test, '_schema', None)
- if schema_file is not None:
- setattr(test, 'scenarios',
- NegativeAutoTest.generate_scenario(schema_file))
- elif schema is not None:
+ if schema is not None:
setattr(test, 'scenarios',
NegativeAutoTest.generate_scenario(schema))
return testscenarios.load_tests_apply_scenarios(*args)