Merge "Enable some volumes v2 tests by sharing codes part2"
diff --git a/tempest/api/compute/admin/test_security_group_default_rules.py b/tempest/api/compute/admin/test_security_group_default_rules.py
new file mode 100644
index 0000000..07408a8
--- /dev/null
+++ b/tempest/api/compute/admin/test_security_group_default_rules.py
@@ -0,0 +1,127 @@
+# Copyright 2014 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import testtools
+
+from tempest.api.compute import base
+from tempest import config
+from tempest import exceptions
+from tempest import test
+
+CONF = config.CONF
+
+
+class SecurityGroupDefaultRulesTest(base.BaseV2ComputeAdminTest):
+
+ @classmethod
+ # TODO(GMann): Once Bug# 1311500 is fixed, these test can run
+ # for Neutron also.
+ @testtools.skipIf(CONF.service_available.neutron,
+ "Skip as this functionality is not yet "
+ "implemented in Neutron. Related Bug#1311500")
+ @test.safe_setup
+ def setUpClass(cls):
+ # A network and a subnet will be created for these tests
+ cls.set_network_resources(network=True, subnet=True)
+ super(SecurityGroupDefaultRulesTest, cls).setUpClass()
+ cls.adm_client = cls.os_adm.security_group_default_rules_client
+
+ def _create_security_group_default_rules(self, ip_protocol='tcp',
+ from_port=22, to_port=22,
+ cidr='10.10.0.0/24'):
+ # Create Security Group default rule
+ _, rule = self.adm_client.create_security_default_group_rule(
+ ip_protocol,
+ from_port,
+ to_port,
+ cidr=cidr)
+ self.assertEqual(ip_protocol, rule['ip_protocol'])
+ self.assertEqual(from_port, rule['from_port'])
+ self.assertEqual(to_port, rule['to_port'])
+ self.assertEqual(cidr, rule['ip_range']['cidr'])
+ return rule
+
+ @test.attr(type='smoke')
+ def test_create_delete_security_group_default_rules(self):
+ # Create and delete Security Group default rule
+ ip_protocols = {'tcp', 'udp', 'icmp'}
+ for ip_protocol in ip_protocols:
+ rule = self._create_security_group_default_rules(ip_protocol)
+ # Delete Security Group default rule
+ self.adm_client.delete_security_group_default_rule(rule['id'])
+ self.assertRaises(exceptions.NotFound,
+ self.adm_client.get_security_group_default_rule,
+ rule['id'])
+
+ @test.attr(type='smoke')
+ def test_create_security_group_default_rule_without_cidr(self):
+ ip_protocol = 'udp'
+ from_port = 80
+ to_port = 80
+ _, rule = self.adm_client.create_security_default_group_rule(
+ ip_protocol,
+ from_port,
+ to_port)
+ self.addCleanup(self.adm_client.delete_security_group_default_rule,
+ rule['id'])
+ self.assertNotEqual(0, rule['id'])
+ self.assertEqual('0.0.0.0/0', rule['ip_range']['cidr'])
+
+ @test.attr(type='smoke')
+ def test_create_security_group_default_rule_with_blank_cidr(self):
+ ip_protocol = 'icmp'
+ from_port = 10
+ to_port = 10
+ cidr = ''
+ _, rule = self.adm_client.create_security_default_group_rule(
+ ip_protocol,
+ from_port,
+ to_port,
+ cidr=cidr)
+ self.addCleanup(self.adm_client.delete_security_group_default_rule,
+ rule['id'])
+ self.assertNotEqual(0, rule['id'])
+ self.assertEqual('0.0.0.0/0', rule['ip_range']['cidr'])
+
+ @test.attr(type='smoke')
+ def test_security_group_default_rules_list(self):
+ ip_protocol = 'tcp'
+ from_port = 22
+ to_port = 22
+ cidr = '10.10.0.0/24'
+ rule = self._create_security_group_default_rules(ip_protocol,
+ from_port,
+ to_port,
+ cidr)
+ self.addCleanup(self.adm_client.delete_security_group_default_rule,
+ rule['id'])
+ _, rules = self.adm_client.list_security_group_default_rules()
+ self.assertNotEqual(0, len(rules))
+ self.assertIn(rule, rules)
+
+ @test.attr(type='smoke')
+ def test_default_security_group_default_rule_show(self):
+ ip_protocol = 'tcp'
+ from_port = 22
+ to_port = 22
+ cidr = '10.10.0.0/24'
+ rule = self._create_security_group_default_rules(ip_protocol,
+ from_port,
+ to_port,
+ cidr)
+ self.addCleanup(self.adm_client.delete_security_group_default_rule,
+ rule['id'])
+ _, fetched_rule = self.adm_client.get_security_group_default_rule(
+ rule['id'])
+ self.assertEqual(rule, fetched_rule)
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 70a9604..a3295eb 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -83,6 +83,8 @@
cls.hypervisor_client = cls.os.hypervisor_client
cls.certificates_client = cls.os.certificates_client
cls.migrations_client = cls.os.migrations_client
+ cls.security_group_default_rules_client = (
+ cls.os.security_group_default_rules_client)
elif cls._api_version == 3:
if not CONF.compute_feature_enabled.api_v3:
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
new file mode 100644
index 0000000..a3944e2
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -0,0 +1,73 @@
+# Copyright 2014 Hewlett-Packard Development Company, L.P
+# 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 test
+
+
+class ListProjectsTestJSON(base.BaseIdentityV3AdminTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ListProjectsTestJSON, cls).setUpClass()
+ cls.project_ids = list()
+ cls.data.setup_test_domain()
+ # Create project with domain
+ cls.p1_name = data_utils.rand_name('project')
+ _, cls.p1 = cls.client.create_project(
+ cls.p1_name, enabled=False, domain_id=cls.data.domain['id'])
+ cls.data.projects.append(cls.p1)
+ cls.project_ids.append(cls.p1['id'])
+ # Create default project
+ p2_name = data_utils.rand_name('project')
+ _, cls.p2 = cls.client.create_project(p2_name)
+ cls.data.projects.append(cls.p2)
+ cls.project_ids.append(cls.p2['id'])
+
+ @test.attr(type='gate')
+ def test_projects_list(self):
+ # List projects
+ resp, list_projects = self.client.list_projects()
+
+ for p in self.project_ids:
+ _, get_project = self.client.get_project(p)
+ self.assertIn(get_project, list_projects)
+
+ @test.attr(type='gate')
+ def test_list_projects_with_domains(self):
+ # List projects with domain
+ self._list_projects_with_params(
+ {'domain_id': self.data.domain['id']}, 'domain_id')
+
+ @test.attr(type='gate')
+ def test_list_projects_with_enabled(self):
+ # List the projects with enabled
+ self._list_projects_with_params({'enabled': False}, 'enabled')
+
+ @test.attr(type='gate')
+ def test_list_projects_with_name(self):
+ # List projects with name
+ self._list_projects_with_params({'name': self.p1_name}, 'name')
+
+ def _list_projects_with_params(self, params, key):
+ resp, body = self.client.list_projects(params)
+ self.assertIn(self.p1[key], map(lambda x: x[key], body))
+ self.assertNotIn(self.p2[key], map(lambda x: x[key], body))
+
+
+class ListProjectsTestXML(ListProjectsTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 77acd57..5890eab 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -13,35 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
-from six import moves
-
from tempest.api.identity import base
from tempest.common.utils import data_utils
-from tempest import exceptions
from tempest import test
class ProjectsTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
- def _delete_project(self, project_id):
- self.client.delete_project(project_id)
- self.assertRaises(
- exceptions.NotFound, self.client.get_project, project_id)
-
- @test.attr(type='gate')
- def test_project_list_delete(self):
- # Create several projects and delete them
- for _ in moves.xrange(3):
- _, project = self.client.create_project(
- data_utils.rand_name('project-new'))
- self.addCleanup(self._delete_project, project['id'])
-
- _, list_projects = self.client.list_projects()
-
- _, get_project = self.client.get_project(project['id'])
- self.assertIn(get_project, list_projects)
-
@test.attr(type='gate')
def test_project_create_with_description(self):
# Create project with a description
@@ -60,6 +39,21 @@
'to be set')
@test.attr(type='gate')
+ def test_project_create_with_domain(self):
+ # Create project with a domain
+ self.data.setup_test_domain()
+ project_name = data_utils.rand_name('project')
+ resp, project = self.client.create_project(
+ project_name, domain_id=self.data.domain['id'])
+ self.data.projects.append(project)
+ project_id = project['id']
+ self.assertEqual(project_name, project['name'])
+ self.assertEqual(self.data.domain['id'], project['domain_id'])
+ _, body = self.client.get_project(project_id)
+ self.assertEqual(project_name, body['name'])
+ self.assertEqual(self.data.domain['id'], body['domain_id'])
+
+ @test.attr(type='gate')
def test_project_create_enabled(self):
# Create a project that is enabled
project_name = data_utils.rand_name('project-')
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index e61b738..bd08614 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -48,6 +48,7 @@
self.assertRaises(exceptions.NotFound, self.client.get_token,
subject_token)
+ @test.skip_because(bug="1351026")
@test.attr(type='gate')
def test_rescope_token(self):
"""Rescope a token.
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 02d391b..c875b2f 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -100,7 +100,7 @@
cls.alt_tenant_id = cls.alt_img_cli.tenant_id
def _create_image(self):
- image_file = StringIO.StringIO('*' * 1024)
+ image_file = StringIO.StringIO(data_utils.random_bytes())
resp, image = self.create_image(container_format='bare',
disk_format='raw',
is_public=False,
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 8528e42..bf55b89 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -44,7 +44,7 @@
self.assertEqual(val, body.get('properties')[key])
# Now try uploading an image file
- image_file = StringIO.StringIO(('*' * 1024))
+ image_file = StringIO.StringIO(data_utils.random_bytes())
_, body = self.client.update_image(image_id, data=image_file)
self.assertIn('size', body)
self.assertEqual(1024, body.get('size'))
@@ -157,7 +157,7 @@
image. Note that the size of the new image is a random number between
1024 and 4096
"""
- image_file = StringIO.StringIO('*' * size)
+ image_file = StringIO.StringIO(data_utils.random_bytes(size))
name = 'New Standard Image %s' % name
_, image = cls.create_image(name=name,
container_format=container_format,
@@ -338,10 +338,9 @@
disk_format, size):
"""
Create a new standard image and return the ID of the newly-registered
- image. Note that the size of the new image is a random number between
- 1024 and 4096
+ image.
"""
- image_file = StringIO.StringIO('*' * size)
+ image_file = StringIO.StringIO(data_utils.random_bytes(size))
name = 'New Standard Image %s' % name
_, image = cls.create_image(name=name,
container_format=container_format,
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 4226815..a974ebb 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -52,7 +52,7 @@
self.assertEqual('queued', body['status'])
# Now try uploading an image file
- file_content = '*' * 1024
+ file_content = data_utils.random_bytes()
image_file = StringIO.StringIO(file_content)
self.client.store_image(image_id, image_file)
@@ -104,8 +104,7 @@
image_id = body['id']
# Now try uploading an image file
- file_content = '*' * 1024
- image_file = StringIO.StringIO(file_content)
+ image_file = StringIO.StringIO(data_utils.random_bytes())
self.client.store_image(image_id, image_file)
# Update Image
@@ -146,7 +145,8 @@
image. Note that the size of the new image is a random number between
1024 and 4096
"""
- image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
+ size = random.randint(1024, 4096)
+ image_file = StringIO.StringIO(data_utils.random_bytes(size))
name = data_utils.rand_name('image-')
_, body = cls.create_image(name=name,
container_format=container_format,
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 087b87a..d75339c 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -85,57 +85,58 @@
@classmethod
def tearDownClass(cls):
- # Clean up ipsec policies
- for ipsecpolicy in cls.ipsecpolicies:
- cls.client.delete_ipsecpolicy(ipsecpolicy['id'])
- # Clean up firewall policies
- for fw_policy in cls.fw_policies:
- cls.client.delete_firewall_policy(fw_policy['id'])
- # Clean up firewall rules
- for fw_rule in cls.fw_rules:
- cls.client.delete_firewall_rule(fw_rule['id'])
- # Clean up ike policies
- for ikepolicy in cls.ikepolicies:
- cls.client.delete_ikepolicy(ikepolicy['id'])
- # Clean up vpn services
- for vpnservice in cls.vpnservices:
- cls.client.delete_vpnservice(vpnservice['id'])
- # Clean up floating IPs
- for floating_ip in cls.floating_ips:
- cls.client.delete_floatingip(floating_ip['id'])
- # Clean up routers
- for router in cls.routers:
- cls.delete_router(router)
+ if CONF.service_available.neutron:
+ # Clean up ipsec policies
+ for ipsecpolicy in cls.ipsecpolicies:
+ cls.client.delete_ipsecpolicy(ipsecpolicy['id'])
+ # Clean up firewall policies
+ for fw_policy in cls.fw_policies:
+ cls.client.delete_firewall_policy(fw_policy['id'])
+ # Clean up firewall rules
+ for fw_rule in cls.fw_rules:
+ cls.client.delete_firewall_rule(fw_rule['id'])
+ # Clean up ike policies
+ for ikepolicy in cls.ikepolicies:
+ cls.client.delete_ikepolicy(ikepolicy['id'])
+ # Clean up vpn services
+ for vpnservice in cls.vpnservices:
+ cls.client.delete_vpnservice(vpnservice['id'])
+ # Clean up floating IPs
+ for floating_ip in cls.floating_ips:
+ cls.client.delete_floatingip(floating_ip['id'])
+ # Clean up routers
+ for router in cls.routers:
+ cls.delete_router(router)
- # Clean up health monitors
- for health_monitor in cls.health_monitors:
- cls.client.delete_health_monitor(health_monitor['id'])
- # Clean up members
- for member in cls.members:
- cls.client.delete_member(member['id'])
- # Clean up vips
- for vip in cls.vips:
- cls.client.delete_vip(vip['id'])
- # Clean up pools
- for pool in cls.pools:
- cls.client.delete_pool(pool['id'])
- # Clean up metering label rules
- for metering_label_rule in cls.metering_label_rules:
- cls.admin_client.delete_metering_label_rule(
- metering_label_rule['id'])
- # Clean up metering labels
- for metering_label in cls.metering_labels:
- cls.admin_client.delete_metering_label(metering_label['id'])
- # Clean up ports
- for port in cls.ports:
- cls.client.delete_port(port['id'])
- # Clean up subnets
- for subnet in cls.subnets:
- cls.client.delete_subnet(subnet['id'])
- # Clean up networks
- for network in cls.networks:
- cls.client.delete_network(network['id'])
- cls.clear_isolated_creds()
+ # Clean up health monitors
+ for health_monitor in cls.health_monitors:
+ cls.client.delete_health_monitor(health_monitor['id'])
+ # Clean up members
+ for member in cls.members:
+ cls.client.delete_member(member['id'])
+ # Clean up vips
+ for vip in cls.vips:
+ cls.client.delete_vip(vip['id'])
+ # Clean up pools
+ for pool in cls.pools:
+ cls.client.delete_pool(pool['id'])
+ # Clean up metering label rules
+ for metering_label_rule in cls.metering_label_rules:
+ cls.admin_client.delete_metering_label_rule(
+ metering_label_rule['id'])
+ # Clean up metering labels
+ for metering_label in cls.metering_labels:
+ cls.admin_client.delete_metering_label(metering_label['id'])
+ # Clean up ports
+ for port in cls.ports:
+ cls.client.delete_port(port['id'])
+ # Clean up subnets
+ for subnet in cls.subnets:
+ cls.client.delete_subnet(subnet['id'])
+ # Clean up networks
+ for network in cls.networks:
+ cls.client.delete_network(network['id'])
+ cls.clear_isolated_creds()
super(BaseNetworkTest, cls).tearDownClass()
@classmethod
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 1ef9aa1..b21aa44 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -17,7 +17,7 @@
import hashlib
import random
import re
-from six import moves
+import six
import time
import zlib
@@ -54,15 +54,36 @@
object_name = data_utils.rand_name(name='LObject')
data = data_utils.arbitrary_string()
segments = 10
- data_segments = [data + str(i) for i in moves.xrange(segments)]
+ data_segments = [data + str(i) for i in six.moves.xrange(segments)]
# uploading segments
- for i in moves.xrange(segments):
+ for i in six.moves.xrange(segments):
resp, _ = self.object_client.create_object_segments(
self.container_name, object_name, i, data_segments[i])
self.assertEqual(resp['status'], '201')
return object_name, data_segments
+ def _copy_object_2d(self, src_object_name, metadata=None):
+ dst_object_name = data_utils.rand_name(name='TestObject')
+ resp, _ = self.object_client.copy_object_2d_way(self.container_name,
+ src_object_name,
+ dst_object_name,
+ metadata=metadata)
+ return dst_object_name, resp
+
+ def _check_copied_obj(self, dst_object_name, src_body,
+ in_meta=None, not_in_meta=None):
+ resp, dest_body = self.object_client.get_object(self.container_name,
+ dst_object_name)
+
+ self.assertEqual(src_body, dest_body)
+ if in_meta:
+ for meta_key in in_meta:
+ self.assertIn('x-object-meta-' + meta_key, resp)
+ if not_in_meta:
+ for meta_key in not_in_meta:
+ self.assertNotIn('x-object-meta-' + meta_key, resp)
+
@test.attr(type='gate')
def test_create_object(self):
# create object
@@ -765,10 +786,7 @@
# change the content type of an existing object
# create object
- object_name = data_utils.rand_name(name='TestObject')
- data = data_utils.arbitrary_string()
- self.object_client.create_object(self.container_name,
- object_name, data)
+ object_name, data = self._create_object()
# get the old content type
resp_tmp, _ = self.object_client.list_object_metadata(
self.container_name, object_name)
@@ -805,20 +823,12 @@
dst_object_name)
self.assertEqual(resp['status'], '201')
self.assertHeaders(resp, 'Object', 'COPY')
-
- self.assertIn('last-modified', resp)
- self.assertIn('x-copied-from', resp)
- self.assertIn('x-copied-from-last-modified', resp)
- self.assertNotEqual(len(resp['last-modified']), 0)
self.assertEqual(
resp['x-copied-from'],
self.container_name + "/" + src_object_name)
- self.assertNotEqual(len(resp['x-copied-from-last-modified']), 0)
# check data
- resp, body = self.object_client.get_object(self.container_name,
- dst_object_name)
- self.assertEqual(body, src_data)
+ self._check_copied_obj(dst_object_name, src_data)
@test.attr(type='smoke')
def test_copy_object_across_containers(self):
@@ -862,15 +872,82 @@
self.assertIn(actual_meta_key, resp)
self.assertEqual(resp[actual_meta_key], meta_value)
+ @test.attr(type='smoke')
+ def test_copy_object_with_x_fresh_metadata(self):
+ # create source object
+ metadata = {'x-object-meta-src': 'src_value'}
+ src_object_name, data = self._create_object(metadata)
+
+ # copy source object with x_fresh_metadata header
+ metadata = {'X-Fresh-Metadata': 'true'}
+ dst_object_name, resp = self._copy_object_2d(src_object_name,
+ metadata)
+
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'COPY')
+
+ self.assertNotIn('x-object-meta-src', resp)
+ self.assertEqual(resp['x-copied-from'],
+ self.container_name + "/" + src_object_name)
+
+ # check that destination object does NOT have any object-meta
+ self._check_copied_obj(dst_object_name, data, not_in_meta=["src"])
+
+ @test.attr(type='smoke')
+ def test_copy_object_with_x_object_metakey(self):
+ # create source object
+ metadata = {'x-object-meta-src': 'src_value'}
+ src_obj_name, data = self._create_object(metadata)
+
+ # copy source object to destination with x-object-meta-key
+ metadata = {'x-object-meta-test': ''}
+ dst_obj_name, resp = self._copy_object_2d(src_obj_name, metadata)
+
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'COPY')
+
+ expected = {'x-object-meta-test': '',
+ 'x-object-meta-src': 'src_value',
+ 'x-copied-from': self.container_name + "/" + src_obj_name}
+ for key, value in six.iteritems(expected):
+ self.assertIn(key, resp)
+ self.assertEqual(value, resp[key])
+
+ # check destination object
+ self._check_copied_obj(dst_obj_name, data, in_meta=["test", "src"])
+
+ @test.attr(type='smoke')
+ def test_copy_object_with_x_object_meta(self):
+ # create source object
+ metadata = {'x-object-meta-src': 'src_value'}
+ src_obj_name, data = self._create_object(metadata)
+
+ # copy source object to destination with object metadata
+ metadata = {'x-object-meta-test': 'value'}
+ dst_obj_name, resp = self._copy_object_2d(src_obj_name, metadata)
+
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'COPY')
+
+ expected = {'x-object-meta-test': 'value',
+ 'x-object-meta-src': 'src_value',
+ 'x-copied-from': self.container_name + "/" + src_obj_name}
+ for key, value in six.iteritems(expected):
+ self.assertIn(key, resp)
+ self.assertEqual(value, resp[key])
+
+ # check destination object
+ self._check_copied_obj(dst_obj_name, data, in_meta=["test", "src"])
+
@test.attr(type='gate')
def test_object_upload_in_segments(self):
# create object
object_name = data_utils.rand_name(name='LObject')
data = data_utils.arbitrary_string()
segments = 10
- data_segments = [data + str(i) for i in moves.xrange(segments)]
+ data_segments = [data + str(i) for i in six.moves.xrange(segments)]
# uploading segments
- for i in moves.xrange(segments):
+ for i in six.moves.xrange(segments):
resp, _ = self.object_client.create_object_segments(
self.container_name, object_name, i, data_segments[i])
self.assertEqual(resp['status'], '201')
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
index e22a08b..e3ffdaf 100644
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
@@ -48,7 +48,7 @@
for resource in resources:
cls.test_resources[resource['logical_resource_id']] = resource
- @test.attr(type='slow')
+ @test.attr(type='gate')
def test_created_resources(self):
"""Verifies created keypair resource."""
@@ -68,7 +68,7 @@
self.assertEqual(resource_type, resource['resource_type'])
self.assertEqual('CREATE_COMPLETE', resource['resource_status'])
- @test.attr(type='slow')
+ @test.attr(type='gate')
def test_stack_keypairs_output(self):
resp, stack = self.client.get_stack(self.stack_name)
self.assertEqual('200', resp['status'])
diff --git a/tempest/api_schema/compute/v2/servers.py b/tempest/api_schema/compute/v2/servers.py
index 551924a..4abadf4 100644
--- a/tempest/api_schema/compute/v2/servers.py
+++ b/tempest/api_schema/compute/v2/servers.py
@@ -28,14 +28,11 @@
'id': {'type': 'string'},
'security_groups': {'type': 'array'},
'links': parameter_types.links,
- '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'.
- # NOTE: adminPass is not required because it can be deactivated
- # with nova API flag enable_instance_password=False
'required': ['id', 'security_groups', 'links']
}
},
@@ -43,6 +40,12 @@
}
}
+create_server_with_admin_pass = copy.deepcopy(create_server)
+create_server_with_admin_pass['response_body']['properties']['server'][
+ 'properties'].update({'adminPass': {'type': 'string'}})
+create_server_with_admin_pass['response_body']['properties']['server'][
+ 'required'].append('adminPass')
+
update_server = copy.deepcopy(servers.base_update_get_server)
update_server['response_body']['properties']['server']['properties'].update({
'hostId': {'type': 'string'},
@@ -281,3 +284,14 @@
'properties'].update({'adminPass': {'type': 'string'}})
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('adminPass')
+
+rescue_server = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'adminPass': {'type': 'string'}
+ },
+ 'required': ['adminPass']
+ }
+}
diff --git a/tempest/api_schema/compute/v3/servers.py b/tempest/api_schema/compute/v3/servers.py
index cebb2d7..a84ac3c 100644
--- a/tempest/api_schema/compute/v3/servers.py
+++ b/tempest/api_schema/compute/v3/servers.py
@@ -28,7 +28,6 @@
'id': {'type': 'string'},
'os-security-groups:security_groups': {'type': 'array'},
'links': parameter_types.links,
- 'admin_password': {'type': 'string'},
'os-access-ips:access_ip_v4': parameter_types.access_ip_v4,
'os-access-ips:access_ip_v6': parameter_types.access_ip_v6
},
@@ -36,13 +35,19 @@
# and some environments return a response without these
# attributes. So they are not 'required'.
'required': ['id', 'os-security-groups:security_groups',
- 'links', 'admin_password']
+ 'links']
}
},
'required': ['server']
}
}
+create_server_with_admin_pass = copy.deepcopy(create_server)
+create_server_with_admin_pass['response_body']['properties']['server'][
+ 'properties'].update({'admin_password': {'type': 'string'}})
+create_server_with_admin_pass['response_body']['properties']['server'][
+ 'required'].append('admin_password')
+
addresses_v3 = copy.deepcopy(parameter_types.addresses)
addresses_v3['patternProperties']['^[a-zA-Z0-9-_.]+$']['items'][
'properties'].update({
@@ -190,3 +195,18 @@
'properties'].update({'admin_password': {'type': 'string'}})
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('admin_password')
+
+rescue_server_with_admin_pass = {
+ 'status_code': [202],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'admin_password': {'type': 'string'}
+ },
+ 'required': ['admin_password']
+ }
+}
+
+rescue_server = copy.deepcopy(rescue_server_with_admin_pass)
+del rescue_server['response_body']['properties']
+del rescue_server['response_body']['required']
diff --git a/tempest/auth.py b/tempest/auth.py
index 830dca9..5df6224 100644
--- a/tempest/auth.py
+++ b/tempest/auth.py
@@ -13,10 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+import abc
import copy
import datetime
import exceptions
import re
+import six
import urlparse
from tempest import config
@@ -31,6 +33,7 @@
LOG = logging.getLogger(__name__)
+@six.add_metaclass(abc.ABCMeta)
class AuthProvider(object):
"""
Provide authentication
@@ -70,18 +73,21 @@
interface=self.interface, cache=self.cache
)
+ @abc.abstractmethod
def _decorate_request(self, filters, method, url, headers=None, body=None,
auth_data=None):
"""
Decorate request with authentication data
"""
- raise NotImplementedError
+ return
+ @abc.abstractmethod
def _get_auth(self):
- raise NotImplementedError
+ return
+ @abc.abstractmethod
def _fill_credentials(self, auth_data_body):
- raise NotImplementedError
+ return
def fill_credentials(self):
"""
@@ -130,8 +136,9 @@
self.cache = None
self.credentials.reset()
+ @abc.abstractmethod
def is_expired(self, auth_data):
- raise NotImplementedError
+ return
def auth_request(self, method, url, headers=None, body=None, filters=None):
"""
@@ -188,11 +195,12 @@
self.alt_part = request_part
self.alt_auth_data = auth_data
+ @abc.abstractmethod
def base_url(self, filters, auth_data=None):
"""
Extracts the base_url based on provided filters
"""
- raise NotImplementedError
+ return
class KeystoneAuthProvider(AuthProvider):
@@ -225,11 +233,13 @@
# no change to method or body
return str(_url), _headers, body
+ @abc.abstractmethod
def _auth_client(self):
- raise NotImplementedError
+ return
+ @abc.abstractmethod
def _auth_params(self):
- raise NotImplementedError
+ return
def _get_auth(self):
# Bypasses the cache
@@ -321,7 +331,7 @@
if noversion_path != "":
path += "/" + noversion_path
_base_url = _base_url.replace(parts.path, path)
- if filters.get('skip_path', None) is not None:
+ if filters.get('skip_path', None) is not None and parts.path != '':
_base_url = _base_url.replace(parts.path, "/")
return _base_url
diff --git a/tempest/clients.py b/tempest/clients.py
index 276b702..0edcdf4 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -52,6 +52,8 @@
MigrationsClientJSON
from tempest.services.compute.json.quotas_client import QuotaClassesClientJSON
from tempest.services.compute.json.quotas_client import QuotasClientJSON
+from tempest.services.compute.json.security_group_default_rules_client import \
+ SecurityGroupDefaultRulesClientJSON
from tempest.services.compute.json.security_groups_client import \
SecurityGroupsClientJSON
from tempest.services.compute.json.servers_client import ServersClientJSON
@@ -422,6 +424,8 @@
self.data_processing_client = DataProcessingClient(
self.auth_provider)
self.migrations_client = MigrationsClientJSON(self.auth_provider)
+ self.security_group_default_rules_client = (
+ SecurityGroupDefaultRulesClientJSON(self.auth_provider))
class AltManager(Manager):
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index c1a2e46..67b92b0 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -219,7 +219,7 @@
def check_objects(self):
"""Check that the objects created are still there."""
- if 'objects' not in self.res:
+ if not self.res.get('objects'):
return
LOG.info("checking objects")
for obj in self.res['objects']:
@@ -231,7 +231,7 @@
def check_servers(self):
"""Check that the servers are still up and running."""
- if 'servers' not in self.res:
+ if not self.res.get('servers'):
return
LOG.info("checking servers")
for server in self.res['servers']:
@@ -254,7 +254,7 @@
def check_volumes(self):
"""Check that the volumes are still there and attached."""
- if 'volumes' not in self.res:
+ if not self.res.get('volumes'):
return
LOG.info("checking volumes")
for volume in self.res['volumes']:
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
index 4a7921f..996c365 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -69,10 +69,24 @@
elif self.target == 'Object':
if 'etag' not in actual:
return NonExistentHeader('etag')
- elif self.method == 'PUT' or self.method == 'COPY':
+ if 'last-modified' not in actual:
+ return NonExistentHeader('last-modified')
+ elif self.method == 'PUT':
if self.target == 'Object':
if 'etag' not in actual:
return NonExistentHeader('etag')
+ if 'last-modified' not in actual:
+ return NonExistentHeader('last-modified')
+ elif self.method == 'COPY':
+ if self.target == 'Object':
+ if 'etag' not in actual:
+ return NonExistentHeader('etag')
+ if 'last-modified' not in actual:
+ return NonExistentHeader('last-modified')
+ if 'x-copied-from' not in actual:
+ return NonExistentHeader('x-copied-from')
+ if 'x-copied-from-last-modified' not in actual:
+ return NonExistentHeader('x-copied-from-last-modified')
return None
@@ -122,11 +136,17 @@
return InvalidFormat(key, value)
elif key == 'content-type' and not value:
return InvalidFormat(key, value)
+ elif key == 'x-copied-from' and not re.match("\S+/\S+", value):
+ return InvalidFormat(key, value)
+ elif key == 'x-copied-from-last-modified' and not value:
+ return InvalidFormat(key, value)
elif key == 'x-trans-id' and \
not re.match("^tx[0-9a-f]{21}-[0-9a-f]{10}.*", value):
return InvalidFormat(key, value)
elif key == 'date' and not value:
return InvalidFormat(key, value)
+ elif key == 'last-modified' and not value:
+ return InvalidFormat(key, value)
elif key == 'accept-ranges' and not value == 'bytes':
return InvalidFormat(key, value)
elif key == 'etag' and not value.isalnum():
diff --git a/tempest/common/utils/data_utils.py b/tempest/common/utils/data_utils.py
index 174e557..5a29ea0 100644
--- a/tempest/common/utils/data_utils.py
+++ b/tempest/common/utils/data_utils.py
@@ -71,3 +71,11 @@
if not base_text:
base_text = 'test'
return ''.join(itertools.islice(itertools.cycle(base_text), size))
+
+
+def random_bytes(size=1024):
+ """
+ Return size randomly selected bytes as a string.
+ """
+ return ''.join([chr(random.randint(0, 255))
+ for i in range(size)])
diff --git a/tempest/services/compute/json/security_group_default_rules_client.py b/tempest/services/compute/json/security_group_default_rules_client.py
new file mode 100644
index 0000000..6d29837
--- /dev/null
+++ b/tempest/services/compute/json/security_group_default_rules_client.py
@@ -0,0 +1,74 @@
+# Copyright 2014 NEC Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from tempest.common import rest_client
+from tempest import config
+
+CONF = config.CONF
+
+
+class SecurityGroupDefaultRulesClientJSON(rest_client.RestClient):
+
+ def __init__(self, auth_provider):
+ super(SecurityGroupDefaultRulesClientJSON,
+ self).__init__(auth_provider)
+ self.service = CONF.compute.catalog_type
+
+ def create_security_default_group_rule(self, ip_protocol, from_port,
+ to_port, **kwargs):
+ """
+ Creating security group default rules.
+ ip_protocol : ip_protocol (icmp, tcp, udp).
+ from_port: Port at start of range.
+ to_port : Port at end of range.
+ cidr : CIDR for address range.
+ """
+ post_body = {
+ 'ip_protocol': ip_protocol,
+ 'from_port': from_port,
+ 'to_port': to_port,
+ 'cidr': kwargs.get('cidr'),
+ }
+ post_body = json.dumps({'security_group_default_rule': post_body})
+ url = 'os-security-group-default-rules'
+ resp, body = self.post(url, post_body)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return resp, body['security_group_default_rule']
+
+ def delete_security_group_default_rule(self,
+ security_group_default_rule_id):
+ """Deletes the provided Security Group default rule."""
+ resp, body = self.delete('os-security-group-default-rules/%s' % str(
+ security_group_default_rule_id))
+ self.expected_success(204, resp.status)
+ return resp, body
+
+ def list_security_group_default_rules(self):
+ """List all Security Group default rules."""
+ resp, body = self.get('os-security-group-default-rules')
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return resp, body['security_group_default_rules']
+
+ def get_security_group_default_rule(self, security_group_default_rule_id):
+ """Return the details of provided Security Group default rule."""
+ resp, body = self.get('os-security-group-default-rules/%s' % str(
+ security_group_default_rule_id))
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return resp, body['security_group_default_rule']
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 05f74cd..a4e3641 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -93,7 +93,11 @@
# with return reservation id set True
if 'reservation_id' in body:
return resp, body
- self.validate_response(schema.create_server, resp, body)
+ if CONF.compute_feature_enabled.enable_instance_password:
+ create_schema = schema.create_server_with_admin_pass
+ else:
+ create_schema = schema.create_server
+ self.validate_response(create_schema, resp, body)
return resp, body['server']
def update_server(self, server_id, name=None, meta=None, accessIPv4=None,
@@ -455,7 +459,8 @@
def rescue_server(self, server_id, **kwargs):
"""Rescue the provided server."""
- return self.action(server_id, 'rescue', 'adminPass', None, **kwargs)
+ return self.action(server_id, 'rescue', 'adminPass',
+ schema.rescue_server, **kwargs)
def unrescue_server(self, server_id):
"""Unrescue the provided server."""
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index 27e95e8..c3fd355 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -96,7 +96,11 @@
# with return reservation id set True
if 'servers_reservation' in body:
return resp, body['servers_reservation']
- self.validate_response(schema.create_server, resp, body)
+ if CONF.compute_feature_enabled.enable_instance_password:
+ create_schema = schema.create_server_with_admin_pass
+ else:
+ create_schema = schema.create_server
+ self.validate_response(create_schema, resp, body)
return resp, body['server']
def update_server(self, server_id, name=None, meta=None, access_ip_v4=None,
@@ -450,8 +454,16 @@
def rescue_server(self, server_id, **kwargs):
"""Rescue the provided server."""
- return self.action(server_id, 'rescue', 'admin_password',
- None, **kwargs)
+ post_body = json.dumps({'rescue': kwargs})
+ resp, body = self.post('servers/%s/action' % str(server_id),
+ post_body)
+ if CONF.compute_feature_enabled.enable_instance_password:
+ rescue_schema = schema.rescue_server_with_admin_pass
+ else:
+ rescue_schema = schema.rescue_server
+ body = json.loads(body)
+ self.validate_response(rescue_schema, resp, body)
+ return resp, body
def unrescue_server(self, server_id):
"""Unrescue the provided server."""
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index 0188c2a..d57b931 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -135,8 +135,11 @@
body = json.loads(body)
return resp, body['project']
- def list_projects(self):
- resp, body = self.get("projects")
+ def list_projects(self, params=None):
+ url = "projects"
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return resp, body['projects']
diff --git a/tempest/services/identity/v3/xml/identity_client.py b/tempest/services/identity/v3/xml/identity_client.py
index f3e084e..c2bd77e 100644
--- a/tempest/services/identity/v3/xml/identity_client.py
+++ b/tempest/services/identity/v3/xml/identity_client.py
@@ -197,9 +197,12 @@
body = self._parse_body(etree.fromstring(body))
return resp, body
- def list_projects(self):
+ def list_projects(self, params=None):
"""Get the list of projects."""
- resp, body = self.get("projects")
+ url = 'projects'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = self._parse_projects(etree.fromstring(body))
return resp, body
diff --git a/tempest/tests/test_auth.py b/tempest/tests/test_auth.py
index 1dcddad..6a2e335 100644
--- a/tempest/tests/test_auth.py
+++ b/tempest/tests/test_auth.py
@@ -59,12 +59,24 @@
obviously don't test not implemented method or the ones which strongly
depends on them.
"""
- _auth_provider_class = auth.AuthProvider
- def test_check_credentials_class(self):
- self.assertRaises(NotImplementedError,
- self.auth_provider.check_credentials,
- auth.Credentials())
+ class FakeAuthProviderImpl(auth.AuthProvider):
+ def _decorate_request():
+ pass
+
+ def _fill_credentials():
+ pass
+
+ def _get_auth():
+ pass
+
+ def base_url():
+ pass
+
+ def is_expired():
+ pass
+
+ _auth_provider_class = FakeAuthProviderImpl
def test_check_credentials_bad_type(self):
self.assertFalse(self.auth_provider.check_credentials([]))
@@ -74,16 +86,6 @@
auth_provider = self._auth(credentials={})
self.assertIsInstance(auth_provider.credentials, auth.Credentials)
- def test_instantiate_with_bad_credentials_type(self):
- """
- Assure that credentials with bad type fail with TypeError
- """
- self.assertRaises(TypeError, self._auth, [])
-
- def test_auth_data_property(self):
- self.assertRaises(NotImplementedError, getattr, self.auth_provider,
- 'auth_data')
-
def test_auth_data_property_when_cache_exists(self):
self.auth_provider.cache = 'foo'
self.useFixture(mockpatch.PatchObject(self.auth_provider,
@@ -110,9 +112,10 @@
self.assertIsNone(self.auth_provider.alt_part)
self.assertIsNone(self.auth_provider.alt_auth_data)
- def test_fill_credentials(self):
- self.assertRaises(NotImplementedError,
- self.auth_provider.fill_credentials)
+ def test_auth_class(self):
+ self.assertRaises(TypeError,
+ auth.AuthProvider,
+ fake_credentials.FakeCredentials)
class TestKeystoneV2AuthProvider(BaseAuthTestsSetUp):