Merge "port related volumes tests into nova v3 part2"
diff --git a/HACKING.rst b/HACKING.rst
index 1083075..3fa1ff5 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -39,7 +39,7 @@
when the additional operations leads to another exception.
Just letting an exception to propagate, is not bad idea in a test case,
- at all.
+at all.
Try to avoid using any exception handling construct which can hide the errors
origin.
@@ -53,10 +53,10 @@
test fails part way through.
Use the ``self.assert*`` methods provided by the unit test framework
- the signal failures early.
+the signal failures early.
Avoid using the ``self.fail`` alone, it's stack trace will signal
- the ``self.fail`` line as the origin of the error.
+the ``self.fail`` line as the origin of the error.
Avoid constructing complex boolean expressions for assertion.
The ``self.assertTrue`` or ``self.assertFalse`` without a ``msg`` argument,
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 1080ddf..607ba8b 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -94,6 +94,265 @@
#syslog_log_facility=LOG_USER
+[image]
+
+#
+# Options defined in tempest.config
+#
+
+# Catalog type of the Image service. (string value)
+#catalog_type=image
+
+# The image region name to use. If empty, the value of
+# identity.region is used instead. If no such region is found
+# in the service catalog, the first found one is used. (string
+# value)
+#region=
+
+# http accessible image (string value)
+#http_image=http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz
+
+
+[object-storage]
+
+#
+# Options defined in tempest.config
+#
+
+# Catalog type of the Object-Storage service. (string value)
+#catalog_type=object-store
+
+# The object-storage region name to use. If empty, the value
+# of identity.region is used instead. If no such region is
+# found in the service catalog, the first found one is used.
+# (string value)
+#region=
+
+# Number of seconds to time on waiting for a containerto
+# container synchronization complete. (integer value)
+#container_sync_timeout=120
+
+# Number of seconds to wait while looping to check thestatus
+# of a container to container synchronization (integer value)
+#container_sync_interval=5
+
+# Role to add to users created for swift tests to enable
+# creating containers (string value)
+#operator_role=Member
+
+
+[network]
+
+#
+# Options defined in tempest.config
+#
+
+# Catalog type of the Neutron service. (string value)
+#catalog_type=network
+
+# The network region name to use. If empty, the value of
+# identity.region is used instead. If no such region is found
+# in the service catalog, the first found one is used. (string
+# value)
+#region=
+
+# The cidr block to allocate tenant networks from (string
+# value)
+#tenant_network_cidr=10.100.0.0/16
+
+# The mask bits for tenant networks (integer value)
+#tenant_network_mask_bits=28
+
+# Whether tenant network connectivity should be evaluated
+# directly (boolean value)
+#tenant_networks_reachable=false
+
+# Id of the public network that provides external connectivity
+# (string value)
+#public_network_id=
+
+# Id of the public router that provides external connectivity
+# (string value)
+#public_router_id=
+
+
+[data_processing]
+
+#
+# Options defined in tempest.config
+#
+
+# Catalog type of the data processing service. (string value)
+#catalog_type=data_processing
+
+
+[object-storage-feature-enabled]
+
+#
+# Options defined in tempest.config
+#
+
+# Set to True if the Container Quota middleware is enabled
+# (boolean value)
+#container_quotas=true
+
+# Set to True if the Account Quota middleware is enabled
+# (boolean value)
+#accounts_quotas=true
+
+# Set to True if the Crossdomain middleware is enabled
+# (boolean value)
+#crossdomain=true
+
+# Set to True if the TempURL middleware is enabled (boolean
+# value)
+#tempurl=true
+
+
+[network-feature-enabled]
+
+#
+# Options defined in tempest.config
+#
+
+# A list of enabled extensions with a special entry all which
+# indicates every extension is enabled (list value)
+#api_extensions=all
+
+
+[volume-feature-enabled]
+
+#
+# Options defined in tempest.config
+#
+
+# Runs Cinder multi-backend test (requires 2 backends)
+# (boolean value)
+#multi_backend=false
+
+# A list of enabled extensions with a special entry all which
+# indicates every extension is enabled (list value)
+#api_extensions=all
+
+
+[image-feature-enabled]
+
+#
+# Options defined in tempest.config
+#
+
+# Is the v2 image API enabled (boolean value)
+#api_v2=true
+
+# Is the v1 image API enabled (boolean value)
+#api_v1=true
+
+
+[compute-admin]
+
+#
+# Options defined in tempest.config
+#
+
+# Administrative Username to use for Nova API requests.
+# (string value)
+#username=admin
+
+# Administrative Tenant name to use for Nova API requests.
+# (string value)
+#tenant_name=admin
+
+# API key to use when authenticating as admin. (string value)
+#password=pass
+
+
+[volume]
+
+#
+# Options defined in tempest.config
+#
+
+# Time in seconds between volume availability checks. (integer
+# value)
+#build_interval=10
+
+# Timeout in seconds to wait for a volume to becomeavailable.
+# (integer value)
+#build_timeout=300
+
+# Catalog type of the Volume Service (string value)
+#catalog_type=volume
+
+# The volume region name to use. If empty, the value of
+# identity.region is used instead. If no such region is found
+# in the service catalog, the first found one is used. (string
+# value)
+#region=
+
+# Name of the backend1 (must be declared in cinder.conf)
+# (string value)
+#backend1_name=BACKEND_1
+
+# Name of the backend2 (must be declared in cinder.conf)
+# (string value)
+#backend2_name=BACKEND_2
+
+# Backend protocol to target when creating volume types
+# (string value)
+#storage_protocol=iSCSI
+
+# Backend vendor to target when creating volume types (string
+# value)
+#vendor_name=Open Source
+
+# Disk format to use when copying a volume to image (string
+# value)
+#disk_format=raw
+
+
+[compute-feature-enabled]
+
+#
+# Options defined in tempest.config
+#
+
+# If false, skip all nova v3 tests. (boolean value)
+#api_v3=true
+
+# If false, skip disk config tests (boolean value)
+#disk_config=true
+
+# A list of enabled extensions with a special entry all which
+# indicates every extension is enabled (list value)
+#api_extensions=all
+
+# A list of enabled v3 extensions with a special entry all
+# which indicates every extension is enabled (list value)
+#api_v3_extensions=all
+
+# Does the test environment support changing the admin
+# password? (boolean value)
+#change_password=false
+
+# Does the test environment support snapshots? (boolean value)
+#create_image=false
+
+# Does the test environment support resizing? (boolean value)
+#resize=false
+
+# Does the test environment support live migration available?
+# (boolean value)
+#live_migration=false
+
+# Does the test environment use block devices for live
+# migration (boolean value)
+#block_migration_for_live_migration=false
+
+# Does the test environment block migration support cinder
+# iSCSI volumes (boolean value)
+#block_migrate_cinder_iscsi=false
+
+
[identity]
#
@@ -157,6 +416,23 @@
#admin_password=pass
+[cli]
+
+#
+# Options defined in tempest.cli
+#
+
+# enable cli tests (boolean value)
+#enabled=true
+
+# directory where python client binaries are located (string
+# value)
+#cli_dir=/usr/local/bin
+
+# Number of seconds to wait on a CLI timeout (integer value)
+#timeout=15
+
+
[stress]
#
@@ -195,19 +471,6 @@
#default_thread_number_per_action=4
-[image-feature-enabled]
-
-#
-# Options defined in tempest.config
-#
-
-# Is the v2 image API enabled (boolean value)
-#api_v2=true
-
-# Is the v1 image API enabled (boolean value)
-#api_v1=true
-
-
[compute]
#
@@ -256,7 +519,7 @@
# (integer value)
#build_timeout=300
-# Does the test environment support snapshots? (boolean value)
+# Should the tests ssh to instances? (boolean value)
#run_ssh=false
# User name used to authenticate to an instance. (string
@@ -319,39 +582,30 @@
#shelved_offload_time=0
-[network]
+[scenario]
#
# Options defined in tempest.config
#
-# Catalog type of the Neutron service. (string value)
-#catalog_type=network
+# Directory containing image files (string value)
+#img_dir=/opt/stack/new/devstack/files/images/cirros-0.3.1-x86_64-uec
-# The network region name to use. If empty, the value of
-# identity.region is used instead. If no such region is found
-# in the service catalog, the first found one is used. (string
-# value)
-#region=
+# AMI image file name (string value)
+#ami_img_file=cirros-0.3.1-x86_64-blank.img
-# The cidr block to allocate tenant networks from (string
-# value)
-#tenant_network_cidr=10.100.0.0/16
+# ARI image file name (string value)
+#ari_img_file=cirros-0.3.1-x86_64-initrd
-# The mask bits for tenant networks (integer value)
-#tenant_network_mask_bits=28
+# AKI image file name (string value)
+#aki_img_file=cirros-0.3.1-x86_64-vmlinuz
-# Whether tenant network connectivity should be evaluated
-# directly (boolean value)
-#tenant_networks_reachable=false
+# ssh username for the image file (string value)
+#ssh_user=cirros
-# Id of the public network that provides external connectivity
-# (string value)
-#public_network_id=
-
-# Id of the public router that provides external connectivity
-# (string value)
-#public_router_id=
+# specifies how many resources to request at once. Used for
+# large operations testing. (integer value)
+#large_ops_number=0
[boto]
@@ -400,140 +654,6 @@
#build_interval=1
-[scenario]
-
-#
-# Options defined in tempest.config
-#
-
-# Directory containing image files (string value)
-#img_dir=/opt/stack/new/devstack/files/images/cirros-0.3.1-x86_64-uec
-
-# AMI image file name (string value)
-#ami_img_file=cirros-0.3.1-x86_64-blank.img
-
-# ARI image file name (string value)
-#ari_img_file=cirros-0.3.1-x86_64-initrd
-
-# AKI image file name (string value)
-#aki_img_file=cirros-0.3.1-x86_64-vmlinuz
-
-# ssh username for the image file (string value)
-#ssh_user=cirros
-
-# specifies how many resources to request at once. Used for
-# large operations testing. (integer value)
-#large_ops_number=0
-
-
-[image]
-
-#
-# Options defined in tempest.config
-#
-
-# Catalog type of the Image service. (string value)
-#catalog_type=image
-
-# The image region name to use. If empty, the value of
-# identity.region is used instead. If no such region is found
-# in the service catalog, the first found one is used. (string
-# value)
-#region=
-
-# http accessible image (string value)
-#http_image=http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz
-
-
-[compute-admin]
-
-#
-# Options defined in tempest.config
-#
-
-# Administrative Username to use for Nova API requests.
-# (string value)
-#username=admin
-
-# Administrative Tenant name to use for Nova API requests.
-# (string value)
-#tenant_name=admin
-
-# API key to use when authenticating as admin. (string value)
-#password=pass
-
-
-[cli]
-
-#
-# Options defined in tempest.cli
-#
-
-# enable cli tests (boolean value)
-#enabled=true
-
-# directory where python client binaries are located (string
-# value)
-#cli_dir=/usr/local/bin
-
-# Number of seconds to wait on a CLI timeout (integer value)
-#timeout=15
-
-
-[volume]
-
-#
-# Options defined in tempest.config
-#
-
-# Time in seconds between volume availability checks. (integer
-# value)
-#build_interval=10
-
-# Timeout in seconds to wait for a volume to becomeavailable.
-# (integer value)
-#build_timeout=300
-
-# Catalog type of the Volume Service (string value)
-#catalog_type=volume
-
-# The volume region name to use. If empty, the value of
-# identity.region is used instead. If no such region is found
-# in the service catalog, the first found one is used. (string
-# value)
-#region=
-
-# Name of the backend1 (must be declared in cinder.conf)
-# (string value)
-#backend1_name=BACKEND_1
-
-# Name of the backend2 (must be declared in cinder.conf)
-# (string value)
-#backend2_name=BACKEND_2
-
-# Backend protocol to target when creating volume types
-# (string value)
-#storage_protocol=iSCSI
-
-# Backend vendor to target when creating volume types (string
-# value)
-#vendor_name=Open Source
-
-# Disk format to use when copying a volume to image (string
-# value)
-#disk_format=raw
-
-
-[debug]
-
-#
-# Options defined in tempest.config
-#
-
-# Enable diagnostic commands (boolean value)
-#enable=true
-
-
[dashboard]
#
@@ -592,43 +712,14 @@
#max_template_size=524288
-[object-storage]
+[debug]
#
# Options defined in tempest.config
#
-# Catalog type of the Object-Storage service. (string value)
-#catalog_type=object-store
-
-# The object-storage region name to use. If empty, the value
-# of identity.region is used instead. If no such region is
-# found in the service catalog, the first found one is used.
-# (string value)
-#region=
-
-# Number of seconds to time on waiting for a containerto
-# container synchronization complete. (integer value)
-#container_sync_timeout=120
-
-# Number of seconds to wait while looping to check thestatus
-# of a container to container synchronization (integer value)
-#container_sync_interval=5
-
-# Role to add to users created for swift tests to enable
-# creating containers (string value)
-#operator_role=Member
-
-
-[network-feature-enabled]
-
-#
-# Options defined in tempest.config
-#
-
-# A list of enabled extensions with a special entry all which
-# indicates every extension is enabled (list value)
-#api_extensions=all
+# Enable diagnostic commands (boolean value)
+#enable=true
[service_available]
@@ -669,85 +760,8 @@
# value)
#horizon=true
-
-[compute-feature-enabled]
-
-#
-# Options defined in tempest.config
-#
-
-# If false, skip all nova v3 tests. (boolean value)
-#api_v3=true
-
-# If false, skip disk config tests (boolean value)
-#disk_config=true
-
-# A list of enabled extensions with a special entry all which
-# indicates every extension is enabled (list value)
-#api_extensions=all
-
-# A list of enabled v3 extensions with a special entry all
-# which indicates every extension is enabled (list value)
-#api_v3_extensions=all
-
-# Does the test environment support changing the admin
-# password? (boolean value)
-#change_password=false
-
-# Does the test environment support snapshots? (boolean value)
-#create_image=false
-
-# Does the test environment support resizing? (boolean value)
-#resize=false
-
-# Does the test environment support live migration available?
-# (boolean value)
-#live_migration=false
-
-# Does the test environment use block devices for live
-# migration (boolean value)
-#block_migration_for_live_migration=false
-
-# Does the test environment block migration support cinder
-# iSCSI volumes (boolean value)
-#block_migrate_cinder_iscsi=false
-
-
-[object-storage-feature-enabled]
-
-#
-# Options defined in tempest.config
-#
-
-# Set to True if the Container Quota middleware is enabled
-# (boolean value)
-#container_quotas=true
-
-# Set to True if the Account Quota middleware is enabled
-# (boolean value)
-#accounts_quotas=true
-
-# Set to True if the Crossdomain middleware is enabled
-# (boolean value)
-#crossdomain=true
-
-# Set to True if the TempURL middleware is enabled (boolean
+# Whether or not Savanna is expected to be available (boolean
# value)
-#tempurl=true
-
-
-[volume-feature-enabled]
-
-#
-# Options defined in tempest.config
-#
-
-# Runs Cinder multi-backend test (requires 2 backends)
-# (boolean value)
-#multi_backend=false
-
-# A list of enabled extensions with a special entry all which
-# indicates every extension is enabled (list value)
-#api_extensions=all
+#savanna=false
diff --git a/tempest/api/compute/__init__.py b/tempest/api/compute/__init__.py
index d20068e..a385f83 100644
--- a/tempest/api/compute/__init__.py
+++ b/tempest/api/compute/__init__.py
@@ -15,13 +15,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest import config
+# from tempest import config
from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
-CONFIG = config.TempestConfig()
-CREATE_IMAGE_ENABLED = CONFIG.compute_feature_enabled.create_image
-RESIZE_AVAILABLE = CONFIG.compute_feature_enabled.resize
-CHANGE_PASSWORD_AVAILABLE = CONFIG.compute_feature_enabled.change_password
-DISK_CONFIG_ENABLED = CONFIG.compute_feature_enabled.disk_config
+# CONFIG = config.CONF
+# CREATE_IMAGE_ENABLED = CONFIG.compute_feature_enabled.create_image
+# RESIZE_AVAILABLE = CONFIG.compute_feature_enabled.resize
+# CHANGE_PASSWORD_AVAILABLE = CONFIG.compute_feature_enabled.change_password
+# DISK_CONFIG_ENABLED = CONFIG.compute_feature_enabled.disk_config
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 467a6f9..609d2c6 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -18,7 +18,6 @@
from tempest.api.compute import base
from tempest.common import tempest_fixtures as fixtures
from tempest.common.utils import data_utils
-from tempest import exceptions
from tempest.test import attr
@@ -35,7 +34,6 @@
def setUpClass(cls):
super(AggregatesAdminTestJSON, cls).setUpClass()
cls.client = cls.os_adm.aggregates_client
- cls.user_client = cls.aggregates_client
cls.aggregate_name_prefix = 'test_aggregate_'
cls.az_name_prefix = 'test_az_'
@@ -141,54 +139,6 @@
(x['id'], x['name'], x['availability_zone']),
aggregates))
- @attr(type=['negative', 'gate'])
- def test_aggregate_create_as_user(self):
- # Regular user is not allowed to create an aggregate.
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- self.assertRaises(exceptions.Unauthorized,
- self.user_client.create_aggregate,
- aggregate_name)
-
- @attr(type=['negative', 'gate'])
- def test_aggregate_delete_as_user(self):
- # Regular user is not allowed to delete an aggregate.
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- resp, aggregate = self.client.create_aggregate(aggregate_name)
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
- self.assertRaises(exceptions.Unauthorized,
- self.user_client.delete_aggregate,
- aggregate['id'])
-
- @attr(type=['negative', 'gate'])
- def test_aggregate_list_as_user(self):
- # Regular user is not allowed to list aggregates.
- self.assertRaises(exceptions.Unauthorized,
- self.user_client.list_aggregates)
-
- @attr(type=['negative', 'gate'])
- def test_aggregate_get_details_as_user(self):
- # Regular user is not allowed to get aggregate details.
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- resp, aggregate = self.client.create_aggregate(aggregate_name)
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
- self.assertRaises(exceptions.Unauthorized,
- self.user_client.get_aggregate,
- aggregate['id'])
-
- @attr(type=['negative', 'gate'])
- def test_aggregate_delete_with_invalid_id(self):
- # Delete an aggregate with invalid id should raise exceptions.
- self.assertRaises(exceptions.NotFound,
- self.client.delete_aggregate, -1)
-
- @attr(type=['negative', 'gate'])
- def test_aggregate_get_details_with_invalid_id(self):
- # Get aggregate details with invalid id should raise exceptions.
- self.assertRaises(exceptions.NotFound,
- self.client.get_aggregate, -1)
-
@attr(type='gate')
def test_aggregate_add_remove_host(self):
# Add an host to the given aggregate and remove.
@@ -262,48 +212,6 @@
resp, body = admin_servers_client.get_server(server['id'])
self.assertEqual(self.host, body[self._host_key])
- @attr(type=['negative', 'gate'])
- def test_aggregate_add_non_exist_host(self):
- # Adding a non-exist host to an aggregate should raise exceptions.
- resp, hosts_all = self.os_adm.hosts_client.list_hosts()
- hosts = map(lambda x: x['host_name'], hosts_all)
- while True:
- non_exist_host = data_utils.rand_name('nonexist_host_')
- if non_exist_host not in hosts:
- break
-
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- resp, aggregate = self.client.create_aggregate(aggregate_name)
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
- self.assertRaises(exceptions.NotFound, self.client.add_host,
- aggregate['id'], non_exist_host)
-
- @attr(type=['negative', 'gate'])
- def test_aggregate_add_host_as_user(self):
- # Regular user is not allowed to add a host to an aggregate.
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- resp, aggregate = self.client.create_aggregate(aggregate_name)
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
- self.assertRaises(exceptions.Unauthorized,
- self.user_client.add_host,
- aggregate['id'], self.host)
-
- @attr(type=['negative', 'gate'])
- def test_aggregate_remove_host_as_user(self):
- # Regular user is not allowed to remove a host from an aggregate.
- self.useFixture(fixtures.LockFixture('availability_zone'))
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- resp, aggregate = self.client.create_aggregate(aggregate_name)
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
- self.client.add_host(aggregate['id'], self.host)
- self.addCleanup(self.client.remove_host, aggregate['id'], self.host)
-
- self.assertRaises(exceptions.Unauthorized,
- self.user_client.remove_host,
- aggregate['id'], self.host)
-
class AggregatesAdminTestXML(AggregatesAdminTestJSON):
_host_key = (
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
new file mode 100644
index 0000000..8506206
--- /dev/null
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -0,0 +1,196 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+# 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.compute import base
+from tempest.common import tempest_fixtures as fixtures
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class AggregatesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+ """
+ Tests Aggregates API that require admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(AggregatesAdminNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.os_adm.aggregates_client
+ cls.user_client = cls.aggregates_client
+ cls.aggregate_name_prefix = 'test_aggregate_'
+ cls.az_name_prefix = 'test_az_'
+
+ resp, hosts_all = cls.os_adm.hosts_client.list_hosts()
+ hosts = map(lambda x: x['host_name'],
+ filter(lambda y: y['service'] == 'compute', hosts_all))
+ cls.host = hosts[0]
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_create_as_user(self):
+ # Regular user is not allowed to create an aggregate.
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.create_aggregate,
+ aggregate_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_create_aggregate_name_length_less_than_1(self):
+ # the length of aggregate name should >= 1 and <=255
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_aggregate,
+ '')
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_create_aggregate_name_length_exceeds_255(self):
+ # the length of aggregate name should >= 1 and <=255
+ aggregate_name = 'a' * 256
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_aggregate,
+ aggregate_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_create_with_existent_aggregate_name(self):
+ # creating an aggregate with existent aggregate name is forbidden
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ resp, aggregate = self.client.create_aggregate(aggregate_name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+ self.assertRaises(exceptions.Conflict,
+ self.client.create_aggregate,
+ aggregate_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_delete_as_user(self):
+ # Regular user is not allowed to delete an aggregate.
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ resp, aggregate = self.client.create_aggregate(aggregate_name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.delete_aggregate,
+ aggregate['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_list_as_user(self):
+ # Regular user is not allowed to list aggregates.
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.list_aggregates)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_get_details_as_user(self):
+ # Regular user is not allowed to get aggregate details.
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ resp, aggregate = self.client.create_aggregate(aggregate_name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.get_aggregate,
+ aggregate['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_delete_with_invalid_id(self):
+ # Delete an aggregate with invalid id should raise exceptions.
+ self.assertRaises(exceptions.NotFound,
+ self.client.delete_aggregate, -1)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_get_details_with_invalid_id(self):
+ # Get aggregate details with invalid id should raise exceptions.
+ self.assertRaises(exceptions.NotFound,
+ self.client.get_aggregate, -1)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_add_non_exist_host(self):
+ # Adding a non-exist host to an aggregate should raise exceptions.
+ resp, hosts_all = self.os_adm.hosts_client.list_hosts()
+ hosts = map(lambda x: x['host_name'], hosts_all)
+ while True:
+ non_exist_host = data_utils.rand_name('nonexist_host_')
+ if non_exist_host not in hosts:
+ break
+
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ resp, aggregate = self.client.create_aggregate(aggregate_name)
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+ self.assertRaises(exceptions.NotFound, self.client.add_host,
+ aggregate['id'], non_exist_host)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_add_host_as_user(self):
+ # Regular user is not allowed to add a host to an aggregate.
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ resp, aggregate = self.client.create_aggregate(aggregate_name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.add_host,
+ aggregate['id'], self.host)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_add_existent_host(self):
+ self.useFixture(fixtures.LockFixture('availability_zone'))
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ resp, aggregate = self.client.create_aggregate(aggregate_name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+ resp, body = self.client.add_host(aggregate['id'], self.host)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.remove_host, aggregate['id'], self.host)
+
+ self.assertRaises(exceptions.Conflict, self.client.add_host,
+ aggregate['id'], self.host)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_remove_host_as_user(self):
+ # Regular user is not allowed to remove a host from an aggregate.
+ self.useFixture(fixtures.LockFixture('availability_zone'))
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ resp, aggregate = self.client.create_aggregate(aggregate_name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+ resp, body = self.client.add_host(aggregate['id'], self.host)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.remove_host, aggregate['id'], self.host)
+
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.remove_host,
+ aggregate['id'], self.host)
+
+ @attr(type=['negative', 'gate'])
+ def test_aggregate_remove_nonexistent_host(self):
+ non_exist_host = data_utils.rand_name('nonexist_host_')
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ resp, aggregate = self.client.create_aggregate(aggregate_name)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+
+ self.assertRaises(exceptions.NotFound, self.client.remove_host,
+ aggregate['id'], non_exist_host)
+
+
+class AggregatesAdminNegativeTestXML(AggregatesAdminNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index d6488c4..e1a1a5d 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -16,21 +16,20 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
from tempest.test import attr
-class AvailabilityZoneAdminTestJSON(base.BaseV2ComputeAdminTest):
+class AZAdminTestJSON(base.BaseV2ComputeAdminTest):
"""
- Tests Availability Zone API List that require admin privileges
+ Tests Availability Zone API List
"""
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(AvailabilityZoneAdminTestJSON, cls).setUpClass()
+ super(AZAdminTestJSON, cls).setUpClass()
cls.client = cls.os_adm.availability_zone_client
cls.non_adm_client = cls.availability_zone_client
@@ -57,14 +56,6 @@
self.assertEqual(200, resp.status)
self.assertTrue(len(availability_zone) > 0)
- @attr(type=['negative', 'gate'])
- def test_get_availability_zone_list_detail_with_non_admin_user(self):
- # List of availability zones and available services with
- # non-administrator user
- self.assertRaises(
- exceptions.Unauthorized,
- self.non_adm_client.get_availability_zone_list_detail)
-
-class AvailabilityZoneAdminTestXML(AvailabilityZoneAdminTestJSON):
+class AZAdminTestXML(AZAdminTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/admin/test_availability_zone_negative.py b/tempest/api/compute/admin/test_availability_zone_negative.py
new file mode 100644
index 0000000..6ba8d58
--- /dev/null
+++ b/tempest/api/compute/admin/test_availability_zone_negative.py
@@ -0,0 +1,45 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class AZAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+ """
+ Tests Availability Zone API List
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(AZAdminNegativeTestJSON, cls).setUpClass()
+ cls.non_adm_client = cls.availability_zone_client
+
+ @attr(type=['negative', 'gate'])
+ def test_get_availability_zone_list_detail_with_non_admin_user(self):
+ # List of availability zones and available services with
+ # non-administrator user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_availability_zone_list_detail)
+
+
+class AZAdminNegativeTestXML(AZAdminNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 427f728..4989d6f 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -16,7 +16,6 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
from tempest.test import attr
@@ -30,7 +29,6 @@
msg = ("%s skipped as neutron is available" % cls.__name__)
raise cls.skipException(msg)
cls.client = cls.os_adm.fixed_ips_client
- cls.non_admin_client = cls.fixed_ips_client
resp, server = cls.create_test_server(wait_until='ACTIVE')
resp, server = cls.servers_client.get_server(server['id'])
for ip_set in server['addresses']:
@@ -46,11 +44,6 @@
resp, fixed_ip = self.client.get_fixed_ip_details(self.ip)
self.assertEqual(fixed_ip['address'], self.ip)
- @attr(type=['negative', 'gate'])
- def test_list_fixed_ip_details_with_non_admin_user(self):
- self.assertRaises(exceptions.Unauthorized,
- self.non_admin_client.get_fixed_ip_details, self.ip)
-
@attr(type='gate')
def test_set_reserve(self):
body = {"reserve": "None"}
@@ -63,36 +56,6 @@
resp, body = self.client.reserve_fixed_ip(self.ip, body)
self.assertEqual(resp.status, 202)
- @attr(type=['negative', 'gate'])
- def test_set_reserve_with_non_admin_user(self):
- body = {"reserve": "None"}
- self.assertRaises(exceptions.Unauthorized,
- self.non_admin_client.reserve_fixed_ip,
- self.ip, body)
-
- @attr(type=['negative', 'gate'])
- def test_set_unreserve_with_non_admin_user(self):
- body = {"unreserve": "None"}
- self.assertRaises(exceptions.Unauthorized,
- self.non_admin_client.reserve_fixed_ip,
- self.ip, body)
-
- @attr(type=['negative', 'gate'])
- def test_set_reserve_with_invalid_ip(self):
- # NOTE(maurosr): since this exercises the same code snippet, we do it
- # only for reserve action
- body = {"reserve": "None"}
- self.assertRaises(exceptions.NotFound,
- self.client.reserve_fixed_ip,
- "my.invalid.ip", body)
-
- @attr(type=['negative', 'gate'])
- def test_fixed_ip_with_invalid_action(self):
- body = {"invalid_action": "None"}
- self.assertRaises(exceptions.BadRequest,
- self.client.reserve_fixed_ip,
- self.ip, body)
-
class FixedIPsTestXml(FixedIPsTestJson):
_interface = 'xml'
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
new file mode 100644
index 0000000..cf48f0a
--- /dev/null
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -0,0 +1,80 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class FixedIPsNegativeTestJson(base.BaseV2ComputeAdminTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(FixedIPsNegativeTestJson, cls).setUpClass()
+ if cls.config.service_available.neutron:
+ msg = ("%s skipped as neutron is available" % cls.__name__)
+ raise cls.skipException(msg)
+ cls.client = cls.os_adm.fixed_ips_client
+ cls.non_admin_client = cls.fixed_ips_client
+ resp, server = cls.create_test_server(wait_until='ACTIVE')
+ resp, server = cls.servers_client.get_server(server['id'])
+ for ip_set in server['addresses']:
+ for ip in server['addresses'][ip_set]:
+ if ip['OS-EXT-IPS:type'] == 'fixed':
+ cls.ip = ip['addr']
+ break
+ if cls.ip:
+ break
+
+ @attr(type=['negative', 'gate'])
+ def test_list_fixed_ip_details_with_non_admin_user(self):
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_admin_client.get_fixed_ip_details, self.ip)
+
+ @attr(type=['negative', 'gate'])
+ def test_set_reserve_with_non_admin_user(self):
+ body = {"reserve": "None"}
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_admin_client.reserve_fixed_ip,
+ self.ip, body)
+
+ @attr(type=['negative', 'gate'])
+ def test_set_unreserve_with_non_admin_user(self):
+ body = {"unreserve": "None"}
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_admin_client.reserve_fixed_ip,
+ self.ip, body)
+
+ @attr(type=['negative', 'gate'])
+ def test_set_reserve_with_invalid_ip(self):
+ # NOTE(maurosr): since this exercises the same code snippet, we do it
+ # only for reserve action
+ body = {"reserve": "None"}
+ self.assertRaises(exceptions.NotFound,
+ self.client.reserve_fixed_ip,
+ "my.invalid.ip", body)
+
+ @attr(type=['negative', 'gate'])
+ def test_fixed_ip_with_invalid_action(self):
+ body = {"invalid_action": "None"}
+ self.assertRaises(exceptions.BadRequest,
+ self.client.reserve_fixed_ip,
+ self.ip, body)
+
+
+class FixedIPsNegativeTestXml(FixedIPsNegativeTestJson):
+ _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index cf72e49..0fb9460 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -130,40 +130,6 @@
flag = True
self.assertTrue(flag)
- @test.attr(type=['negative', 'gate'])
- def test_get_flavor_details_for_deleted_flavor(self):
- # Delete a flavor and ensure it is not listed
- # Create a test flavor
- flavor_name = data_utils.rand_name(self.flavor_name_prefix)
- new_flavor_id = data_utils.rand_int_id(start=1000)
-
- resp, flavor = self.client.create_flavor(flavor_name,
- self.ram,
- self.vcpus, self.disk,
- new_flavor_id,
- ephemeral=self.ephemeral,
- swap=self.swap,
- rxtx=self.rxtx)
- # Delete the flavor
- new_flavor_id = flavor['id']
- resp_delete, body = self.client.delete_flavor(new_flavor_id)
- self.assertEqual(200, resp.status)
- self.assertEqual(202, resp_delete.status)
-
- # Deleted flavors can be seen via detailed GET
- resp, flavor = self.client.get_flavor_details(new_flavor_id)
- self.assertEqual(resp.status, 200)
- self.assertEqual(flavor['name'], flavor_name)
-
- # Deleted flavors should not show up in a list however
- resp, flavors = self.client.list_flavors_with_detail()
- self.assertEqual(resp.status, 200)
- flag = True
- for flavor in flavors:
- if flavor['name'] == flavor_name:
- flag = False
- self.assertTrue(flag)
-
@test.attr(type='gate')
def test_create_list_flavor_without_extra_data(self):
# Create a flavor and ensure it is listed
@@ -347,49 +313,6 @@
self.assertEqual(flavor['ram'], int(ram))
self.assertEqual(int(flavor['id']), new_flavor_id)
- @test.attr(type=['negative', 'gate'])
- def test_invalid_is_public_string(self):
- self.assertRaises(exceptions.BadRequest,
- self.client.list_flavors_with_detail,
- {'is_public': 'invalid'})
-
- @test.attr(type=['negative', 'gate'])
- def test_create_flavor_as_user(self):
- flavor_name = data_utils.rand_name(self.flavor_name_prefix)
- new_flavor_id = data_utils.rand_int_id(start=1000)
-
- self.assertRaises(exceptions.Unauthorized,
- self.user_client.create_flavor,
- flavor_name, self.ram, self.vcpus, self.disk,
- new_flavor_id, ephemeral=self.ephemeral,
- swap=self.swap, rxtx=self.rxtx)
-
- @test.attr(type=['negative', 'gate'])
- def test_delete_flavor_as_user(self):
- self.assertRaises(exceptions.Unauthorized,
- self.user_client.delete_flavor,
- self.flavor_ref_alt)
-
- @test.attr(type=['negative', 'gate'])
- def test_create_flavor_using_invalid_ram(self):
- flavor_name = data_utils.rand_name(self.flavor_name_prefix)
- new_flavor_id = data_utils.rand_int_id(start=1000)
-
- self.assertRaises(exceptions.BadRequest,
- self.client.create_flavor,
- flavor_name, -1, self.vcpus,
- self.disk, new_flavor_id)
-
- @test.attr(type=['negative', 'gate'])
- def test_create_flavor_using_invalid_vcpus(self):
- flavor_name = data_utils.rand_name(self.flavor_name_prefix)
- new_flavor_id = data_utils.rand_int_id(start=1000)
-
- self.assertRaises(exceptions.BadRequest,
- self.client.create_flavor,
- flavor_name, self.ram, 0,
- self.disk, new_flavor_id)
-
class FlavorsAdminTestXML(FlavorsAdminTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/admin/test_flavors_negative.py b/tempest/api/compute/admin/test_flavors_negative.py
new file mode 100644
index 0000000..1d9198a
--- /dev/null
+++ b/tempest/api/compute/admin/test_flavors_negative.py
@@ -0,0 +1,344 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+
+class FlavorsAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+ """
+ Tests Flavors API Create and Delete that require admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(FlavorsAdminNegativeTestJSON, cls).setUpClass()
+ if not test.is_extension_enabled('FlavorExtraData', 'compute'):
+ msg = "FlavorExtraData extension not enabled."
+ raise cls.skipException(msg)
+
+ cls.client = cls.os_adm.flavors_client
+ cls.user_client = cls.os.flavors_client
+ cls.flavor_name_prefix = 'test_flavor_'
+ cls.ram = 512
+ cls.vcpus = 1
+ cls.disk = 10
+ cls.ephemeral = 10
+ cls.swap = 1024
+ cls.rxtx = 2
+
+ def flavor_clean_up(self, flavor_id):
+ resp, body = self.client.delete_flavor(flavor_id)
+ self.assertEqual(resp.status, 202)
+ self.client.wait_for_resource_deletion(flavor_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_get_flavor_details_for_deleted_flavor(self):
+ # Delete a flavor and ensure it is not listed
+ # Create a test flavor
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+
+ # no need to specify flavor_id, we can get the flavor_id from a
+ # response of create_flavor() call.
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram,
+ self.vcpus, self.disk,
+ '',
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+ # Delete the flavor
+ new_flavor_id = flavor['id']
+ resp_delete, body = self.client.delete_flavor(new_flavor_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(202, resp_delete.status)
+
+ # Deleted flavors can be seen via detailed GET
+ resp, flavor = self.client.get_flavor_details(new_flavor_id)
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(flavor['name'], flavor_name)
+
+ # Deleted flavors should not show up in a list however
+ resp, flavors = self.client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ flag = True
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = False
+ self.assertTrue(flag)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_invalid_is_public_string(self):
+ # the 'is_public' parameter can be 'none/true/false' if it exists
+ self.assertRaises(exceptions.BadRequest,
+ self.client.list_flavors_with_detail,
+ {'is_public': 'invalid'})
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_as_user(self):
+ # only admin user can create a flavor
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.create_flavor,
+ flavor_name, self.ram, self.vcpus, self.disk,
+ new_flavor_id, ephemeral=self.ephemeral,
+ swap=self.swap, rxtx=self.rxtx)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_delete_flavor_as_user(self):
+ # only admin user can delete a flavor
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.delete_flavor,
+ self.flavor_ref_alt)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_using_invalid_ram(self):
+ # the 'ram' attribute must be positive integer
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ flavor_name, -1, self.vcpus,
+ self.disk, new_flavor_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_using_invalid_vcpus(self):
+ # the 'vcpu' attribute must be positive integer
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ flavor_name, self.ram, -1,
+ self.disk, new_flavor_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_name_length_less_than_1(self):
+ # ensure name length >= 1
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ '',
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_name_length_exceeds_255(self):
+ # ensure name do not exceed 255 characters
+ new_flavor_name = 'a' * 256
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_name(self):
+ # the regex of flavor_name is '^[\w\.\- ]*$'
+ invalid_flavor_name = data_utils.rand_name('invalid-!@#$%-')
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ invalid_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_flavor_id(self):
+ # the regex of flavor_id is '^[\w\.\- ]*$', and it cannot contain
+ # leading and/or trailing whitespace
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ invalid_flavor_id = '!@#$%'
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ invalid_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_id_length_exceeds_255(self):
+ # the length of flavor_id should not exceed 255 characters
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ invalid_flavor_id = 'a' * 256
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ invalid_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_root_gb(self):
+ # root_gb attribute should be non-negative ( >= 0) integer
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ -1,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_ephemeral_gb(self):
+ # ephemeral_gb attribute should be non-negative ( >= 0) integer
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=-1,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_swap(self):
+ # swap attribute should be non-negative ( >= 0) integer
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=-1,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_rxtx_factor(self):
+ # rxtx_factor attribute should be a positive float
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=-1.5,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_is_public(self):
+ # is_public attribute should be boolean
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='Invalid')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_already_exists(self):
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+
+ self.assertRaises(exceptions.Conflict,
+ self.client.create_flavor,
+ flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_delete_nonexistent_flavor(self):
+ nonexistent_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.NotFound,
+ self.client.delete_flavor,
+ nonexistent_flavor_id)
+
+
+class FlavorsAdminNegativeTestXML(FlavorsAdminNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index f49aae4..66d41b8 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -22,6 +22,8 @@
from tempest.test import attr
from tempest.test import skip_because
+CONF = config.CONF
+
class QuotasAdminTestJSON(base.BaseV2ComputeAdminTest):
_interface = 'json'
@@ -156,7 +158,7 @@
self.assertRaises(exceptions.OverLimit, self.create_test_server)
@skip_because(bug="1186354",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type=['negative', 'gate'])
def test_security_groups_exceed_limit(self):
# Negative test: Creation Security Groups over limit should FAIL
@@ -180,7 +182,7 @@
"sg-overlimit", "sg-desc")
@skip_because(bug="1186354",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type=['negative', 'gate'])
def test_security_groups_rules_exceed_limit(self):
# Negative test: Creation of Security Group Rules should FAIL
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
new file mode 100644
index 0000000..da2a1a1
--- /dev/null
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -0,0 +1,98 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NTT Data
+# 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.common.utils import data_utils
+from tempest import config
+from tempest.test import attr
+
+CONF = config.CONF
+
+
+class SecurityGroupsTestAdminJSON(base.BaseV2ComputeAdminTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(SecurityGroupsTestAdminJSON, cls).setUpClass()
+ cls.adm_client = cls.os_adm.security_groups_client
+ cls.client = cls.security_groups_client
+
+ def _delete_security_group(self, securitygroup_id, admin=True):
+ if admin:
+ resp, _ = self.adm_client.delete_security_group(securitygroup_id)
+ else:
+ resp, _ = self.client.delete_security_group(securitygroup_id)
+
+ self.assertEqual(202, resp.status)
+
+ @testtools.skipIf(CONF.service_available.neutron,
+ "Skipped because neutron do not support all_tenants"
+ "search filter.")
+ @attr(type='smoke')
+ def test_list_security_groups_list_all_tenants_filter(self):
+ # Admin can list security groups of all tenants
+ # List of all security groups created
+ security_group_list = []
+ # Create two security groups for a non-admin tenant
+ for i in range(2):
+ name = data_utils.rand_name('securitygroup-')
+ description = data_utils.rand_name('description-')
+ resp, securitygroup = (self.client
+ .create_security_group(name, description))
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self._delete_security_group,
+ securitygroup['id'], admin=False)
+ security_group_list.append(securitygroup)
+
+ client_tenant_id = securitygroup['tenant_id']
+ # Create two security groups for admin tenant
+ for i in range(2):
+ name = data_utils.rand_name('securitygroup-')
+ description = data_utils.rand_name('description-')
+ resp, adm_securitygroup = (self.adm_client
+ .create_security_group(name,
+ description))
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self._delete_security_group,
+ adm_securitygroup['id'])
+ security_group_list.append(adm_securitygroup)
+
+ # Fetch all security groups based on 'all_tenants' search filter
+ param = {'all_tenants': 'true'}
+ resp, fetched_list = self.adm_client.list_security_groups(params=param)
+ self.assertEqual(200, resp.status)
+ sec_group_id_list = map(lambda sg: sg['id'], fetched_list)
+ # Now check if all created Security Groups are present in fetched list
+ for sec_group in security_group_list:
+ self.assertIn(sec_group['id'], sec_group_id_list)
+
+ # Fetch all security groups for non-admin user with 'all_tenants'
+ # search filter
+ resp, fetched_list = self.client.list_security_groups(params=param)
+ self.assertEqual(200, resp.status)
+ # Now check if all created Security Groups are present in fetched list
+ for sec_group in fetched_list:
+ self.assertEqual(sec_group['tenant_id'], client_tenant_id,
+ "Failed to get all security groups for "
+ "non admin user.")
+
+
+class SecurityGroupsTestAdminXML(SecurityGroupsTestAdminJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index d18b749..9cb425a 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -267,6 +267,7 @@
cls.hypervisor_client = cls.os.hypervisor_v3_client
cls.tenant_usages_client = cls.os.tenant_usages_v3_client
cls.volumes_client = cls.os.volumes_client
+ cls.certificates_client = cls.os.certificates_v3_client
@classmethod
def create_image_from_server(cls, server_id, **kwargs):
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index 81e4f87..0b20e90 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -18,7 +18,6 @@
import uuid
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest import exceptions
from tempest.test import attr
@@ -31,14 +30,6 @@
super(FlavorsNegativeTestJSON, cls).setUpClass()
cls.client = cls.flavors_client
- # Generating a nonexistent flavor id
- resp, flavors = cls.client.list_flavors()
- flavor_ids = [flavor['id'] for flavor in flavors]
- while True:
- cls.nonexistent_flavor_id = data_utils.rand_int_id(start=999)
- if cls.nonexistent_flavor_id not in flavor_ids:
- break
-
@attr(type=['negative', 'gate'])
def test_invalid_minRam_filter(self):
self.assertRaises(exceptions.BadRequest,
@@ -52,17 +43,11 @@
{'minDisk': 'invalid'})
@attr(type=['negative', 'gate'])
- def test_get_flavor_details_for_invalid_flavor_id(self):
- # Ensure 404 returned for invalid flavor ID
- invalid_flavor_id = str(uuid.uuid4())
- self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
- invalid_flavor_id)
-
- @attr(type=['negative', 'gate'])
def test_non_existent_flavor_id(self):
# flavor details are not returned for non-existent flavors
+ nonexistent_flavor_id = str(uuid.uuid4())
self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
- self.nonexistent_flavor_id)
+ nonexistent_flavor_id)
class FlavorsNegativeTestXML(FlavorsNegativeTestJSON):
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 6387f4e..e4d03ae 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -15,11 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest import exceptions
from tempest.test import attr
@@ -78,24 +74,6 @@
finally:
self.client.delete_floating_ip(floating_ip_id)
- @attr(type=['negative', 'gate'])
- def test_get_nonexistant_floating_ip_details(self):
- # Negative test:Should not be able to GET the details
- # of non-existent floating IP
- floating_ip_id = []
- resp, body = self.client.list_floating_ips()
- for i in range(len(body)):
- floating_ip_id.append(body[i]['id'])
- # Creating a non-existent floatingIP id
- while True:
- non_exist_id = data_utils.rand_int_id(start=999)
- if self.config.service_available.neutron:
- non_exist_id = str(uuid.uuid4())
- if non_exist_id not in floating_ip_id:
- break
- self.assertRaises(exceptions.NotFound,
- self.client.get_floating_ip_details, non_exist_id)
-
@attr(type='gate')
def test_list_floating_ip_pools(self):
# Positive test:Should return the list of floating IP Pools
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
new file mode 100644
index 0000000..e7dc8ee
--- /dev/null
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -0,0 +1,48 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class FloatingIPDetailsNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(FloatingIPDetailsNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.floating_ips_client
+
+ @attr(type=['negative', 'gate'])
+ def test_get_nonexistent_floating_ip_details(self):
+ # Negative test:Should not be able to GET the details
+ # of non-existent floating IP
+ # Creating a non-existent floatingIP id
+ if self.config.service_available.neutron:
+ non_exist_id = str(uuid.uuid4())
+ else:
+ non_exist_id = data_utils.rand_int_id(start=999)
+ self.assertRaises(exceptions.NotFound,
+ self.client.get_floating_ip_details, non_exist_id)
+
+
+class FloatingIPDetailsNegativeTestXML(FloatingIPDetailsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index b0ff7ab..c711bd5 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -17,13 +17,14 @@
import testtools
-from tempest.api import compute
from tempest.api.compute import base
from tempest import clients
from tempest.common.utils import data_utils
+from tempest import config
from tempest.openstack.common import log as logging
from tempest.test import attr
+CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -84,7 +85,7 @@
resp, flavor = self.flavors_client.get_flavor_details(flavor_id)
return flavor['disk']
- @testtools.skipUnless(compute.CREATE_IMAGE_ENABLED,
+ @testtools.skipUnless(CONF.compute_feature_enabled.create_image,
'Environment unable to create images.')
@attr(type='smoke')
def test_create_delete_image(self):
@@ -117,6 +118,20 @@
self.assertEqual('204', resp['status'])
self.client.wait_for_resource_deletion(image_id)
+ @attr(type=['gate'])
+ def test_create_image_specify_multibyte_character_image_name(self):
+ if self.__class__._interface == "xml":
+ # NOTE(sdague): not entirely accurage, but we'd need a ton of work
+ # in our XML client to make this good
+ raise self.skipException("Not testable in XML")
+ # prefix character is:
+ # http://www.fileformat.info/info/unicode/char/1F4A9/index.htm
+ utf8_name = data_utils.rand_name(u'\xF0\x9F\x92\xA9')
+ resp, body = self.client.create_image(self.server_id, utf8_name)
+ image_id = data_utils.parse_image_id(resp['location'])
+ self.addCleanup(self.client.delete_image, image_id)
+ self.assertEqual('202', resp['status'])
+
class ImagesOneServerTestXML(ImagesOneServerTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 2d27b81..b8a4304 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -86,11 +86,15 @@
@skip_because(bug="1006725")
@attr(type=['negative', 'gate'])
def test_create_image_specify_multibyte_character_image_name(self):
- # Return an error if the image name has multi-byte characters
- snapshot_name = data_utils.rand_name('\xef\xbb\xbf')
+ if self.__class__._interface == "xml":
+ raise self.skipException("Not testable in XML")
+ # invalid multibyte sequence from:
+ # http://stackoverflow.com/questions/1301402/
+ # example-invalid-utf8-string
+ invalid_name = data_utils.rand_name(u'\xc3\x28')
self.assertRaises(exceptions.BadRequest,
self.client.create_image, self.server_id,
- snapshot_name)
+ invalid_name)
@attr(type=['negative', 'gate'])
def test_create_image_specify_invalid_metadata(self):
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 50b6c77..b36595c 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -17,7 +17,7 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class KeyPairsTestJSON(base.BaseV2ComputeTest):
@@ -28,14 +28,23 @@
super(KeyPairsTestJSON, cls).setUpClass()
cls.client = cls.keypairs_client
- @attr(type='gate')
+ def _delete_keypair(self, keypair_name):
+ resp, _ = self.client.delete_keypair(keypair_name)
+ self.assertEqual(202, resp.status)
+
+ def _create_keypair(self, keypair_name, pub_key=None):
+ resp, body = self.client.create_keypair(keypair_name, pub_key)
+ self.addCleanup(self._delete_keypair, keypair_name)
+ return resp, body
+
+ @test.attr(type='gate')
def test_keypairs_create_list_delete(self):
# Keypairs created should be available in the response list
# Create 3 keypairs
key_list = list()
for i in range(3):
k_name = data_utils.rand_name('keypair-')
- resp, keypair = self.client.create_keypair(k_name)
+ resp, keypair = self._create_keypair(k_name)
# Need to pop these keys so that our compare doesn't fail later,
# as the keypair dicts from list API doesn't have them.
keypair.pop('private_key')
@@ -57,16 +66,12 @@
self.assertFalse(missing_kps,
"Failed to find keypairs %s in fetched list"
% ', '.join(m_key['name'] for m_key in missing_kps))
- # Delete all the keypairs created
- for keypair in key_list:
- resp, _ = self.client.delete_keypair(keypair['name'])
- self.assertEqual(202, resp.status)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_keypair_create_delete(self):
# Keypair should be created, verified and deleted
k_name = data_utils.rand_name('keypair-')
- resp, keypair = self.client.create_keypair(k_name)
+ resp, keypair = self._create_keypair(k_name)
self.assertEqual(200, resp.status)
private_key = keypair['private_key']
key_name = keypair['name']
@@ -75,15 +80,12 @@
"to the requested name")
self.assertTrue(private_key is not None,
"Field private_key is empty or not found.")
- resp, _ = self.client.delete_keypair(k_name)
- self.assertEqual(202, resp.status)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_keypair_detail(self):
# Keypair should be created, Got details by name and deleted
k_name = data_utils.rand_name('keypair-')
- resp, keypair = self.client.create_keypair(k_name)
- self.addCleanup(self.client.delete_keypair, k_name)
+ resp, keypair = self._create_keypair(k_name)
resp, keypair_detail = self.client.get_keypair(k_name)
self.assertEqual(200, resp.status)
self.assertIn('name', keypair_detail)
@@ -95,7 +97,7 @@
self.assertTrue(public_key is not None,
"Field public_key is empty or not found.")
- @attr(type='gate')
+ @test.attr(type='gate')
def test_keypair_create_with_pub_key(self):
# Keypair should be created with a given public key
k_name = data_utils.rand_name('keypair-')
@@ -108,7 +110,7 @@
"LOeB1kYMOBaiUPLQTWXR3JpckqFIQwhIH0zoHlJvZE8hh90"
"XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
"snSA8wzBx3A/8y9Pp1B nova@ubuntu")
- resp, keypair = self.client.create_keypair(k_name, pub_key)
+ resp, keypair = self._create_keypair(k_name, pub_key)
self.assertEqual(200, resp.status)
self.assertFalse('private_key' in keypair,
"Field private_key is not empty!")
@@ -116,8 +118,6 @@
self.assertEqual(key_name, k_name,
"The created keypair name is not equal "
"to the requested name!")
- resp, _ = self.client.delete_keypair(k_name)
- self.assertEqual(202, resp.status)
class KeyPairsTestXML(KeyPairsTestJSON):
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index fad985e..621487c 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -19,7 +19,7 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class KeyPairsNegativeTestJSON(base.BaseV2ComputeTest):
@@ -30,67 +30,71 @@
super(KeyPairsNegativeTestJSON, cls).setUpClass()
cls.client = cls.keypairs_client
- @attr(type=['negative', 'gate'])
+ def _create_keypair(self, keypair_name, pub_key=None):
+ self.client.create_keypair(keypair_name, pub_key)
+ self.addCleanup(self.client.delete_keypair, keypair_name)
+
+ @test.attr(type=['negative', 'gate'])
def test_keypair_create_with_invalid_pub_key(self):
# Keypair should not be created with a non RSA public key
k_name = data_utils.rand_name('keypair-')
pub_key = "ssh-rsa JUNK nova@ubuntu"
self.assertRaises(exceptions.BadRequest,
- self.client.create_keypair, k_name, pub_key)
+ self._create_keypair, k_name, pub_key)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_keypair_delete_nonexistant_key(self):
# Non-existant key deletion should throw a proper error
k_name = data_utils.rand_name("keypair-non-existant-")
self.assertRaises(exceptions.NotFound, self.client.delete_keypair,
k_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_with_empty_public_key(self):
# Keypair should not be created with an empty public key
k_name = data_utils.rand_name("keypair-")
pub_key = ' '
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
k_name, pub_key)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
# Keypair should not be created when public key bits are too long
k_name = data_utils.rand_name("keypair-")
pub_key = 'ssh-rsa ' + 'A' * 2048 + ' openstack@ubuntu'
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
k_name, pub_key)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_with_duplicate_name(self):
# Keypairs with duplicate names should not be created
k_name = data_utils.rand_name('keypair-')
resp, _ = self.client.create_keypair(k_name)
self.assertEqual(200, resp.status)
# Now try the same keyname to create another key
- self.assertRaises(exceptions.Conflict, self.client.create_keypair,
+ self.assertRaises(exceptions.Conflict, self._create_keypair,
k_name)
resp, _ = self.client.delete_keypair(k_name)
self.assertEqual(202, resp.status)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_with_empty_name_string(self):
# Keypairs with name being an empty string should not be created
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
'')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_with_long_keynames(self):
# Keypairs with name longer than 255 chars should not be created
k_name = 'keypair-'.ljust(260, '0')
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
k_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_keypair_invalid_name(self):
# Keypairs with name being an invalid name should not be created
k_name = 'key_/.\@:'
- self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
k_name)
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index 1c38268..c0b202d 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -24,6 +24,8 @@
from tempest.test import attr
from tempest.test import skip_because
+CONF = config.CONF
+
class SecurityGroupRulesNegativeTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
@@ -34,7 +36,7 @@
cls.client = cls.security_groups_client
@skip_because(bug="1182384",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_non_existent_id(self):
# Negative test: Creation of Security Group rule should FAIL
@@ -48,7 +50,7 @@
self.client.create_security_group_rule,
parent_group_id, ip_protocol, from_port, to_port)
- @testtools.skipIf(config.TempestConfig().service_available.neutron,
+ @testtools.skipIf(CONF.service_available.neutron,
"Neutron not check the security_group_id")
@attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_invalid_id(self):
@@ -168,7 +170,7 @@
secgroup_id, ip_protocol, from_port, to_port)
@skip_because(bug="1182384",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type=['negative', 'smoke'])
def test_delete_security_group_rule_with_non_existent_id(self):
# Negative test: Deletion of Security Group rule should be FAIL
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 7cb96af..95e9171 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -25,6 +25,8 @@
from tempest.test import attr
from tempest.test import skip_because
+CONF = config.CONF
+
class SecurityGroupsTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
@@ -129,7 +131,7 @@
non_exist_id)
@skip_because(bug="1161411",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type=['negative', 'gate'])
def test_security_group_create_with_invalid_group_name(self):
# Negative test: Security Group should not be created with group name
@@ -149,7 +151,7 @@
s_description)
@skip_because(bug="1161411",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type=['negative', 'gate'])
def test_security_group_create_with_invalid_group_description(self):
# Negative test:Security Group should not be created with description
@@ -167,7 +169,7 @@
self.client.create_security_group, s_name,
s_description)
- @testtools.skipIf(config.TempestConfig().service_available.neutron,
+ @testtools.skipIf(CONF.service_available.neutron,
"Neutron allows duplicate names for security groups")
@attr(type=['negative', 'gate'])
def test_security_group_create_with_duplicate_name(self):
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 24ade96..5d62e1b 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -20,17 +20,18 @@
import netaddr
import testtools
-from tempest.api import compute
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common.utils.linux.remote_client import RemoteClient
-import tempest.config
+from tempest import config
from tempest.test import attr
+CONF = config.CONF
+
class ServersTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
- run_ssh = tempest.config.TempestConfig().compute.run_ssh
+ run_ssh = CONF.compute.run_ssh
disk_config = 'AUTO'
@classmethod
@@ -93,13 +94,6 @@
@testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
@attr(type='gate')
- def test_can_log_into_created_server(self):
- # Check that the user can authenticate with the generated password
- linux_client = RemoteClient(self.server, self.ssh_user, self.password)
- self.assertTrue(linux_client.can_authenticate())
-
- @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
- @attr(type='gate')
def test_verify_created_server_vcpus(self):
# Verify that the number of vcpus reported by the instance matches
# the amount stated by the flavor
@@ -120,7 +114,7 @@
@classmethod
def setUpClass(cls):
- if not compute.DISK_CONFIG_ENABLED:
+ if not CONF.compute_feature_enabled.disk_config:
msg = "DiskConfig extension not enabled."
raise cls.skipException(msg)
super(ServersTestManualDisk, cls).setUpClass()
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index 0121c42..358728e 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -17,17 +17,19 @@
import testtools
-from tempest.api import compute
from tempest.api.compute import base
+from tempest import config
from tempest.test import attr
+CONF = config.CONF
+
class ServerDiskConfigTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
@classmethod
def setUpClass(cls):
- if not compute.DISK_CONFIG_ENABLED:
+ if not CONF.compute_feature_enabled.disk_config:
msg = "DiskConfig extension not enabled."
raise cls.skipException(msg)
super(ServerDiskConfigTestJSON, cls).setUpClass()
@@ -85,7 +87,8 @@
else:
return self.flavor_ref
- @testtools.skipUnless(compute.RESIZE_AVAILABLE, 'Resize not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+ 'Resize not available.')
@attr(type='gate')
def test_resize_server_from_manual_to_auto(self):
# A server should be resized from manual to auto disk config
@@ -101,7 +104,8 @@
resp, server = self.client.get_server(self.server_id)
self.assertEqual('AUTO', server['OS-DCF:diskConfig'])
- @testtools.skipUnless(compute.RESIZE_AVAILABLE, 'Resize not available.')
+ @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+ 'Resize not available.')
@attr(type='gate')
def test_resize_server_from_auto_to_manual(self):
# A server should be resized from auto to manual disk config
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 4cbf94d..3748e37 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -23,6 +23,8 @@
from tempest.test import attr
from tempest.test import skip_because
+CONF = config.CONF
+
class ListServerFiltersTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
@@ -210,7 +212,7 @@
self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
@skip_because(bug="1182883",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type='gate')
def test_list_servers_filtered_by_ip_regex(self):
# Filter servers by regex ip
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 5552d0b..d985d2b 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -20,21 +20,21 @@
import testtools
-from tempest.api import compute
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common.utils.linux.remote_client import RemoteClient
-import tempest.config
+from tempest import config
from tempest import exceptions
from tempest.test import attr
from tempest.test import skip_because
+CONF = config.CONF
+
class ServerActionsTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
- resize_available = tempest.config.TempestConfig().\
- compute_feature_enabled.resize
- run_ssh = tempest.config.TempestConfig().compute.run_ssh
+ resize_available = CONF.compute_feature_enabled.resize
+ run_ssh = CONF.compute.run_ssh
def setUp(self):
# NOTE(afazekas): Normally we use the same server with all test cases,
@@ -53,7 +53,7 @@
cls.client = cls.servers_client
cls.server_id = cls.rebuild_server(None)
- @testtools.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
+ @testtools.skipUnless(CONF.compute_feature_enabled.change_password,
'Change password not available.')
@attr(type='gate')
def test_change_server_password(self):
@@ -67,7 +67,7 @@
# Verify that the user can authenticate with the new password
resp, server = self.client.get_server(self.server_id)
linux_client = RemoteClient(server, self.ssh_user, new_password)
- self.assertTrue(linux_client.can_authenticate())
+ linux_client.validate_authentication()
@attr(type='smoke')
def test_reboot_server_hard(self):
@@ -141,7 +141,7 @@
if self.run_ssh:
# Verify that the user can authenticate with the provided password
linux_client = RemoteClient(server, self.ssh_user, password)
- self.assertTrue(linux_client.can_authenticate())
+ linux_client.validate_authentication()
@attr(type='gate')
def test_rebuild_server_in_stop_state(self):
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 8142250..6532032 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -17,13 +17,12 @@
import base64
import sys
-import uuid
from tempest.api.compute import base
from tempest import clients
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ServersNegativeTestJSON(base.BaseV2ComputeTest):
@@ -40,13 +39,12 @@
def setUpClass(cls):
super(ServersNegativeTestJSON, cls).setUpClass()
cls.client = cls.servers_client
- cls.img_client = cls.images_client
cls.alt_os = clients.AltManager()
cls.alt_client = cls.alt_os.servers_client
resp, server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_server_name_blank(self):
# Create a server with name parameter empty
@@ -54,7 +52,7 @@
self.create_test_server,
name='')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_personality_file_contents_not_encoded(self):
# Use an unencoded file when creating a server with personality
@@ -66,7 +64,7 @@
self.create_test_server,
personality=person)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_invalid_image(self):
# Create a server with an unknown image
@@ -74,7 +72,7 @@
self.create_test_server,
image_id=-1)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_invalid_flavor(self):
# Create a server with an unknown flavor
@@ -82,7 +80,7 @@
self.create_test_server,
flavor=-1,)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_invalid_access_ip_v4_address(self):
# An access IPv4 address must match a valid address pattern
@@ -90,7 +88,7 @@
self.assertRaises(exceptions.BadRequest,
self.create_test_server, accessIPv4=IPv4)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_invalid_ip_v6_address(self):
# An access IPv6 address must match a valid address pattern
@@ -99,34 +97,35 @@
self.assertRaises(exceptions.BadRequest,
self.create_test_server, accessIPv6=IPv6)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resize_nonexistent_server(self):
- nonexistent_server = str(uuid.uuid4())
+ # Resize a non-existent server
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound,
self.client.resize,
nonexistent_server, self.flavor_ref)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resize_server_with_non_existent_flavor(self):
# Resize a server with non-existent flavor
- nonexistent_flavor = str(uuid.uuid4())
+ nonexistent_flavor = data_utils.rand_uuid()
self.assertRaises(exceptions.BadRequest, self.client.resize,
self.server_id, flavor_ref=nonexistent_flavor)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resize_server_with_null_flavor(self):
# Resize a server with null flavor
self.assertRaises(exceptions.BadRequest, self.client.resize,
self.server_id, flavor_ref="")
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_reboot_non_existent_server(self):
# Reboot a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.reboot,
nonexistent_server, 'SOFT')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_pause_paused_server(self):
# Pause a paused server.
self.client.pause_server(self.server_id)
@@ -137,7 +136,7 @@
self.client.pause_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_rebuild_reboot_deleted_server(self):
# Rebuild and Reboot a deleted server
_, server = self.create_test_server()
@@ -150,10 +149,10 @@
self.assertRaises(exceptions.NotFound, self.client.reboot,
server['id'], 'SOFT')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_rebuild_non_existent_server(self):
# Rebuild a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
meta = {'rebuild': 'server'}
new_name = data_utils.rand_name('server')
file_contents = 'Test server rebuild.'
@@ -167,7 +166,7 @@
personality=personality,
adminPass='rebuild')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_numeric_server_name(self):
# Create a server with a numeric name
if self.__class__._interface == "xml":
@@ -178,7 +177,7 @@
self.create_test_server,
name=server_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_server_name_length_exceeds_256(self):
# Create a server with name length exceeding 256 characters
@@ -187,7 +186,7 @@
self.create_test_server,
name=server_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_invalid_network_uuid(self):
# Pass invalid network uuid while creating a server
@@ -197,7 +196,7 @@
self.create_test_server,
networks=networks)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_non_existant_keypair(self):
# Pass a non-existent keypair while creating a server
@@ -206,7 +205,7 @@
self.create_test_server,
key_name=key_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_server_metadata_exceeds_length_limit(self):
# Pass really long metadata while creating a server
@@ -215,7 +214,7 @@
self.create_test_server,
meta=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_name_of_non_existent_server(self):
# Update name of a non-existent server
@@ -225,7 +224,7 @@
self.assertRaises(exceptions.NotFound, self.client.update_server,
server_name, name=new_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_server_set_empty_name(self):
# Update name of the server to an empty string
@@ -235,7 +234,7 @@
self.assertRaises(exceptions.BadRequest, self.client.update_server,
server_name, name=new_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_server_of_another_tenant(self):
# Update name of a server that belongs to another tenant
@@ -244,7 +243,7 @@
self.alt_client.update_server, self.server_id,
name=new_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_server_name_length_exceeds_256(self):
# Update name of server exceed the name length limit
@@ -254,34 +253,35 @@
self.server_id,
name=new_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_non_existent_server(self):
# Delete a non existent server
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.delete_server,
- '999erra43')
+ nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_a_server_of_another_tenant(self):
# Delete a server that belongs to another tenant
self.assertRaises(exceptions.NotFound,
self.alt_client.delete_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_server_pass_negative_id(self):
# Pass an invalid string parameter to delete server
self.assertRaises(exceptions.NotFound, self.client.delete_server, -1)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_server_pass_id_exceeding_length_limit(self):
# Pass a server ID that exceeds length limit to delete server
self.assertRaises(exceptions.NotFound, self.client.delete_server,
sys.maxint + 1)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_nonexistent_security_group(self):
# Create a server with a nonexistent security group
@@ -290,49 +290,49 @@
self.create_test_server,
security_groups=security_groups)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_non_existent_server(self):
# Get a non existent server details
-
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.get_server,
- '999erra43')
+ nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_stop_non_existent_server(self):
# Stop a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.servers_client.stop,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_pause_non_existent_server(self):
# pause a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.pause_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unpause_non_existent_server(self):
# unpause a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.unpause_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unpause_server_invalid_state(self):
# unpause an active server.
self.assertRaises(exceptions.Conflict,
self.client.unpause_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_suspend_non_existent_server(self):
# suspend a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.suspend_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_suspend_server_invalid_state(self):
# suspend a suspended server.
resp, _ = self.client.suspend_server(self.server_id)
@@ -344,66 +344,66 @@
self.client.suspend_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resume_non_existent_server(self):
# resume a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.resume_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resume_server_invalid_state(self):
# resume an active server.
self.assertRaises(exceptions.Conflict,
self.client.resume_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_console_output_of_non_existent_server(self):
# get the console output for a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound,
self.client.get_console_output,
nonexistent_server, 10)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_force_delete_nonexistent_server_id(self):
- non_existent_server_id = str(uuid.uuid4())
-
+ # force-delete a non existent server
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound,
self.client.force_delete_server,
- non_existent_server_id)
+ nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_force_delete_server_invalid_state(self):
# we can only force-delete a server in 'soft-delete' state
self.assertRaises(exceptions.Conflict,
self.client.force_delete_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_restore_nonexistent_server_id(self):
- non_existent_server_id = str(uuid.uuid4())
-
+ # restore-delete a non existent server
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound,
self.client.restore_soft_deleted_server,
- non_existent_server_id)
+ nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_restore_server_invalid_state(self):
# we can only restore-delete a server in 'soft-delete' state
self.assertRaises(exceptions.Conflict,
self.client.restore_soft_deleted_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_shelve_non_existent_server(self):
# shelve a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.shelve_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_shelve_shelved_server(self):
# shelve a shelved server.
resp, server = self.client.shelve_server(self.server_id)
@@ -430,14 +430,14 @@
self.client.shelve_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unshelve_non_existent_server(self):
# unshelve a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.unshelve_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unshelve_server_invalid_state(self):
# unshelve an active server.
self.assertRaises(exceptions.Conflict,
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 77ada0b..164c6df 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -28,7 +28,7 @@
class VirtualInterfacesTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
- CONF = config.TempestConfig()
+ CONF = config.CONF
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/compute/test_auth_token.py b/tempest/api/compute/test_auth_token.py
index ffeede8..e52c415 100644
--- a/tempest/api/compute/test_auth_token.py
+++ b/tempest/api/compute/test_auth_token.py
@@ -18,6 +18,8 @@
from tempest.api.compute import base
import tempest.config as config
+CONF = config.CONF
+
class AuthTokenTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
@@ -37,7 +39,7 @@
# picking list_servers because it's easy.
self.servers_v2.list_servers()
- @testtools.skipIf(not config.TempestConfig().identity.uri_v3,
+ @testtools.skipIf(not CONF.identity.uri_v3,
'v3 auth client not configured')
def test_v3_token(self):
# Can get a token using v3 of the identity API and use that to perform
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index 8f1e446..b0bffc4 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -17,18 +17,31 @@
from tempest.api.compute import base
-from tempest.test import attr
+from tempest import test
+import testtools
class ExtensionsTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
- @attr(type='gate')
+ @testtools.skipIf(not test.is_extension_enabled('os-consoles', 'compute'),
+ 'os-consoles extension not enabled.')
+ @test.attr(type='gate')
def test_list_extensions(self):
# List of all extensions
resp, extensions = self.extensions_client.list_extensions()
self.assertIn("extensions", extensions)
self.assertEqual(200, resp.status)
+ self.assertTrue(self.extensions_client.is_enabled("Consoles"))
+
+ @testtools.skipIf(not test.is_extension_enabled('os-consoles', 'compute'),
+ 'os-consoles extension not enabled.')
+ @test.attr(type='gate')
+ def test_get_extension(self):
+ # get the specified extensions
+ resp, extension = self.extensions_client.get_extension('os-consoles')
+ self.assertEqual(200, resp.status)
+ self.assertEqual('os-consoles', extension['alias'])
class ExtensionsTestXML(ExtensionsTestJSON):
diff --git a/tempest/api/compute/test_live_block_migration.py b/tempest/api/compute/test_live_block_migration.py
index a7b6cd2..d2a3d28 100644
--- a/tempest/api/compute/test_live_block_migration.py
+++ b/tempest/api/compute/test_live_block_migration.py
@@ -30,7 +30,7 @@
_host_key = 'OS-EXT-SRV-ATTR:host'
_interface = 'json'
- CONF = config.TempestConfig()
+ CONF = config.CONF
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/compute/v3/admin/test_availability_zone.py b/tempest/api/compute/v3/admin/test_availability_zone.py
index ff2765c..dca556f 100644
--- a/tempest/api/compute/v3/admin/test_availability_zone.py
+++ b/tempest/api/compute/v3/admin/test_availability_zone.py
@@ -16,21 +16,20 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
from tempest.test import attr
-class AvailabilityZoneAdminV3TestJSON(base.BaseV3ComputeAdminTest):
+class AZAdminV3TestJSON(base.BaseV3ComputeAdminTest):
"""
- Tests Availability Zone API List that require admin privileges
+ Tests Availability Zone API List
"""
_interface = 'json'
@classmethod
def setUpClass(cls):
- super(AvailabilityZoneAdminV3TestJSON, cls).setUpClass()
+ super(AZAdminV3TestJSON, cls).setUpClass()
cls.client = cls.availability_zone_admin_client
cls.non_adm_client = cls.availability_zone_client
@@ -57,14 +56,6 @@
self.assertEqual(200, resp.status)
self.assertTrue(len(availability_zone) > 0)
- @attr(type=['negative', 'gate'])
- def test_get_availability_zone_list_detail_with_non_admin_user(self):
- # List of availability zones and available services with
- # non-administrator user
- self.assertRaises(
- exceptions.Unauthorized,
- self.non_adm_client.get_availability_zone_list_detail)
-
-class AvailabilityZoneAdminV3TestXML(AvailabilityZoneAdminV3TestJSON):
+class AZAdminV3TestXML(AZAdminV3TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_availability_zone_negative.py b/tempest/api/compute/v3/admin/test_availability_zone_negative.py
new file mode 100644
index 0000000..93a57e3
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_availability_zone_negative.py
@@ -0,0 +1,47 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class AZAdminNegativeV3TestJSON(base.BaseV3ComputeAdminTest):
+
+ """
+ Tests Availability Zone API List
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(AZAdminNegativeV3TestJSON, cls).setUpClass()
+ cls.client = cls.availability_zone_admin_client
+ cls.non_adm_client = cls.availability_zone_client
+
+ @attr(type=['negative', 'gate'])
+ def test_get_availability_zone_list_detail_with_non_admin_user(self):
+ # List of availability zones and available services with
+ # non-administrator user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_availability_zone_list_detail)
+
+
+class AZAdminNegativeV3TestXML(AZAdminNegativeV3TestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py b/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py
new file mode 100644
index 0000000..cea6e92
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py
@@ -0,0 +1,64 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM 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 datetime
+
+from tempest.api.compute import base
+from tempest.test import attr
+import urllib
+
+
+class InstanceUsageAuditLogTestJSON(base.BaseV2ComputeAdminTest):
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(InstanceUsageAuditLogTestJSON, cls).setUpClass()
+ cls.adm_client = cls.os_adm.instance_usages_audit_log_client
+
+ @attr(type='gate')
+ def test_list_instance_usage_audit_logs(self):
+ # list instance usage audit logs
+ resp, body = self.adm_client.list_instance_usage_audit_logs()
+ self.assertEqual(200, resp.status)
+ expected_items = ['total_errors', 'total_instances', 'log',
+ 'num_hosts_running', 'num_hosts_done',
+ 'num_hosts', 'hosts_not_run', 'overall_status',
+ 'period_ending', 'period_beginning',
+ 'num_hosts_not_run']
+ for item in expected_items:
+ self.assertIn(item, body)
+
+ @attr(type='gate')
+ def test_get_instance_usage_audit_log(self):
+ # Get instance usage audit log before specified time
+ now = datetime.datetime.now()
+ resp, body = self.adm_client.get_instance_usage_audit_log(
+ urllib.quote(now.strftime("%Y-%m-%d %H:%M:%S")))
+
+ self.assertEqual(200, resp.status)
+ expected_items = ['total_errors', 'total_instances', 'log',
+ 'num_hosts_running', 'num_hosts_done', 'num_hosts',
+ 'hosts_not_run', 'overall_status', 'period_ending',
+ 'period_beginning', 'num_hosts_not_run']
+ for item in expected_items:
+ self.assertIn(item, body)
+
+
+class InstanceUsageAuditLogTestXML(InstanceUsageAuditLogTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py
new file mode 100644
index 0000000..dcf41c5
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py
@@ -0,0 +1,56 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM 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 datetime
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+import urllib
+
+
+class InstanceUsageAuditLogNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(InstanceUsageAuditLogNegativeTestJSON, cls).setUpClass()
+ cls.adm_client = cls.os_adm.instance_usages_audit_log_client
+
+ @attr(type=['negative', 'gate'])
+ def test_instance_usage_audit_logs_with_nonadmin_user(self):
+ # the instance_usage_audit_logs API just can be accessed by admin user
+ self.assertRaises(exceptions.Unauthorized,
+ self.instance_usages_audit_log_client.
+ list_instance_usage_audit_logs)
+ now = datetime.datetime.now()
+ self.assertRaises(exceptions.Unauthorized,
+ self.instance_usages_audit_log_client.
+ get_instance_usage_audit_log,
+ urllib.quote(now.strftime("%Y-%m-%d %H:%M:%S")))
+
+ @attr(type=['negative', 'gate'])
+ def test_get_instance_usage_audit_logs_with_invalid_time(self):
+ self.assertRaises(exceptions.BadRequest,
+ self.adm_client.get_instance_usage_audit_log,
+ "invalid_time")
+
+
+class InstanceUsageAuditLogNegativeTestXML(
+ InstanceUsageAuditLogNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_quotas.py b/tempest/api/compute/v3/admin/test_quotas.py
index f49aae4..66d41b8 100644
--- a/tempest/api/compute/v3/admin/test_quotas.py
+++ b/tempest/api/compute/v3/admin/test_quotas.py
@@ -22,6 +22,8 @@
from tempest.test import attr
from tempest.test import skip_because
+CONF = config.CONF
+
class QuotasAdminTestJSON(base.BaseV2ComputeAdminTest):
_interface = 'json'
@@ -156,7 +158,7 @@
self.assertRaises(exceptions.OverLimit, self.create_test_server)
@skip_because(bug="1186354",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type=['negative', 'gate'])
def test_security_groups_exceed_limit(self):
# Negative test: Creation Security Groups over limit should FAIL
@@ -180,7 +182,7 @@
"sg-overlimit", "sg-desc")
@skip_because(bug="1186354",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type=['negative', 'gate'])
def test_security_groups_rules_exceed_limit(self):
# Negative test: Creation of Security Group Rules should FAIL
diff --git a/tempest/api/compute/v3/admin/test_services.py b/tempest/api/compute/v3/admin/test_services.py
index 64135ed..602f914 100644
--- a/tempest/api/compute/v3/admin/test_services.py
+++ b/tempest/api/compute/v3/admin/test_services.py
@@ -17,7 +17,6 @@
# under the License.
from tempest.api.compute import base
-from tempest import exceptions
from tempest.test import attr
@@ -33,7 +32,6 @@
def setUpClass(cls):
super(ServicesAdminV3TestJSON, cls).setUpClass()
cls.client = cls.services_admin_client
- cls.non_admin_client = cls.services_client
@attr(type='gate')
def test_list_services(self):
@@ -41,11 +39,6 @@
self.assertEqual(200, resp.status)
self.assertNotEqual(0, len(services))
- @attr(type=['negative', 'gate'])
- def test_list_services_with_non_admin_user(self):
- self.assertRaises(exceptions.Unauthorized,
- self.non_admin_client.list_services)
-
@attr(type='gate')
def test_get_service_by_service_binary_name(self):
binary_name = 'nova-compute'
@@ -74,15 +67,6 @@
# on order.
self.assertEqual(sorted(s1), sorted(s2))
- @attr(type=['negative', 'gate'])
- def test_get_service_by_invalid_params(self):
- # return all services if send the request with invalid parameter
- resp, services = self.client.list_services()
- params = {'xxx': 'nova-compute'}
- resp, services_xxx = self.client.list_services(params)
- self.assertEqual(200, resp.status)
- self.assertEqual(len(services), len(services_xxx))
-
@attr(type='gate')
def test_get_service_by_service_and_host_name(self):
resp, services = self.client.list_services()
@@ -95,24 +79,6 @@
self.assertEqual(host_name, services[0]['host'])
self.assertEqual(binary_name, services[0]['binary'])
- @attr(type=['negative', 'gate'])
- def test_get_service_by_invalid_service_and_valid_host(self):
- resp, services = self.client.list_services()
- host_name = services[0]['host']
- params = {'host': host_name, 'binary': 'xxx'}
- resp, services = self.client.list_services(params)
- self.assertEqual(200, resp.status)
- self.assertEqual(0, len(services))
-
- @attr(type=['negative', 'gate'])
- def test_get_service_with_valid_service_and_invalid_host(self):
- resp, services = self.client.list_services()
- binary_name = services[0]['binary']
- params = {'host': 'xxx', 'binary': binary_name}
- resp, services = self.client.list_services(params)
- self.assertEqual(200, resp.status)
- self.assertEqual(0, len(services))
-
class ServicesAdminV3TestXML(ServicesAdminV3TestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_services_negative.py b/tempest/api/compute/v3/admin/test_services_negative.py
new file mode 100644
index 0000000..337c051
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_services_negative.py
@@ -0,0 +1,72 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ServicesAdminNegativeV3TestJSON(base.BaseV3ComputeAdminTest):
+
+ """
+ Tests Services API. List and Enable/Disable require admin privileges.
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServicesAdminNegativeV3TestJSON, cls).setUpClass()
+ cls.client = cls.services_admin_client
+ cls.non_admin_client = cls.services_client
+
+ @attr(type=['negative', 'gate'])
+ def test_list_services_with_non_admin_user(self):
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_admin_client.list_services)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_service_by_invalid_params(self):
+ # return all services if send the request with invalid parameter
+ resp, services = self.client.list_services()
+ params = {'xxx': 'nova-compute'}
+ resp, services_xxx = self.client.list_services(params)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(len(services), len(services_xxx))
+
+ @attr(type=['negative', 'gate'])
+ def test_get_service_by_invalid_service_and_valid_host(self):
+ resp, services = self.client.list_services()
+ host_name = services[0]['host']
+ params = {'host': host_name, 'binary': 'xxx'}
+ resp, services = self.client.list_services(params)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(0, len(services))
+
+ @attr(type=['negative', 'gate'])
+ def test_get_service_with_valid_service_and_invalid_host(self):
+ resp, services = self.client.list_services()
+ binary_name = services[0]['binary']
+ params = {'host': 'xxx', 'binary': binary_name}
+ resp, services = self.client.list_services(params)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(0, len(services))
+
+
+class ServicesAdminNegativeV3TestXML(ServicesAdminNegativeV3TestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/certificates/__init__.py b/tempest/api/compute/v3/certificates/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/compute/v3/certificates/__init__.py
diff --git a/tempest/api/compute/v3/certificates/test_certificates.py b/tempest/api/compute/v3/certificates/test_certificates.py
new file mode 100644
index 0000000..fa6f191
--- /dev/null
+++ b/tempest/api/compute/v3/certificates/test_certificates.py
@@ -0,0 +1,40 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest.test import attr
+
+
+class CertificatesV3TestJSON(base.BaseV3ComputeTest):
+ _interface = 'json'
+
+ @attr(type='gate')
+ def test_create_and_get_root_certificate(self):
+ # create certificates
+ resp, create_body = self.certificates_client.create_certificate()
+ self.assertEqual(201, resp.status)
+ self.assertIn('data', create_body)
+ self.assertIn('private_key', create_body)
+ # get the root certificate
+ resp, body = self.certificates_client.get_certificate('root')
+ self.assertEqual(200, resp.status)
+ self.assertIn('data', body)
+ self.assertIn('private_key', body)
+
+
+class CertificatesV3TestXML(CertificatesV3TestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/keypairs/__init__.py b/tempest/api/compute/v3/keypairs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/compute/v3/keypairs/__init__.py
diff --git a/tempest/api/compute/v3/keypairs/test_keypairs.py b/tempest/api/compute/v3/keypairs/test_keypairs.py
new file mode 100644
index 0000000..b36595c
--- /dev/null
+++ b/tempest/api/compute/v3/keypairs/test_keypairs.py
@@ -0,0 +1,124 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class KeyPairsTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(KeyPairsTestJSON, cls).setUpClass()
+ cls.client = cls.keypairs_client
+
+ def _delete_keypair(self, keypair_name):
+ resp, _ = self.client.delete_keypair(keypair_name)
+ self.assertEqual(202, resp.status)
+
+ def _create_keypair(self, keypair_name, pub_key=None):
+ resp, body = self.client.create_keypair(keypair_name, pub_key)
+ self.addCleanup(self._delete_keypair, keypair_name)
+ return resp, body
+
+ @test.attr(type='gate')
+ def test_keypairs_create_list_delete(self):
+ # Keypairs created should be available in the response list
+ # Create 3 keypairs
+ key_list = list()
+ for i in range(3):
+ k_name = data_utils.rand_name('keypair-')
+ resp, keypair = self._create_keypair(k_name)
+ # Need to pop these keys so that our compare doesn't fail later,
+ # as the keypair dicts from list API doesn't have them.
+ keypair.pop('private_key')
+ keypair.pop('user_id')
+ self.assertEqual(200, resp.status)
+ key_list.append(keypair)
+ # Fetch all keypairs and verify the list
+ # has all created keypairs
+ resp, fetched_list = self.client.list_keypairs()
+ self.assertEqual(200, resp.status)
+ # We need to remove the extra 'keypair' element in the
+ # returned dict. See comment in keypairs_client.list_keypairs()
+ new_list = list()
+ for keypair in fetched_list:
+ new_list.append(keypair['keypair'])
+ fetched_list = new_list
+ # Now check if all the created keypairs are in the fetched list
+ missing_kps = [kp for kp in key_list if kp not in fetched_list]
+ self.assertFalse(missing_kps,
+ "Failed to find keypairs %s in fetched list"
+ % ', '.join(m_key['name'] for m_key in missing_kps))
+
+ @test.attr(type='gate')
+ def test_keypair_create_delete(self):
+ # Keypair should be created, verified and deleted
+ k_name = data_utils.rand_name('keypair-')
+ resp, keypair = self._create_keypair(k_name)
+ self.assertEqual(200, resp.status)
+ private_key = keypair['private_key']
+ key_name = keypair['name']
+ self.assertEqual(key_name, k_name,
+ "The created keypair name is not equal "
+ "to the requested name")
+ self.assertTrue(private_key is not None,
+ "Field private_key is empty or not found.")
+
+ @test.attr(type='gate')
+ def test_get_keypair_detail(self):
+ # Keypair should be created, Got details by name and deleted
+ k_name = data_utils.rand_name('keypair-')
+ resp, keypair = self._create_keypair(k_name)
+ resp, keypair_detail = self.client.get_keypair(k_name)
+ self.assertEqual(200, resp.status)
+ self.assertIn('name', keypair_detail)
+ self.assertIn('public_key', keypair_detail)
+ self.assertEqual(keypair_detail['name'], k_name,
+ "The created keypair name is not equal "
+ "to requested name")
+ public_key = keypair_detail['public_key']
+ self.assertTrue(public_key is not None,
+ "Field public_key is empty or not found.")
+
+ @test.attr(type='gate')
+ def test_keypair_create_with_pub_key(self):
+ # Keypair should be created with a given public key
+ k_name = data_utils.rand_name('keypair-')
+ pub_key = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCs"
+ "Ne3/1ILNCqFyfYWDeTKLD6jEXC2OQHLmietMWW+/vd"
+ "aZq7KZEwO0jhglaFjU1mpqq4Gz5RX156sCTNM9vRbw"
+ "KAxfsdF9laBYVsex3m3Wmui3uYrKyumsoJn2g9GNnG1P"
+ "I1mrVjZ61i0GY3khna+wzlTpCCmy5HNlrmbj3XLqBUpip"
+ "TOXmsnr4sChzC53KCd8LXuwc1i/CZPvF+3XipvAgFSE53pCt"
+ "LOeB1kYMOBaiUPLQTWXR3JpckqFIQwhIH0zoHlJvZE8hh90"
+ "XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
+ "snSA8wzBx3A/8y9Pp1B nova@ubuntu")
+ resp, keypair = self._create_keypair(k_name, pub_key)
+ self.assertEqual(200, resp.status)
+ self.assertFalse('private_key' in keypair,
+ "Field private_key is not empty!")
+ key_name = keypair['name']
+ self.assertEqual(key_name, k_name,
+ "The created keypair name is not equal "
+ "to the requested name!")
+
+
+class KeyPairsTestXML(KeyPairsTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/keypairs/test_keypairs_negative.py b/tempest/api/compute/v3/keypairs/test_keypairs_negative.py
new file mode 100644
index 0000000..621487c
--- /dev/null
+++ b/tempest/api/compute/v3/keypairs/test_keypairs_negative.py
@@ -0,0 +1,102 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# Copyright 2013 IBM Corp
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# 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.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+
+class KeyPairsNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(KeyPairsNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.keypairs_client
+
+ def _create_keypair(self, keypair_name, pub_key=None):
+ self.client.create_keypair(keypair_name, pub_key)
+ self.addCleanup(self.client.delete_keypair, keypair_name)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_keypair_create_with_invalid_pub_key(self):
+ # Keypair should not be created with a non RSA public key
+ k_name = data_utils.rand_name('keypair-')
+ pub_key = "ssh-rsa JUNK nova@ubuntu"
+ self.assertRaises(exceptions.BadRequest,
+ self._create_keypair, k_name, pub_key)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_keypair_delete_nonexistant_key(self):
+ # Non-existant key deletion should throw a proper error
+ k_name = data_utils.rand_name("keypair-non-existant-")
+ self.assertRaises(exceptions.NotFound, self.client.delete_keypair,
+ k_name)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_with_empty_public_key(self):
+ # Keypair should not be created with an empty public key
+ k_name = data_utils.rand_name("keypair-")
+ pub_key = ' '
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ k_name, pub_key)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
+ # Keypair should not be created when public key bits are too long
+ k_name = data_utils.rand_name("keypair-")
+ pub_key = 'ssh-rsa ' + 'A' * 2048 + ' openstack@ubuntu'
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ k_name, pub_key)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_with_duplicate_name(self):
+ # Keypairs with duplicate names should not be created
+ k_name = data_utils.rand_name('keypair-')
+ resp, _ = self.client.create_keypair(k_name)
+ self.assertEqual(200, resp.status)
+ # Now try the same keyname to create another key
+ self.assertRaises(exceptions.Conflict, self._create_keypair,
+ k_name)
+ resp, _ = self.client.delete_keypair(k_name)
+ self.assertEqual(202, resp.status)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_with_empty_name_string(self):
+ # Keypairs with name being an empty string should not be created
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ '')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_with_long_keynames(self):
+ # Keypairs with name longer than 255 chars should not be created
+ k_name = 'keypair-'.ljust(260, '0')
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ k_name)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_keypair_invalid_name(self):
+ # Keypairs with name being an invalid name should not be created
+ k_name = 'key_/.\@:'
+ self.assertRaises(exceptions.BadRequest, self._create_keypair,
+ k_name)
+
+
+class KeyPairsNegativeTestXML(KeyPairsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_create_server.py b/tempest/api/compute/v3/servers/test_create_server.py
index 24ade96..94175ab 100644
--- a/tempest/api/compute/v3/servers/test_create_server.py
+++ b/tempest/api/compute/v3/servers/test_create_server.py
@@ -20,17 +20,18 @@
import netaddr
import testtools
-from tempest.api import compute
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common.utils.linux.remote_client import RemoteClient
-import tempest.config
+from tempest import config
from tempest.test import attr
+CONF = config.CONF
+
class ServersTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
- run_ssh = tempest.config.TempestConfig().compute.run_ssh
+ run_ssh = CONF.compute.run_ssh
disk_config = 'AUTO'
@classmethod
@@ -120,7 +121,7 @@
@classmethod
def setUpClass(cls):
- if not compute.DISK_CONFIG_ENABLED:
+ if not CONF.compute_feature_enabled.disk_config:
msg = "DiskConfig extension not enabled."
raise cls.skipException(msg)
super(ServersTestManualDisk, cls).setUpClass()
diff --git a/tempest/api/compute/v3/servers/test_list_server_filters.py b/tempest/api/compute/v3/servers/test_list_server_filters.py
index d333a1d..3dd7b0b 100644
--- a/tempest/api/compute/v3/servers/test_list_server_filters.py
+++ b/tempest/api/compute/v3/servers/test_list_server_filters.py
@@ -23,6 +23,8 @@
from tempest.test import attr
from tempest.test import skip_because
+CONF = config.CONF
+
class ListServerFiltersV3TestJSON(base.BaseV3ComputeTest):
_interface = 'json'
@@ -211,7 +213,7 @@
self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
@skip_because(bug="1182883",
- condition=config.TempestConfig().service_available.neutron)
+ condition=CONF.service_available.neutron)
@attr(type='gate')
def test_list_servers_filtered_by_ip_regex(self):
# Filter servers by regex ip
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 090f4dd..8cd6c11 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -20,21 +20,21 @@
import testtools
-from tempest.api import compute
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common.utils.linux.remote_client import RemoteClient
-import tempest.config
+from tempest import config
from tempest import exceptions
from tempest.test import attr
from tempest.test import skip_because
+CONF = config.CONF
+
class ServerActionsV3TestJSON(base.BaseV3ComputeTest):
_interface = 'json'
- resize_available = tempest.config.TempestConfig().\
- compute_feature_enabled.resize
- run_ssh = tempest.config.TempestConfig().compute.run_ssh
+ resize_available = CONF.compute_feature_enabled.resize
+ run_ssh = CONF.compute.run_ssh
def setUp(self):
# NOTE(afazekas): Normally we use the same server with all test cases,
@@ -53,7 +53,7 @@
cls.client = cls.servers_client
cls.server_id = cls.rebuild_server(None)
- @testtools.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
+ @testtools.skipUnless(CONF.compute_feature_enabled.change_password,
'Change password not available.')
@attr(type='gate')
def test_change_server_password(self):
@@ -67,7 +67,7 @@
# Verify that the user can authenticate with the new password
resp, server = self.client.get_server(self.server_id)
linux_client = RemoteClient(server, self.ssh_user, new_password)
- self.assertTrue(linux_client.can_authenticate())
+ linux_client.validate_authentication()
@attr(type='smoke')
def test_reboot_server_hard(self):
@@ -140,7 +140,7 @@
if self.run_ssh:
# Verify that the user can authenticate with the provided password
linux_client = RemoteClient(server, self.ssh_user, password)
- self.assertTrue(linux_client.can_authenticate())
+ linux_client.validate_authentication()
def _detect_server_image_flavor(self, server_id):
# Detects the current server image flavor ref.
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
index 8142250..6532032 100644
--- a/tempest/api/compute/v3/servers/test_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_servers_negative.py
@@ -17,13 +17,12 @@
import base64
import sys
-import uuid
from tempest.api.compute import base
from tempest import clients
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ServersNegativeTestJSON(base.BaseV2ComputeTest):
@@ -40,13 +39,12 @@
def setUpClass(cls):
super(ServersNegativeTestJSON, cls).setUpClass()
cls.client = cls.servers_client
- cls.img_client = cls.images_client
cls.alt_os = clients.AltManager()
cls.alt_client = cls.alt_os.servers_client
resp, server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_server_name_blank(self):
# Create a server with name parameter empty
@@ -54,7 +52,7 @@
self.create_test_server,
name='')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_personality_file_contents_not_encoded(self):
# Use an unencoded file when creating a server with personality
@@ -66,7 +64,7 @@
self.create_test_server,
personality=person)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_invalid_image(self):
# Create a server with an unknown image
@@ -74,7 +72,7 @@
self.create_test_server,
image_id=-1)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_invalid_flavor(self):
# Create a server with an unknown flavor
@@ -82,7 +80,7 @@
self.create_test_server,
flavor=-1,)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_invalid_access_ip_v4_address(self):
# An access IPv4 address must match a valid address pattern
@@ -90,7 +88,7 @@
self.assertRaises(exceptions.BadRequest,
self.create_test_server, accessIPv4=IPv4)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_invalid_ip_v6_address(self):
# An access IPv6 address must match a valid address pattern
@@ -99,34 +97,35 @@
self.assertRaises(exceptions.BadRequest,
self.create_test_server, accessIPv6=IPv6)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resize_nonexistent_server(self):
- nonexistent_server = str(uuid.uuid4())
+ # Resize a non-existent server
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound,
self.client.resize,
nonexistent_server, self.flavor_ref)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resize_server_with_non_existent_flavor(self):
# Resize a server with non-existent flavor
- nonexistent_flavor = str(uuid.uuid4())
+ nonexistent_flavor = data_utils.rand_uuid()
self.assertRaises(exceptions.BadRequest, self.client.resize,
self.server_id, flavor_ref=nonexistent_flavor)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resize_server_with_null_flavor(self):
# Resize a server with null flavor
self.assertRaises(exceptions.BadRequest, self.client.resize,
self.server_id, flavor_ref="")
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_reboot_non_existent_server(self):
# Reboot a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.reboot,
nonexistent_server, 'SOFT')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_pause_paused_server(self):
# Pause a paused server.
self.client.pause_server(self.server_id)
@@ -137,7 +136,7 @@
self.client.pause_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_rebuild_reboot_deleted_server(self):
# Rebuild and Reboot a deleted server
_, server = self.create_test_server()
@@ -150,10 +149,10 @@
self.assertRaises(exceptions.NotFound, self.client.reboot,
server['id'], 'SOFT')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_rebuild_non_existent_server(self):
# Rebuild a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
meta = {'rebuild': 'server'}
new_name = data_utils.rand_name('server')
file_contents = 'Test server rebuild.'
@@ -167,7 +166,7 @@
personality=personality,
adminPass='rebuild')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_numeric_server_name(self):
# Create a server with a numeric name
if self.__class__._interface == "xml":
@@ -178,7 +177,7 @@
self.create_test_server,
name=server_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_server_name_length_exceeds_256(self):
# Create a server with name length exceeding 256 characters
@@ -187,7 +186,7 @@
self.create_test_server,
name=server_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_invalid_network_uuid(self):
# Pass invalid network uuid while creating a server
@@ -197,7 +196,7 @@
self.create_test_server,
networks=networks)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_non_existant_keypair(self):
# Pass a non-existent keypair while creating a server
@@ -206,7 +205,7 @@
self.create_test_server,
key_name=key_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_server_metadata_exceeds_length_limit(self):
# Pass really long metadata while creating a server
@@ -215,7 +214,7 @@
self.create_test_server,
meta=metadata)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_name_of_non_existent_server(self):
# Update name of a non-existent server
@@ -225,7 +224,7 @@
self.assertRaises(exceptions.NotFound, self.client.update_server,
server_name, name=new_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_server_set_empty_name(self):
# Update name of the server to an empty string
@@ -235,7 +234,7 @@
self.assertRaises(exceptions.BadRequest, self.client.update_server,
server_name, name=new_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_server_of_another_tenant(self):
# Update name of a server that belongs to another tenant
@@ -244,7 +243,7 @@
self.alt_client.update_server, self.server_id,
name=new_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_update_server_name_length_exceeds_256(self):
# Update name of server exceed the name length limit
@@ -254,34 +253,35 @@
self.server_id,
name=new_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_non_existent_server(self):
# Delete a non existent server
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.delete_server,
- '999erra43')
+ nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_a_server_of_another_tenant(self):
# Delete a server that belongs to another tenant
self.assertRaises(exceptions.NotFound,
self.alt_client.delete_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_server_pass_negative_id(self):
# Pass an invalid string parameter to delete server
self.assertRaises(exceptions.NotFound, self.client.delete_server, -1)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_server_pass_id_exceeding_length_limit(self):
# Pass a server ID that exceeds length limit to delete server
self.assertRaises(exceptions.NotFound, self.client.delete_server,
sys.maxint + 1)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_with_nonexistent_security_group(self):
# Create a server with a nonexistent security group
@@ -290,49 +290,49 @@
self.create_test_server,
security_groups=security_groups)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_non_existent_server(self):
# Get a non existent server details
-
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.get_server,
- '999erra43')
+ nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_stop_non_existent_server(self):
# Stop a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.servers_client.stop,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_pause_non_existent_server(self):
# pause a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.pause_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unpause_non_existent_server(self):
# unpause a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.unpause_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unpause_server_invalid_state(self):
# unpause an active server.
self.assertRaises(exceptions.Conflict,
self.client.unpause_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_suspend_non_existent_server(self):
# suspend a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.suspend_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_suspend_server_invalid_state(self):
# suspend a suspended server.
resp, _ = self.client.suspend_server(self.server_id)
@@ -344,66 +344,66 @@
self.client.suspend_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resume_non_existent_server(self):
# resume a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.resume_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_resume_server_invalid_state(self):
# resume an active server.
self.assertRaises(exceptions.Conflict,
self.client.resume_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_get_console_output_of_non_existent_server(self):
# get the console output for a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound,
self.client.get_console_output,
nonexistent_server, 10)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_force_delete_nonexistent_server_id(self):
- non_existent_server_id = str(uuid.uuid4())
-
+ # force-delete a non existent server
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound,
self.client.force_delete_server,
- non_existent_server_id)
+ nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_force_delete_server_invalid_state(self):
# we can only force-delete a server in 'soft-delete' state
self.assertRaises(exceptions.Conflict,
self.client.force_delete_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_restore_nonexistent_server_id(self):
- non_existent_server_id = str(uuid.uuid4())
-
+ # restore-delete a non existent server
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound,
self.client.restore_soft_deleted_server,
- non_existent_server_id)
+ nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_restore_server_invalid_state(self):
# we can only restore-delete a server in 'soft-delete' state
self.assertRaises(exceptions.Conflict,
self.client.restore_soft_deleted_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_shelve_non_existent_server(self):
# shelve a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.shelve_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_shelve_shelved_server(self):
# shelve a shelved server.
resp, server = self.client.shelve_server(self.server_id)
@@ -430,14 +430,14 @@
self.client.shelve_server,
self.server_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unshelve_non_existent_server(self):
# unshelve a non existent server
- nonexistent_server = str(uuid.uuid4())
+ nonexistent_server = data_utils.rand_uuid()
self.assertRaises(exceptions.NotFound, self.client.unshelve_server,
nonexistent_server)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_unshelve_server_invalid_state(self):
# unshelve an active server.
self.assertRaises(exceptions.Conflict,
diff --git a/tempest/api/compute/v3/test_extensions.py b/tempest/api/compute/v3/test_extensions.py
index d7269d1..2affd86 100644
--- a/tempest/api/compute/v3/test_extensions.py
+++ b/tempest/api/compute/v3/test_extensions.py
@@ -17,18 +17,26 @@
from tempest.api.compute import base
-from tempest.test import attr
+from tempest import test
class ExtensionsV3TestJSON(base.BaseV3ComputeTest):
_interface = 'json'
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_extensions(self):
# List of all extensions
resp, extensions = self.extensions_client.list_extensions()
self.assertIn("extensions", extensions)
self.assertEqual(200, resp.status)
+ self.assertTrue(self.extensions_client.is_enabled("Consoles"))
+
+ @test.attr(type='gate')
+ def test_get_extension(self):
+ # get the specified extensions
+ resp, extension = self.extensions_client.get_extension('servers')
+ self.assertEqual(200, resp.status)
+ self.assertEqual('servers', extension['alias'])
class ExtensionsV3TestXML(ExtensionsV3TestJSON):
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 660de95..c1ebc08 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -19,13 +19,15 @@
from tempest.api.compute import base
from tempest.common.utils.linux.remote_client import RemoteClient
-import tempest.config
+from tempest import config
from tempest.test import attr
+CONF = config.CONF
+
class AttachVolumeTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
- run_ssh = tempest.config.TempestConfig().compute.run_ssh
+ run_ssh = CONF.compute.run_ssh
def __init__(self, *args, **kwargs):
super(AttachVolumeTestJSON, self).__init__(*args, **kwargs)
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 6408c15..090f31f 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -25,7 +25,6 @@
class BasicOperationsImagesTest(base.BaseV2ImageTest):
-
"""
Here we test the basic operations of images
"""
@@ -95,7 +94,6 @@
class ListImagesTest(base.BaseV2ImageTest):
-
"""
Here we test the listing of image information
"""
@@ -105,31 +103,114 @@
super(ListImagesTest, cls).setUpClass()
# We add a few images here to test the listing functionality of
# the images API
- for x in xrange(0, 10):
- cls._create_standard_image(x)
+ cls._create_standard_image('bare', 'raw')
+ cls._create_standard_image('bare', 'raw')
+ cls._create_standard_image('ami', 'raw')
+ # Add some more for listing
+ cls._create_standard_image('ami', 'ami')
+ cls._create_standard_image('ari', 'ari')
+ cls._create_standard_image('aki', 'aki')
@classmethod
- def _create_standard_image(cls, number):
+ def _create_standard_image(cls, container_format, disk_format):
"""
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_file = StringIO.StringIO('*' * random.randint(1024, 4096))
- name = 'New Standard Image %s' % number
- resp, body = cls.create_image(name=name, container_format='bare',
- disk_format='raw',
+ name = data_utils.rand_name('image-')
+ resp, body = cls.create_image(name=name,
+ container_format=container_format,
+ disk_format=disk_format,
visibility='public')
image_id = body['id']
resp, body = cls.client.store_image(image_id, data=image_file)
return image_id
+ def _list_by_param_value_and_assert(self, params):
+ """
+ Perform list action with given params and validates result.
+ """
+ resp, images_list = self.client.image_list(params=params)
+ self.assertEqual(200, resp.status)
+ # Validating params of fetched images
+ for image in images_list:
+ for key in params:
+ msg = "Failed to list images by %s" % key
+ self.assertEqual(params[key], image[key], msg)
+
@attr(type='gate')
def test_index_no_params(self):
# Simple test to see all fixture images returned
resp, images_list = self.client.image_list()
self.assertEqual(resp['status'], '200')
image_list = map(lambda x: x['id'], images_list)
+
for image in self.created_images:
self.assertIn(image, image_list)
+
+ @attr(type='gate')
+ def test_list_images_param_container_format(self):
+ # Test to get all images with container_format='bare'
+ params = {"container_format": "bare"}
+ self._list_by_param_value_and_assert(params)
+
+ @attr(type='gate')
+ def test_list_images_param_disk_format(self):
+ # Test to get all images with disk_format = raw
+ params = {"disk_format": "raw"}
+ self._list_by_param_value_and_assert(params)
+
+ @attr(type='gate')
+ def test_list_images_param_visibility(self):
+ # Test to get all images with visibility = public
+ params = {"visibility": "public"}
+ self._list_by_param_value_and_assert(params)
+
+ @attr(type='gate')
+ def test_list_images_param_size(self):
+ # Test to get all images by size
+ image_id = self.created_images[1]
+ # Get image metadata
+ resp, image = self.client.get_image(image_id)
+ self.assertEqual(resp['status'], '200')
+
+ params = {"size": image['size']}
+ self._list_by_param_value_and_assert(params)
+
+ @attr(type='gate')
+ def test_list_images_param_min_max_size(self):
+ # Test to get all images with size between 2000 to 3000
+ image_id = self.created_images[1]
+ # Get image metadata
+ resp, image = self.client.get_image(image_id)
+ self.assertEqual(resp['status'], '200')
+
+ size = image['size']
+ params = {"size_min": size - 500, "size_max": size + 500}
+ resp, images_list = self.client.image_list(params=params)
+ self.assertEqual(resp['status'], '200')
+ image_size_list = map(lambda x: x['size'], images_list)
+
+ for image_size in image_size_list:
+ self.assertTrue(image_size >= params['size_min'] and
+ image_size <= params['size_max'],
+ "Failed to get images by size_min and size_max")
+
+ @attr(type='gate')
+ def test_list_images_param_status(self):
+ # Test to get all available images
+ params = {"status": "available"}
+ self._list_by_param_value_and_assert(params)
+
+ @attr(type='gate')
+ def test_list_images_param_limit(self):
+ # Test to get images by limit
+ params = {"limit": 2}
+ resp, images_list = self.client.image_list(params=params)
+ self.assertEqual(resp['status'], '200')
+
+ self.assertEqual(len(images_list), params['limit'],
+ "Failed to get images by limit")
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 9c187fd..64ab051 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -16,7 +16,7 @@
from tempest.api.network import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class L3AgentSchedulerJSON(base.BaseAdminNetworkTest):
@@ -36,8 +36,11 @@
@classmethod
def setUpClass(cls):
super(L3AgentSchedulerJSON, cls).setUpClass()
+ if not test.is_extension_enabled('l3_agent_scheduler', 'network'):
+ msg = "L3 Agent Scheduler Extension not enabled."
+ raise cls.skipException(msg)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_routers_on_l3_agent(self):
resp, body = self.admin_client.list_agents()
agents = body['agents']
@@ -48,7 +51,7 @@
agent['id'])
self.assertEqual('200', resp['status'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_l3_agents_hosting_router(self):
name = data_utils.rand_name('router-')
resp, router = self.client.create_router(name)
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 61af91f..064eaff 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -71,10 +71,10 @@
def tearDownClass(cls):
# Clean up ike policies
for ikepolicy in cls.ikepolicies:
- cls.client.delete_ike_policy(ikepolicy['id'])
+ cls.client.delete_ikepolicy(ikepolicy['id'])
# Clean up vpn services
for vpnservice in cls.vpnservices:
- cls.client.delete_vpn_service(vpnservice['id'])
+ cls.client.delete_vpnservice(vpnservice['id'])
# Clean up routers
for router in cls.routers:
resp, body = cls.client.list_router_interfaces(router['id'])
@@ -213,7 +213,7 @@
@classmethod
def create_vpnservice(cls, subnet_id, router_id):
"""Wrapper utility that returns a test vpn service."""
- resp, body = cls.client.create_vpn_service(
+ resp, body = cls.client.create_vpnservice(
subnet_id, router_id, admin_state_up=True,
name=data_utils.rand_name("vpnservice-"))
vpnservice = body['vpnservice']
@@ -221,9 +221,9 @@
return vpnservice
@classmethod
- def create_ike_policy(cls, name):
+ def create_ikepolicy(cls, name):
"""Wrapper utility that returns a test ike policy."""
- resp, body = cls.client.create_ike_policy(name)
+ resp, body = cls.client.create_ikepolicy(name)
ikepolicy = body['ikepolicy']
cls.ikepolicies.append(ikepolicy)
return ikepolicy
diff --git a/tempest/api/network/common.py b/tempest/api/network/common.py
index 528a204..ae0eda1 100644
--- a/tempest/api/network/common.py
+++ b/tempest/api/network/common.py
@@ -102,8 +102,7 @@
self.floating_ip_address)
def __str__(self):
- return '<"FloatingIP" addr="%s" id="%s">' % (self.__class__.__name__,
- self.floating_ip_address,
+ return '<"FloatingIP" addr="%s" id="%s">' % (self.floating_ip_address,
self.id)
def delete(self):
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
index 1b27d1b..9d872f9 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -17,7 +17,7 @@
from tempest.api.network import base
-from tempest.test import attr
+from tempest import test
class ExtensionsTestJSON(base.BaseNetworkTest):
@@ -38,7 +38,7 @@
def setUpClass(cls):
super(ExtensionsTestJSON, cls).setUpClass()
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_show_extensions(self):
# List available extensions for the tenant
expected_alias = ['security-group', 'l3_agent_scheduler',
@@ -55,7 +55,7 @@
ext_name = ext['name']
ext_alias = ext['alias']
actual_alias.append(ext['alias'])
- resp, ext_details = self.client.show_extension_details(ext_alias)
+ resp, ext_details = self.client.show_extension(ext_alias)
self.assertEqual('200', resp['status'])
ext_details = ext_details['extension']
@@ -70,9 +70,11 @@
self.assertEqual(ext_details['alias'], ext_alias)
self.assertEqual(ext_details, ext)
# Verify if expected extensions are present in the actual list
- # of extensions returned
+ # of extensions returned, but only for those that have been
+ # enabled via configuration
for e in expected_alias:
- self.assertIn(e, actual_alias)
+ if test.is_extension_enabled(e, 'network'):
+ self.assertIn(e, actual_alias)
class ExtensionsTestXML(ExtensionsTestJSON):
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index 35d4fa8..3a41f4f 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -68,10 +68,10 @@
def _delete_floating_ip(self, floating_ip_id):
# Deletes a floating IP and verifies if it is deleted or not
- resp, _ = self.client.delete_floating_ip(floating_ip_id)
+ resp, _ = self.client.delete_floatingip(floating_ip_id)
self.assertEqual(204, resp.status)
# Asserting that the floating_ip is not found in list after deletion
- resp, floating_ips = self.client.list_floating_ips()
+ resp, floating_ips = self.client.list_floatingips()
floatingip_id_list = list()
for f in floating_ips['floatingips']:
floatingip_id_list.append(f['id'])
@@ -106,7 +106,7 @@
self.assertEqual(show_floating_ip['port_id'], self.port[0]['id'])
# Verify the floating ip exists in the list of all floating_ips
- resp, floating_ips = self.client.list_floating_ips()
+ resp, floating_ips = self.client.list_floatingips()
self.assertEqual('200', resp['status'])
floatingip_id_list = list()
for f in floating_ips['floatingips']:
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 14c8500..68ca66a 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -184,24 +184,6 @@
found = n['id']
self.assertIsNotNone(found, "Port list doesn't contain created port")
- @attr(type=['negative', 'smoke'])
- def test_show_non_existent_network(self):
- non_exist_id = data_utils.rand_name('network')
- self.assertRaises(exceptions.NotFound, self.client.show_network,
- non_exist_id)
-
- @attr(type=['negative', 'smoke'])
- def test_show_non_existent_subnet(self):
- non_exist_id = data_utils.rand_name('subnet')
- self.assertRaises(exceptions.NotFound, self.client.show_subnet,
- non_exist_id)
-
- @attr(type=['negative', 'smoke'])
- def test_show_non_existent_port(self):
- non_exist_id = data_utils.rand_name('port')
- self.assertRaises(exceptions.NotFound, self.client.show_port,
- non_exist_id)
-
class NetworksTestXML(NetworksTestJSON):
_interface = 'xml'
diff --git a/tempest/api/network/test_networks_negative.py b/tempest/api/network/test_networks_negative.py
new file mode 100644
index 0000000..6820c25
--- /dev/null
+++ b/tempest/api/network/test_networks_negative.py
@@ -0,0 +1,60 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class NetworksNegativeTestJSON(base.BaseNetworkTest):
+ _interface = 'json'
+
+ @attr(type=['negative', 'smoke'])
+ def test_show_non_existent_network(self):
+ non_exist_id = data_utils.rand_name('network')
+ self.assertRaises(exceptions.NotFound, self.client.show_network,
+ non_exist_id)
+
+ @attr(type=['negative', 'smoke'])
+ def test_show_non_existent_subnet(self):
+ non_exist_id = data_utils.rand_name('subnet')
+ self.assertRaises(exceptions.NotFound, self.client.show_subnet,
+ non_exist_id)
+
+ @attr(type=['negative', 'smoke'])
+ def test_show_non_existent_port(self):
+ non_exist_id = data_utils.rand_name('port')
+ self.assertRaises(exceptions.NotFound, self.client.show_port,
+ non_exist_id)
+
+ @attr(type=['negative', 'smoke'])
+ def test_update_non_existent_network(self):
+ non_exist_id = data_utils.rand_name('network')
+ self.assertRaises(exceptions.NotFound, self.client.update_network,
+ non_exist_id, "new_name")
+
+ @attr(type=['negative', 'smoke'])
+ def test_delete_non_existent_network(self):
+ non_exist_id = data_utils.rand_name('network')
+ self.assertRaises(exceptions.NotFound, self.client.delete_network,
+ non_exist_id)
+
+
+class NetworksNegativeTestXML(NetworksNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/network/test_quotas.py b/tempest/api/network/test_quotas.py
index f7ba3cb..8b77637 100644
--- a/tempest/api/network/test_quotas.py
+++ b/tempest/api/network/test_quotas.py
@@ -74,12 +74,13 @@
resp, non_default_quotas = self.admin_client.list_quotas()
self.assertEqual('200', resp['status'])
found = False
- for qs in non_default_quotas:
+ for qs in non_default_quotas['quotas']:
if qs['tenant_id'] == tenant_id:
found = True
self.assertTrue(found)
# Confirm from APi quotas were changed as requested for tenant
resp, quota_set = self.admin_client.show_quotas(tenant_id)
+ quota_set = quota_set['quota']
self.assertEqual('200', resp['status'])
self.assertEqual(0, quota_set['network'])
self.assertEqual(0, quota_set['security_group'])
@@ -88,5 +89,5 @@
self.assertEqual('204', resp['status'])
resp, non_default_quotas = self.admin_client.list_quotas()
self.assertEqual('200', resp['status'])
- for q in non_default_quotas:
+ for q in non_default_quotas['quotas']:
self.assertNotEqual(tenant_id, q['tenant_id'])
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 06e07bb..21934f2 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -19,7 +19,7 @@
from tempest.api.network import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class RoutersTest(base.BaseAdminNetworkTest):
@@ -54,7 +54,7 @@
router_id, port_id)
self.assertEqual('200', resp['status'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_show_list_update_delete_router(self):
# Create a router
# NOTE(salv-orlando): Do not invoke self.create_router
@@ -97,7 +97,7 @@
create_body['router']['id'])
self.assertEqual(show_body['router']['name'], updated_name)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_add_remove_router_interface_with_subnet_id(self):
network = self.create_network()
subnet = self.create_subnet(network)
@@ -116,7 +116,7 @@
self.assertEqual(show_port_body['port']['device_id'],
router['id'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_add_remove_router_interface_with_port_id(self):
network = self.create_network()
self.create_subnet(network)
@@ -160,7 +160,7 @@
public_subnet_id = public_net_body['network']['subnets'][0]
self.assertEqual(fixed_ips[0]['subnet_id'], public_subnet_id)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_router_set_gateway(self):
router = self.create_router(data_utils.rand_name('router-'))
self.client.update_router(
@@ -175,7 +175,7 @@
{'network_id': self.network_cfg.public_network_id})
self._verify_gateway_port(router['id'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_router_set_gateway_with_snat_explicit(self):
router = self.create_router(data_utils.rand_name('router-'))
self.admin_client.update_router_with_snat_gw_info(
@@ -189,7 +189,7 @@
'enable_snat': True})
self._verify_gateway_port(router['id'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_router_set_gateway_without_snat(self):
router = self.create_router(data_utils.rand_name('router-'))
self.admin_client.update_router_with_snat_gw_info(
@@ -203,7 +203,7 @@
'enable_snat': False})
self._verify_gateway_port(router['id'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_router_unset_gateway(self):
router = self.create_router(
data_utils.rand_name('router-'),
@@ -216,7 +216,7 @@
device_id=router['id'])
self.assertFalse(list_body['ports'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_router_reset_gateway_without_snat(self):
router = self.create_router(
data_utils.rand_name('router-'),
@@ -232,7 +232,8 @@
'enable_snat': False})
self._verify_gateway_port(router['id'])
- @attr(type='smoke')
+ @test.requires_ext(extension='extraroute', service='network')
+ @test.attr(type='smoke')
def test_update_extra_route(self):
self.network = self.create_network()
self.name = self.network['name']
diff --git a/tempest/api/network/test_vpnaas_extensions.py b/tempest/api/network/test_vpnaas_extensions.py
index 63a8e24..fc3b1d9 100644
--- a/tempest/api/network/test_vpnaas_extensions.py
+++ b/tempest/api/network/test_vpnaas_extensions.py
@@ -46,20 +46,20 @@
cls.create_router_interface(cls.router['id'], cls.subnet['id'])
cls.vpnservice = cls.create_vpnservice(cls.subnet['id'],
cls.router['id'])
- cls.ikepolicy = cls.create_ike_policy(data_utils.rand_name(
- "ike-policy-"))
+ cls.ikepolicy = cls.create_ikepolicy(
+ data_utils.rand_name("ike-policy-"))
def _delete_ike_policy(self, ike_policy_id):
# Deletes a ike policy and verifies if it is deleted or not
ike_list = list()
- resp, all_ike = self.client.list_ike_policies()
+ resp, all_ike = self.client.list_ikepolicies()
for ike in all_ike['ikepolicies']:
ike_list.append(ike['id'])
if ike_policy_id in ike_list:
- resp, _ = self.client.delete_ike_policy(ike_policy_id)
+ resp, _ = self.client.delete_ikepolicy(ike_policy_id)
self.assertEqual(204, resp.status)
# Asserting that the policy is not found in list after deletion
- resp, ikepolicies = self.client.list_ike_policies()
+ resp, ikepolicies = self.client.list_ikepolicies()
ike_id_list = list()
for i in ikepolicies['ikepolicies']:
ike_id_list.append(i['id'])
@@ -68,7 +68,7 @@
@attr(type='smoke')
def test_list_vpn_services(self):
# Verify the VPN service exists in the list of all VPN services
- resp, body = self.client.list_vpn_services()
+ resp, body = self.client.list_vpnservices()
self.assertEqual('200', resp['status'])
vpnservices = body['vpnservices']
self.assertIn(self.vpnservice['id'], [v['id'] for v in vpnservices])
@@ -77,14 +77,14 @@
def test_create_update_delete_vpn_service(self):
# Creates a VPN service
name = data_utils.rand_name('vpn-service-')
- resp, body = self.client.create_vpn_service(self.subnet['id'],
- self.router['id'],
- name=name,
- admin_state_up=True)
+ resp, body = self.client.create_vpnservice(self.subnet['id'],
+ self.router['id'],
+ name=name,
+ admin_state_up=True)
self.assertEqual('201', resp['status'])
vpnservice = body['vpnservice']
# Assert if created vpnservices are not found in vpnservices list
- resp, body = self.client.list_vpn_services()
+ resp, body = self.client.list_vpnservices()
vpn_services = [vs['id'] for vs in body['vpnservices']]
self.assertIsNotNone(vpnservice['id'])
self.assertIn(vpnservice['id'], vpn_services)
@@ -95,17 +95,17 @@
# should be "ACTIVE" not "PENDING*"
# Verification of vpn service delete
- resp, body = self.client.delete_vpn_service(vpnservice['id'])
+ resp, body = self.client.delete_vpnservice(vpnservice['id'])
self.assertEqual('204', resp['status'])
# Asserting if vpn service is found in the list after deletion
- resp, body = self.client.list_vpn_services()
+ resp, body = self.client.list_vpnservices()
vpn_services = [vs['id'] for vs in body['vpnservices']]
self.assertNotIn(vpnservice['id'], vpn_services)
@attr(type='smoke')
def test_show_vpn_service(self):
# Verifies the details of a vpn service
- resp, body = self.client.show_vpn_service(self.vpnservice['id'])
+ resp, body = self.client.show_vpnservice(self.vpnservice['id'])
self.assertEqual('200', resp['status'])
vpnservice = body['vpnservice']
self.assertEqual(self.vpnservice['id'], vpnservice['id'])
@@ -119,7 +119,7 @@
@attr(type='smoke')
def test_list_ike_policies(self):
# Verify the ike policy exists in the list of all IKE policies
- resp, body = self.client.list_ike_policies()
+ resp, body = self.client.list_ikepolicies()
self.assertEqual('200', resp['status'])
ikepolicies = body['ikepolicies']
self.assertIn(self.ikepolicy['id'], [i['id'] for i in ikepolicies])
@@ -128,7 +128,7 @@
def test_create_update_delete_ike_policy(self):
# Creates a IKE policy
name = data_utils.rand_name('ike-policy-')
- resp, body = (self.client.create_ike_policy(
+ resp, body = (self.client.create_ikepolicy(
name,
ike_version="v1",
encryption_algorithm="aes-128",
@@ -140,19 +140,19 @@
description = "Updated ike policy"
new_ike = {'description': description, 'pfs': 'group5',
'name': data_utils.rand_name("New-IKE-")}
- resp, body = self.client.update_ike_policy(ikepolicy['id'],
- **new_ike)
+ resp, body = self.client.update_ikepolicy(ikepolicy['id'],
+ **new_ike)
self.assertEqual('200', resp['status'])
updated_ike_policy = body['ikepolicy']
self.assertEqual(updated_ike_policy['description'], description)
# Verification of ike policy delete
- resp, body = self.client.delete_ike_policy(ikepolicy['id'])
+ resp, body = self.client.delete_ikepolicy(ikepolicy['id'])
self.assertEqual('204', resp['status'])
@attr(type='smoke')
def test_show_ike_policy(self):
# Verifies the details of a ike policy
- resp, body = self.client.show_ike_policy(self.ikepolicy['id'])
+ resp, body = self.client.show_ikepolicy(self.ikepolicy['id'])
self.assertEqual('200', resp['status'])
ikepolicy = body['ikepolicy']
self.assertEqual(self.ikepolicy['id'], ikepolicy['id'])
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index c1b3391..ac1c7d1 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -23,10 +23,12 @@
from tempest import exceptions
from tempest.test import attr
+CONF = config.CONF
+
class AccountQuotasTest(base.BaseObjectTest):
accounts_quotas_available = \
- config.TempestConfig().object_storage_feature_enabled.accounts_quotas
+ CONF.object_storage_feature_enabled.accounts_quotas
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index c7b5e28..513d24a 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -24,6 +24,7 @@
from tempest.test import attr
from tempest.test import HTTP_SUCCESS
+CONF = config.CONF
QUOTA_BYTES = 10
QUOTA_COUNT = 3
SKIP_MSG = "Container quotas middleware not available."
@@ -32,7 +33,7 @@
class ContainerQuotasTest(base.BaseObjectTest):
"""Attemps to test the perfect behavior of quotas in a container."""
container_quotas_available = \
- config.TempestConfig().object_storage_feature_enabled.container_quotas
+ CONF.object_storage_feature_enabled.container_quotas
def setUp(self):
"""Creates and sets a container with quotas.
diff --git a/tempest/api/object_storage/test_crossdomain.py b/tempest/api/object_storage/test_crossdomain.py
index 51ecd16..41430c8 100644
--- a/tempest/api/object_storage/test_crossdomain.py
+++ b/tempest/api/object_storage/test_crossdomain.py
@@ -23,10 +23,12 @@
from tempest.test import attr
from tempest.test import HTTP_SUCCESS
+CONF = config.CONF
+
class CrossdomainTest(base.BaseObjectTest):
crossdomain_available = \
- config.TempestConfig().object_storage_feature_enabled.crossdomain
+ CONF.object_storage_feature_enabled.crossdomain
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index bb03932..9d5a1c0 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -27,11 +27,13 @@
from tempest.test import attr
from tempest.test import HTTP_SUCCESS
+CONF = config.CONF
+
class ObjectTempUrlTest(base.BaseObjectTest):
tempurl_available = \
- config.TempestConfig().object_storage_feature_enabled.tempurl
+ CONF.object_storage_feature_enabled.tempurl
@classmethod
def setUpClass(cls):
diff --git a/tempest/api/orchestration/stacks/test_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
index 35a7326..b19344d 100644
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ b/tempest/api/orchestration/stacks/test_non_empty_stack.py
@@ -28,75 +28,57 @@
template = """
HeatTemplateFormatVersion: '2012-12-12'
Description: |
- Template which creates single EC2 instance
+ Template which creates some simple resources
Parameters:
- KeyName:
+ trigger:
Type: String
- InstanceType:
- Type: String
- ImageId:
- Type: String
- Subnet:
- Type: String
+ Default: not_yet
Resources:
- SmokeServer:
- Type: AWS::EC2::Instance
+ fluffy:
+ Type: AWS::AutoScaling::LaunchConfiguration
Metadata:
- Name: SmokeServer
+ kittens:
+ - Tom
+ - Stinky
Properties:
- ImageId: {Ref: ImageId}
- InstanceType: {Ref: InstanceType}
- KeyName: {Ref: KeyName}
- SubnetId: {Ref: Subnet}
+ ImageId: not_used
+ InstanceType: not_used
UserData:
- Fn::Base64:
- Fn::Join:
- - ''
- - - '#!/bin/bash -v
-
- '
- - /opt/aws/bin/cfn-signal -e 0 -r "SmokeServer created" '
- - {Ref: WaitHandle}
- - '''
-
- '
- WaitHandle:
- Type: AWS::CloudFormation::WaitConditionHandle
- WaitCondition:
- Type: AWS::CloudFormation::WaitCondition
- DependsOn: SmokeServer
- Properties:
- Handle: {Ref: WaitHandle}
- Timeout: '600'
+ Fn::Replace:
+ - variable_a: {Ref: trigger}
+ variable_b: bee
+ - |
+ A == variable_a
+ B == variable_b
+Outputs:
+ fluffy:
+ Description: "fluffies irc nick"
+ Value:
+ Fn::Replace:
+ - nick: {Ref: fluffy}
+ - |
+ #nick
"""
@classmethod
def setUpClass(cls):
super(StacksTestJSON, cls).setUpClass()
- if not cls.orchestration_cfg.image_ref:
- raise cls.skipException("No image available to test")
cls.client = cls.orchestration_client
cls.stack_name = data_utils.rand_name('heat')
- keypair_name = (cls.orchestration_cfg.keypair_name or
- cls._create_keypair()['name'])
# create the stack
- subnet = cls._get_default_network()['subnets'][0]
cls.stack_identifier = cls.create_stack(
cls.stack_name,
cls.template,
parameters={
- 'KeyName': keypair_name,
- 'InstanceType': cls.orchestration_cfg.instance_type,
- 'ImageId': cls.orchestration_cfg.image_ref,
- 'Subnet': subnet
+ 'trigger': 'start'
})
cls.stack_id = cls.stack_identifier.split('/')[1]
- cls.resource_name = 'SmokeServer'
- cls.resource_type = 'AWS::EC2::Instance'
+ cls.resource_name = 'fluffy'
+ cls.resource_type = 'AWS::AutoScaling::LaunchConfiguration'
cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
- @attr(type='slow')
+ @attr(type='gate')
def test_stack_list(self):
"""Created stack should be on the list of existing stacks."""
resp, stacks = self.client.list_stacks()
@@ -105,7 +87,7 @@
stacks_names = map(lambda stack: stack['stack_name'], stacks)
self.assertIn(self.stack_name, stacks_names)
- @attr(type='slow')
+ @attr(type='gate')
def test_stack_show(self):
"""Getting details about created stack should be possible."""
resp, stack = self.client.get_stack(self.stack_name)
@@ -114,7 +96,7 @@
self.assertEqual(self.stack_name, stack['stack_name'])
self.assertEqual(self.stack_id, stack['id'])
- @attr(type='slow')
+ @attr(type='gate')
def test_list_resources(self):
"""Getting list of created resources for the stack should be possible.
"""
@@ -128,7 +110,7 @@
resources)
self.assertIn(self.resource_type, resources_types)
- @attr(type='slow')
+ @attr(type='gate')
def test_show_resource(self):
"""Getting details about created resource should be possible."""
resp, resource = self.client.get_resource(self.stack_identifier,
@@ -137,7 +119,7 @@
self.assertEqual(self.resource_name, resource['logical_resource_id'])
self.assertEqual(self.resource_type, resource['resource_type'])
- @attr(type='slow')
+ @attr(type='gate')
def test_resource_metadata(self):
"""Getting metadata for created resource should be possible."""
resp, metadata = self.client.show_resource_metadata(
@@ -145,9 +127,9 @@
self.resource_name)
self.assertEqual('200', resp['status'])
self.assertIsInstance(metadata, dict)
- self.assertEqual(self.resource_name, metadata.get('Name', None))
+ self.assertEqual(['Tom', 'Stinky'], metadata.get('kittens', None))
- @attr(type='slow')
+ @attr(type='gate')
def test_list_events(self):
"""Getting list of created events for the stack should be possible."""
resp, events = self.client.list_events(self.stack_identifier)
@@ -157,7 +139,7 @@
self.assertIn('CREATE_IN_PROGRESS', resource_statuses)
self.assertIn('CREATE_COMPLETE', resource_statuses)
- @attr(type='slow')
+ @attr(type='gate')
def test_show_event(self):
"""Getting details about existing event should be possible."""
resp, events = self.client.list_resource_events(self.stack_identifier,
@@ -168,7 +150,7 @@
resp, event = self.client.show_event(self.stack_identifier,
self.resource_name, event_id)
self.assertEqual('200', resp['status'])
- self.assertEqual('CREATE_IN_PROGRESS', event['resource_status'])
+ self.assertIsInstance(event, dict)
+ self.assertEqual(self.resource_name, event['resource_name'])
self.assertEqual('state changed', event['resource_status_reason'])
self.assertEqual(self.resource_name, event['logical_resource_id'])
- self.assertIsInstance(event, dict)
diff --git a/tempest/api/orchestration/stacks/test_server_cfn_init.py b/tempest/api/orchestration/stacks/test_server_cfn_init.py
index 3c2a2d2..6fbbb5b 100644
--- a/tempest/api/orchestration/stacks/test_server_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_server_cfn_init.py
@@ -18,18 +18,17 @@
from tempest.api.orchestration import base
from tempest.common.utils import data_utils
from tempest.common.utils.linux.remote_client import RemoteClient
-import tempest.config
+from tempest import config
from tempest.openstack.common import log as logging
from tempest.test import attr
-
+CONF = config.CONF
LOG = logging.getLogger(__name__)
class ServerCfnInitTestJSON(base.BaseOrchestrationTest):
_interface = 'json'
- existing_keypair = (tempest.config.TempestConfig().
- orchestration.keypair_name is not None)
+ existing_keypair = CONF.orchestration.keypair_name is not None
template = """
HeatTemplateFormatVersion: '2012-12-12'
@@ -169,9 +168,9 @@
body['physical_resource_id'])
# Check that the user can authenticate with the generated password
- linux_client = RemoteClient(
- server, 'ec2-user', pkey=self.keypair['private_key'])
- self.assertTrue(linux_client.can_authenticate())
+ linux_client = RemoteClient(server, 'ec2-user',
+ pkey=self.keypair['private_key'])
+ linux_client.validate_authentication()
@attr(type='slow')
def test_stack_wait_condition_data(self):
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 465f570..ba99309 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -42,6 +42,7 @@
cls.volumes_client = os.volumes_client
cls.snapshots_client = os.snapshots_client
cls.servers_client = os.servers_client
+ cls.volumes_extension_client = os.volumes_extension_client
cls.image_ref = cls.config.compute.image_ref
cls.flavor_ref = cls.config.compute.flavor_ref
cls.build_interval = cls.config.volume.build_interval
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
new file mode 100644
index 0000000..90988a2
--- /dev/null
+++ b/tempest/api/volume/test_extensions.py
@@ -0,0 +1,43 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from tempest.api.volume import base
+from tempest.test import attr
+
+
+class ExtensionsTestJSON(base.BaseVolumeTest):
+ _interface = 'json'
+
+ @attr(type='gate')
+ def test_list_extensions(self):
+ # List of all extensions
+ resp, extensions = self.volumes_extension_client.list_extensions()
+ self.assertEqual(200, resp.status)
+ if len(self.config.volume_feature_enabled.api_extensions) == 0:
+ raise self.skipException('There are not any extensions configured')
+ ext = self.config.volume_feature_enabled.api_extensions[0]
+ if ext == 'all':
+ self.assertIn('Hosts', map(lambda x: x['name'], extensions))
+ elif ext:
+ self.assertIn(ext, map(lambda x: x['name'], extensions))
+ else:
+ raise self.skipException('There are not any extensions configured')
+
+
+class ExtensionsTestXML(ExtensionsTestJSON):
+ _interface = 'xml'
diff --git a/tempest/clients.py b/tempest/clients.py
index ac79ce0..3333b9b 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -52,6 +52,8 @@
VolumesExtensionsClientJSON
from tempest.services.compute.v3.json.availability_zone_client import \
AvailabilityZoneV3ClientJSON
+from tempest.services.compute.v3.json.certificates_client import \
+ CertificatesV3ClientJSON
from tempest.services.compute.v3.json.extensions_client import \
ExtensionsV3ClientJSON
from tempest.services.compute.v3.json.hypervisor_client import \
@@ -66,6 +68,8 @@
TenantUsagesV3ClientJSON
from tempest.services.compute.v3.xml.availability_zone_client import \
AvailabilityZoneV3ClientXML
+from tempest.services.compute.v3.xml.certificates_client import \
+ CertificatesV3ClientXML
from tempest.services.compute.v3.xml.extensions_client import \
ExtensionsV3ClientXML
from tempest.services.compute.v3.xml.hypervisor_client import \
@@ -104,6 +108,7 @@
TenantUsagesClientXML
from tempest.services.compute.xml.volumes_extensions_client import \
VolumesExtensionsClientXML
+from tempest.services.data_processing.v1_1.client import DataProcessingClient
from tempest.services.identity.json.identity_client import IdentityClientJSON
from tempest.services.identity.json.identity_client import TokenClientJSON
from tempest.services.identity.v3.json.credentials_client import \
@@ -144,15 +149,20 @@
VolumeHostsClientJSON
from tempest.services.volume.json.admin.volume_types_client import \
VolumeTypesClientJSON
+from tempest.services.volume.json.extensions_client import \
+ ExtensionsClientJSON as VolumeExtensionClientJSON
from tempest.services.volume.json.snapshots_client import SnapshotsClientJSON
from tempest.services.volume.json.volumes_client import VolumesClientJSON
from tempest.services.volume.xml.admin.volume_hosts_client import \
VolumeHostsClientXML
from tempest.services.volume.xml.admin.volume_types_client import \
VolumeTypesClientXML
+from tempest.services.volume.xml.extensions_client import \
+ ExtensionsClientXML as VolumeExtensionClientXML
from tempest.services.volume.xml.snapshots_client import SnapshotsClientXML
from tempest.services.volume.xml.volumes_client import VolumesClientXML
+CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -173,13 +183,12 @@
:param password: Override of the password
:param tenant_name: Override of the tenant name
"""
- self.config = config.TempestConfig()
-
+ self.config = CONF
# If no creds are provided, we fall back on the defaults
# in the config file for the Compute API.
- self.username = username or self.config.identity.username
- self.password = password or self.config.identity.password
- self.tenant_name = tenant_name or self.config.identity.tenant_name
+ self.username = username or CONF.identity.username
+ self.password = password or CONF.identity.password
+ self.tenant_name = tenant_name or CONF.identity.tenant_name
if None in (self.username, self.password, self.tenant_name):
msg = ("Missing required credentials. "
@@ -188,15 +197,15 @@
{'u': username, 'p': password, 't': tenant_name})
raise exceptions.InvalidConfiguration(msg)
- self.auth_url = self.config.identity.uri
- self.auth_url_v3 = self.config.identity.uri_v3
+ self.auth_url = CONF.identity.uri
+ self.auth_url_v3 = CONF.identity.uri_v3
- client_args = (self.config, self.username, self.password,
+ client_args = (CONF, self.username, self.password,
self.auth_url, self.tenant_name)
if self.auth_url_v3:
auth_version = 'v3'
- client_args_v3_auth = (self.config, self.username,
+ client_args_v3_auth = (CONF, self.username,
self.password, self.auth_url_v3,
self.tenant_name, auth_version)
else:
@@ -206,6 +215,7 @@
if interface == 'xml':
self.certificates_client = CertificatesClientXML(*client_args)
+ self.certificates_v3_client = CertificatesV3ClientXML(*client_args)
self.servers_client = ServersClientXML(*client_args)
self.servers_v3_client = ServersV3ClientXML(*client_args)
self.limits_client = LimitsClientXML(*client_args)
@@ -223,7 +233,7 @@
self.volume_types_client = VolumeTypesClientXML(*client_args)
self.identity_client = IdentityClientXML(*client_args)
self.identity_v3_client = IdentityV3ClientXML(*client_args)
- self.token_client = TokenClientXML(self.config)
+ self.token_client = TokenClientXML(CONF)
self.security_groups_client = SecurityGroupsClientXML(
*client_args)
self.interfaces_v3_client = InterfacesV3ClientXML(*client_args)
@@ -250,6 +260,8 @@
self.instance_usages_audit_log_client = \
InstanceUsagesAuditLogClientXML(*client_args)
self.volume_hosts_client = VolumeHostsClientXML(*client_args)
+ self.volumes_extension_client = VolumeExtensionClientXML(
+ *client_args)
if client_args_v3_auth:
self.servers_client_v3_auth = ServersClientXML(
@@ -257,6 +269,8 @@
elif interface == 'json':
self.certificates_client = CertificatesClientJSON(*client_args)
+ self.certificates_v3_client = CertificatesV3ClientJSON(
+ *client_args)
self.servers_client = ServersClientJSON(*client_args)
self.servers_v3_client = ServersV3ClientJSON(*client_args)
self.limits_client = LimitsClientJSON(*client_args)
@@ -274,7 +288,7 @@
self.volume_types_client = VolumeTypesClientJSON(*client_args)
self.identity_client = IdentityClientJSON(*client_args)
self.identity_v3_client = IdentityV3ClientJSON(*client_args)
- self.token_client = TokenClientJSON(self.config)
+ self.token_client = TokenClientJSON(CONF)
self.security_groups_client = SecurityGroupsClientJSON(
*client_args)
self.interfaces_v3_client = InterfacesV3ClientJSON(*client_args)
@@ -301,6 +315,8 @@
self.instance_usages_audit_log_client = \
InstanceUsagesAuditLogClientJSON(*client_args)
self.volume_hosts_client = VolumeHostsClientJSON(*client_args)
+ self.volumes_extension_client = VolumeExtensionClientJSON(
+ *client_args)
if client_args_v3_auth:
self.servers_client_v3_auth = ServersClientJSON(
@@ -312,7 +328,7 @@
# common clients
self.hosts_client = HostsClientJSON(*client_args)
self.account_client = AccountClient(*client_args)
- if self.config.service_available.glance:
+ if CONF.service_available.glance:
self.image_client = ImageClientJSON(*client_args)
self.image_client_v2 = ImageClientV2JSON(*client_args)
self.container_client = ContainerClient(*client_args)
@@ -323,6 +339,7 @@
self.custom_object_client = ObjectClientCustomizedHeader(*client_args)
self.custom_account_client = \
AccountClientCustomizedHeader(*client_args)
+ self.data_processing_client = DataProcessingClient(*client_args)
class AltManager(Manager):
@@ -333,10 +350,9 @@
"""
def __init__(self, interface='json'):
- conf = config.TempestConfig()
- super(AltManager, self).__init__(conf.identity.alt_username,
- conf.identity.alt_password,
- conf.identity.alt_tenant_name,
+ super(AltManager, self).__init__(CONF.identity.alt_username,
+ CONF.identity.alt_password,
+ CONF.identity.alt_tenant_name,
interface=interface)
@@ -348,10 +364,9 @@
"""
def __init__(self, interface='json'):
- conf = config.TempestConfig()
- super(AdminManager, self).__init__(conf.identity.admin_username,
- conf.identity.admin_password,
- conf.identity.admin_tenant_name,
+ super(AdminManager, self).__init__(CONF.identity.admin_username,
+ CONF.identity.admin_password,
+ CONF.identity.admin_tenant_name,
interface=interface)
@@ -363,11 +378,10 @@
"""
def __init__(self, interface='json'):
- conf = config.TempestConfig()
base = super(ComputeAdminManager, self)
- base.__init__(conf.compute_admin.username,
- conf.compute_admin.password,
- conf.compute_admin.tenant_name,
+ base.__init__(CONF.compute_admin.username,
+ CONF.compute_admin.password,
+ CONF.compute_admin.tenant_name,
interface=interface)
@@ -377,9 +391,8 @@
so that heat templates can create users
"""
def __init__(self, interface='json'):
- conf = config.TempestConfig()
base = super(OrchestrationManager, self)
- base.__init__(conf.identity.admin_username,
- conf.identity.admin_password,
- conf.identity.tenant_name,
+ base.__init__(CONF.identity.admin_username,
+ CONF.identity.admin_password,
+ CONF.identity.tenant_name,
interface=interface)
diff --git a/tempest/common/debug.py b/tempest/common/debug.py
index 69c933c..f132f6a 100644
--- a/tempest/common/debug.py
+++ b/tempest/common/debug.py
@@ -19,13 +19,14 @@
from tempest.openstack.common import log as logging
+CONF = config.CONF
LOG = logging.getLogger(__name__)
tables = ['filter', 'nat', 'mangle']
def log_ip_ns():
- if not config.TempestConfig().debug.enable:
+ if not CONF.debug.enable:
return
LOG.info("Host Addr:\n" + commands.ip_addr_raw())
LOG.info("Host Route:\n" + commands.ip_route_raw())
diff --git a/tempest/common/generate_sample_tempest.py b/tempest/common/generate_sample_tempest.py
index 545703b..22be4aa 100644
--- a/tempest/common/generate_sample_tempest.py
+++ b/tempest/common/generate_sample_tempest.py
@@ -18,7 +18,7 @@
import sys
-from tempest import config
+import tempest.config
from tempest.openstack.common.config import generator
# NOTE(mtreinish): This hack is needed because of how oslo config is used in
@@ -31,5 +31,7 @@
# the issue by manually loading the config file (which may or may not exist)
# which will populate all the options before running the generator.
-config.TempestConfig()
-generator.generate(sys.argv[1:])
+
+if __name__ == "__main__":
+ CONF = tempest.config.TempestConfig()
+ generator.generate(sys.argv[1:])
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 5dbb3a7..da60318 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -25,6 +25,7 @@
from tempest import exceptions
from tempest.openstack.common import log as logging
+CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -36,7 +37,7 @@
self.isolated_net_resources = {}
self.ports = []
self.name = name
- self.config = config.TempestConfig()
+ self.config = CONF
self.tempest_client = tempest_client
self.interface = interface
self.password = password
@@ -44,11 +45,11 @@
self._get_admin_clients())
def _get_official_admin_clients(self):
- username = self.config.identity.admin_username
- password = self.config.identity.admin_password
- tenant_name = self.config.identity.admin_tenant_name
- auth_url = self.config.identity.uri
- dscv = self.config.identity.disable_ssl_certificate_validation
+ username = CONF.identity.admin_username
+ password = CONF.identity.admin_password
+ tenant_name = CONF.identity.admin_tenant_name
+ auth_url = CONF.identity.uri
+ dscv = CONF.identity.disable_ssl_certificate_validation
identity_client = keystoneclient.Client(username=username,
password=password,
tenant_name=tenant_name,
@@ -164,7 +165,7 @@
role = None
try:
roles = self._list_roles()
- admin_role = self.config.identity.admin_role
+ admin_role = CONF.identity.admin_role
if self.tempest_client:
role = next(r for r in roles if r['name'] == admin_role)
else:
@@ -229,8 +230,8 @@
if not self.tempest_client:
body = {'subnet': {'name': subnet_name, 'tenant_id': tenant_id,
'network_id': network_id, 'ip_version': 4}}
- base_cidr = netaddr.IPNetwork(self.config.network.tenant_network_cidr)
- mask_bits = self.config.network.tenant_network_mask_bits
+ base_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
+ mask_bits = CONF.network.tenant_network_mask_bits
for subnet_cidr in base_cidr.subnet(mask_bits):
try:
if self.tempest_client:
@@ -252,7 +253,7 @@
def _create_router(self, router_name, tenant_id):
external_net_id = dict(
- network_id=self.config.network.public_network_id)
+ network_id=CONF.network.public_network_id)
if self.tempest_client:
resp, resp_body = self.network_admin_client.create_router(
router_name,
@@ -328,7 +329,7 @@
self.isolated_creds['primary'] = (user, tenant)
LOG.info("Acquired isolated creds:\n user: %s, tenant: %s"
% (username, tenant_name))
- if self.config.service_available.neutron:
+ if CONF.service_available.neutron:
network, subnet, router = self._create_network_resources(
self._get_tenant_id(tenant))
self.isolated_net_resources['primary'] = (
@@ -347,7 +348,7 @@
self.isolated_creds['admin'] = (user, tenant)
LOG.info("Acquired admin isolated creds:\n user: %s, tenant: %s"
% (username, tenant_name))
- if self.config.service_available.neutron:
+ if CONF.service_available.neutron:
network, subnet, router = self._create_network_resources(
self._get_tenant_id(tenant))
self.isolated_net_resources['admin'] = (
@@ -366,7 +367,7 @@
self.isolated_creds['alt'] = (user, tenant)
LOG.info("Acquired alt isolated creds:\n user: %s, tenant: %s"
% (username, tenant_name))
- if self.config.service_available.neutron:
+ if CONF.service_available.neutron:
network, subnet, router = self._create_network_resources(
self._get_tenant_id(tenant))
self.isolated_net_resources['alt'] = (
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index c397b7c..bca2f9e 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -23,6 +23,7 @@
import warnings
from tempest import exceptions
+from tempest.openstack.common import log as logging
with warnings.catch_warnings():
@@ -30,6 +31,9 @@
import paramiko
+LOG = logging.getLogger(__name__)
+
+
class Client(object):
def __init__(self, host, username, password=None, timeout=300, pkey=None,
@@ -49,33 +53,44 @@
def _get_ssh_connection(self, sleep=1.5, backoff=1.01):
"""Returns an ssh connection to the specified host."""
- _timeout = True
bsleep = sleep
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(
paramiko.AutoAddPolicy())
_start_time = time.time()
-
- while not self._is_timed_out(_start_time):
+ if self.pkey is not None:
+ LOG.info("Creating ssh connection to '%s' as '%s'"
+ " with public key authentication",
+ self.host, self.username)
+ else:
+ LOG.info("Creating ssh connection to '%s' as '%s'"
+ " with password %s",
+ self.host, self.username, str(self.password))
+ attempts = 0
+ while True:
try:
ssh.connect(self.host, username=self.username,
password=self.password,
look_for_keys=self.look_for_keys,
key_filename=self.key_filename,
timeout=self.channel_timeout, pkey=self.pkey)
- _timeout = False
- break
+ LOG.info("ssh connection to %s@%s sucessfuly created",
+ self.username, self.host)
+ return ssh
except (socket.error,
- paramiko.AuthenticationException,
paramiko.SSHException):
+ attempts += 1
time.sleep(bsleep)
bsleep *= backoff
- continue
- if _timeout:
- raise exceptions.SSHTimeout(host=self.host,
- user=self.username,
- password=self.password)
- return ssh
+ if not self._is_timed_out(_start_time):
+ continue
+ else:
+ LOG.exception("Failed to establish authenticated ssh"
+ " connection to %s@%s after %d attempts",
+ self.username, self.host, attempts)
+ raise exceptions.SSHTimeout(host=self.host,
+ user=self.username,
+ password=self.password)
def _is_timed_out(self, start_time):
return (time.time() - self.timeout) > start_time
@@ -144,11 +159,6 @@
return ''.join(out_data)
def test_connection_auth(self):
- """Returns true if ssh can connect to server."""
- try:
- connection = self._get_ssh_connection()
- connection.close()
- except paramiko.AuthenticationException:
- return False
-
- return True
+ """Raises an exception when we can not connect to server via ssh."""
+ connection = self._get_ssh_connection()
+ connection.close()
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 0d0e794..fa59e14 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -17,19 +17,20 @@
from tempest.common.ssh import Client
from tempest.common import utils
-from tempest.config import TempestConfig
+from tempest import config
from tempest.exceptions import ServerUnreachable
-from tempest.exceptions import SSHTimeout
+
+CONF = config.CONF
class RemoteClient():
# NOTE(afazekas): It should always get an address instead of server
def __init__(self, server, username, password=None, pkey=None):
- ssh_timeout = TempestConfig().compute.ssh_timeout
- network = TempestConfig().compute.network_for_ssh
- ip_version = TempestConfig().compute.ip_version_for_ssh
- ssh_channel_timeout = TempestConfig().compute.ssh_channel_timeout
+ ssh_timeout = CONF.compute.ssh_timeout
+ network = CONF.compute.network_for_ssh
+ ip_version = CONF.compute.ip_version_for_ssh
+ ssh_channel_timeout = CONF.compute.ssh_channel_timeout
if isinstance(server, basestring):
ip_address = server
else:
@@ -40,16 +41,15 @@
break
else:
raise ServerUnreachable()
-
self.ssh_client = Client(ip_address, username, password, ssh_timeout,
pkey=pkey,
channel_timeout=ssh_channel_timeout)
- if not self.ssh_client.test_connection_auth():
- raise SSHTimeout()
- def can_authenticate(self):
- # Re-authenticate
- return self.ssh_client.test_connection_auth()
+ def validate_authentication(self):
+ """Validate ssh connection and authentication
+ This method raises an Exception when the validation fails.
+ """
+ self.ssh_client.test_connection_auth()
def hostname_equals_servername(self, expected_hostname):
# Get host name using command "hostname"
@@ -89,3 +89,11 @@
# usually to /dev/ttyS0
cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
return self.ssh_client.exec_command(cmd)
+
+ def ping_host(self, host):
+ cmd = 'ping -c1 -w1 %s' % host
+ return self.ssh_client.exec_command(cmd)
+
+ def get_mac_address(self):
+ cmd = "/sbin/ifconfig | awk '/HWaddr/ {print $5}'"
+ return self.ssh_client.exec_command(cmd)
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 44198f0..aedba15 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -19,7 +19,7 @@
from tempest import exceptions
from tempest.openstack.common import log as logging
-CONFIG = config.TempestConfig()
+CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -55,7 +55,7 @@
# responses
if str(task_state) == "None":
# without state api extension 3 sec usually enough
- time.sleep(CONFIG.compute.ready_wait)
+ time.sleep(CONF.compute.ready_wait)
return
else:
return
@@ -88,3 +88,34 @@
raise exceptions.TimeoutException(message)
old_status = server_status
old_task_state = task_state
+
+
+def wait_for_image_status(client, image_id, status):
+ """Waits for an image to reach a given status.
+
+ The client should have a get_image(image_id) method to get the image.
+ The client should also have build_interval and build_timeout attributes.
+ """
+ resp, image = client.get_image(image_id)
+ start = int(time.time())
+
+ while image['status'] != status:
+ time.sleep(client.build_interval)
+ resp, image = client.get_image(image_id)
+ if image['status'] == 'ERROR':
+ raise exceptions.AddImageException(image_id=image_id)
+
+ # check the status again to avoid a false negative where we hit
+ # the timeout at the same time that the image reached the expected
+ # status
+ if image['status'] == status:
+ return
+
+ if int(time.time()) - start >= client.build_timeout:
+ message = ('Image %(image_id)s failed to reach %(status)s '
+ 'status within the required time (%(timeout)s s).' %
+ {'image_id': image_id,
+ 'status': status,
+ 'timeout': client.build_timeout})
+ message += ' Current status: %s.' % image['status']
+ raise exceptions.TimeoutException(message)
diff --git a/tempest/config.py b/tempest/config.py
index 3d9eba9..140c521 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -20,10 +20,8 @@
import os
import sys
-
from oslo.config import cfg
-from tempest.common.utils.misc import singleton
from tempest.openstack.common import log as logging
@@ -137,7 +135,7 @@
help="Timeout in seconds to wait for an instance to build."),
cfg.BoolOpt('run_ssh',
default=False,
- help="Does the test environment support snapshots?"),
+ help="Should the tests ssh to instances?"),
cfg.StrOpt('ssh_user',
default='root',
help="User name used to authenticate to an instance."),
@@ -486,6 +484,16 @@
]
+data_processing_group = cfg.OptGroup(name="data_processing",
+ title="Data Processing options")
+
+DataProcessingGroup = [
+ cfg.StrOpt('catalog_type',
+ default='data_processing',
+ help="Catalog type of the data processing service.")
+]
+
+
boto_group = cfg.OptGroup(name='boto',
title='EC2/S3 options')
BotoGroup = [
@@ -621,6 +629,9 @@
cfg.BoolOpt('horizon',
default=True,
help="Whether or not Horizon is expected to be available"),
+ cfg.BoolOpt('savanna',
+ default=False,
+ help="Whether or not Savanna is expected to be available"),
]
debug_group = cfg.OptGroup(name="debug",
@@ -633,8 +644,7 @@
]
-@singleton
-class TempestConfig:
+class TempestConfig(object):
"""Provides OpenStack configuration information."""
DEFAULT_CONFIG_DIR = os.path.join(
@@ -645,6 +655,7 @@
def __init__(self):
"""Initialize a configuration from a conf directory and conf file."""
+ super(TempestConfig, self).__init__()
config_files = []
failsafe_path = "/etc/tempest/" + self.DEFAULT_CONFIG_FILE
@@ -688,6 +699,8 @@
ObjectStoreFeaturesGroup)
register_opt_group(cfg.CONF, orchestration_group, OrchestrationGroup)
register_opt_group(cfg.CONF, dashboard_group, DashboardGroup)
+ register_opt_group(cfg.CONF, data_processing_group,
+ DataProcessingGroup)
register_opt_group(cfg.CONF, boto_group, BotoGroup)
register_opt_group(cfg.CONF, compute_admin_group, ComputeAdminGroup)
register_opt_group(cfg.CONF, stress_group, StressGroup)
@@ -709,6 +722,7 @@
'object-storage-feature-enabled']
self.orchestration = cfg.CONF.orchestration
self.dashboard = cfg.CONF.dashboard
+ self.data_processing = cfg.CONF.data_processing
self.boto = cfg.CONF.boto
self.compute_admin = cfg.CONF['compute-admin']
self.stress = cfg.CONF.stress
@@ -719,3 +733,16 @@
self.compute_admin.username = self.identity.admin_username
self.compute_admin.password = self.identity.admin_password
self.compute_admin.tenant_name = self.identity.admin_tenant_name
+
+
+class TempestConfigProxy(object):
+ _config = None
+
+ def __getattr__(self, attr):
+ if not self._config:
+ self._config = TempestConfig()
+
+ return getattr(self._config, attr)
+
+
+CONF = TempestConfigProxy()
diff --git a/tempest/manager.py b/tempest/manager.py
index e3aeb31..42b8c8f 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import tempest.config
+from tempest import config
from tempest import exceptions
@@ -29,7 +29,7 @@
"""
def __init__(self):
- self.config = tempest.config.TempestConfig()
+ self.config = config.CONF
self.client_attr_names = []
# we do this everywhere, have it be part of the super class
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index e839d20..207c542 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -33,7 +33,6 @@
from tempest.api.network import common as net_common
from tempest.common import isolated_creds
-from tempest.common import ssh
from tempest.common.utils import data_utils
from tempest.common.utils.linux.remote_client import RemoteClient
from tempest import exceptions
@@ -208,7 +207,7 @@
cls.isolated_creds = isolated_creds.IsolatedCreds(
__name__, tempest_client=False)
- username, tenant_name, password = cls.credentials()
+ username, password, tenant_name = cls.credentials()
cls.manager = OfficialClientManager(username, password, tenant_name)
cls.compute_client = cls.manager.compute_client
@@ -221,14 +220,27 @@
cls.os_resources = []
@classmethod
- def credentials(cls):
+ def _get_credentials(cls, get_creds, prefix):
if cls.config.compute.allow_tenant_isolation:
- return cls.isolated_creds.get_primary_creds()
+ username, tenant_name, password = get_creds()
+ else:
+ username = getattr(cls.config.identity, prefix + 'username')
+ password = getattr(cls.config.identity, prefix + 'password')
+ tenant_name = getattr(cls.config.identity, prefix + 'tenant_name')
+ return username, password, tenant_name
- username = cls.config.identity.username
- password = cls.config.identity.password
- tenant_name = cls.config.identity.tenant_name
- return username, tenant_name, password
+ @classmethod
+ def credentials(cls):
+ return cls._get_credentials(cls.isolated_creds.get_primary_creds, '')
+
+ @classmethod
+ def alt_credentials(cls):
+ return cls._get_credentials(cls.isolated_creds.get_alt_creds, 'alt_')
+
+ @classmethod
+ def admin_credentials(cls):
+ return cls._get_credentials(cls.isolated_creds.get_admin_creds,
+ 'admin_')
@classmethod
def tearDownClass(cls):
@@ -248,7 +260,8 @@
thing.delete()
except Exception as e:
# If the resource is already missing, mission accomplished.
- if e.__class__.__name__ == 'NotFound':
+ # add status code as workaround for bug 1247568
+ if e.__class__.__name__ == 'NotFound' or e.status_code == 404:
continue
raise
@@ -348,8 +361,8 @@
# so case sensitive comparisons can really mess things
# up.
if new_status.lower() == error_status.lower():
- message = "%s failed to get to expected status. \
- In %s state." % (thing, new_status)
+ message = ("%s failed to get to expected status. "
+ "In %s state.") % (thing, new_status)
raise exceptions.BuildErrorException(message)
elif new_status == expected_status and expected_status is not None:
return True # All good.
@@ -360,8 +373,8 @@
check_status,
self.config.compute.build_timeout,
self.config.compute.build_interval):
- message = "Timed out waiting for thing %s \
- to become %s" % (thing_id, log_status)
+ message = ("Timed out waiting for thing %s "
+ "to become %s") % (thing_id, log_status)
raise exceptions.TimeoutException(message)
def _create_loginable_secgroup_rule_nova(self, client=None,
@@ -608,12 +621,15 @@
self.set_resource(name, port)
return port
- def _create_floating_ip(self, server, external_network_id):
+ def _get_server_port_id(self, server):
result = self.network_client.list_ports(device_id=server.id)
ports = result.get('ports', [])
self.assertEqual(len(ports), 1,
"Unable to determine which port to target.")
- port_id = ports[0]['id']
+ return ports[0]['id']
+
+ def _create_floating_ip(self, server, external_network_id):
+ port_id = self._get_server_port_id(server)
body = dict(
floatingip=dict(
floating_network_id=external_network_id,
@@ -628,6 +644,12 @@
self.set_resource(data_utils.rand_name('floatingip-'), floating_ip)
return floating_ip
+ def _associate_floating_ip(self, floating_ip, server):
+ port_id = self._get_server_port_id(server)
+ floating_ip.update(port_id=port_id)
+ self.assertEqual(port_id, floating_ip.port_id)
+ return floating_ip
+
def _disassociate_floating_ip(self, floating_ip):
"""
:param floating_ip: type DeletableFloatingIp
@@ -649,13 +671,6 @@
return tempest.test.call_until_true(
ping, self.config.compute.ping_timeout, 1)
- def _is_reachable_via_ssh(self, ip_address, username, private_key,
- timeout):
- ssh_client = ssh.Client(ip_address, username,
- pkey=private_key,
- timeout=timeout)
- return ssh_client.test_connection_auth()
-
def _check_vm_connectivity(self, ip_address,
username=None,
private_key=None,
@@ -680,13 +695,9 @@
msg=msg)
if should_connect:
# no need to check ssh for negative connectivity
- self.assertTrue(self._is_reachable_via_ssh(
- ip_address,
- username,
- private_key,
- timeout=self.config.compute.ssh_timeout),
- 'Auth failure in connecting to %s@%s via ssh' %
- (username, ip_address))
+ linux_client = self.get_remote_client(ip_address, username,
+ private_key)
+ linux_client.validate_authentication()
def _create_security_group_nova(self, client=None,
namestart='secgroup-smoke-',
@@ -853,6 +864,12 @@
return rules
+ def _ssh_to_server(self, server, private_key):
+ ssh_login = self.config.compute.image_ssh_user
+ return self.get_remote_client(server,
+ username=ssh_login,
+ private_key=private_key)
+
def _show_quota_network(self, tenant_id):
quota = self.network_client.show_quota(tenant_id)
return quota['quota']['network']
@@ -865,6 +882,60 @@
quota = self.network_client.show_quota(tenant_id)
return quota['quota']['port']
+ def _get_router(self, tenant_id):
+ """Retrieve a router for the given tenant id.
+
+ If a public router has been configured, it will be returned.
+
+ If a public router has not been configured, but a public
+ network has, a tenant router will be created and returned that
+ routes traffic to the public network.
+ """
+ router_id = self.config.network.public_router_id
+ network_id = self.config.network.public_network_id
+ if router_id:
+ result = self.network_client.show_router(router_id)
+ return net_common.AttributeDict(**result['router'])
+ elif network_id:
+ router = self._create_router(tenant_id)
+ router.add_gateway(network_id)
+ return router
+ else:
+ raise Exception("Neither of 'public_router_id' or "
+ "'public_network_id' has been defined.")
+
+ def _create_router(self, tenant_id, namestart='router-smoke-'):
+ name = data_utils.rand_name(namestart)
+ body = dict(
+ router=dict(
+ name=name,
+ admin_state_up=True,
+ tenant_id=tenant_id,
+ ),
+ )
+ result = self.network_client.create_router(body=body)
+ router = net_common.DeletableRouter(client=self.network_client,
+ **result['router'])
+ self.assertEqual(router.name, name)
+ self.set_resource(name, router)
+ return router
+
+ def _create_networks(self, tenant_id=None):
+ """Create a network with a subnet connected to a router.
+
+ :returns: network, subnet, router
+ """
+ if tenant_id is None:
+ tenant_id = self.tenant_id
+ network = self._create_network(tenant_id)
+ router = self._get_router(tenant_id)
+ subnet = self._create_subnet(network)
+ subnet.add_to_router(router.id)
+ self.networks.append(network)
+ self.subnets.append(subnet)
+ self.routers.append(router)
+ return network, subnet, router
+
class OrchestrationScenarioTest(OfficialClientTest):
"""
diff --git a/tempest/scenario/test_cross_tenant_connectivity.py b/tempest/scenario/test_cross_tenant_connectivity.py
new file mode 100644
index 0000000..faba987
--- /dev/null
+++ b/tempest/scenario/test_cross_tenant_connectivity.py
@@ -0,0 +1,489 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.common import debug
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.openstack.common import log as logging
+from tempest.scenario import manager
+from tempest.scenario.manager import OfficialClientManager
+from tempest.test import attr
+from tempest.test import call_until_true
+from tempest.test import services
+
+LOG = logging.getLogger(__name__)
+
+
+class TestNetworkCrossTenant(manager.NetworkScenarioTest):
+
+ """
+ This test suite assumes that Nova has been configured to
+ boot VM's with Neutron-managed networking, and attempts to
+ verify cross tenant connectivity as follows
+
+ ssh:
+ in order to overcome "ip namespace", each tenant has an "access point"
+ VM with floating-ip open to incoming ssh connection allowing network
+ commands (ping/ssh) to be executed from within the
+ tenant-network-namespace
+ Tempest host performs key-based authentication to the ssh server via
+ floating IP address
+
+ connectivity test is done by pinging destination server via source server
+ ssh connection.
+ success - ping returns
+ failure - ping_timeout reached
+
+ setup:
+ for each tenant (demo and alt):
+ 1. create a network&subnet
+ 2. create a router (if public router isn't configured)
+ 3. connect tenant network to public network via router
+ 4. create an access point:
+ a. a security group open to incoming ssh connection
+ b. a VM with a floating ip
+ 5. create a general empty security group (same as "default", but
+ without rules allowing in-tenant traffic)
+ 6. for demo tenant - create another server to test in-tenant
+ connections
+
+ tests:
+ 1. _verify_network_details
+ 2. _verify_mac_addr: for each access point verify that
+ (subnet, fix_ip, mac address) are as defined in the port list
+ 3. _test_in_tenant_block: test that in-tenant traffic is disabled
+ without rules allowing it
+ 4. _test_in_tenant_allow: test that in-tenant traffic is enabled
+ once an appropriate rule has been created
+ 5. _test_cross_tenant_block: test that cross-tenant traffic is disabled
+ without a rule allowing it on destination tenant
+ 6. _test_cross_tenant_allow:
+ * test that cross-tenant traffic is enabled once an appropriate
+ rule has been created on destination tenant.
+ * test that reverse traffic is still blocked
+ * test than revesre traffic is enabled once an appropriate rule has
+ been created on source tenant
+
+ assumptions:
+ 1. alt_tenant/user existed and is different from demo_tenant/user
+ 2. Public network is defined and reachable from the Tempest host
+ 3. Public router can either be:
+ * defined, in which case all tenants networks can connect directly
+ to it, and cross tenant check will be done on the private IP of the
+ destination tenant
+ or
+ * not defined (empty string), in which case each tanant will have
+ its own router connected to the public network
+ """
+
+ class TenantProperties():
+ '''
+ helper class to save tenant details
+ id
+ credentials
+ network
+ subnet
+ security groups
+ servers
+ access point
+ '''
+
+ def __init__(self, tenant_id, tenant_user, tenant_pass, tenant_name):
+ self.manager = OfficialClientManager(
+ tenant_user,
+ tenant_pass,
+ tenant_name
+ )
+ self.tenant_id = tenant_id
+ self.tenant_name = tenant_name
+ self.tenant_user = tenant_user
+ self.tenant_pass = tenant_pass
+ self.network = None
+ self.subnet = None
+ self.router = None
+ self.security_groups = {}
+ self.servers = list()
+
+ def _set_network(self, network, subnet, router):
+ self.network = network
+ self.subnet = subnet
+ self.router = router
+
+ def _get_tenant_credentials(self):
+ return self.tenant_user, self.tenant_pass, self.tenant_name
+
+ @classmethod
+ def check_preconditions(cls):
+ super(TestNetworkCrossTenant, cls).check_preconditions()
+ if (cls.alt_tenant_id is None) or (cls.tenant_id is cls.alt_tenant_id):
+ msg = 'No alt_tenant defined'
+ cls.enabled = False
+ raise cls.skipException(msg)
+ cfg = cls.config.network
+ if not (cfg.tenant_networks_reachable or cfg.public_network_id):
+ msg = ('Either tenant_networks_reachable must be "true", or '
+ 'public_network_id must be defined.')
+ cls.enabled = False
+ raise cls.skipException(msg)
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestNetworkCrossTenant, cls).setUpClass()
+ alt_creds = cls.alt_credentials()
+ cls.alt_tenant_id = cls.manager._get_identity_client(
+ *alt_creds
+ ).tenant_id
+ cls.check_preconditions()
+ # TODO(mnewby) Consider looking up entities as needed instead
+ # of storing them as collections on the class.
+ cls.keypairs = {}
+ cls.security_groups = {}
+ cls.networks = []
+ cls.subnets = []
+ cls.routers = []
+ cls.servers = []
+ cls.floating_ips = {}
+ cls.tenants = {}
+ cls.demo_tenant = cls.TenantProperties(
+ cls.tenant_id,
+ *cls.credentials()
+ )
+ cls.alt_tenant = cls.TenantProperties(
+ cls.alt_tenant_id,
+ *alt_creds
+ )
+ for tenant in [cls.demo_tenant, cls.alt_tenant]:
+ cls.tenants[tenant.tenant_id] = tenant
+ if not cls.config.network.public_router_id:
+ cls.floating_ip_access = True
+ else:
+ cls.floating_ip_access = False
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestNetworkCrossTenant, cls).tearDownClass()
+
+ def _create_tenant_keypairs(self, tenant_id):
+ self.keypairs[tenant_id] = self.create_keypair(
+ name=data_utils.rand_name('keypair-smoke-'))
+
+ def _create_tenant_security_groups(self, tenant):
+ self.security_groups.setdefault(self.tenant_id, [])
+ access_sg = self._create_empty_security_group(
+ namestart='secgroup_access-',
+ tenant_id=tenant.tenant_id
+ )
+ # don't use default secgroup since it allows in-tenant traffic
+ def_sg = self._create_empty_security_group(
+ namestart='secgroup_general-',
+ tenant_id=tenant.tenant_id
+ )
+ tenant.security_groups.update(access=access_sg, default=def_sg)
+ ssh_rule = dict(
+ protocol='tcp',
+ port_range_min=22,
+ port_range_max=22,
+ direction='ingress',
+ )
+ self._create_security_group_rule(secgroup=access_sg,
+ **ssh_rule
+ )
+
+ def _verify_network_details(self, tenant):
+ # Checks that we see the newly created network/subnet/router via
+ # checking the result of list_[networks,routers,subnets]
+ # Check that (router, subnet) couple exist in port_list
+ seen_nets = self._list_networks()
+ seen_names = [n['name'] for n in seen_nets]
+ seen_ids = [n['id'] for n in seen_nets]
+
+ self.assertIn(tenant.network.name, seen_names)
+ self.assertIn(tenant.network.id, seen_ids)
+
+ seen_subnets = [(n['id'], n['cidr'], n['network_id'])
+ for n in self._list_subnets()]
+ mysubnet = (tenant.subnet.id, tenant.subnet.cidr, tenant.network.id)
+ self.assertIn(mysubnet, seen_subnets)
+
+ seen_routers = self._list_routers()
+ seen_router_ids = [n['id'] for n in seen_routers]
+ seen_router_names = [n['name'] for n in seen_routers]
+
+ self.assertIn(tenant.router.name, seen_router_names)
+ self.assertIn(tenant.router.id, seen_router_ids)
+
+ myport = (tenant.router.id, tenant.subnet.id)
+ router_ports = [(i['device_id'], i['fixed_ips'][0]['subnet_id']) for i
+ in self.network_client.list_ports()['ports']
+ if i['device_owner'] == 'network:router_interface']
+
+ self.assertIn(myport, router_ports)
+
+ def _create_server(self, name, tenant, security_groups=None):
+ """
+ creates a server and assigns to security group
+ """
+ self._set_compute_context(tenant)
+ if security_groups is None:
+ security_groups = [tenant.security_groups['default'].name]
+ create_kwargs = {
+ 'nics': [
+ {'net-id': tenant.network.id},
+ ],
+ 'key_name': self.keypairs[tenant.tenant_id].name,
+ 'security_groups': security_groups,
+ 'tenant_id': tenant.tenant_id
+ }
+ server = self.create_server(name=name, create_kwargs=create_kwargs)
+ return server
+
+ def _create_tenant_servers(self, tenant, num=1):
+ for i in range(num):
+ name = 'server-{tenant}-gen-{num}-'.format(
+ tenant=tenant.tenant_name,
+ num=i
+ )
+ name = data_utils.rand_name(name)
+ server = self._create_server(name, tenant)
+ self.servers.append(server)
+ tenant.servers.append(server)
+
+ def _set_access_point(self, tenant):
+ """
+ creates a server in a secgroup with rule allowing external ssh
+ in order to access tenant internal network
+ workaround ip namespace
+ """
+ secgroups = [sg.name for sg in tenant.security_groups.values()]
+ name = 'server-{tenant}-access_point-'.format(tenant=tenant.tenant_name
+ )
+ name = data_utils.rand_name(name)
+ server = self._create_server(name, tenant,
+ security_groups=secgroups)
+ self.servers.append(server)
+ tenant.access_point = server
+ self._assign_floating_ips(server)
+
+ def _assign_floating_ips(self, server):
+ public_network_id = self.config.network.public_network_id
+ floating_ip = self._create_floating_ip(server, public_network_id)
+ self.floating_ips.setdefault(server, floating_ip)
+
+ def _create_tenant_network(self, tenant):
+ tenant._set_network(*self._create_networks(tenant.tenant_id))
+
+ def _set_compute_context(self, tenant):
+ self.compute_client = tenant.manager.compute_client
+ return self.compute_client
+
+ def _deploy_tenant(self, tenant_or_id):
+ """
+ creates:
+ network
+ subnet
+ router (if public not defined)
+ access security group
+ access-point server
+ for demo_tenant:
+ creates general server to test against
+ """
+ if not isinstance(tenant_or_id, self.TenantProperties):
+ tenant = self.tenants[tenant_or_id]
+ tenant_id = tenant_or_id
+ else:
+ tenant = tenant_or_id
+ tenant_id = tenant.tenant_id
+ self._set_compute_context(tenant)
+ self._create_tenant_keypairs(tenant_id)
+ self._create_tenant_network(tenant)
+ self._create_tenant_security_groups(tenant)
+ if tenant is self.demo_tenant:
+ self._create_tenant_servers(tenant, num=1)
+ self._set_access_point(tenant)
+
+ def _get_server_ip(self, server, floating=False):
+ '''
+ returns the ip (floating/internal) of a server
+ '''
+ if floating:
+ return self.floating_ips[server].floating_ip_address
+ else:
+ network_name = self.tenants[server.tenant_id].network.name
+ return server.networks[network_name][0]
+
+ def _connect_to_access_point(self, tenant):
+ """
+ create ssh connection to tenant access point
+ """
+ access_point_ssh = \
+ self.floating_ips[tenant.access_point].floating_ip_address
+ private_key = self.keypairs[tenant.tenant_id].private_key
+ access_point_ssh = self._ssh_to_server(access_point_ssh,
+ private_key=private_key)
+ return access_point_ssh
+
+ def _test_remote_connectivity(self, source, dest, should_succeed=True):
+ """
+ check ping server via source ssh connection
+
+ :param source: RemoteClient: an ssh connection from which to ping
+ :param dest: and IP to ping against
+ :param should_succeed: boolean should ping succeed or not
+ :returns: boolean -- should_succeed == ping
+ :returns: ping is false if ping failed
+ """
+ def ping_remote():
+ try:
+ source.ping_host(dest)
+ except exceptions.SSHExecCommandFailed as ex:
+ LOG.debug(ex)
+ return not should_succeed
+ return should_succeed
+
+ return call_until_true(ping_remote,
+ self.config.compute.ping_timeout,
+ 1)
+
+ def _check_connectivity(self, access_point, ip, should_succeed=True):
+ if should_succeed:
+ msg = "Timed out waiting for %s to become reachable" % ip
+ else:
+ # todo(yfried): remove this line when bug 1252620 is fixed
+ return True
+ msg = "%s is reachable" % ip
+ try:
+ self.assertTrue(self._test_remote_connectivity(access_point, ip,
+ should_succeed),
+ msg)
+ except Exception:
+ debug.log_ip_ns()
+ raise
+
+ def _test_in_tenant_block(self, tenant):
+ access_point_ssh = self._connect_to_access_point(tenant)
+ for server in tenant.servers:
+ self._check_connectivity(access_point=access_point_ssh,
+ ip=self._get_server_ip(server),
+ should_succeed=False)
+
+ def _test_in_tenant_allow(self, tenant):
+ ruleset = dict(
+ protocol='icmp',
+ remote_group_id=tenant.security_groups['default'].id,
+ direction='ingress'
+ )
+ rule = self._create_security_group_rule(
+ secgroup=tenant.security_groups['default'],
+ **ruleset
+ )
+ access_point_ssh = self._connect_to_access_point(tenant)
+ for server in tenant.servers:
+ self._check_connectivity(access_point=access_point_ssh,
+ ip=self._get_server_ip(server))
+ rule.delete()
+
+ def _test_cross_tenant_block(self, source_tenant, dest_tenant):
+ '''
+ if public router isn't defined, then dest_tenant access is via
+ floating-ip
+ '''
+ access_point_ssh = self._connect_to_access_point(source_tenant)
+ ip = self._get_server_ip(dest_tenant.access_point,
+ floating=self.floating_ip_access)
+ self._check_connectivity(access_point=access_point_ssh, ip=ip,
+ should_succeed=False)
+
+ def _test_cross_tenant_allow(self, source_tenant, dest_tenant):
+ '''
+ check for each direction:
+ creating rule for tenant incoming traffic enables only 1way traffic
+ '''
+ ruleset = dict(
+ protocol='icmp',
+ direction='ingress'
+ )
+ rule_s2d = self._create_security_group_rule(
+ secgroup=dest_tenant.security_groups['default'],
+ **ruleset
+ )
+ try:
+ access_point_ssh = self._connect_to_access_point(source_tenant)
+ ip = self._get_server_ip(dest_tenant.access_point,
+ floating=self.floating_ip_access)
+ self._check_connectivity(access_point_ssh, ip)
+
+ # test that reverse traffic is still blocked
+ self._test_cross_tenant_block(dest_tenant, source_tenant)
+
+ # allow reverse traffic and check
+ rule_d2s = self._create_security_group_rule(
+ secgroup=source_tenant.security_groups['default'],
+ **ruleset
+ )
+ try:
+ access_point_ssh_2 = self._connect_to_access_point(dest_tenant)
+ ip = self._get_server_ip(source_tenant.access_point,
+ floating=self.floating_ip_access)
+ self._check_connectivity(access_point_ssh_2, ip)
+
+ # clean_rules
+ rule_s2d.delete()
+ rule_d2s.delete()
+
+ except Exception as e:
+ rule_d2s.delete()
+ raise e
+
+ except Exception as e:
+ rule_s2d.delete()
+ raise e
+
+ def _verify_mac_addr(self, tenant):
+ """
+ verify that VM (tenant's access point) has the same ip,mac as listed in
+ port list
+ """
+ access_point_ssh = self._connect_to_access_point(tenant)
+ mac_addr = access_point_ssh.get_mac_address()
+ mac_addr = mac_addr.strip().lower()
+ port_list = self.network_client.list_ports()['ports']
+ port_detail_list = [
+ (port['fixed_ips'][0]['subnet_id'],
+ port['fixed_ips'][0]['ip_address'],
+ port['mac_address'].lower()) for port in port_list
+ ]
+ server_ip = self._get_server_ip(tenant.access_point)
+ subnet_id = tenant.subnet.id
+ self.assertIn((subnet_id, server_ip, mac_addr), port_detail_list)
+
+ @attr(type='smoke')
+ @services('compute', 'network')
+ def test_cross_tenant_traffic(self):
+ for tenant_id in self.tenants.keys():
+ self._deploy_tenant(tenant_id)
+ self._verify_network_details(self.tenants[tenant_id])
+ self._verify_mac_addr(self.tenants[tenant_id])
+
+ # in-tenant check
+ self._test_in_tenant_block(self.demo_tenant)
+ self._test_in_tenant_allow(self.demo_tenant)
+
+ # cross tenant check
+ source_tenant = self.demo_tenant
+ dest_tenant = self.alt_tenant
+ self._test_cross_tenant_block(source_tenant, dest_tenant)
+ self._test_cross_tenant_allow(source_tenant, dest_tenant)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index d605dff..33dd6c0 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -16,7 +16,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.network import common as net_common
from tempest.common import debug
from tempest.common.utils import data_utils
from tempest import config
@@ -28,6 +27,7 @@
from tempest.test import attr
from tempest.test import services
+CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -91,6 +91,10 @@
- detach the floating-ip from the VM and verify that it becomes
unreachable
+ - associate detached floating ip to a new VM and verify connectivity.
+ VMs are created with unique keypair so connectivity also asserts that
+ floating IP is associated with the new VM instead of the old one
+
# TODO(mnewby) - Need to implement the following:
- the Tempest host can ssh into the VM via the IP address and
successfully execute the following:
@@ -139,8 +143,6 @@
"""
- CONF = config.TempestConfig()
-
@classmethod
def check_preconditions(cls):
super(TestNetworkBasicOps, cls).check_preconditions()
@@ -157,70 +159,17 @@
cls.check_preconditions()
# TODO(mnewby) Consider looking up entities as needed instead
# of storing them as collections on the class.
- cls.keypairs = {}
cls.security_groups = {}
cls.networks = []
cls.subnets = []
cls.routers = []
- cls.servers = []
+ cls.servers = {}
cls.floating_ips = {}
- def _get_router(self, tenant_id):
- """Retrieve a router for the given tenant id.
-
- If a public router has been configured, it will be returned.
-
- If a public router has not been configured, but a public
- network has, a tenant router will be created and returned that
- routes traffic to the public network.
-
- """
- router_id = self.config.network.public_router_id
- network_id = self.config.network.public_network_id
- if router_id:
- result = self.network_client.show_router(router_id)
- return net_common.AttributeDict(**result['router'])
- elif network_id:
- router = self._create_router(tenant_id)
- router.add_gateway(network_id)
- return router
- else:
- raise Exception("Neither of 'public_router_id' or "
- "'public_network_id' has been defined.")
-
- def _create_router(self, tenant_id, namestart='router-smoke-'):
- name = data_utils.rand_name(namestart)
- body = dict(
- router=dict(
- name=name,
- admin_state_up=True,
- tenant_id=tenant_id,
- ),
- )
- result = self.network_client.create_router(body=body)
- router = net_common.DeletableRouter(client=self.network_client,
- **result['router'])
- self.assertEqual(router.name, name)
- self.set_resource(name, router)
- return router
-
- def _create_keypairs(self):
- self.keypairs[self.tenant_id] = self.create_keypair(
- name=data_utils.rand_name('keypair-smoke-'))
-
def _create_security_groups(self):
self.security_groups[self.tenant_id] =\
self._create_security_group_neutron(tenant_id=self.tenant_id)
- def _create_networks(self):
- network = self._create_network(self.tenant_id)
- router = self._get_router(self.tenant_id)
- subnet = self._create_subnet(network)
- subnet.add_to_router(router.id)
- self.networks.append(network)
- self.subnets.append(subnet)
- self.routers.append(router)
-
def _check_networks(self):
# Checks that we see the newly created network/subnet/router via
# checking the result of list_[networks,routers,subnets]
@@ -246,39 +195,38 @@
def _create_server(self, name, network):
tenant_id = network.tenant_id
- keypair_name = self.keypairs[tenant_id].name
+ keypair = self.create_keypair(name='keypair-%s' % name)
security_groups = [self.security_groups[tenant_id].name]
create_kwargs = {
'nics': [
{'net-id': network.id},
],
- 'key_name': keypair_name,
+ 'key_name': keypair.name,
'security_groups': security_groups,
}
server = self.create_server(name=name, create_kwargs=create_kwargs)
+ self.servers[server] = keypair
return server
def _create_servers(self):
for i, network in enumerate(self.networks):
name = data_utils.rand_name('server-smoke-%d-' % i)
- server = self._create_server(name, network)
- self.servers.append(server)
+ self._create_server(name, network)
def _check_tenant_network_connectivity(self):
- if not self.config.network.tenant_networks_reachable:
+ if not CONF.network.tenant_networks_reachable:
msg = 'Tenant networks not configured to be reachable.'
LOG.info(msg)
return
# The target login is assumed to have been configured for
# key-based authentication by cloud-init.
- ssh_login = self.config.compute.image_ssh_user
- private_key = self.keypairs[self.tenant_id].private_key
+ ssh_login = CONF.compute.image_ssh_user
try:
- for server in self.servers:
+ for server, key in self.servers.items():
for net_name, ip_addresses in server.networks.iteritems():
for ip_address in ip_addresses:
self._check_vm_connectivity(ip_address, ssh_login,
- private_key)
+ key.private_key)
except Exception as exc:
LOG.exception(exc)
debug.log_ip_ns()
@@ -290,25 +238,27 @@
self.assertTrue(
tempest.test.call_until_true(
- ip_tracker.run_checks, self.config.compute.build_timeout,
- self.config.compute.build_interval),
+ ip_tracker.run_checks, CONF.compute.build_timeout,
+ CONF.compute.build_interval),
"Timed out while waiting for the floating IP assignments "
"to propagate")
def _create_and_associate_floating_ips(self):
- public_network_id = self.config.network.public_network_id
- for server in self.servers:
+ public_network_id = CONF.network.public_network_id
+ for server in self.servers.keys():
floating_ip = self._create_floating_ip(server, public_network_id)
self.floating_ips[floating_ip] = server
def _check_public_network_connectivity(self, should_connect=True):
# The target login is assumed to have been configured for
# key-based authentication by cloud-init.
- ssh_login = self.config.compute.image_ssh_user
- private_key = self.keypairs[self.tenant_id].private_key
+ ssh_login = CONF.compute.image_ssh_user
try:
for floating_ip, server in self.floating_ips.iteritems():
ip_address = floating_ip.floating_ip_address
+ private_key = None
+ if should_connect:
+ private_key = self.servers[server].private_key
self._check_vm_connectivity(ip_address,
ssh_login,
private_key,
@@ -323,10 +273,18 @@
self._disassociate_floating_ip(floating_ip)
self.floating_ips[floating_ip] = None
+ def _reassociate_floating_ips(self):
+ network = self.networks[0]
+ for floating_ip in self.floating_ips.keys():
+ name = data_utils.rand_name('new_server-smoke-')
+ # create a new server for the floating ip
+ server = self._create_server(name, network)
+ self._associate_floating_ip(floating_ip, server)
+ self.floating_ips[floating_ip] = server
+
@attr(type='smoke')
@services('compute', 'network')
def test_network_basic_ops(self):
- self._create_keypairs()
self._create_security_groups()
self._create_networks()
self._check_networks()
@@ -337,3 +295,6 @@
self._check_public_network_connectivity(should_connect=True)
self._disassociate_floating_ips()
self._check_public_network_connectivity(should_connect=False)
+ self._reassociate_floating_ips()
+ self._wait_for_floating_ip_association()
+ self._check_public_network_connectivity(should_connect=True)
diff --git a/tempest/services/compute/json/extensions_client.py b/tempest/services/compute/json/extensions_client.py
index ce46a9b..ad5354c 100644
--- a/tempest/services/compute/json/extensions_client.py
+++ b/tempest/services/compute/json/extensions_client.py
@@ -37,3 +37,8 @@
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
+
+ def get_extension(self, extension_alias):
+ resp, body = self.get('extensions/%s' % extension_alias)
+ body = json.loads(body)
+ return resp, body['extension']
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index 5f17894..c05571a 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -16,10 +16,10 @@
# under the License.
import json
-import time
import urllib
from tempest.common.rest_client import RestClient
+from tempest.common import waiters
from tempest import exceptions
@@ -82,18 +82,7 @@
def wait_for_image_status(self, image_id, status):
"""Waits for an image to reach a given status."""
- resp, image = self.get_image(image_id)
- start = int(time.time())
-
- while image['status'] != status:
- time.sleep(self.build_interval)
- resp, image = self.get_image(image_id)
-
- if image['status'] == 'ERROR':
- raise exceptions.AddImageException(image_id=image_id)
-
- if int(time.time()) - start >= self.build_timeout:
- raise exceptions.TimeoutException
+ waiters.wait_for_image_status(self, image_id, status)
def list_image_metadata(self, image_id):
"""Lists all metadata items for an image."""
diff --git a/tempest/services/compute/v3/json/certificates_client.py b/tempest/services/compute/v3/json/certificates_client.py
new file mode 100644
index 0000000..bf0152b
--- /dev/null
+++ b/tempest/services/compute/v3/json/certificates_client.py
@@ -0,0 +1,42 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from tempest.common.rest_client import RestClient
+
+
+class CertificatesV3ClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(CertificatesV3ClientJSON, self).__init__(config, username,
+ password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_v3_type
+
+ def get_certificate(self, id):
+ url = "os-certificates/%s" % (id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['certificate']
+
+ def create_certificate(self):
+ """create certificates."""
+ url = "os-certificates"
+ resp, body = self.post(url, None, self.headers)
+ body = json.loads(body)
+ return resp, body['certificate']
diff --git a/tempest/services/compute/v3/json/extensions_client.py b/tempest/services/compute/v3/json/extensions_client.py
index 60c0217..6e0dc9d 100644
--- a/tempest/services/compute/v3/json/extensions_client.py
+++ b/tempest/services/compute/v3/json/extensions_client.py
@@ -38,3 +38,8 @@
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
+
+ def get_extension(self, extension_alias):
+ resp, body = self.get('extensions/%s' % extension_alias)
+ body = json.loads(body)
+ return resp, body['extension']
diff --git a/tempest/services/compute/v3/json/instance_usage_audit_log_client.py b/tempest/services/compute/v3/json/instance_usage_audit_log_client.py
new file mode 100644
index 0000000..07ce1bb
--- /dev/null
+++ b/tempest/services/compute/v3/json/instance_usage_audit_log_client.py
@@ -0,0 +1,40 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM 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.rest_client import RestClient
+
+
+class InstanceUsagesAuditLogClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(InstanceUsagesAuditLogClientJSON, self).__init__(
+ config, username, password, auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def list_instance_usage_audit_logs(self):
+ url = 'os-instance_usage_audit_log'
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body["instance_usage_audit_logs"]
+
+ def get_instance_usage_audit_log(self, time_before):
+ url = 'os-instance_usage_audit_log/%s' % time_before
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body["instance_usage_audit_log"]
diff --git a/tempest/services/compute/v3/json/keypairs_client.py b/tempest/services/compute/v3/json/keypairs_client.py
new file mode 100644
index 0000000..5e1900c
--- /dev/null
+++ b/tempest/services/compute/v3/json/keypairs_client.py
@@ -0,0 +1,56 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from tempest.common.rest_client import RestClient
+
+
+class KeyPairsClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(KeyPairsClientJSON, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def list_keypairs(self):
+ resp, body = self.get("os-keypairs")
+ body = json.loads(body)
+ # Each returned keypair is embedded within an unnecessary 'keypair'
+ # element which is a deviation from other resources like floating-ips,
+ # servers, etc. A bug?
+ # For now we shall adhere to the spec, but the spec for keypairs
+ # is yet to be found
+ return resp, body['keypairs']
+
+ def get_keypair(self, key_name):
+ resp, body = self.get("os-keypairs/%s" % str(key_name))
+ body = json.loads(body)
+ return resp, body['keypair']
+
+ def create_keypair(self, name, pub_key=None):
+ post_body = {'keypair': {'name': name}}
+ if pub_key:
+ post_body['keypair']['public_key'] = pub_key
+ post_body = json.dumps(post_body)
+ resp, body = self.post("os-keypairs",
+ headers=self.headers, body=post_body)
+ body = json.loads(body)
+ return resp, body['keypair']
+
+ def delete_keypair(self, key_name):
+ return self.delete("os-keypairs/%s" % str(key_name))
diff --git a/tempest/services/compute/v3/xml/certificates_client.py b/tempest/services/compute/v3/xml/certificates_client.py
new file mode 100644
index 0000000..99dc337
--- /dev/null
+++ b/tempest/services/compute/v3/xml/certificates_client.py
@@ -0,0 +1,41 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from tempest.common.rest_client import RestClientXML
+
+
+class CertificatesV3ClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(CertificatesV3ClientXML, self).__init__(config, username,
+ password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_v3_type
+
+ def get_certificate(self, id):
+ url = "os-certificates/%s" % (id)
+ resp, body = self.get(url, self.headers)
+ body = self._parse_resp(body)
+ return resp, body
+
+ def create_certificate(self):
+ """create certificates."""
+ url = "os-certificates"
+ resp, body = self.post(url, None, self.headers)
+ body = self._parse_resp(body)
+ return resp, body
diff --git a/tempest/services/compute/v3/xml/extensions_client.py b/tempest/services/compute/v3/xml/extensions_client.py
index e03251c..8f97692 100644
--- a/tempest/services/compute/v3/xml/extensions_client.py
+++ b/tempest/services/compute/v3/xml/extensions_client.py
@@ -43,3 +43,8 @@
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
+
+ def get_extension(self, extension_alias):
+ resp, body = self.get('extensions/%s' % extension_alias, self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
diff --git a/tempest/services/compute/v3/xml/instance_usage_audit_log_client.py b/tempest/services/compute/v3/xml/instance_usage_audit_log_client.py
new file mode 100644
index 0000000..175997b
--- /dev/null
+++ b/tempest/services/compute/v3/xml/instance_usage_audit_log_client.py
@@ -0,0 +1,41 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class InstanceUsagesAuditLogClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(InstanceUsagesAuditLogClientXML, self).__init__(
+ config, username, password, auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def list_instance_usage_audit_logs(self):
+ url = 'os-instance_usage_audit_log'
+ resp, body = self.get(url, self.headers)
+ instance_usage_audit_logs = xml_to_json(etree.fromstring(body))
+ return resp, instance_usage_audit_logs
+
+ def get_instance_usage_audit_log(self, time_before):
+ url = 'os-instance_usage_audit_log/%s' % time_before
+ resp, body = self.get(url, self.headers)
+ instance_usage_audit_log = xml_to_json(etree.fromstring(body))
+ return resp, instance_usage_audit_log
diff --git a/tempest/services/compute/v3/xml/keypairs_client.py b/tempest/services/compute/v3/xml/keypairs_client.py
new file mode 100644
index 0000000..0157245
--- /dev/null
+++ b/tempest/services/compute/v3/xml/keypairs_client.py
@@ -0,0 +1,69 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from lxml import etree
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import Text
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class KeyPairsClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(KeyPairsClientXML, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def list_keypairs(self):
+ resp, body = self.get("os-keypairs", self.headers)
+ node = etree.fromstring(body)
+ body = [{'keypair': xml_to_json(x)} for x in node.getchildren()]
+ return resp, body
+
+ def get_keypair(self, key_name):
+ resp, body = self.get("os-keypairs/%s" % str(key_name), self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def create_keypair(self, name, pub_key=None):
+ doc = Document()
+
+ keypair_element = Element("keypair")
+
+ if pub_key:
+ public_key_element = Element("public_key")
+ public_key_text = Text(pub_key)
+ public_key_element.append(public_key_text)
+ keypair_element.append(public_key_element)
+
+ name_element = Element("name")
+ name_text = Text(name)
+ name_element.append(name_text)
+ keypair_element.append(name_element)
+
+ doc.append(keypair_element)
+
+ resp, body = self.post("os-keypairs",
+ headers=self.headers, body=str(doc))
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def delete_keypair(self, key_name):
+ return self.delete("os-keypairs/%s" % str(key_name))
diff --git a/tempest/services/compute/xml/extensions_client.py b/tempest/services/compute/xml/extensions_client.py
index 1395b5a..b17fc4f 100644
--- a/tempest/services/compute/xml/extensions_client.py
+++ b/tempest/services/compute/xml/extensions_client.py
@@ -43,3 +43,8 @@
_, extensions = self.list_extensions()
exts = extensions['extensions']
return any([e for e in exts if e['name'] == extension])
+
+ def get_extension(self, extension_alias):
+ resp, body = self.get('extensions/%s' % extension_alias, self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index b17ae78..20fcc9b 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -15,12 +15,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import time
import urllib
from lxml import etree
from tempest.common.rest_client import RestClientXML
+from tempest.common import waiters
from tempest import exceptions
from tempest.services.compute.xml.common import Document
from tempest.services.compute.xml.common import Element
@@ -140,17 +140,7 @@
def wait_for_image_status(self, image_id, status):
"""Waits for an image to reach a given status."""
- resp, image = self.get_image(image_id)
- start = int(time.time())
-
- while image['status'] != status:
- time.sleep(self.build_interval)
- resp, image = self.get_image(image_id)
- if image['status'] == 'ERROR':
- raise exceptions.AddImageException(image_id=image_id)
-
- if int(time.time()) - start >= self.build_timeout:
- raise exceptions.TimeoutException
+ waiters.wait_for_image_status(self, image_id, status)
def _metadata_body(self, meta):
post_body = Element('metadata')
diff --git a/tempest/services/data_processing/__init__.py b/tempest/services/data_processing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/data_processing/__init__.py
diff --git a/tempest/services/data_processing/v1_1/__init__.py b/tempest/services/data_processing/v1_1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/data_processing/v1_1/__init__.py
diff --git a/tempest/services/data_processing/v1_1/client.py b/tempest/services/data_processing/v1_1/client.py
new file mode 100644
index 0000000..bd147e8
--- /dev/null
+++ b/tempest/services/data_processing/v1_1/client.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2013 Mirantis Inc.
+#
+# 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
+
+
+class DataProcessingClient(rest_client.RestClient):
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(DataProcessingClient, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.data_processing.catalog_type
+
+ @classmethod
+ def _request_and_parse(cls, req_fun, uri, res_name, *args, **kwargs):
+ """Make a request using specified req_fun and parse response.
+
+ It returns pair: resp and parsed resource(s) body.
+ """
+
+ resp, body = req_fun(uri, headers={
+ 'Content-Type': 'application/json'
+ }, *args, **kwargs)
+ body = json.loads(body)
+ return resp, body[res_name]
+
+ def list_node_group_templates(self):
+ """List all node group templates for a user."""
+
+ uri = 'node-group-templates'
+ return self._request_and_parse(self.get, uri, 'node_group_templates')
+
+ def get_node_group_template(self, tmpl_id):
+ """Returns the details of a single node group template."""
+
+ uri = "node-group-templates/%s" % tmpl_id
+ return self._request_and_parse(self.get, uri, 'node_group_template')
+
+ def create_node_group_template(self, name, plugin_name, hadoop_version,
+ node_processes, flavor_id,
+ node_configs=None, **kwargs):
+ """Creates node group template with specified params.
+
+ It supports passing additional params using kwargs and returns created
+ object.
+ """
+ uri = "node-group-templates"
+ body = kwargs.copy()
+ body.update({
+ 'name': name,
+ 'plugin_name': plugin_name,
+ 'hadoop_version': hadoop_version,
+ 'node_processes': node_processes,
+ 'flavor_id': flavor_id,
+ 'node_configs': node_configs or dict(),
+ })
+ return self._request_and_parse(self.post, uri, 'node_group_template',
+ body=json.dumps(body))
+
+ def delete_node_group_template(self, tmpl_id):
+ """Deletes the specified node group template by id."""
+
+ uri = "node-group-templates/%s" % tmpl_id
+ return self.delete(uri)
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 9f5a405..61dd050 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -36,7 +36,8 @@
super(ImageClientJSON, self).__init__(config, username, password,
auth_url, tenant_name)
self.service = self.config.images.catalog_type
- self.http = self._get_http()
+ if config.service_available.glance:
+ self.http = self._get_http()
def _image_meta_from_headers(self, headers):
meta = {'properties': {}}
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 3d37267..c654a49 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -31,7 +31,8 @@
super(ImageClientV2JSON, self).__init__(config, username, password,
auth_url, tenant_name)
self.service = self.config.images.catalog_type
- self.http = self._get_http()
+ if config.service_available.glance:
+ self.http = self._get_http()
def _get_http(self):
token, endpoint = self.keystone_auth(self.user, self.password,
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 3199537..b323dc6 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -15,9 +15,10 @@
import json
from tempest.common.rest_client import RestClient
+from tempest.services.network import network_client_base
-class NetworkClientJSON(RestClient):
+class NetworkClientJSON(network_client_base.NetworkClientBase):
"""
Tempest REST client for Neutron. Uses v2 of the Neutron API, since the
@@ -32,25 +33,25 @@
quotas
"""
- def __init__(self, config, username, password, auth_url, tenant_name=None):
- super(NetworkClientJSON, self).__init__(config, username, password,
- auth_url, tenant_name)
- self.service = self.config.network.catalog_type
- self.version = '2.0'
- self.uri_prefix = "v%s" % (self.version)
+ def get_rest_client(self, config, username,
+ password, auth_url, tenant_name=None):
+ return RestClient(config, username, password, auth_url, tenant_name)
- def list_networks(self):
- uri = '%s/networks' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
+ def deserialize_single(self, body):
+ return json.loads(body)
+
+ def deserialize_list(self, body):
+ res = json.loads(body)
+ # expecting response in form
+ # {'resources': [ res1, res2] }
+ return res[res.keys()[0]]
def create_network(self, name, **kwargs):
post_body = {'network': kwargs}
post_body['network']['name'] = name
body = json.dumps(post_body)
uri = '%s/networks' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
@@ -61,21 +62,10 @@
post_body = {'networks': network_list}
body = json.dumps(post_body)
uri = '%s/networks' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
- def show_network(self, uuid):
- uri = '%s/networks/%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def delete_network(self, uuid):
- uri = '%s/networks/%s' % (self.uri_prefix, uuid)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
def create_subnet(self, net_uuid, cidr, ip_version=4, **kwargs):
post_body = {'subnet': kwargs}
post_body['subnet']['ip_version'] = ip_version
@@ -83,24 +73,7 @@
post_body['subnet']['cidr'] = cidr
body = json.dumps(post_body)
uri = '%s/subnets' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
- body = json.loads(body)
- return resp, body
-
- def delete_subnet(self, uuid):
- uri = '%s/subnets/%s' % (self.uri_prefix, uuid)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
- def list_subnets(self):
- uri = '%s/subnets' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def show_subnet(self, uuid):
- uri = '%s/subnets/%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
@@ -114,28 +87,7 @@
post_body['port'][key] = val
body = json.dumps(post_body)
uri = '%s/ports' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
- body = json.loads(body)
- return resp, body
-
- def delete_port(self, port_id):
- uri = '%s/ports/%s' % (self.uri_prefix, port_id)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
- def list_ports(self, **filters):
- uri = '%s/ports' % (self.uri_prefix)
- filter_items = ["%s=%s" % (k, v) for (k, v) in filters.iteritems()]
- querystring = "&".join(filter_items)
- if querystring:
- uri = "%s?%s" % (uri, querystring)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def show_port(self, port_id):
- uri = '%s/ports/%s' % (self.uri_prefix, port_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
@@ -143,27 +95,15 @@
put_body = {'quota': kwargs}
body = json.dumps(put_body)
uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
- resp, body = self.put(uri, body, self.headers)
- body = json.loads(body)
- return resp, body['quota']
-
- def show_quotas(self, tenant_id):
- uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body['quota']
def reset_quotas(self, tenant_id):
uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
- resp, body = self.delete(uri, self.headers)
+ resp, body = self.delete(uri)
return resp, body
- def list_quotas(self):
- uri = '%s/quotas' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body['quotas']
-
def update_subnet(self, subnet_id, new_name):
put_body = {
'subnet': {
@@ -172,7 +112,7 @@
}
body = json.dumps(put_body)
uri = '%s/subnets/%s' % (self.uri_prefix, subnet_id)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -184,7 +124,7 @@
}
body = json.dumps(put_body)
uri = '%s/ports/%s' % (self.uri_prefix, port_id)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -196,13 +136,7 @@
}
body = json.dumps(put_body)
uri = '%s/networks/%s' % (self.uri_prefix, network_id)
- resp, body = self.put(uri, body=body, headers=self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_routers(self):
- uri = '%s/routers' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -212,24 +146,13 @@
post_body['router']['admin_state_up'] = admin_state_up
body = json.dumps(post_body)
uri = '%s/routers' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
- body = json.loads(body)
- return resp, body
-
- def delete_router(self, router_id):
- uri = '%s/routers/%s' % (self.uri_prefix, router_id)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
- def show_router(self, router_id):
- uri = '%s/routers/%s' % (self.uri_prefix, router_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
def _update_router(self, router_id, set_enable_snat, **kwargs):
uri = '%s/routers/%s' % (self.uri_prefix, router_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = json.loads(body)
update_body = {}
update_body['name'] = kwargs.get('name', body['router']['name'])
@@ -242,7 +165,7 @@
'external_gateway_info', body['router']['external_gateway_info'])
update_body = dict(router=update_body)
update_body = json.dumps(update_body)
- resp, body = self.put(uri, update_body, self.headers)
+ resp, body = self.put(uri, update_body)
body = json.loads(body)
return resp, body
@@ -268,7 +191,7 @@
router_id)
update_body = {"subnet_id": subnet_id}
update_body = json.dumps(update_body)
- resp, body = self.put(uri, update_body, self.headers)
+ resp, body = self.put(uri, update_body)
body = json.loads(body)
return resp, body
@@ -277,7 +200,7 @@
router_id)
update_body = {"port_id": port_id}
update_body = json.dumps(update_body)
- resp, body = self.put(uri, update_body, self.headers)
+ resp, body = self.put(uri, update_body)
body = json.loads(body)
return resp, body
@@ -286,7 +209,7 @@
router_id)
update_body = {"subnet_id": subnet_id}
update_body = json.dumps(update_body)
- resp, body = self.put(uri, update_body, self.headers)
+ resp, body = self.put(uri, update_body)
body = json.loads(body)
return resp, body
@@ -295,7 +218,7 @@
router_id)
update_body = {"port_id": port_id}
update_body = json.dumps(update_body)
- resp, body = self.put(uri, update_body, self.headers)
+ resp, body = self.put(uri, update_body)
body = json.loads(body)
return resp, body
@@ -305,21 +228,10 @@
post_body['floatingip']['floating_network_id'] = ext_network_id
body = json.dumps(post_body)
uri = '%s/floatingips' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body=body)
body = json.loads(body)
return resp, body
- def list_security_groups(self):
- uri = '%s/security-groups' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def delete_security_group(self, secgroup_id):
- uri = '%s/security-groups/%s' % (self.uri_prefix, secgroup_id)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
def create_security_group(self, name, **kwargs):
post_body = {
'security_group': {
@@ -330,45 +242,16 @@
post_body['security_group'][str(key)] = value
body = json.dumps(post_body)
uri = '%s/security-groups' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
- def show_floating_ip(self, floating_ip_id):
- uri = '%s/floatingips/%s' % (self.uri_prefix, floating_ip_id)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def show_security_group(self, secgroup_id):
- uri = '%s/security-groups/%s' % (self.uri_prefix, secgroup_id)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_floating_ips(self):
- uri = '%s/floatingips' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_security_group_rules(self):
- uri = '%s/security-group-rules' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def delete_floating_ip(self, floating_ip_id):
- uri = '%s/floatingips/%s' % (self.uri_prefix, floating_ip_id)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
def update_floating_ip(self, floating_ip_id, **kwargs):
post_body = {
'floatingip': kwargs}
body = json.dumps(post_body)
uri = '%s/floatingips/%s' % (self.uri_prefix, floating_ip_id)
- resp, body = self.put(uri, headers=self.headers, body=body)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -384,7 +267,7 @@
post_body['security_group_rule'][str(key)] = value
body = json.dumps(post_body)
uri = '%s/security-group-rules' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
@@ -392,18 +275,7 @@
post_body = {'subnets': subnet_list}
body = json.dumps(post_body)
uri = '%s/subnets' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
- body = json.loads(body)
- return resp, body
-
- def delete_security_group_rule(self, rule_id):
- uri = '%s/security-group-rules/%s' % (self.uri_prefix, rule_id)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
- def show_security_group_rule(self, rule_id):
- uri = '%s/security-group-rules/%s' % (self.uri_prefix, rule_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
@@ -411,13 +283,7 @@
post_body = {'ports': port_list}
body = json.dumps(post_body)
uri = '%s/ports' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
- body = json.loads(body)
- return resp, body
-
- def list_vips(self):
- uri = '%s/lb/vips' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
@@ -433,7 +299,7 @@
}
body = json.dumps(post_body)
uri = '%s/lb/vips' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
@@ -448,26 +314,10 @@
}
body = json.dumps(post_body)
uri = '%s/lb/pools' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
- def show_vip(self, uuid):
- uri = '%s/lb/vips/%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def delete_vip(self, uuid):
- uri = '%s/lb/vips/%s' % (self.uri_prefix, uuid)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
- def delete_pool(self, uuid):
- uri = '%s/lb/pools/%s' % (self.uri_prefix, uuid)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
def update_vip(self, vip_id, new_name):
put_body = {
"vip": {
@@ -476,7 +326,7 @@
}
body = json.dumps(put_body)
uri = '%s/lb/vips/%s' % (self.uri_prefix, vip_id)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -488,25 +338,7 @@
}
body = json.dumps(put_body)
uri = '%s/lb/pools/%s' % (self.uri_prefix, pool_id)
- resp, body = self.put(uri, body=body, headers=self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_pools(self):
- uri = '%s/lb/pools' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def show_pool(self, uuid):
- uri = '%s/lb/pools/%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_members(self):
- uri = '%s/lb/members' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -520,21 +352,10 @@
}
body = json.dumps(post_body)
uri = '%s/lb/members' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
- def show_member(self, uuid):
- uri = '%s/lb/members/%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def delete_member(self, uuid):
- uri = '%s/lb/members/%s' % (self.uri_prefix, uuid)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
def update_member(self, admin_state_up, member_id):
put_body = {
"member": {
@@ -543,13 +364,7 @@
}
body = json.dumps(put_body)
uri = '%s/lb/members/%s' % (self.uri_prefix, member_id)
- resp, body = self.put(uri, body=body, headers=self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_health_monitors(self):
- uri = '%s/lb/health_monitors' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -564,21 +379,10 @@
}
body = json.dumps(post_body)
uri = '%s/lb/health_monitors' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
- def show_health_monitor(self, uuid):
- uri = '%s/lb/health_monitors/%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def delete_health_monitor(self, uuid):
- uri = '%s/lb/health_monitors/%s' % (self.uri_prefix, uuid)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
def update_health_monitor(self, admin_state_up, uuid):
put_body = {
"health_monitor": {
@@ -587,7 +391,7 @@
}
body = json.dumps(put_body)
uri = '%s/lb/health_monitors/%s' % (self.uri_prefix, uuid)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -601,7 +405,7 @@
body = json.dumps(post_body)
uri = '%s/lb/pools/%s/health_monitors' % (self.uri_prefix,
pool_id)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
@@ -609,28 +413,10 @@
pool_id):
uri = '%s/lb/pools/%s/health_monitors/%s' % (self.uri_prefix, pool_id,
health_monitor_id)
- resp, body = self.delete(uri, headers=self.headers)
+ resp, body = self.delete(uri)
return resp, body
- def list_extensions(self):
- uri = '%s/extensions' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def show_extension_details(self, ext_alias):
- uri = '%s/extensions/%s' % (self.uri_prefix, ext_alias)
- resp, body = self.get(uri, headers=self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_vpn_services(self):
- uri = '%s/vpn/vpnservices' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def create_vpn_service(self, subnet_id, router_id, **kwargs):
+ def create_vpnservice(self, subnet_id, router_id, **kwargs):
post_body = {
"vpnservice": {
"subnet_id": subnet_id,
@@ -641,22 +427,11 @@
post_body['vpnservice'][key] = val
body = json.dumps(post_body)
uri = '%s/vpn/vpnservices' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
- def show_vpn_service(self, uuid):
- uri = '%s/vpn/vpnservices/%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def delete_vpn_service(self, uuid):
- uri = '%s/vpn/vpnservices/%s' % (self.uri_prefix, uuid)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
- def update_vpn_service(self, uuid, description):
+ def update_vpnservice(self, uuid, description):
put_body = {
"vpnservice": {
"description": description
@@ -664,25 +439,13 @@
}
body = json.dumps(put_body)
uri = '%s/vpn/vpnservices/%s' % (self.uri_prefix, uuid)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
def list_router_interfaces(self, uuid):
uri = '%s/ports?device_id=%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_agents(self):
- uri = '%s/agents' % self.uri_prefix
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def show_agent(self, agent_id):
- uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = json.loads(body)
return resp, body
@@ -694,53 +457,41 @@
uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
agent = {"agent": agent_info}
body = json.dumps(agent)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
def list_routers_on_l3_agent(self, agent_id):
uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = json.loads(body)
return resp, body
def list_l3_agents_hosting_router(self, router_id):
uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def list_service_providers(self):
- uri = '%s/service-providers' % self.uri_prefix
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = json.loads(body)
return resp, body
def list_dhcp_agent_hosting_network(self, network_id):
uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = json.loads(body)
return resp, body
def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = json.loads(body)
return resp, body
def remove_network_from_dhcp_agent(self, agent_id, network_id):
uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id,
network_id)
- resp, body = self.delete(uri, self.headers)
+ resp, body = self.delete(uri)
return resp, body
- def list_ike_policies(self):
- uri = '%s/vpn/ikepolicies' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def create_ike_policy(self, name, **kwargs):
+ def create_ikepolicy(self, name, **kwargs):
post_body = {
"ikepolicy": {
"name": name,
@@ -750,26 +501,15 @@
post_body['ikepolicy'][key] = val
body = json.dumps(post_body)
uri = '%s/vpn/ikepolicies' % (self.uri_prefix)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body)
body = json.loads(body)
return resp, body
- def show_ike_policy(self, uuid):
- uri = '%s/vpn/ikepolicies/%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
- body = json.loads(body)
- return resp, body
-
- def delete_ike_policy(self, uuid):
- uri = '%s/vpn/ikepolicies/%s' % (self.uri_prefix, uuid)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
- def update_ike_policy(self, uuid, **kwargs):
+ def update_ikepolicy(self, uuid, **kwargs):
put_body = {'ikepolicy': kwargs}
body = json.dumps(put_body)
uri = '%s/vpn/ikepolicies/%s' % (self.uri_prefix, uuid)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -782,7 +522,7 @@
}
}
body = json.dumps(put_body)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
@@ -795,6 +535,6 @@
}
}
body = json.dumps(put_body)
- resp, body = self.put(uri, body=body, headers=self.headers)
+ resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
diff --git a/tempest/services/network/network_client_base.py b/tempest/services/network/network_client_base.py
new file mode 100644
index 0000000..b48dd34
--- /dev/null
+++ b/tempest/services/network/network_client_base.py
@@ -0,0 +1,136 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# 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 urllib
+
+# the folliwing map is used to construct proper URI
+# for the given neutron resource
+service_resource_prefix_map = {
+ 'networks': '',
+ 'subnets': '',
+ 'ports': '',
+ 'pools': 'lb',
+ 'vips': 'lb',
+ 'health_monitors': 'lb',
+ 'members': 'lb',
+ 'vpnservices': 'vpn',
+ 'ikepolicies': 'vpn'
+}
+
+# The following list represents resource names that do not require
+# changing underscore to a hyphen
+hyphen_exceptions = ["health_monitors"]
+
+# map from resource name to a plural name
+# needed only for those which can't be constructed as name + 's'
+resource_plural_map = {
+ 'security_groups': 'security_groups',
+ 'security_group_rules': 'security_group_rules',
+ 'ikepolicy': 'ikepolicies',
+ 'floating_ip': 'floatingips',
+ 'quotas': 'quotas'
+}
+
+
+class NetworkClientBase(object):
+ def __init__(self, config, username, password,
+ auth_url, tenant_name=None):
+ self.rest_client = self.get_rest_client(
+ config, username, password, auth_url, tenant_name)
+ self.rest_client.service = self.rest_client.config.network.catalog_type
+ self.version = '2.0'
+ self.uri_prefix = "v%s" % (self.version)
+
+ def get_rest_client(self, config, username, password,
+ auth_url, tenant_name):
+ raise NotImplementedError
+
+ def post(self, uri, body, headers=None):
+ headers = headers or self.rest_client.headers
+ return self.rest_client.post(uri, body, headers)
+
+ def put(self, uri, body, headers=None):
+ headers = headers or self.rest_client.headers
+ return self.rest_client.put(uri, body, headers)
+
+ def get(self, uri, headers=None):
+ headers = headers or self.rest_client.headers
+ return self.rest_client.get(uri, headers)
+
+ def delete(self, uri, headers=None):
+ headers = headers or self.rest_client.headers
+ return self.rest_client.delete(uri, headers)
+
+ def deserialize_list(self, body):
+ raise NotImplementedError
+
+ def deserialize_single(self, body):
+ raise NotImplementedError
+
+ def get_uri(self, plural_name):
+ # get service prefix from resource name
+ service_prefix = service_resource_prefix_map.get(
+ plural_name)
+ if plural_name not in hyphen_exceptions:
+ plural_name = plural_name.replace("_", "-")
+ if service_prefix:
+ uri = '%s/%s/%s' % (self.uri_prefix, service_prefix,
+ plural_name)
+ else:
+ uri = '%s/%s' % (self.uri_prefix, plural_name)
+ return uri
+
+ def pluralize(self, resource_name):
+ # get plural from map or just add 's'
+ return resource_plural_map.get(resource_name, resource_name + 's')
+
+ def _lister(self, plural_name):
+ def _list(**filters):
+ uri = self.get_uri(plural_name)
+ if filters:
+ uri += '?' + urllib.urlencode(filters)
+ resp, body = self.get(uri)
+ result = {plural_name: self.deserialize_list(body)}
+ return resp, result
+
+ return _list
+
+ def _deleter(self, resource_name):
+ def _delete(resource_id):
+ plural = self.pluralize(resource_name)
+ uri = '%s/%s' % (self.get_uri(plural), resource_id)
+ return self.delete(uri)
+
+ return _delete
+
+ def _shower(self, resource_name):
+ def _show(resource_id):
+ plural = self.pluralize(resource_name)
+ uri = '%s/%s' % (self.get_uri(plural), resource_id)
+ resp, body = self.get(uri)
+ body = self.deserialize_single(body)
+ return resp, body
+
+ return _show
+
+ def __getattr__(self, name):
+ method_prefixes = ["list_", "delete_", "show_"]
+ method_functors = [self._lister,
+ self._deleter,
+ self._shower]
+ for index, prefix in enumerate(method_prefixes):
+ prefix_len = len(prefix)
+ if name[:prefix_len] == prefix:
+ return method_functors[index](name[prefix_len:])
+ raise AttributeError(name)
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
old mode 100755
new mode 100644
index c46a523..a999e31
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -20,30 +20,28 @@
from tempest.services.compute.xml.common import Document
from tempest.services.compute.xml.common import Element
from tempest.services.compute.xml.common import xml_to_json
+from tempest.services.network import network_client_base as client_base
-class NetworkClientXML(RestClientXML):
+class NetworkClientXML(client_base.NetworkClientBase):
- def __init__(self, config, username, password, auth_url, tenant_name=None):
- super(NetworkClientXML, self).__init__(config, username, password,
- auth_url, tenant_name)
- self.service = self.config.network.catalog_type
- self.version = '2.0'
- self.uri_prefix = "v%s" % (self.version)
+ def get_rest_client(self, config, username, password,
+ auth_url, tenant_name=None):
+ return RestClientXML(config, username, password,
+ auth_url, tenant_name)
- def list_networks(self):
- uri = '%s/networks' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- networks = self._parse_array(etree.fromstring(body))
- networks = {"networks": networks}
- return resp, networks
+ def deserialize_list(self, body):
+ return self._parse_array(etree.fromstring(body))
+
+ def deserialize_single(self, body):
+ return _root_tag_fetcher_and_xml_to_json_parse(body)
def create_network(self, name):
uri = '%s/networks' % (self.uri_prefix)
post_body = Element("network")
p2 = Element("name", name)
post_body.append(p2)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -55,21 +53,11 @@
p2 = Element("name", names[i])
p1.append(p2)
post_body.append(p1)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
networks = self._parse_array(etree.fromstring(body))
networks = {"networks": networks}
return resp, networks
- def delete_network(self, uuid):
- uri = '%s/networks/%s' % (self.uri_prefix, str(uuid))
- return self.delete(uri, self.headers)
-
- def show_network(self, uuid):
- uri = '%s/networks/%s' % (self.uri_prefix, str(uuid))
- resp, body = self.get(uri, self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
def create_subnet(self, net_uuid, cidr):
uri = '%s/subnets' % (self.uri_prefix)
subnet = Element("subnet")
@@ -79,24 +67,7 @@
subnet.append(p2)
subnet.append(p3)
subnet.append(p4)
- resp, body = self.post(uri, str(Document(subnet)), self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def delete_subnet(self, subnet_id):
- uri = '%s/subnets/%s' % (self.uri_prefix, str(subnet_id))
- return self.delete(uri, self.headers)
-
- def list_subnets(self):
- uri = '%s/subnets' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- subnets = self._parse_array(etree.fromstring(body))
- subnets = {"subnets": subnets}
- return resp, subnets
-
- def show_subnet(self, uuid):
- uri = '%s/subnets/%s' % (self.uri_prefix, str(uuid))
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, str(Document(subnet)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -108,39 +79,22 @@
for key, val in kwargs.items():
key = Element(key, val)
port.append(key)
- resp, body = self.post(uri, str(Document(port)), self.headers)
+ resp, body = self.post(uri, str(Document(port)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def delete_port(self, port_id):
- uri = '%s/ports/%s' % (self.uri_prefix, str(port_id))
- return self.delete(uri, self.headers)
-
def _parse_array(self, node):
array = []
for child in node.getchildren():
array.append(xml_to_json(child))
return array
- def list_ports(self):
- url = '%s/ports' % (self.uri_prefix)
- resp, body = self.get(url, self.headers)
- ports = self._parse_array(etree.fromstring(body))
- ports = {"ports": ports}
- return resp, ports
-
- def show_port(self, port_id):
- uri = '%s/ports/%s' % (self.uri_prefix, str(port_id))
- resp, body = self.get(uri, self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
def update_port(self, port_id, name):
uri = '%s/ports/%s' % (self.uri_prefix, str(port_id))
port = Element("port")
p2 = Element("name", name)
port.append(p2)
- resp, body = self.put(uri, str(Document(port)), self.headers)
+ resp, body = self.put(uri, str(Document(port)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -149,7 +103,7 @@
subnet = Element("subnet")
p2 = Element("name", name)
subnet.append(p2)
- resp, body = self.put(uri, str(Document(subnet)), self.headers)
+ resp, body = self.put(uri, str(Document(subnet)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -158,7 +112,7 @@
network = Element("network")
p2 = Element("name", name)
network.append(p2)
- resp, body = self.put(uri, str(Document(network)), self.headers)
+ resp, body = self.put(uri, str(Document(network)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -167,34 +121,10 @@
post_body = Element("security_group")
p2 = Element("name", name)
post_body.append(p2)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def list_security_groups(self):
- url = '%s/security-groups' % (self.uri_prefix)
- resp, body = self.get(url, self.headers)
- secgroups = self._parse_array(etree.fromstring(body))
- secgroups = {"security_groups": secgroups}
- return resp, secgroups
-
- def delete_security_group(self, secgroup_id):
- uri = '%s/security-groups/%s' % (self.uri_prefix, str(secgroup_id))
- return self.delete(uri, self.headers)
-
- def show_security_group(self, secgroup_id):
- uri = '%s/security-groups/%s' % (self.uri_prefix, str(secgroup_id))
- resp, body = self.get(uri, self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def list_security_group_rules(self):
- url = '%s/security-group-rules' % (self.uri_prefix)
- resp, body = self.get(url, self.headers)
- rules = self._parse_array(etree.fromstring(body))
- rules = {"security_group_rules": rules}
- return resp, rules
-
def create_security_group_rule(self, secgroup_id,
direction='ingress', **kwargs):
uri = '%s/security-group-rules' % (self.uri_prefix)
@@ -206,17 +136,7 @@
for key, val in kwargs.items():
key = Element(key, val)
rule.append(key)
- resp, body = self.post(uri, str(Document(rule)), self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def delete_security_group_rule(self, rule_id):
- uri = '%s/security-group-rules/%s' % (self.uri_prefix, str(rule_id))
- return self.delete(uri, self.headers)
-
- def show_security_group_rule(self, rule_id):
- uri = '%s/security-group-rules/%s' % (self.uri_prefix, str(rule_id))
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, str(Document(rule)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -230,7 +150,7 @@
p2 = Element(k, kv)
p1.append(p2)
post_body.append(p1)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
subnets = self._parse_array(etree.fromstring(body))
subnets = {"subnets": subnets}
return resp, subnets
@@ -245,18 +165,11 @@
p2 = Element(k, kv)
p1.append(p2)
post_body.append(p1)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
ports = self._parse_array(etree.fromstring(body))
ports = {"ports": ports}
return resp, ports
- def list_vips(self):
- url = '%s/lb/vips' % (self.uri_prefix)
- resp, body = self.get(url, self.headers)
- vips = self._parse_array(etree.fromstring(body))
- vips = {"vips": vips}
- return resp, vips
-
def create_vip(self, name, protocol, protocol_port, subnet_id, pool_id):
uri = '%s/lb/vips' % (self.uri_prefix)
post_body = Element("vip")
@@ -270,17 +183,7 @@
post_body.append(p3)
post_body.append(p4)
post_body.append(p5)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def delete_vip(self, vip_id):
- uri = '%s/lb/vips/%s' % (self.uri_prefix, str(vip_id))
- return self.delete(uri, self.headers)
-
- def show_vip(self, vip_id):
- uri = '%s/lb/vips/%s' % (self.uri_prefix, str(vip_id))
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -289,17 +192,10 @@
put_body = Element("vip")
p2 = Element("name", new_name)
put_body.append(p2)
- resp, body = self.put(uri, str(Document(put_body)), self.headers)
+ resp, body = self.put(uri, str(Document(put_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def list_pools(self):
- url = '%s/lb/pools' % (self.uri_prefix)
- resp, body = self.get(url, self.headers)
- pools = self._parse_array(etree.fromstring(body))
- pools = {"pools": pools}
- return resp, pools
-
def create_pool(self, name, lb_method, protocol, subnet_id):
uri = '%s/lb/pools' % (self.uri_prefix)
post_body = Element("pool")
@@ -309,17 +205,7 @@
post_body.append(p1)
post_body.append(p2)
post_body.append(p3)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def delete_pool(self, pool_id):
- uri = '%s/lb/pools/%s' % (self.uri_prefix, str(pool_id))
- return self.delete(uri, self.headers)
-
- def show_pool(self, pool_id):
- uri = '%s/lb/pools/%s' % (self.uri_prefix, str(pool_id))
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -328,17 +214,10 @@
put_body = Element("pool")
p2 = Element("name", new_name)
put_body.append(p2)
- resp, body = self.put(uri, str(Document(put_body)), self.headers)
+ resp, body = self.put(uri, str(Document(put_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def list_members(self):
- url = '%s/lb/members' % (self.uri_prefix)
- resp, body = self.get(url, self.headers)
- members = self._parse_array(etree.fromstring(body))
- members = {"members": members}
- return resp, members
-
def create_member(self, address, protocol_port, pool_id):
uri = '%s/lb/members' % (self.uri_prefix)
post_body = Element("member")
@@ -348,17 +227,7 @@
post_body.append(p1)
post_body.append(p2)
post_body.append(p3)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def delete_member(self, member_id):
- uri = '%s/lb/members/%s' % (self.uri_prefix, str(member_id))
- return self.delete(uri, self.headers)
-
- def show_member(self, member_id):
- uri = '%s/lb/members/%s' % (self.uri_prefix, str(member_id))
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -367,17 +236,10 @@
put_body = Element("member")
p2 = Element("admin_state_up", admin_state_up)
put_body.append(p2)
- resp, body = self.put(uri, str(Document(put_body)), self.headers)
+ resp, body = self.put(uri, str(Document(put_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def list_health_monitors(self):
- uri = '%s/lb/health_monitors' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- body = self._parse_array(etree.fromstring(body))
- body = {"health_monitors": body}
- return resp, body
-
def create_health_monitor(self, delay, max_retries, Type, timeout):
uri = '%s/lb/health_monitors' % (self.uri_prefix)
post_body = Element("health_monitor")
@@ -389,17 +251,7 @@
post_body.append(p2)
post_body.append(p3)
post_body.append(p4)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def delete_health_monitor(self, uuid):
- uri = '%s/lb/health_monitors/%s' % (self.uri_prefix, str(uuid))
- return self.delete(uri, self.headers)
-
- def show_health_monitor(self, uuid):
- uri = '%s/lb/health_monitors/%s' % (self.uri_prefix, str(uuid))
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -408,7 +260,7 @@
put_body = Element("health_monitor")
p2 = Element("admin_state_up", admin_state_up)
put_body.append(p2)
- resp, body = self.put(uri, str(Document(put_body)), self.headers)
+ resp, body = self.put(uri, str(Document(put_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -419,7 +271,7 @@
post_body = Element("health_monitor")
p1 = Element("id", health_monitor_id,)
post_body.append(p1)
- resp, body = self.post(uri, str(Document(post_body)), self.headers)
+ resp, body = self.post(uri, str(Document(post_body)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -427,18 +279,11 @@
pool_id):
uri = '%s/lb/pools/%s/health_monitors/%s' % (self.uri_prefix, pool_id,
health_monitor_id)
- return self.delete(uri, self.headers)
-
- def list_extensions(self):
- url = '%s/extensions' % (self.uri_prefix)
- resp, body = self.get(url, self.headers)
- extensions = self._parse_array(etree.fromstring(body))
- extensions = {"extensions": extensions}
- return resp, extensions
+ return self.delete(uri)
def show_extension_details(self, ext_alias):
uri = '%s/extensions/%s' % (self.uri_prefix, str(ext_alias))
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -447,18 +292,7 @@
router = Element("router")
router.append(Element("name", name))
deep_dict_to_xml(router, kwargs)
- resp, body = self.post(uri, str(Document(router)), self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def delete_router(self, router_id):
- uri = '%s/routers/%s' % (self.uri_prefix, router_id)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
- def show_router(self, router_id):
- uri = '%s/routers/%s' % (self.uri_prefix, router_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.post(uri, str(Document(router)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -467,7 +301,7 @@
router = Element("router")
for element, content in kwargs.iteritems():
router.append(Element(element, content))
- resp, body = self.put(uri, str(Document(router)), self.headers)
+ resp, body = self.put(uri, str(Document(router)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -475,7 +309,7 @@
uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
router_id)
subnet = Element("subnet_id", subnet_id)
- resp, body = self.put(uri, str(Document(subnet)), self.headers)
+ resp, body = self.put(uri, str(Document(subnet)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -483,7 +317,7 @@
uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
router_id)
port = Element("port_id", port_id)
- resp, body = self.put(uri, str(Document(port)), self.headers)
+ resp, body = self.put(uri, str(Document(port)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -491,7 +325,7 @@
uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
router_id)
subnet = Element("subnet_id", subnet_id)
- resp, body = self.put(uri, str(Document(subnet)), self.headers)
+ resp, body = self.put(uri, str(Document(subnet)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -499,7 +333,7 @@
uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
router_id)
port = Element("port_id", port_id)
- resp, body = self.put(uri, str(Document(port)), self.headers)
+ resp, body = self.put(uri, str(Document(port)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
@@ -509,28 +343,10 @@
floatingip.append(Element("floating_network_id", ext_network_id))
for element, content in kwargs.iteritems():
floatingip.append(Element(element, content))
- resp, body = self.post(uri, str(Document(floatingip)), self.headers)
+ resp, body = self.post(uri, str(Document(floatingip)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def show_floating_ip(self, floating_ip_id):
- uri = '%s/floatingips/%s' % (self.uri_prefix, floating_ip_id)
- resp, body = self.get(uri, self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def list_floating_ips(self):
- uri = '%s/floatingips' % (self.uri_prefix)
- resp, body = self.get(uri, self.headers)
- floatingips = self._parse_array(etree.fromstring(body))
- floatingips = {"floatingips": floatingips}
- return resp, floatingips
-
- def delete_floating_ip(self, floating_ip_id):
- uri = '%s/floatingips/%s' % (self.uri_prefix, floating_ip_id)
- resp, body = self.delete(uri, self.headers)
- return resp, body
-
def update_floating_ip(self, floating_ip_id, **kwargs):
uri = '%s/floatingips/%s' % (self.uri_prefix, floating_ip_id)
floatingip = Element('floatingip')
@@ -543,70 +359,49 @@
floatingip.append(xml_elem)
else:
floatingip.append(Element(element, content))
- resp, body = self.put(uri, str(Document(floatingip)), self.headers)
+ resp, body = self.put(uri, str(Document(floatingip)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def list_router_interfaces(self, uuid):
uri = '%s/ports?device_id=%s' % (self.uri_prefix, uuid)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
ports = self._parse_array(etree.fromstring(body))
ports = {"ports": ports}
return resp, ports
- def list_agents(self):
- uri = '%s/agents' % self.uri_prefix
- resp, body = self.get(uri, self.headers)
- agents = self._parse_array(etree.fromstring(body))
- agents = {'agents': agents}
- return resp, agents
-
- def show_agent(self, agent_id):
- uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
- resp, body = self.get(uri, self.headers)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
def update_agent(self, agent_id, agent_info):
uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
agent = Element('agent')
for (key, value) in agent_info.items():
p = Element(key, value)
agent.append(p)
- resp, body = self.put(uri, body=str(Document(agent)),
- headers=self.headers)
+ resp, body = self.put(uri, str(Document(agent)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def list_routers_on_l3_agent(self, agent_id):
uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
def list_l3_agents_hosting_router(self, router_id):
uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def list_service_providers(self):
- uri = '%s/service-providers' % self.uri_prefix
- resp, body = self.get(uri, self.headers)
- providers = self._parse_array(etree.fromstring(body))
- body = {'service_providers': providers}
- return resp, body
-
def list_dhcp_agent_hosting_network(self, network_id):
uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
agents = self._parse_array(etree.fromstring(body))
body = {'agents': agents}
return resp, body
def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
networks = self._parse_array(etree.fromstring(body))
body = {'networks': networks}
return resp, body
@@ -614,7 +409,7 @@
def remove_network_from_dhcp_agent(self, agent_id, network_id):
uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id,
network_id)
- resp, body = self.delete(uri, self.headers)
+ resp, body = self.delete(uri)
return resp, body
diff --git a/tempest/services/volume/json/extensions_client.py b/tempest/services/volume/json/extensions_client.py
new file mode 100644
index 0000000..01dd3e9
--- /dev/null
+++ b/tempest/services/volume/json/extensions_client.py
@@ -0,0 +1,34 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from tempest.common.rest_client import RestClient
+
+
+class ExtensionsClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(ExtensionsClientJSON, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.volume.catalog_type
+
+ def list_extensions(self):
+ url = 'extensions'
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['extensions']
diff --git a/tempest/services/volume/xml/extensions_client.py b/tempest/services/volume/xml/extensions_client.py
new file mode 100644
index 0000000..b4e6536
--- /dev/null
+++ b/tempest/services/volume/xml/extensions_client.py
@@ -0,0 +1,40 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from lxml import etree
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class ExtensionsClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(ExtensionsClientXML, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.volume.catalog_type
+
+ def _parse_array(self, node):
+ array = []
+ for child in node:
+ array.append(xml_to_json(child))
+ return array
+
+ def list_extensions(self):
+ url = 'extensions'
+ resp, body = self.get(url, self.headers)
+ body = self._parse_array(etree.fromstring(body))
+ return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index ceb2c80..56c0554 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -33,6 +33,8 @@
LOG = logging.getLogger(__name__)
+CONF = config.CONF
+
# All the successful HTTP status codes from RFC 2616
HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206)
@@ -146,7 +148,7 @@
"""A function that will check the list of enabled extensions from config
"""
- configs = config.TempestConfig()
+ configs = CONF
config_dict = {
'compute': configs.compute_feature_enabled.api_extensions,
'compute_v3': configs.compute_feature_enabled.api_v3_extensions,
@@ -214,7 +216,7 @@
testtools.testcase.WithAttributes,
testresources.ResourcedTestCase):
- config = config.TempestConfig()
+ config = CONF
setUpClassCalled = False
diff --git a/tempest/test_discover/__init__.py b/tempest/test_discover/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/test_discover/__init__.py
diff --git a/tempest/test_discover/test_discover.py b/tempest/test_discover/test_discover.py
new file mode 100644
index 0000000..2e19bf2
--- /dev/null
+++ b/tempest/test_discover/test_discover.py
@@ -0,0 +1,32 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+import unittest
+
+
+def load_tests(loader, tests, pattern):
+ suite = unittest.TestSuite()
+ base_path = os.path.split(os.path.dirname(os.path.abspath(__file__)))[0]
+ base_path = os.path.split(base_path)[0]
+ for test_dir in ['./tempest/api', './tempest/cli', './tempest/scenario',
+ './tempest/thirdparty']:
+ if not pattern:
+ suite.addTests(loader.discover(test_dir, top_level_dir=base_path))
+ else:
+ suite.addTests(loader.discover(test_dir, pattern=pattern,
+ top_level_dir=base_path))
+ return suite
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
new file mode 100644
index 0000000..baf6b2b
--- /dev/null
+++ b/tempest/tests/fake_config.py
@@ -0,0 +1,28 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+class FakeConfig(object):
+
+ class fake_compute(object):
+ build_interval = 10
+ build_timeout = 10
+
+ class fake_identity(object):
+ disable_ssl_certificate_validation = True
+
+ compute = fake_compute()
+ identity = fake_identity()
diff --git a/tempest/tests/fake_http.py b/tempest/tests/fake_http.py
new file mode 100644
index 0000000..5974377
--- /dev/null
+++ b/tempest/tests/fake_http.py
@@ -0,0 +1,48 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import httplib2
+
+
+class fake_httplib2(object):
+
+ def __init__(self, return_type=None):
+ self.return_type = return_type
+
+ def request(self, uri, method="GET", body=None, headers=None,
+ redirections=5, connection_type=None):
+ if not self.return_type:
+ fake_headers = httplib2.Response(headers)
+ return_obj = {
+ 'uri': uri,
+ 'method': method,
+ 'body': body,
+ 'headers': headers
+ }
+ return (fake_headers, return_obj)
+ # return (headers, return_obj)
+ elif isinstance(self.return_type, int):
+ body = "fake_body"
+ header_info = {
+ 'content-type': 'text/plain',
+ 'status': str(self.return_type),
+ 'content-length': len(body)
+ }
+ resp_header = httplib2.Response(header_info)
+ return (resp_header, body)
+ else:
+ msg = "unsupported return type %s" % self.return_type
+ raise TypeError(msg)
diff --git a/tempest/tests/test_rest_client.py b/tempest/tests/test_rest_client.py
new file mode 100644
index 0000000..ae6174c
--- /dev/null
+++ b/tempest/tests/test_rest_client.py
@@ -0,0 +1,92 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import httplib2
+
+from tempest.common import rest_client
+from tempest import exceptions
+from tempest.openstack.common.fixture import mockpatch
+from tempest.tests import base
+from tempest.tests import fake_config
+from tempest.tests import fake_http
+
+
+class BaseRestClientTestClass(base.TestCase):
+
+ def _set_token(self):
+ self.rest_client.token = 'fake token'
+
+ def setUp(self):
+ super(BaseRestClientTestClass, self).setUp()
+ self.rest_client = rest_client.RestClient(fake_config.FakeConfig(),
+ 'fake_user', 'fake_pass',
+ 'http://fake_url/v2.0')
+ self.stubs.Set(httplib2.Http, 'request', self.fake_http.request)
+ self.useFixture(mockpatch.PatchObject(self.rest_client, '_set_auth',
+ side_effect=self._set_token()))
+ self.useFixture(mockpatch.PatchObject(self.rest_client,
+ '_log_response'))
+
+
+class TestRestClientHTTPMethods(BaseRestClientTestClass):
+ def setUp(self):
+ self.fake_http = fake_http.fake_httplib2()
+ super(TestRestClientHTTPMethods, self).setUp()
+ self.useFixture(mockpatch.PatchObject(self.rest_client,
+ '_error_checker'))
+
+ def test_post(self):
+ __, return_dict = self.rest_client.post('fake_endpoint', {},
+ {})
+ self.assertEqual('POST', return_dict['method'])
+
+ def test_get(self):
+ __, return_dict = self.rest_client.get('fake_endpoint')
+ self.assertEqual('GET', return_dict['method'])
+
+ def test_delete(self):
+ __, return_dict = self.rest_client.delete('fake_endpoint')
+ self.assertEqual('DELETE', return_dict['method'])
+
+ def test_patch(self):
+ __, return_dict = self.rest_client.patch('fake_endpoint', {},
+ {})
+ self.assertEqual('PATCH', return_dict['method'])
+
+ def test_put(self):
+ __, return_dict = self.rest_client.put('fake_endpoint', {},
+ {})
+ self.assertEqual('PUT', return_dict['method'])
+
+ def test_head(self):
+ self.useFixture(mockpatch.PatchObject(self.rest_client,
+ 'response_checker'))
+ __, return_dict = self.rest_client.head('fake_endpoint')
+ self.assertEqual('HEAD', return_dict['method'])
+
+ def test_copy(self):
+ __, return_dict = self.rest_client.copy('fake_endpoint')
+ self.assertEqual('COPY', return_dict['method'])
+
+
+class TestRestClientNotFoundHandling(BaseRestClientTestClass):
+ def setUp(self):
+ self.fake_http = fake_http.fake_httplib2(404)
+ super(TestRestClientNotFoundHandling, self).setUp()
+
+ def test_post(self):
+ self.assertRaises(exceptions.NotFound, self.rest_client.post,
+ 'fake_endpoint', {}, {})
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index 5ae21c8..2f7a650 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -29,7 +29,7 @@
import tempest.clients
from tempest.common.utils.file_utils import have_effective_read_access
-import tempest.config
+from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
import tempest.test
@@ -37,6 +37,7 @@
from tempest.thirdparty.boto.utils.wait import state_wait
from tempest.thirdparty.boto.utils.wait import wait_exception
+CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -50,11 +51,10 @@
def all_read(*args):
return all(map(have_effective_read_access, args))
- config = tempest.config.TempestConfig()
- materials_path = config.boto.s3_materials_path
- ami_path = materials_path + os.sep + config.boto.ami_manifest
- aki_path = materials_path + os.sep + config.boto.aki_manifest
- ari_path = materials_path + os.sep + config.boto.ari_manifest
+ materials_path = CONF.boto.s3_materials_path
+ ami_path = materials_path + os.sep + CONF.boto.ami_manifest
+ aki_path = materials_path + os.sep + CONF.boto.aki_manifest
+ ari_path = materials_path + os.sep + CONF.boto.ari_manifest
A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
boto_logger = logging.getLogger('boto')
@@ -70,7 +70,7 @@
raise Exception("Unknown (Authentication?) Error")
openstack = tempest.clients.Manager()
try:
- if urlparse.urlparse(config.boto.ec2_url).hostname is None:
+ if urlparse.urlparse(CONF.boto.ec2_url).hostname is None:
raise Exception("Failed to get hostname from the ec2_url")
ec2client = openstack.ec2api_client
try:
@@ -87,7 +87,7 @@
EC2_CAN_CONNECT_ERROR = str(exc)
try:
- if urlparse.urlparse(config.boto.s3_url).hostname is None:
+ if urlparse.urlparse(CONF.boto.s3_url).hostname is None:
raise Exception("Failed to get hostname from the s3_url")
s3client = openstack.s3_client
try:
diff --git a/tempest/thirdparty/boto/utils/wait.py b/tempest/thirdparty/boto/utils/wait.py
index 1cd847b..db2303a 100644
--- a/tempest/thirdparty/boto/utils/wait.py
+++ b/tempest/thirdparty/boto/utils/wait.py
@@ -21,17 +21,12 @@
import boto.exception
from testtools import TestCase
-import tempest.config
+from tempest import config
from tempest.openstack.common import log as logging
+CONF = config.CONF
LOG = logging.getLogger(__name__)
-_boto_config = tempest.config.TempestConfig().boto
-
-default_timeout = _boto_config.build_timeout
-
-default_check_interval = _boto_config.build_interval
-
def state_wait(lfunction, final_set=set(), valid_set=None):
# TODO(afazekas): evaluate using ABC here
@@ -50,12 +45,12 @@
if valid_set is not None and status not in valid_set:
return status
dtime = time.time() - start_time
- if dtime > default_timeout:
+ if dtime > CONF.boto.build_timeout:
raise TestCase.failureException("State change timeout exceeded!"
'(%ds) While waiting'
'for %s at "%s"' %
(dtime, final_set, status))
- time.sleep(default_check_interval)
+ time.sleep(CONF.boto.build_interval)
old_status = status
status = lfunction()
@@ -73,12 +68,12 @@
text)
return result
dtime = time.time() - start_time
- if dtime > default_timeout:
+ if dtime > CONF.boto.build_timeout:
raise TestCase.failureException('Pattern find timeout exceeded!'
'(%ds) While waiting for'
'"%s" pattern in "%s"' %
(dtime, regexp, text))
- time.sleep(default_check_interval)
+ time.sleep(CONF.boto.build_interval)
def wait_no_exception(lfunction, exc_class=None, exc_matcher=None):
@@ -104,10 +99,10 @@
raise exc
# Let the other exceptions propagate
dtime = time.time() - start_time
- if dtime > default_timeout:
+ if dtime > CONF.boto.build_timeout:
raise TestCase.failureException("Wait timeout exceeded! (%ds)" %
dtime)
- time.sleep(default_check_interval)
+ time.sleep(CONF.boto.build_interval)
# NOTE(afazekas): EC2/boto normally raise exception instead of empty list
@@ -122,9 +117,9 @@
time.time() - start_time)
return exc
dtime = time.time() - start_time
- if dtime > default_timeout:
+ if dtime > CONF.boto.build_timeout:
raise TestCase.failureException("Wait timeout exceeded! (%ds)" %
dtime)
- time.sleep(default_check_interval)
+ time.sleep(CONF.boto.build_interval)
# TODO(afazekas): consider strategy design pattern..
diff --git a/tools/verify_tempest_config.py b/tools/verify_tempest_config.py
index 347659d..8850c2e 100755
--- a/tools/verify_tempest_config.py
+++ b/tools/verify_tempest_config.py
@@ -21,7 +21,7 @@
from tempest import config
-CONF = config.TempestConfig()
+CONF = config.CONF
#Dicts matching extension names to config options
NOVA_EXTENSIONS = {
diff --git a/tox.ini b/tox.ini
index c7f92ae..b44b3e0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,6 +8,7 @@
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_ALL=C
+ OS_TEST_PATH=./tempest/test_discover
usedevelop = True
install_command = pip install -U {opts} {packages}