Merge "Fix "mutable" object as default value"
diff --git a/tempest/api/baremetal/admin/test_nodes.py b/tempest/api/baremetal/admin/test_nodes.py
index 43ea1e6..ab6aed3 100644
--- a/tempest/api/baremetal/admin/test_nodes.py
+++ b/tempest/api/baremetal/admin/test_nodes.py
@@ -86,3 +86,23 @@
core_interfaces = ['power', 'deploy']
for interface in core_interfaces:
self.assertIn(interface, body)
+
+ @test.attr(type='smoke')
+ def test_set_node_boot_device(self):
+ body = self.client.set_node_boot_device(self.node['uuid'], 'pxe')
+ # No content
+ self.assertEqual('', body)
+
+ @test.attr(type='smoke')
+ def test_get_node_boot_device(self):
+ body = self.client.get_node_boot_device(self.node['uuid'])
+ self.assertIn('boot_device', body)
+ self.assertIn('persistent', body)
+ self.assertTrue(isinstance(body['boot_device'], six.string_types))
+ self.assertTrue(isinstance(body['persistent'], bool))
+
+ @test.attr(type='smoke')
+ def test_get_node_supported_boot_devices(self):
+ body = self.client.get_node_supported_boot_devices(self.node['uuid'])
+ self.assertIn('supported_boot_devices', body)
+ self.assertTrue(isinstance(body['supported_boot_devices'], list))
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
index 7d30f26..a5c8caa 100644
--- a/tempest/api/database/flavors/test_flavors.py
+++ b/tempest/api/database/flavors/test_flavors.py
@@ -27,8 +27,7 @@
@test.attr(type='smoke')
def test_get_db_flavor(self):
# The expected flavor details should be returned
- resp, flavor = self.client.get_db_flavor_details(self.db_flavor_ref)
- self.assertEqual(200, resp.status)
+ _, flavor = self.client.get_db_flavor_details(self.db_flavor_ref)
self.assertEqual(self.db_flavor_ref, str(flavor['id']))
self.assertIn('ram', flavor)
self.assertIn('links', flavor)
@@ -36,11 +35,9 @@
@test.attr(type='smoke')
def test_list_db_flavors(self):
- resp, flavor = self.client.get_db_flavor_details(self.db_flavor_ref)
- self.assertEqual(200, resp.status)
+ _, flavor = self.client.get_db_flavor_details(self.db_flavor_ref)
# List of all flavors should contain the expected flavor
- resp, flavors = self.client.list_db_flavors()
- self.assertEqual(200, resp.status)
+ _, flavors = self.client.list_db_flavors()
self.assertIn(flavor, flavors)
def _check_values(self, names, db_flavor, os_flavor, in_db=True):
@@ -57,17 +54,14 @@
@test.attr(type='smoke')
@test.services('compute')
def test_compare_db_flavors_with_os(self):
- resp, db_flavors = self.client.list_db_flavors()
- self.assertEqual(200, resp.status)
- resp, os_flavors = self.os_flavors_client.list_flavors_with_detail()
- self.assertEqual(200, resp.status)
+ _, db_flavors = self.client.list_db_flavors()
+ _, os_flavors = self.os_flavors_client.list_flavors_with_detail()
self.assertEqual(len(os_flavors), len(db_flavors),
"OS flavors %s do not match DB flavors %s" %
(os_flavors, db_flavors))
for os_flavor in os_flavors:
- resp, db_flavor =\
+ _, db_flavor =\
self.client.get_db_flavor_details(os_flavor['id'])
- self.assertEqual(200, resp.status)
self._check_values(['id', 'name', 'ram'], db_flavor, os_flavor)
self._check_values(['disk', 'vcpus', 'swap'], db_flavor, os_flavor,
in_db=False)
diff --git a/tempest/api/database/versions/test_versions.py b/tempest/api/database/versions/test_versions.py
index 6101f47..453abe7 100644
--- a/tempest/api/database/versions/test_versions.py
+++ b/tempest/api/database/versions/test_versions.py
@@ -27,8 +27,7 @@
@test.attr(type='smoke')
def test_list_db_versions(self):
- resp, versions = self.client.list_db_versions()
- self.assertEqual(200, resp.status)
+ _, versions = self.client.list_db_versions()
self.assertTrue(len(versions) > 0, "No database versions found")
# List of all versions should contain the current version, and there
# should only be one 'current' version
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index c597255..264a18a 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -185,3 +185,20 @@
resp, body = self.object_client.head(url)
self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'HEAD')
+
+ @test.attr(type='gate')
+ @test.requires_ext(extension='tempurl', service='object')
+ def test_get_object_using_temp_url_with_inline_query_parameter(self):
+ expires = self._get_expiry_date()
+
+ # get a temp URL for the created object
+ url = self._get_temp_url(self.container_name, self.object_name, "GET",
+ expires, self.key)
+ url = url + '&inline'
+
+ # trying to get object using temp url within expiry time
+ resp, body = self.object_client.get(url)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Object', 'GET')
+ self.assertEqual(body, self.content)
+ self.assertEqual(resp['content-disposition'], 'inline')
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index c1e2d59..ffadb16 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -38,6 +38,7 @@
os = clients.Manager()
if not CONF.service_available.neutron:
raise cls.skipException("Neutron support is required")
+ cls.neutron_basic_template = cls.load_template('neutron_basic')
cls.network_client = os.network_client
cls.stack_name = data_utils.rand_name('heat')
template = cls.read_template('neutron_basic')
@@ -87,10 +88,14 @@
@test.attr(type='slow')
def test_created_resources(self):
"""Verifies created neutron resources."""
- resources = [('Network', 'OS::Neutron::Net'),
- ('Subnet', 'OS::Neutron::Subnet'),
- ('RouterInterface', 'OS::Neutron::RouterInterface'),
- ('Server', 'OS::Nova::Server')]
+ resources = [('Network', self.neutron_basic_template['resources'][
+ 'Network']['type']),
+ ('Subnet', self.neutron_basic_template['resources'][
+ 'Subnet']['type']),
+ ('RouterInterface', self.neutron_basic_template[
+ 'resources']['RouterInterface']['type']),
+ ('Server', self.neutron_basic_template['resources'][
+ 'Server']['type'])]
for resource_name, resource_type in resources:
resource = self.test_resources.get(resource_name, None)
self.assertIsInstance(resource, dict)
@@ -107,7 +112,8 @@
network = body['network']
self.assertIsInstance(network, dict)
self.assertEqual(network_id, network['id'])
- self.assertEqual('NewNetwork', network['name'])
+ self.assertEqual(self.neutron_basic_template['resources'][
+ 'Network']['properties']['name'], network['name'])
@test.attr(type='slow')
@test.services('network')
@@ -119,10 +125,12 @@
network_id = self.test_resources.get('Network')['physical_resource_id']
self.assertEqual(subnet_id, subnet['id'])
self.assertEqual(network_id, subnet['network_id'])
- self.assertEqual('NewSubnet', subnet['name'])
+ self.assertEqual(self.neutron_basic_template['resources'][
+ 'Subnet']['properties']['name'], subnet['name'])
self.assertEqual(sorted(CONF.network.dns_servers),
sorted(subnet['dns_nameservers']))
- self.assertEqual(4, subnet['ip_version'])
+ self.assertEqual(self.neutron_basic_template['resources'][
+ 'Subnet']['properties']['ip_version'], subnet['ip_version'])
self.assertEqual(str(self.subnet_cidr), subnet['cidr'])
@test.attr(type='slow')
@@ -132,7 +140,8 @@
router_id = self.test_resources.get('Router')['physical_resource_id']
_, body = self.network_client.show_router(router_id)
router = body['router']
- self.assertEqual('NewRouter', router['name'])
+ self.assertEqual(self.neutron_basic_template['resources'][
+ 'Router']['properties']['name'], router['name'])
self.assertEqual(self.external_network_id,
router['external_gateway_info']['network_id'])
self.assertEqual(True, router['admin_state_up'])
@@ -168,6 +177,7 @@
_, server = self.servers_client.get_server(server_id)
self.assertEqual(self.keypair_name, server['key_name'])
self.assertEqual('ACTIVE', server['status'])
- network = server['addresses']['NewNetwork'][0]
+ network = server['addresses'][self.neutron_basic_template['resources'][
+ 'Network']['properties']['name']][0]
self.assertEqual(4, network['version'])
self.assertIn(netaddr.IPAddress(network['addr']), self.subnet_cidr)
diff --git a/tempest/api/orchestration/stacks/test_resource_types.py b/tempest/api/orchestration/stacks/test_resource_types.py
new file mode 100644
index 0000000..e204894
--- /dev/null
+++ b/tempest/api/orchestration/stacks/test_resource_types.py
@@ -0,0 +1,44 @@
+# 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.orchestration import base
+from tempest import test
+
+
+class ResourceTypesTest(base.BaseOrchestrationTest):
+
+ @test.attr(type='smoke')
+ def test_resource_type_list(self):
+ """Verify it is possible to list resource types."""
+ resource_types = self.client.list_resource_types()
+ self.assertIsInstance(resource_types, list)
+ self.assertIn('OS::Nova::Server', resource_types)
+
+ @test.attr(type='smoke')
+ def test_resource_type_show(self):
+ """Verify it is possible to get schema about resource types."""
+ resource_types = self.client.list_resource_types()
+ self.assertNotEmpty(resource_types)
+
+ for resource_type in resource_types:
+ type_schema = self.client.get_resource_type(resource_type)
+ self.assert_fields_in_dict(type_schema, 'properties',
+ 'attributes', 'resource_type')
+ self.assertEqual(resource_type, type_schema['resource_type'])
+
+ @test.attr(type='smoke')
+ def test_resource_type_template(self):
+ """Verify it is possible to get template about resource types."""
+ type_template = self.client.get_resource_type_template(
+ 'OS::Nova::Server')
+ self.assert_fields_in_dict(type_template, 'Outputs',
+ 'Parameters', 'Resources')
\ No newline at end of file
diff --git a/tempest/config.py b/tempest/config.py
index 1d10a0a..93d4874 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -52,7 +52,6 @@
default=False,
help="Set to True if using self-signed SSL certificates."),
cfg.StrOpt('uri',
- default=None,
help="Full URI of the OpenStack Identity API (Keystone), v2"),
cfg.StrOpt('uri_v3',
help='Full URI of the OpenStack Identity API (Keystone), v3'),
@@ -72,52 +71,40 @@
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for the identity service."),
cfg.StrOpt('username',
- default=None,
help="Username to use for Nova API requests."),
cfg.StrOpt('tenant_name',
- default=None,
help="Tenant name to use for Nova API requests."),
cfg.StrOpt('admin_role',
default='admin',
help="Role required to administrate keystone."),
cfg.StrOpt('password',
- default=None,
help="API key to use when authenticating.",
secret=True),
cfg.StrOpt('domain_name',
- default=None,
help="Domain name for authentication (Keystone V3)."
"The same domain applies to user and project"),
cfg.StrOpt('alt_username',
- default=None,
help="Username of alternate user to use for Nova API "
"requests."),
cfg.StrOpt('alt_tenant_name',
- default=None,
help="Alternate user's Tenant name to use for Nova API "
"requests."),
cfg.StrOpt('alt_password',
- default=None,
help="API key to use when authenticating as alternate user.",
secret=True),
cfg.StrOpt('alt_domain_name',
- default=None,
help="Alternate domain name for authentication (Keystone V3)."
"The same domain applies to user and project"),
cfg.StrOpt('admin_username',
- default=None,
help="Administrative Username to use for "
"Keystone API requests."),
cfg.StrOpt('admin_tenant_name',
- default=None,
help="Administrative Tenant name to use for Keystone API "
"requests."),
cfg.StrOpt('admin_password',
- default=None,
help="API key to use when authenticating as admin.",
secret=True),
cfg.StrOpt('admin_domain_name',
- default=None,
help="Admin domain name for authentication (Keystone V3)."
"The same domain applies to user and project"),
]
@@ -246,7 +233,6 @@
default='computev3',
help="Catalog type of the Compute v3 service."),
cfg.StrOpt('path_to_private_key',
- default=None,
help="Path to a private key file for SSH access to remote "
"hosts"),
cfg.StrOpt('volume_device_name',
@@ -360,18 +346,14 @@
ComputeAdminGroup = [
cfg.StrOpt('username',
- default=None,
help="Administrative Username to use for Nova API requests."),
cfg.StrOpt('tenant_name',
- default=None,
help="Administrative Tenant name to use for Nova API "
"requests."),
cfg.StrOpt('password',
- default=None,
help="API key to use when authenticating as admin.",
secret=True),
cfg.StrOpt('domain_name',
- default=None,
help="Domain name for authentication as admin (Keystone V3)."
"The same domain applies to user and project"),
]
@@ -692,11 +674,9 @@
help="Instance type for tests. Needs to be big enough for a "
"full OS plus the test workload"),
cfg.StrOpt('image_ref',
- default=None,
help="Name of heat-cfntools enabled image to use when "
"launching test instances."),
cfg.StrOpt('keypair_name',
- default=None,
help="Name of existing keypair to launch servers with."),
cfg.IntOpt('max_template_size',
default=524288,
@@ -765,11 +745,9 @@
default="http://localhost:8080",
help="S3 URL"),
cfg.StrOpt('aws_secret',
- default=None,
help="AWS Secret Key",
secret=True),
cfg.StrOpt('aws_access',
- default=None,
help="AWS Access Key"),
cfg.StrOpt('aws_zone',
default="nova",
@@ -808,26 +786,20 @@
StressGroup = [
cfg.StrOpt('nova_logdir',
- default=None,
help='Directory containing log files on the compute nodes'),
cfg.IntOpt('max_instances',
default=16,
help='Maximum number of instances to create during test.'),
cfg.StrOpt('controller',
- default=None,
help='Controller host.'),
# new stress options
cfg.StrOpt('target_controller',
- default=None,
help='Controller host.'),
cfg.StrOpt('target_ssh_user',
- default=None,
help='ssh user.'),
cfg.StrOpt('target_private_key_path',
- default=None,
help='Path to private key.'),
cfg.StrOpt('target_logfiles',
- default=None,
help='regexp for list of log files.'),
cfg.IntOpt('log_check_interval',
default=60,
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 5a1dc04..463f5aa 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -25,7 +25,7 @@
LOG = logging.getLogger(__name__)
-class TestServerAdvancedOps(manager.OfficialClientTest):
+class TestServerAdvancedOps(manager.ScenarioTest):
"""
This test case stresses some advanced server instance operations:
@@ -49,19 +49,19 @@
def test_resize_server_confirm(self):
# We create an instance for use in this test
instance = self.create_server()
- instance_id = instance.id
+ instance_id = instance['id']
resize_flavor = CONF.compute.flavor_ref_alt
LOG.debug("Resizing instance %s from flavor %s to flavor %s",
- instance.id, instance.flavor, resize_flavor)
- instance.resize(resize_flavor)
- self.status_timeout(self.compute_client.servers, instance_id,
- 'VERIFY_RESIZE')
+ instance['id'], instance['flavor']['id'], resize_flavor)
+ self.servers_client.resize(instance_id, resize_flavor)
+ self.servers_client.wait_for_server_status(instance_id,
+ 'VERIFY_RESIZE')
LOG.debug("Confirming resize of instance %s", instance_id)
- instance.confirm_resize()
+ self.servers_client.confirm_resize(instance_id)
- self.status_timeout(
- self.compute_client.servers, instance_id, 'ACTIVE')
+ self.servers_client.wait_for_server_status(instance_id,
+ 'ACTIVE')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
@@ -69,24 +69,27 @@
def test_server_sequence_suspend_resume(self):
# We create an instance for use in this test
instance = self.create_server()
- instance_id = instance.id
+ instance_id = instance['id']
LOG.debug("Suspending instance %s. Current status: %s",
- instance_id, instance.status)
- instance.suspend()
- self.status_timeout(self.compute_client.servers, instance_id,
- 'SUSPENDED')
+ instance_id, instance['status'])
+ self.servers_client.suspend_server(instance_id)
+ self.servers_client.wait_for_server_status(instance_id,
+ 'SUSPENDED')
+ _, fetched_instance = self.servers_client.get_server(instance_id)
LOG.debug("Resuming instance %s. Current status: %s",
- instance_id, instance.status)
- instance.resume()
- self.status_timeout(self.compute_client.servers, instance_id,
- 'ACTIVE')
+ instance_id, fetched_instance['status'])
+ self.servers_client.resume_server(instance_id)
+ self.servers_client.wait_for_server_status(instance_id,
+ 'ACTIVE')
+ _, fetched_instance = self.servers_client.get_server(instance_id)
LOG.debug("Suspending instance %s. Current status: %s",
- instance_id, instance.status)
- instance.suspend()
- self.status_timeout(self.compute_client.servers, instance_id,
- 'SUSPENDED')
+ instance_id, fetched_instance['status'])
+ self.servers_client.suspend_server(instance_id)
+ self.servers_client.wait_for_server_status(instance_id,
+ 'SUSPENDED')
+ _, fetched_instance = self.servers_client.get_server(instance_id)
LOG.debug("Resuming instance %s. Current status: %s",
- instance_id, instance.status)
- instance.resume()
- self.status_timeout(self.compute_client.servers, instance_id,
- 'ACTIVE')
+ instance_id, fetched_instance['status'])
+ self.servers_client.resume_server(instance_id)
+ self.servers_client.wait_for_server_status(instance_id,
+ 'ACTIVE')
diff --git a/tempest/services/baremetal/v1/base_v1.py b/tempest/services/baremetal/v1/base_v1.py
index 9c753c2..07eee8a 100644
--- a/tempest/services/baremetal/v1/base_v1.py
+++ b/tempest/services/baremetal/v1/base_v1.py
@@ -264,3 +264,47 @@
postf='validate')
return self._show_request('nodes', node_uuid, uri=uri)
+
+ @base.handle_errors
+ def set_node_boot_device(self, node_uuid, boot_device, persistent=False):
+ """
+ Set the boot device of the specified node.
+
+ :param node_uuid: The unique identifier of the node.
+ :param boot_device: The boot device name.
+ :param persistent: Boolean value. True if the boot device will
+ persist to all future boots, False if not.
+ Default: False.
+
+ """
+ request = {'boot_device': boot_device, 'persistent': persistent}
+ resp, body = self._put_request('nodes/%s/management/boot_device' %
+ node_uuid, request)
+ self.expected_success(204, resp.status)
+ return body
+
+ @base.handle_errors
+ def get_node_boot_device(self, node_uuid):
+ """
+ Get the current boot device of the specified node.
+
+ :param node_uuid: The unique identifier of the node.
+
+ """
+ path = 'nodes/%s/management/boot_device' % node_uuid
+ resp, body = self._list_request(path)
+ self.expected_success(200, resp.status)
+ return body
+
+ @base.handle_errors
+ def get_node_supported_boot_devices(self, node_uuid):
+ """
+ Get the supported boot devices of the specified node.
+
+ :param node_uuid: The unique identifier of the node.
+
+ """
+ path = 'nodes/%s/management/boot_device/supported' % node_uuid
+ resp, body = self._list_request(path)
+ self.expected_success(200, resp.status)
+ return body
diff --git a/tempest/services/database/json/flavors_client.py b/tempest/services/database/json/flavors_client.py
index 2ec0405..f276a45 100644
--- a/tempest/services/database/json/flavors_client.py
+++ b/tempest/services/database/json/flavors_client.py
@@ -33,8 +33,10 @@
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def get_db_flavor_details(self, db_flavor_id):
resp, body = self.get("flavors/%s" % str(db_flavor_id))
+ self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
diff --git a/tempest/services/database/json/versions_client.py b/tempest/services/database/json/versions_client.py
index 0269c43..81c0e6c 100644
--- a/tempest/services/database/json/versions_client.py
+++ b/tempest/services/database/json/versions_client.py
@@ -35,4 +35,5 @@
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 3b8e81e..d3867cd 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -290,6 +290,27 @@
}
return self._validate_template(post_body)
+ def list_resource_types(self):
+ """List resource types."""
+ resp, body = self.get('resource_types')
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return body['resource_types']
+
+ def get_resource_type(self, resource_type_name):
+ """Return the schema of a resource type."""
+ url = 'resource_types/%s' % resource_type_name
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ return json.loads(body)
+
+ def get_resource_type_template(self, resource_type_name):
+ """Return the template of a resource type."""
+ url = 'resource_types/%s/template' % resource_type_name
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ return json.loads(body)
+
def create_software_config(self, name=None, config=None, group=None,
inputs=None, outputs=None, options=None):
headers, body = self._prep_software_config_create(