Merge "Adds basic tests for disk config extension"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..68c771a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,176 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
diff --git a/etc/config.ini.sample b/etc/config.ini.sample
deleted file mode 100644
index 97ca732..0000000
--- a/etc/config.ini.sample
+++ /dev/null
@@ -1,44 +0,0 @@
-[environment]
-aki_location = include/sample_vm/natty-server-cloudimg-amd64-vmlinuz-virtual
-#ari_location = include/sample_vm/natty-server-cloudimg-amd64-loader
-ami_location = include/sample_vm/natty-server-cloudimg-amd64.img
-
-[swift]
-auth_host = 10.0.0.100
-auth_port = 443
-auth_prefix = /auth/
-auth_ssl = yes
-account = system
-username = root
-password = password
-
-[rabbitmq]
-host = 10.0.0.100
-user = guest
-password = 
-
-[glance]
-host = 10.0.0.100
-apiver = v1.0
-port = 9292
-image_id = 1
-
-[nova]
-host = 10.0.0.100
-port = 8774
-apiver = v1.1
-project = admin
-user = admin
-key = 24BD8F71-6AD8-439D-B722-7E2E25FD1911
-ssh_timeout = 300
-build_timeout = 300
-flavor_ref = 1
-flavor_ref_alt = 2
-
-[keystone]
-service_host = 10.0.0.100
-service_port = 5000
-apiver = v1.1
-user = admin
-password = password
-tenant_id = 1
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index b63b077..be05015 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -1,36 +1,93 @@
 [identity]
-use_ssl=False
-host=127.0.0.1
-port=5000
-api_version=v2.0
-path=tokens
-nonadmin_user1=user1
-nonadmin_user1_password=password
-nonadmin_user1_tenant_name=user1-project
-nonadmin_user2=user2
-nonadmin_user2_password=password
-nonadmin_user2_tenant_name=user2-project
-strategy=keystone
+# This section contains configuration options that a variety of Tempest
+# test clients use when authenticating with different user/tenant
+# combinations
+
+# Set to True if your test environment's Keystone authentication service should
+# be accessed over HTTPS
+use_ssl = False
+# This is the main host address of the authentication service API
+host = 127.0.0.1
+# Port that the authentication service API is running on
+port = 5000
+# Version of the authentication service API (a string)
+api_version = v2.0
+# Path to the authentication service tokens resource (do not modify unless you
+# have a custom authentication API and are not using Keystone)
+path = tokens
+# Should typically be left as keystone unless you have a non-Keystone
+# authentication API service
+strategy = keystone
 
 [compute]
+# This section contains configuration options used when executing tests
+# against the OpenStack Compute API.
+
+# This should be the username of a user WITHOUT administrative privileges
+username = {$USERNAME}
+# The above non-administrative user's password
+password = {$PASSWORD}
+# The above non-administrative user's tenant name
+tenant_name = {$TENANT_NAME}
+
+# This should be the username of an alternate user WITHOUT
+# administrative privileges
+alt_username = {$ALT_USERNAME}
+# The above non-administrative user's password
+alt_password = {$ALT_PASSWORD}
+# The above non-administrative user's tenant name
+alt_tenant_name = {$ALT_TENANT_NAME}
+
 # Reference data for tests. The ref and ref_alt should be
 # distinct images/flavors.
-image_ref=e7ddc02e-92fa-4f82-b36f-59b39bf66a67
-image_ref_alt=346f4039-a81e-44e0-9223-4a3d13c92a07
-flavor_ref=1
-flavor_ref_alt=2
-ssh_timeout=300
-build_interval=10
-build_timeout=600
-catalog_type=compute
-create_image_enabled=true
+image_ref = {$IMAGE_ID}
+image_ref_alt = {$IMAGE_ID_ALT}
+flavor_ref = 1
+flavor_ref_alt = 2
+
+# Number of seconds to wait while looping to check the status of an
+# instance or volume that is building.
+build_interval = 10
+
+# Number of seconds to time out on waiting for an instance or volume
+# to build or reach an expected status
+build_timeout = 600
+
+# The type of endpoint for a Compute API service. Unless you have a
+# custom Keystone service catalog implementation, you probably want to leave
+# this value as "compute"
+catalog_type = compute
+
+# Does the Compute API support creation of images?
+create_image_enabled = true
+
 # For resize to work with libvirt/kvm, one of the following must be true:
 # Single node: allow_resize_to_same_host=True must be set in nova.conf
 # Cluster: the 'nova' user must have scp access between cluster nodes
-resize_available=true
+resize_available = true
+
+# Level to log Compute API request/response details.
+log_level = ERROR
 
 [image]
-username=admin
-password=********
-tenant=admin
-auth_url=http://localhost:5000/v2.0
+# This section contains configuration options used when executing tests
+# against the OpenStack Images API
+
+# This should be the username of a user WITHOUT administrative privileges
+username = {$USERNAME}
+# The above non-administrative user's password
+password = {$PASSWORD}
+# The above non-administrative user's tenant name
+tenant_name = {$TENANT_NAME}
+
+[compute-admin]
+# This section contains configuration options for an administrative
+# user of the Compute API. These options are used in tests that stress
+# the admin-only parts of the Compute API
+
+# This should be the username of a user WITH administrative privileges
+username = {$ADMIN_USERNAME}
+# The above administrative user's password
+password = {$ADMIN_PASSWORD}
+# The above administrative user's tenant name
+tenant_name = {$ADMIN_TENANT_NAME}
diff --git a/etc/tempest.conf.tpl b/etc/tempest.conf.tpl
new file mode 100644
index 0000000..6710fd2
--- /dev/null
+++ b/etc/tempest.conf.tpl
@@ -0,0 +1,93 @@
+[identity]
+# This section contains configuration options that a variety of Tempest
+# test clients use when authenticating with different user/tenant
+# combinations
+
+# Set to True if your test environment's Keystone authentication service should
+# be accessed over HTTPS
+use_ssl = %IDENTITY_USE_SSL%
+# This is the main host address of the authentication service API
+host = %IDENTITY_HOST%
+# Port that the authentication service API is running on
+port = %IDENTITY_PORT%
+# Version of the authentication service API (a string)
+api_version = %IDENTITY_API_VERSION%
+# Path to the authentication service tokens resource (do not modify unless you
+# have a custom authentication API and are not using Keystone)
+path = %IDENTITY_PATH%
+# Should typically be left as keystone unless you have a non-Keystone
+# authentication API service
+strategy = %IDENTITY_STRATEGY%
+
+[compute]
+# This section contains configuration options used when executing tests
+# against the OpenStack Compute API.
+
+# This should be the username of a user WITHOUT administrative privileges
+username = %USERNAME%
+# The above non-administrative user's password
+password = %PASSWORD%
+# The above non-administrative user's tenant name
+tenant_name = %TENANT_NAME%
+
+# This should be the username of an alternate user WITHOUT
+# administrative privileges
+alt_username = %ALT_USERNAME%
+# The above non-administrative user's password
+alt_password = %ALT_PASSWORD%
+# The above non-administrative user's tenant name
+alt_tenant_name = %ALT_TENANT_NAME%
+
+# Reference data for tests. The ref and ref_alt should be
+# distinct images/flavors.
+image_ref = %IMAGE_ID%
+image_ref_alt = %IMAGE_ID_ALT%
+flavor_ref = %FLAVOR_REF%
+flavor_ref_alt = %FLAVOR_REF_ALT%
+
+# Number of seconds to wait while looping to check the status of an
+# instance or volume that is building.
+build_interval = %BUILD_INTERVAL%
+
+# Number of seconds to time out on waiting for an instance or volume
+# to build or reach an expected status
+build_timeout = %BUILD_TIMEOUT%
+
+# The type of endpoint for a Compute API service. Unless you have a
+# custom Keystone service catalog implementation, you probably want to leave
+# this value as "compute"
+catalog_type = %COMPUTE_CATALOG_TYPE%
+
+# Does the Compute API support creation of images?
+create_image_enabled = %COMPUTE_CREATE_IMAGE_ENABLED%
+
+# For resize to work with libvirt/kvm, one of the following must be true:
+# Single node: allow_resize_to_same_host=True must be set in nova.conf
+# Cluster: the 'nova' user must have scp access between cluster nodes
+resize_available = %COMPUTE_RESIZE_AVAILABLE%
+
+# Level to log Compute API request/response details.
+log_level = %COMPUTE_LOG_LEVEL%
+
+[image]
+# This section contains configuration options used when executing tests
+# against the OpenStack Images API
+
+# This should be the username of a user WITHOUT administrative privileges
+username = %USERNAME%
+# The above non-administrative user's password
+password = %PASSWORD%
+# The above non-administrative user's tenant name
+tenant_name = %TENANT_NAME%
+
+[compute-admin]
+# This section contains configuration options for an administrative
+# user of the Compute API. These options are used in tests that stress
+# the admin-only parts of the Compute API
+
+# This should be the username of a user WITH administrative privileges
+username = %ADMIN_USERNAME%
+# The above administrative user's password
+password = %ADMIN_PASSWORD%
+# The above administrative user's tenant name
+tenant_name = %ADMIN_TENANT_NAME%
diff --git a/stress/README.rst b/stress/README.rst
index bf44f13..1667e31 100644
--- a/stress/README.rst
+++ b/stress/README.rst
@@ -32,6 +32,7 @@
   host_admin_user=<name of user for ssh command>

   nova_logdir=<value of --logdir in nova.conf>

   controller=<hostname for calling nova-manage>

+  max_instances=<limit on instances that will be created>

 

 The stress test needs the top-level tempest directory to be on PYTHONPATH

 if you are not using nosetests to run.

diff --git a/stress/__init__.py b/stress/__init__.py
index 256d40e..0875e0b 100644
--- a/stress/__init__.py
+++ b/stress/__init__.py
@@ -1,17 +1,17 @@
-# Copyright 2011 Quanta Research Cambridge, 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.

-"""Basic framework for constructing various simulated workloads for a

-nova cluster."""

-

-__author__ = "David Kranz and Eugene Shih"

+# Copyright 2011 Quanta Research Cambridge, 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.
+"""Basic framework for constructing various simulated workloads for a
+nova cluster."""
+
+__author__ = "David Kranz and Eugene Shih"
diff --git a/stress/basher.py b/stress/basher.py
index b6fcfca..e34738f 100644
--- a/stress/basher.py
+++ b/stress/basher.py
@@ -1,41 +1,41 @@
-# Copyright 2011 Quanta Research Cambridge, 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.

-"""Class to describe actions to be included in a stress test."""

-

-

-class BasherAction(object):

-    """

-    Used to describe each action that you would like to include in a test run.

-    """

-

-    def __init__(self, test_case, probability, pargs=[], kargs={}):

-        """

-        `test_case`  : the name of the class that implements the action

-        `pargs`      : positional arguments to the constructor of `test_case`

-        `kargs`      : keyword arguments to the constructor of `test_case`

-        `probability`: frequency that each action

-        """

-        self.test_case = test_case

-        self.pargs = pargs

-        self.kargs = kargs

-        self.probability = probability

-

-    def invoke(self, manager, state):

-        """

-        Calls the `run` method of the `test_case`.

-        """

-        return self.test_case.run(manager, state, *self.pargs, **self.kargs)

-

-    def __str__(self):

-        return self.test_case.__class__.__name__

+# Copyright 2011 Quanta Research Cambridge, 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.
+"""Class to describe actions to be included in a stress test."""
+
+
+class BasherAction(object):
+    """
+    Used to describe each action that you would like to include in a test run.
+    """
+
+    def __init__(self, test_case, probability, pargs=[], kargs={}):
+        """
+        `test_case`  : the name of the class that implements the action
+        `pargs`      : positional arguments to the constructor of `test_case`
+        `kargs`      : keyword arguments to the constructor of `test_case`
+        `probability`: frequency that each action
+        """
+        self.test_case = test_case
+        self.pargs = pargs
+        self.kargs = kargs
+        self.probability = probability
+
+    def invoke(self, manager, state):
+        """
+        Calls the `run` method of the `test_case`.
+        """
+        return self.test_case.run(manager, state, *self.pargs, **self.kargs)
+
+    def __str__(self):
+        return self.test_case.__class__.__name__
diff --git a/stress/config.py b/stress/config.py
index 0dce816..64091cd 100755
--- a/stress/config.py
+++ b/stress/config.py
@@ -1,48 +1,53 @@
-# Copyright 2011 Quanta Research Cambridge, 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 ConfigParser

-

-

-class StressConfig(object):

-    """Provides configuration information for whitebox stress tests."""

-

-    def __init__(self, conf):

-        self.conf = conf

-

-    def get(self, item_name, default_value=None):

-        try:

-            return self.conf.get("stress", item_name)

-        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):

-            return default_value

-

-    @property

-    def host_private_key_path(self):

-        """Path to ssh key for logging into compute nodes."""

-        return self.get("host_private_key_path", None)

-

-    @property

-    def host_admin_user(self):

-        """Username for logging into compute nodes."""

-        return self.get("host_admin_user", None)

-

-    @property

-    def nova_logdir(self):

-        """Directory containing log files on the compute nodes"""

-        return self.get("nova_logdir", None)

-

-    @property

-    def controller(self):

-        """Controller host"""

-        return self.get("controller", None)

+# Copyright 2011 Quanta Research Cambridge, 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 ConfigParser
+
+
+class StressConfig(object):
+    """Provides configuration information for whitebox stress tests."""
+
+    def __init__(self, conf):
+        self.conf = conf
+
+    def get(self, item_name, default_value=None):
+        try:
+            return self.conf.get("stress", item_name)
+        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+            return default_value
+
+    @property
+    def host_private_key_path(self):
+        """Path to ssh key for logging into compute nodes."""
+        return self.get("host_private_key_path", None)
+
+    @property
+    def host_admin_user(self):
+        """Username for logging into compute nodes."""
+        return self.get("host_admin_user", None)
+
+    @property
+    def nova_logdir(self):
+        """Directory containing log files on the compute nodes"""
+        return self.get("nova_logdir", None)
+
+    @property
+    def controller(self):
+        """Controller host"""
+        return self.get("controller", None)
+
+    @property
+    def max_instances(self):
+        """Maximum number of instances to create during test"""
+        return self.get("max_instances", 16)
diff --git a/stress/driver.py b/stress/driver.py
index b68825e..9f263f6 100644
--- a/stress/driver.py
+++ b/stress/driver.py
@@ -1,206 +1,207 @@
-# Copyright 2011 Quanta Research Cambridge, 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.

-"""The entry point for the execution of a workloadTo execute a workload.

-Users pass in a description of the workload and a nova manager object

-to the bash_openstack function call"""

-

-

-import random

-import datetime

-import time

-

-

-# local imports

-from test_case import *

-from state import State

-import utils.util

-from config import StressConfig

-

-# setup logging to file

-logging.basicConfig(

-    format='%(asctime)s %(name)-20s %(levelname)-8s %(message)s',

-    datefmt='%m-%d %H:%M:%S',

-    filename="stress.debug.log",

-    filemode="w",

-    level=logging.DEBUG,

-    )

-

-# define a Handler which writes INFO messages or higher to the sys.stdout

-_console = logging.StreamHandler()

-_console.setLevel(logging.INFO)

-# set a format which is simpler for console use

-_formatter = logging.Formatter('%(name)-20s: %(levelname)-8s %(message)s')

-# tell the handler to use this format

-_console.setFormatter(_formatter)

-# add the handler to the root logger

-logging.getLogger('').addHandler(_console)

-

-

-def _create_cases(choice_spec):

-    """

-    Generate a workload of tests from workload description

-    """

-    cases = []

-    count = 0

-    for choice in choice_spec:

-        p = choice.probability

-        for i in range(p):

-            cases.append(choice)

-        i = i + p

-        count = count + p

-    assert(count == 100)

-    return cases

-

-

-def _get_compute_nodes(keypath, user, controller):

-    """

-    Returns a list of active compute nodes. List is generated by running

-    nova-manage on the controller.

-    """

-    nodes = []

-    if keypath == None or user == None:

-        return nodes

-    lines = utils.util.ssh(keypath, user, controller,

-                     "nova-manage service list | grep ^nova-compute").\

-                     split('\n')

-    # For example: nova-compute xg11eth0 nova enabled :-) 2011-10-31 18:57:46

-    # This is fragile but there is, at present, no other way to get this info.

-    for line in lines:

-        words = line.split()

-        if len(words) > 0 and words[4] == ":-)":

-            nodes.append(words[1])

-    return nodes

-

-

-def _error_in_logs(keypath, logdir, user, nodes):

-    """

-    Detect errors in the nova log files on the controller and compute nodes.

-    """

-    grep = 'egrep "ERROR\|TRACE" %s/*.log' % logdir

-    for node in nodes:

-        errors = utils.util.ssh(keypath, user, node, grep, check=False)

-        if len(errors) > 0:

-            logging.error('%s: %s' % (node, errors))

-            return True

-    return False

-

-

-def bash_openstack(manager,

-                   choice_spec,

-                   **kwargs):

-    """

-    Workload driver. Executes a workload as specified by the `choice_spec`

-    parameter against a nova-cluster.

-

-    `manager`  : Manager object

-    `choice_spec` : list of BasherChoice actions to run on the cluster

-    `kargs`       : keyword arguments to the constructor of `test_case`

-                    `duration`   = how long this test should last (3 sec)

-                    `sleep_time` = time to sleep between actions (in msec)

-                    `test_name`  = human readable workload description

-                                   (default: unnamed test)

-                    `max_vms`    = maximum number of instances to launch

-                                   (default: 32)

-                    `seed`       = random seed (default: None)

-    """

-    # get keyword arguments

-    duration = kwargs.get('duration', datetime.timedelta(seconds=10))

-    seed = kwargs.get('seed', None)

-    sleep_time = float(kwargs.get('sleep_time', 3000)) / 1000

-    max_vms = int(kwargs.get('max_vms', 32))

-    test_name = kwargs.get('test_name', 'unamed test')

-

-    stress_config = StressConfig(manager.config._conf)

-    keypath = stress_config.host_private_key_path

-    user = stress_config.host_admin_user

-    logdir = stress_config.nova_logdir

-    computes = _get_compute_nodes(keypath, user, manager.config.identity.host)

-    utils.util.execute_on_all(keypath, user, computes,

-                              "rm -f %s/*.log" % logdir)

-    random.seed(seed)

-    cases = _create_cases(choice_spec)

-    test_end_time = time.time() + duration.seconds

-    state = State(max_vms=max_vms)

-

-    retry_list = []

-    last_retry = time.time()

-    cooldown = False

-    logcheck_count = 0

-    test_succeeded = True

-    logging.debug('=== Test \"%s\" on %s ===' %

-                  (test_name, time.asctime(time.localtime())))

-    for kw in kwargs:

-        logging.debug('\t%s = %s', kw, kwargs[kw])

-

-    while True:

-        if not cooldown:

-            if time.time() < test_end_time:

-                case = random.choice(cases)

-                logging.debug('Chose %s' % case)

-                retry = case.invoke(manager, state)

-                if retry != None:

-                    retry_list.append(retry)

-            else:

-                logging.info('Cooling down...')

-                cooldown = True

-        if cooldown and len(retry_list) == 0:

-            if _error_in_logs(keypath, logdir, user, computes):

-                test_succeeded = False

-            break

-        # Retry verifications every 5 seconds.

-        if time.time() - last_retry > 5:

-            logging.debug('retry verifications for %d tasks', len(retry_list))

-            new_retry_list = []

-            for v in retry_list:

-                if not v.retry():

-                    new_retry_list.append(v)

-            retry_list = new_retry_list

-            last_retry = time.time()

-        time.sleep(sleep_time)

-        # Check error logs after 100 actions

-        if logcheck_count > 100:

-            if _error_in_logs(keypath, logdir, user, computes):

-                test_succeeded = False

-                break

-            else:

-                logcheck_count = 0

-        else:

-            logcheck_count = logcheck_count + 1

-    # Cleanup

-    logging.info('Cleaning up: terminating virtual machines...')

-    vms = state.get_instances()

-    active_vms = [v for _k, v in vms.iteritems() if v and v[1] == 'ACTIVE']

-    for target in active_vms:

-        manager.servers_client.delete_server(target[0]['id'])

-        # check to see that the server was actually killed

-    for target in active_vms:

-        kill_id = target[0]['id']

-        i = 0

-        while True:

-            try:

-                manager.servers_client.get_server(kill_id)

-            except Exception:

-                break

-            i += 1

-            if i > 60:

-                raise

-            time.sleep(1)

-        logging.info('killed %s' % kill_id)

-        state.delete_instance_state(kill_id)

-

-    if test_succeeded:

-        logging.info('*** Test succeeded ***')

-    else:

-        logging.info('*** Test had errors ***')

-    return test_succeeded

+# Copyright 2011 Quanta Research Cambridge, 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.
+"""The entry point for the execution of a workloadTo execute a workload.
+Users pass in a description of the workload and a nova manager object
+to the bash_openstack function call"""
+
+
+import random
+import datetime
+import time
+
+
+# local imports
+from test_case import *
+from state import State
+import utils.util
+from config import StressConfig
+
+# setup logging to file
+logging.basicConfig(
+    format='%(asctime)s %(name)-20s %(levelname)-8s %(message)s',
+    datefmt='%m-%d %H:%M:%S',
+    filename="stress.debug.log",
+    filemode="w",
+    level=logging.DEBUG,
+    )
+
+# define a Handler which writes INFO messages or higher to the sys.stdout
+_console = logging.StreamHandler()
+_console.setLevel(logging.INFO)
+# set a format which is simpler for console use
+_formatter = logging.Formatter('%(name)-20s: %(levelname)-8s %(message)s')
+# tell the handler to use this format
+_console.setFormatter(_formatter)
+# add the handler to the root logger
+logging.getLogger('').addHandler(_console)
+
+
+def _create_cases(choice_spec):
+    """
+    Generate a workload of tests from workload description
+    """
+    cases = []
+    count = 0
+    for choice in choice_spec:
+        p = choice.probability
+        for i in range(p):
+            cases.append(choice)
+        i = i + p
+        count = count + p
+    assert(count == 100)
+    return cases
+
+
+def _get_compute_nodes(keypath, user, controller):
+    """
+    Returns a list of active compute nodes. List is generated by running
+    nova-manage on the controller.
+    """
+    nodes = []
+    if keypath == None or user == None:
+        return nodes
+    lines = utils.util.ssh(keypath, user, controller,
+                     "nova-manage service list | grep ^nova-compute").\
+                     split('\n')
+    # For example: nova-compute xg11eth0 nova enabled :-) 2011-10-31 18:57:46
+    # This is fragile but there is, at present, no other way to get this info.
+    for line in lines:
+        words = line.split()
+        if len(words) > 0 and words[4] == ":-)":
+            nodes.append(words[1])
+    return nodes
+
+
+def _error_in_logs(keypath, logdir, user, nodes):
+    """
+    Detect errors in the nova log files on the controller and compute nodes.
+    """
+    grep = 'egrep "ERROR\|TRACE" %s/*.log' % logdir
+    for node in nodes:
+        errors = utils.util.ssh(keypath, user, node, grep, check=False)
+        if len(errors) > 0:
+            logging.error('%s: %s' % (node, errors))
+            return True
+    return False
+
+
+def bash_openstack(manager,
+                   choice_spec,
+                   **kwargs):
+    """
+    Workload driver. Executes a workload as specified by the `choice_spec`
+    parameter against a nova-cluster.
+
+    `manager`  : Manager object
+    `choice_spec` : list of BasherChoice actions to run on the cluster
+    `kargs`       : keyword arguments to the constructor of `test_case`
+                    `duration`   = how long this test should last (3 sec)
+                    `sleep_time` = time to sleep between actions (in msec)
+                    `test_name`  = human readable workload description
+                                   (default: unnamed test)
+                    `max_vms`    = maximum number of instances to launch
+                                   (default: 32)
+                    `seed`       = random seed (default: None)
+    """
+    stress_config = StressConfig(manager.config._conf)
+    # get keyword arguments
+    duration = kwargs.get('duration', datetime.timedelta(seconds=10))
+    seed = kwargs.get('seed', None)
+    sleep_time = float(kwargs.get('sleep_time', 3000)) / 1000
+    max_vms = int(kwargs.get('max_vms', stress_config.max_instances))
+    test_name = kwargs.get('test_name', 'unamed test')
+
+    keypath = stress_config.host_private_key_path
+    user = stress_config.host_admin_user
+    logdir = stress_config.nova_logdir
+    computes = _get_compute_nodes(keypath, user, manager.config.identity.host)
+    utils.util.execute_on_all(keypath, user, computes,
+                              "rm -f %s/*.log" % logdir)
+    random.seed(seed)
+    cases = _create_cases(choice_spec)
+    test_end_time = time.time() + duration.seconds
+    state = State(max_vms=max_vms)
+
+    retry_list = []
+    last_retry = time.time()
+    cooldown = False
+    logcheck_count = 0
+    test_succeeded = True
+    logging.debug('=== Test \"%s\" on %s ===' %
+                  (test_name, time.asctime(time.localtime())))
+    for kw in kwargs:
+        logging.debug('\t%s = %s', kw, kwargs[kw])
+
+    while True:
+        if not cooldown:
+            if time.time() < test_end_time:
+                case = random.choice(cases)
+                logging.debug('Chose %s' % case)
+                retry = case.invoke(manager, state)
+                if retry != None:
+                    retry_list.append(retry)
+            else:
+                logging.info('Cooling down...')
+                cooldown = True
+        if cooldown and len(retry_list) == 0:
+            if _error_in_logs(keypath, logdir, user, computes):
+                test_succeeded = False
+            break
+        # Retry verifications every 5 seconds.
+        if time.time() - last_retry > 5:
+            logging.debug('retry verifications for %d tasks', len(retry_list))
+            new_retry_list = []
+            for v in retry_list:
+                if not v.retry():
+                    new_retry_list.append(v)
+            retry_list = new_retry_list
+            last_retry = time.time()
+        time.sleep(sleep_time)
+        # Check error logs after 100 actions
+        if logcheck_count > 100:
+            if _error_in_logs(keypath, logdir, user, computes):
+                test_succeeded = False
+                break
+            else:
+                logcheck_count = 0
+        else:
+            logcheck_count = logcheck_count + 1
+    # Cleanup
+    logging.info('Cleaning up: terminating virtual machines...')
+    vms = state.get_instances()
+    active_vms = [v for _k, v in vms.iteritems() if v and v[1] == 'ACTIVE']
+    for target in active_vms:
+        manager.servers_client.delete_server(target[0]['id'])
+        # check to see that the server was actually killed
+    for target in active_vms:
+        kill_id = target[0]['id']
+        i = 0
+        while True:
+            try:
+                manager.servers_client.get_server(kill_id)
+            except Exception:
+                break
+            i += 1
+            if i > 60:
+                _error_in_logs(keypath, logdir, user, computes)
+                raise Exception("Cleanup timed out")
+            time.sleep(1)
+        logging.info('killed %s' % kill_id)
+        state.delete_instance_state(kill_id)
+
+    if test_succeeded:
+        logging.info('*** Test succeeded ***')
+    else:
+        logging.info('*** Test had errors ***')
+    return test_succeeded
diff --git a/stress/state.py b/stress/state.py
index c7eac37..60b1acc 100644
--- a/stress/state.py
+++ b/stress/state.py
@@ -1,41 +1,41 @@
-# Copyright 2011 Quanta Research Cambridge, 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.

-"""A class to store the state of various persistent objects in the Nova

-cluster, e.g. instances, volumes.  Use methods to query to state which than

-can be compared to the current state of the objects in Nova"""

-

-

-class State(object):

-

-    def __init__(self, **kwargs):

-        self._max_vms = kwargs.get('max_vms', 32)

-        self._instances = {}

-        self._volumes = {}

-

-    # machine state methods

-    def get_instances(self):

-        """return the instances dictionary that we believe are in cluster."""

-        return self._instances

-

-    def get_max_instances(self):

-        """return the maximum number of instances we can create."""

-        return self._max_vms

-

-    def set_instance_state(self, key, val):

-        """Store `val` in the dictionary indexed at `key`."""

-        self._instances[key] = val

-

-    def delete_instance_state(self, key):

-        """Delete state indexed at `key`."""

-        del self._instances[key]

+# Copyright 2011 Quanta Research Cambridge, 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.
+"""A class to store the state of various persistent objects in the Nova
+cluster, e.g. instances, volumes.  Use methods to query to state which than
+can be compared to the current state of the objects in Nova"""
+
+
+class State(object):
+
+    def __init__(self, **kwargs):
+        self._max_vms = kwargs.get('max_vms', 32)
+        self._instances = {}
+        self._volumes = {}
+
+    # machine state methods
+    def get_instances(self):
+        """return the instances dictionary that we believe are in cluster."""
+        return self._instances
+
+    def get_max_instances(self):
+        """return the maximum number of instances we can create."""
+        return self._max_vms
+
+    def set_instance_state(self, key, val):
+        """Store `val` in the dictionary indexed at `key`."""
+        self._instances[key] = val
+
+    def delete_instance_state(self, key):
+        """Delete state indexed at `key`."""
+        del self._instances[key]
diff --git a/stress/test_case.py b/stress/test_case.py
index a9d2d20..fe510d5 100644
--- a/stress/test_case.py
+++ b/stress/test_case.py
@@ -1,29 +1,29 @@
-# Copyright 2011 Quanta Research Cambridge, 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.

-"""Abstract class for implementing an action. You only need to override

-the `run` method which specifies all the actual nova API class you wish

-to make."""

-

-

-import logging

-

-

-class StressTestCase(object):

-

-    def __init__(self):

-        self._logger = logging.getLogger(self.__class__.__name__)

-

-    def run(self, nova_manager, state_obj, *pargs, **kargs):

-        """Nova API methods to call that would modify state of the cluster"""

-        return

+# Copyright 2011 Quanta Research Cambridge, 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.
+"""Abstract class for implementing an action. You only need to override
+the `run` method which specifies all the actual nova API class you wish
+to make."""
+
+
+import logging
+
+
+class StressTestCase(object):
+
+    def __init__(self):
+        self._logger = logging.getLogger(self.__class__.__name__)
+
+    def run(self, nova_manager, state_obj, *pargs, **kargs):
+        """Nova API methods to call that would modify state of the cluster"""
+        return
diff --git a/stress/test_server_actions.py b/stress/test_server_actions.py
index 3cf3698..7080630 100644
--- a/stress/test_server_actions.py
+++ b/stress/test_server_actions.py
@@ -25,7 +25,7 @@
 # local imports
 import test_case
 import pending_action
-from tempest.exceptions import TimeoutException
+from tempest.exceptions import TimeoutException, Duplicate
 from utils.util import *
 
 
@@ -52,51 +52,35 @@
             self._logger.info('no ACTIVE instances to reboot')
             return
 
-        _reboot_type = kwargs.get('type', 'SOFT')
+        _reboot_arg = kwargs.get('type', 'SOFT')
 
         # select active vm to reboot and then send request to nova controller
         target = random.choice(active_vms)
         reboot_target = target[0]
+        # It seems that doing a reboot when in reboot is an error.
+        try:
+            response, body = manager.servers_client.reboot(
+                                                           reboot_target['id'],
+                                                           _reboot_arg)
+        except Duplicate:
+            return
 
-        response, body = manager.servers_client.reboot(
-                                                          reboot_target['id'],
-                                                          _reboot_type)
         if (response.status != 202):
             self._logger.error("response: %s" % response)
             raise Exception
 
-        if _reboot_type == 'SOFT':
-            state_name = 'REBOOT'
+        if _reboot_arg == 'SOFT':
+            reboot_state = 'REBOOT'
         else:
-            state_name = 'REBOOT'  # this is a bug, should be HARD_REBOOT
+            reboot_state = 'HARD_REBOOT'
 
         self._logger.info('waiting for machine %s to change to %s' %
-                          (reboot_target['id'], state_name))
-
-        # check for state transition
-        _resp, body = manager.servers_client.get_server(reboot_target['id'])
-        if body['status'] == state_name:
-            state_string = state_name
-        else:
-            # grab the actual state as we think it is
-            temp_obj = state.get_instances()[self._target['id']]
-            self._logger.debug(
-                "machine %s in state %s" %
-                (reboot_target['id'], temp_obj[1])
-                )
-            state_string = temp_obj[1]
-
-        if state_string == state_name:
-            self._logger.info('machine %s ACTIVE -> %s' %
-                              (reboot_target['id'], state_name))
-            state.set_instance_state(reboot_target['id'],
-                                    (reboot_target, state_name))
+                          (reboot_target['id'], reboot_state))
 
         return VerifyRebootVM(manager,
                               state,
                               reboot_target,
-                              reboot_type=_reboot_type,
-                              state_name=state_string)
+                              reboot_state=reboot_state)
 
 
 class VerifyRebootVM(pending_action.PendingAction):
@@ -104,22 +88,13 @@
     States = enum('REBOOT_CHECK', 'ACTIVE_CHECK')
 
     def __init__(self, manager, state, target_server,
-                 reboot_type=None,
-                 state_name=None,
+                 reboot_state=None,
                  ip_addr=None):
         super(VerifyRebootVM, self).__init__(manager,
                                              state,
                                              target_server)
-        # FIX ME: this is a nova bug
-        if reboot_type == 'SOFT':
-            self._reboot_state = 'REBOOT'
-        else:
-            self._reboot_state = 'REBOOT'  # should be HARD REBOOT
-
-        if state_name == 'ACTIVE':  # was still active, check to see if REBOOT
-            self._retry_state = self.States.REBOOT_CHECK
-        else:  # was REBOOT, so now check for ACTIVE
-            self._retry_state = self.States.ACTIVE_CHECK
+        self._reboot_state = reboot_state
+        self._retry_state = self.States.REBOOT_CHECK
 
     def retry(self):
         """
@@ -155,8 +130,9 @@
             if not self._check_for_status('ACTIVE'):
                 return False
         target = self._target
-        self._logger.info('machine %s REBOOT -> ACTIVE [%.1f secs elapsed]' %
-                              (target['id'], time.time() - self._start_time))
+        self._logger.info('machine %s %s -> ACTIVE [%.1f secs elapsed]' %
+                              (target['id'], reboot_state,
+                                time.time() - self._start_time))
         self._state.set_instance_state(target['id'],
                                       (target, 'ACTIVE'))
 
diff --git a/stress/test_servers.py b/stress/test_servers.py
index 47d30b5..a71bea2 100644
--- a/stress/test_servers.py
+++ b/stress/test_servers.py
@@ -214,7 +214,7 @@
             target = self._target
             self._logger.info('machine %s: DELETED [%.1f secs elapsed]' %
                               (target['id'], time.time() - self._start_time))
-            self._state.delete_machine_state(target['id'])
+            self._state.delete_instance_state(target['id'])
             return True
 
         return False
diff --git a/stress/tests/create_kill.py b/stress/tests/create_kill.py
index 752f72d..26600de 100644
--- a/stress/tests/create_kill.py
+++ b/stress/tests/create_kill.py
@@ -1,35 +1,35 @@
-# Copyright 2011 Quanta Research Cambridge, 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.

-"""More aggressive test that creates and destroys VMs with shorter

-sleep times"""

-

-from stress.test_servers import *

-from stress.basher import BasherAction

-from stress.driver import *

-from tempest import openstack

-

-choice_spec = [

-    BasherAction(TestCreateVM(), 50),

-    BasherAction(TestKillActiveVM(), 50)

-]

-

-nova = openstack.Manager()

-

-bash_openstack(nova,

-               choice_spec,

-               duration=datetime.timedelta(seconds=180),

-               sleep_time=100,  # in milliseconds

-               seed=int(time.time()),

-               test_name="create and delete",

-               max_vms=32)

+# Copyright 2011 Quanta Research Cambridge, 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.
+"""More aggressive test that creates and destroys VMs with shorter
+sleep times"""
+
+from stress.test_servers import *
+from stress.basher import BasherAction
+from stress.driver import *
+from tempest import openstack
+
+choice_spec = [
+    BasherAction(TestCreateVM(), 50),
+    BasherAction(TestKillActiveVM(), 50)
+]
+
+nova = openstack.Manager()
+
+bash_openstack(nova,
+               choice_spec,
+               duration=datetime.timedelta(seconds=180),
+               sleep_time=100,  # in milliseconds
+               seed=int(time.time()),
+               test_name="create and delete",
+               )
diff --git a/stress/tests/hard_reboots.py b/stress/tests/hard_reboots.py
index f38ef6f..324b133 100644
--- a/stress/tests/hard_reboots.py
+++ b/stress/tests/hard_reboots.py
@@ -1,37 +1,37 @@
-# Copyright 2011 Quanta Research Cambridge, 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.

-"""Test that reboots random instances in a Nova cluster"""

-

-

-from stress.test_servers import *

-from stress.test_server_actions import *

-from stress.basher import BasherAction

-from stress.driver import *

-from tempest import openstack

-

-choice_spec = [

-    BasherAction(TestCreateVM(), 50),

-    BasherAction(TestRebootVM(), 50,

-                 kargs={'type': 'HARD'})

-]

-

-nova = openstack.Manager()

-

-bash_openstack(nova,

-               choice_spec,

-               duration=datetime.timedelta(seconds=180),

-               sleep_time=500,  # in milliseconds

-               seed=int(time.time()),

-               test_name="hard reboots",

-               max_vms=32)

+# Copyright 2011 Quanta Research Cambridge, 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.
+"""Test that reboots random instances in a Nova cluster"""
+
+
+from stress.test_servers import *
+from stress.test_server_actions import *
+from stress.basher import BasherAction
+from stress.driver import *
+from tempest import openstack
+
+choice_spec = [
+    BasherAction(TestCreateVM(), 50),
+    BasherAction(TestRebootVM(), 50,
+                 kargs={'type': 'HARD'})
+]
+
+nova = openstack.Manager()
+
+bash_openstack(nova,
+               choice_spec,
+               duration=datetime.timedelta(seconds=180),
+               sleep_time=500,  # in milliseconds
+               seed=int(time.time()),
+               test_name="hard reboots",
+               )
diff --git a/stress/tests/user_script_sample.py b/stress/tests/user_script_sample.py
index e4f53c4..51270a7 100644
--- a/stress/tests/user_script_sample.py
+++ b/stress/tests/user_script_sample.py
@@ -1,38 +1,38 @@
-# Copyright 2011 Quanta Research Cambridge, 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.

-"""Sample stress test that creates a few virtual machines and then

-destroys them"""

-

-

-from stress.test_servers import *

-from stress.basher import BasherAction

-from stress.driver import *

-from tempest import openstack

-

-choice_spec = [

-    BasherAction(TestCreateVM(), 50,

-                 kargs={'timeout': '60'}),

-    BasherAction(TestKillActiveVM(), 50)

-]

-

-

-nova = openstack.Manager()

-

-bash_openstack(nova,

-               choice_spec,

-               duration=datetime.timedelta(seconds=10),

-               sleep_time=1000,  # in milliseconds

-               seed=None,

-               test_name="simple create and delete",

-               max_vms=10)

+# Copyright 2011 Quanta Research Cambridge, 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.
+"""Sample stress test that creates a few virtual machines and then
+destroys them"""
+
+
+from stress.test_servers import *
+from stress.basher import BasherAction
+from stress.driver import *
+from tempest import openstack
+
+choice_spec = [
+    BasherAction(TestCreateVM(), 50,
+                 kargs={'timeout': '60'}),
+    BasherAction(TestKillActiveVM(), 50)
+]
+
+
+nova = openstack.Manager()
+
+bash_openstack(nova,
+               choice_spec,
+               duration=datetime.timedelta(seconds=10),
+               sleep_time=1000,  # in milliseconds
+               seed=None,
+               test_name="simple create and delete",
+               max_vms=4)
diff --git a/stress/tools/nova_destroy_all.py b/stress/tools/nova_destroy_all.py
index 1fa0487..e9010cd 100755
--- a/stress/tools/nova_destroy_all.py
+++ b/stress/tools/nova_destroy_all.py
@@ -1,53 +1,54 @@
-#!/usr/bin/env python

-

-# vim: tabstop=4 shiftwidth=4 softtabstop=4

-

-# Copyright 2011 Quanta Research Cambridge, 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.

-

-from novaclient.v1_1 import client

-import tempest.config

-

-# get the environment variables for credentials

-identity = tempest.config.TempestConfig().identity

-

-nt = client.Client(identity.username, identity.password,

-                   identity.tenant_name, identity.auth_url)

-

-flavor_list = nt.flavors.list()

-server_list = nt.servers.list()

-images_list = nt.images.list()

-keypairs_list = nt.keypairs.list()

-floating_ips_list = nt.floating_ips.list()

-

-print "total servers: %3d, total flavors: %3d, total images: %3d," % \

-    (len(server_list),

-     len(flavor_list),

-     len(images_list)),

-

-print "total keypairs: %3d, total floating ips: %3d" % \

-    (len(keypairs_list),

-     len(floating_ips_list))

-

-print "deleting all servers"

-for s in server_list:

-    s.delete()

-

-print "deleting all keypairs"

-for s in keypairs_list:

-    s.delete()

-

-print "deleting all floating_ips"

-for s in floating_ips_list:

-    s.delete()

+#!/usr/bin/env python
+
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Quanta Research Cambridge, 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.
+
+from novaclient.v1_1 import client
+import tempest.config
+
+# get the environment variables for credentials
+identity = tempest.config.TempestConfig().identity
+compute = tempest.config.TempestConfig().compute
+
+nt = client.Client(compute.username, compute.password,
+                   compute.tenant_name, identity.auth_url)
+
+flavor_list = nt.flavors.list()
+server_list = nt.servers.list()
+images_list = nt.images.list()
+keypairs_list = nt.keypairs.list()
+floating_ips_list = nt.floating_ips.list()
+
+print "total servers: %3d, total flavors: %3d, total images: %3d," % \
+    (len(server_list),
+     len(flavor_list),
+     len(images_list)),
+
+print "total keypairs: %3d, total floating ips: %3d" % \
+    (len(keypairs_list),
+     len(floating_ips_list))
+
+print "deleting all servers"
+for s in server_list:
+    s.delete()
+
+print "deleting all keypairs"
+for s in keypairs_list:
+    s.delete()
+
+print "deleting all floating_ips"
+for s in floating_ips_list:
+    s.delete()
diff --git a/stress/tools/nova_status.py b/stress/tools/nova_status.py
index 65dfc82..d413d7a 100755
--- a/stress/tools/nova_status.py
+++ b/stress/tools/nova_status.py
@@ -1,49 +1,50 @@
-#!/usr/bin/env python

-

-# vim: tabstop=4 shiftwidth=4 softtabstop=4

-

-# Copyright 2011 Quanta Research Cambridge, 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.

-

-from novaclient.v1_1 import client

-import tempest.config

-

-# get the environment variables for credentials

-identity = tempest.config.TempestConfig().identity

-print identity.username, identity.password,\

-    identity.tenant_name, identity.auth_url

-

-nt = client.Client(identity.username, identity.password,

-                   identity.tenant_name, identity.auth_url)

-

-flavor_list = nt.flavors.list()

-server_list = nt.servers.list()

-images_list = nt.images.list()

-keypairs_list = nt.keypairs.list()

-floating_ips_list = nt.floating_ips.list()

-

-print "total servers: %3d, total flavors: %3d, total images: %3d" % \

-    (len(server_list),

-     len(flavor_list),

-     len(images_list))

-

-print "total keypairs: %3d, total floating ips: %3d" % \

-    (len(keypairs_list),

-     len(floating_ips_list))

-

-print "flavors:\t", flavor_list

-print "servers:\t", server_list

-print "images: \t", images_list

-print "keypairs:\t", keypairs_list

-print "floating ips:\t", floating_ips_list

+#!/usr/bin/env python
+
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Quanta Research Cambridge, 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.
+
+from novaclient.v1_1 import client
+import tempest.config
+
+# get the environment variables for credentials
+identity = tempest.config.TempestConfig().identity
+compute = tempest.config.TempestConfig().compute
+print compute.username, compute.password,\
+    compute.tenant_name, identity.auth_url
+
+nt = client.Client(compute.username, compute.password,
+                   compute.tenant_name, identity.auth_url)
+
+flavor_list = nt.flavors.list()
+server_list = nt.servers.list()
+images_list = nt.images.list()
+keypairs_list = nt.keypairs.list()
+floating_ips_list = nt.floating_ips.list()
+
+print "total servers: %3d, total flavors: %3d, total images: %3d" % \
+    (len(server_list),
+     len(flavor_list),
+     len(images_list))
+
+print "total keypairs: %3d, total floating ips: %3d" % \
+    (len(keypairs_list),
+     len(floating_ips_list))
+
+print "flavors:\t", flavor_list
+print "servers:\t", server_list
+print "images: \t", images_list
+print "keypairs:\t", keypairs_list
+print "floating ips:\t", floating_ips_list
diff --git a/stress/utils/__init__.py b/stress/utils/__init__.py
index d4ac711..85300f2 100644
--- a/stress/utils/__init__.py
+++ b/stress/utils/__init__.py
@@ -1,15 +1,15 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4

-

-# Copyright 2011 Quanta Research Cambridge, 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.

+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Quanta Research Cambridge, 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.
diff --git a/stress/utils/util.py b/stress/utils/util.py
index ac8f062..5870ca1 100644
--- a/stress/utils/util.py
+++ b/stress/utils/util.py
@@ -1,55 +1,55 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4

-

-# Copyright 2011 Quanta Research Cambridge, 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 subprocess

-import shlex

-

-SSH_OPTIONS = (" -q" +

-               " -o UserKnownHostsFile=/dev/null" +

-               " -o StrictHostKeyChecking=no -i ")

-

-

-def get_ssh_options(keypath):

-    return SSH_OPTIONS + keypath

-

-

-def scp(keypath, args):

-    options = get_ssh_options(keypath)

-    return subprocess.check_call(shlex.split("scp" + options + args))

-

-

-def ssh(keypath, user, node, command, check=True):

-    command = 'sudo ' + command

-    command = "ssh %s %s@%s %s" % (get_ssh_options(keypath), user,

-                                   node, command)

-    popenargs = shlex.split(command)

-    process = subprocess.Popen(popenargs, stdout=subprocess.PIPE)

-    output, unused_err = process.communicate()

-    retcode = process.poll()

-    if retcode and check:

-        raise Exception("%s: ssh failed with retcode: %s" % (node, retcode))

-    return output

-

-

-def execute_on_all(keypath, user, nodes, command):

-    for node in nodes:

-        ssh(keypath, user, node, command)

-

-

-def enum(*sequential, **named):

-    """Create auto-incremented enumerated types"""

-    enums = dict(zip(sequential, range(len(sequential))), **named)

-    return type('Enum', (), enums)

+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Quanta Research Cambridge, 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 subprocess
+import shlex
+
+SSH_OPTIONS = (" -q" +
+               " -o UserKnownHostsFile=/dev/null" +
+               " -o StrictHostKeyChecking=no -i ")
+
+
+def get_ssh_options(keypath):
+    return SSH_OPTIONS + keypath
+
+
+def scp(keypath, args):
+    options = get_ssh_options(keypath)
+    return subprocess.check_call(shlex.split("scp" + options + args))
+
+
+def ssh(keypath, user, node, command, check=True):
+    command = 'sudo ' + command
+    command = "ssh %s %s@%s %s" % (get_ssh_options(keypath), user,
+                                   node, command)
+    popenargs = shlex.split(command)
+    process = subprocess.Popen(popenargs, stdout=subprocess.PIPE)
+    output, unused_err = process.communicate()
+    retcode = process.poll()
+    if retcode and check:
+        raise Exception("%s: ssh failed with retcode: %s" % (node, retcode))
+    return output
+
+
+def execute_on_all(keypath, user, nodes, command):
+    for node in nodes:
+        ssh(keypath, user, node, command)
+
+
+def enum(*sequential, **named):
+    """Create auto-incremented enumerated types"""
+    enums = dict(zip(sequential, range(len(sequential))), **named)
+    return type('Enum', (), enums)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index ff27384..c8af2ca 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -1,8 +1,26 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack, LLC
+# 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
 import httplib2
 import logging
 import sys
 import time
+
 from tempest import exceptions
 
 
@@ -15,7 +33,7 @@
     def __init__(self, config, user, password, auth_url, service,
                  tenant_name=None):
         self.log = logging.getLogger(__name__)
-        self.log.setLevel(logging.ERROR)
+        self.log.setLevel(getattr(logging, config.compute.log_level))
         self.config = config
         if self.config.identity.strategy == 'keystone':
             self.token, self.base_url = self.keystone_auth(user,
diff --git a/tempest/config.py b/tempest/config.py
index f805d93..8c014bf 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1,24 +1,49 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack, LLC
+# 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 ConfigParser
 import logging
 import os
+
 from tempest.common.utils import data_utils
 
 LOG = logging.getLogger(__name__)
 
 
-class IdentityConfig(object):
-    """Provides configuration information for authenticating with Keystone."""
+class BaseConfig(object):
+
+    SECTION_NAME = None
 
     def __init__(self, conf):
-        """Initialize an Identity-specific configuration object"""
         self.conf = conf
 
     def get(self, item_name, default_value=None):
         try:
-            return self.conf.get("identity", item_name)
+            return self.conf.get(self.SECTION_NAME, item_name)
         except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
             return default_value
 
+
+class IdentityConfig(BaseConfig):
+
+    """Provides configuration information for authenticating with Keystone."""
+
+    SECTION_NAME = "identity"
+
     @property
     def host(self):
         """Host IP for making Identity API requests."""
@@ -55,61 +80,54 @@
         return self.get("use_ssl", 'false').lower() != 'false'
 
     @property
-    def nonadmin_user1(self):
-        """Username to use for Nova API requests."""
-        return self.get("nonadmin_user1")
-
-    @property
-    def nonadmin_user1_tenant_name(self):
-        """Tenant name to use for Nova API requests."""
-        return self.get("nonadmin_user1_tenant_name")
-
-    @property
-    def nonadmin_user1_password(self):
-        """API key to use when authenticating."""
-        return self.get("nonadmin_user1_password")
-
-    @property
-    def nonadmin_user2(self):
-        """Alternate username to use for Nova API requests."""
-        return self.get("nonadmin_user2")
-
-    @property
-    def nonadmin_user2_tenant_name(self):
-        """Alternate tenant name for Nova API requests."""
-        return self.get("nonadmin_user2_tenant_name")
-
-    @property
-    def nonadmin_user2_password(self):
-        """Alternate API key to use when authenticating."""
-        return self.get("nonadmin_user2_password")
-
-    @property
     def strategy(self):
         """Which auth method does the environment use? (basic|keystone)"""
         return self.get("strategy", 'keystone')
 
 
-class ComputeConfig(object):
-    def __init__(self, conf):
-        """Initialize a Compute-specific configuration object."""
-        self.conf = conf
+class ComputeConfig(BaseConfig):
 
-    def get(self, item_name, default_value):
-        try:
-            return self.conf.get("compute", item_name)
-        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
-            return default_value
+    SECTION_NAME = "compute"
+
+    @property
+    def username(self):
+        """Username to use for Nova API requests."""
+        return self.get("username", "demo")
+
+    @property
+    def tenant_name(self):
+        """Tenant name to use for Nova API requests."""
+        return self.get("tenant_name", "demo")
+
+    @property
+    def password(self):
+        """API key to use when authenticating."""
+        return self.get("password", "pass")
+
+    @property
+    def alt_username(self):
+        """Username of alternate user to use for Nova API requests."""
+        return self.get("alt_username")
+
+    @property
+    def alt_tenant_name(self):
+        """Alternate user's Tenant name to use for Nova API requests."""
+        return self.get("alt_tenant_name")
+
+    @property
+    def alt_password(self):
+        """API key to use when authenticating as alternate user."""
+        return self.get("alt_password")
 
     @property
     def image_ref(self):
         """Valid primary image to use in tests."""
-        return self.get("image_ref", 'e7ddc02e-92fa-4f82-b36f-59b39bf66a67')
+        return self.get("image_ref", "{$IMAGE_ID}")
 
     @property
     def image_ref_alt(self):
         """Valid secondary image reference to be used in tests."""
-        return self.get("image_ref_alt", '346f4039-a81e-44e0-9223-4a3d13c907')
+        return self.get("image_ref_alt", "{$IMAGE_ID_ALT}")
 
     @property
     def flavor_ref(self):
@@ -132,21 +150,11 @@
         return self.get("create_image_enabled", 'false').lower() != 'false'
 
     @property
-    def release_name(self):
-        """Which release is this?"""
-        return self.get("release_name", 'essex')
-
-    @property
     def build_interval(self):
         """Time in seconds between build status checks."""
         return float(self.get("build_interval", 10))
 
     @property
-    def ssh_timeout(self):
-        """Timeout in seconds to use when connecting via ssh."""
-        return float(self.get("ssh_timeout", 300))
-
-    @property
     def build_timeout(self):
         """Timeout in seconds to wait for an entity to build."""
         return float(self.get("build_timeout", 300))
@@ -156,21 +164,40 @@
         """Catalog type of the Compute service."""
         return self.get("catalog_type", 'compute')
 
+    @property
+    def log_level(self):
+        """Level for logging compute API calls."""
+        return self.get("log_level", 'ERROR')
 
-class ImagesConfig(object):
+
+class ComputeAdminConfig(BaseConfig):
+
+    SECTION_NAME = "compute-admin"
+
+    @property
+    def username(self):
+        """Administrative Username to use for Nova API requests."""
+        return self.get("username", "admin")
+
+    @property
+    def tenant_name(self):
+        """Administrative Tenant name to use for Nova API requests."""
+        return self.get("tenant_name", "admin")
+
+    @property
+    def password(self):
+        """API key to use when authenticating as admin."""
+        return self.get("password", "pass")
+
+
+class ImagesConfig(BaseConfig):
+
     """
     Provides configuration information for connecting to an
     OpenStack Images service.
     """
 
-    def __init__(self, conf):
-        self.conf = conf
-
-    def get(self, item_name, default_value=None):
-        try:
-            return self.conf.get("image", item_name)
-        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
-            return default_value
+    SECTION_NAME = "image"
 
     @property
     def host(self):
@@ -189,31 +216,34 @@
 
     @property
     def username(self):
-        """Username to use for Images API requests. Defaults to 'admin'."""
-        return self.get("user", "admin")
+        """Username to use for Images API requests. Defaults to 'demo'."""
+        return self.get("user", "demo")
 
     @property
     def password(self):
         """Password for user"""
-        return self.get("password", "")
+        return self.get("password", "pass")
 
     @property
-    def tenant(self):
-        """Tenant to use for Images API requests. Defaults to 'admin'."""
-        return self.get("tenant", "admin")
-
-    @property
-    def service_token(self):
-        """Token to use in querying the API. Default: None"""
-        return self.get("service_token")
-
-    @property
-    def auth_url(self):
-        """Optional URL to auth service. Will be discovered if None"""
-        return self.get("auth_url")
+    def tenant_name(self):
+        """Tenant to use for Images API requests. Defaults to 'demo'."""
+        return self.get("tenant_name", "demo")
 
 
-class TempestConfig(object):
+# TODO(jaypipes): Move this to a common utils (not data_utils...)
+def singleton(cls):
+    """Simple wrapper for classes that should only have a single instance"""
+    instances = {}
+
+    def getinstance():
+        if cls not in instances:
+            instances[cls] = cls()
+        return instances[cls]
+    return getinstance
+
+
+@singleton
+class TempestConfig:
     """Provides OpenStack configuration information."""
 
     DEFAULT_CONFIG_DIR = os.path.join(
@@ -235,12 +265,15 @@
 
         path = os.path.join(conf_dir, conf_file)
 
+        LOG.info("Using tempest config file %s" % path)
+
         if not os.path.exists(path):
             msg = "Config file %(path)s not found" % locals()
             raise RuntimeError(msg)
 
         self._conf = self.load_config(path)
         self.compute = ComputeConfig(self._conf)
+        self.compute_admin = ComputeAdminConfig(self._conf)
         self.identity = IdentityConfig(self._conf)
         self.images = ImagesConfig(self._conf)
 
diff --git a/tempest/openstack.py b/tempest/openstack.py
index d52647b..491f385 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -1,3 +1,22 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack, LLC
+# 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 logging
+
 import tempest.config
 from tempest import exceptions
 from tempest.services.image import service as image_service
@@ -12,25 +31,37 @@
 from tempest.services.nova.json.keypairs_client import KeyPairsClient
 from tempest.services.nova.json.volumes_client import VolumesClient
 
+LOG = logging.getLogger(__name__)
+
 
 class Manager(object):
 
+    """
+    Top level manager for OpenStack Compute clients
+    """
+
     def __init__(self, username=None, password=None, tenant_name=None):
         """
-        Top level manager for all Openstack APIs
+        We allow overriding of the credentials used within the various
+        client classes managed by the Manager object. Left as None, the
+        standard username/password/tenant_name is used.
+
+        :param username: Override of the username
+        :param password: Override of the password
+        :param tenant_name: Override of the tenant name
         """
         self.config = tempest.config.TempestConfig()
 
-        if None in [username, password, tenant_name]:
-            # Pull from the default, the first non-admin user
-            username = self.config.identity.nonadmin_user1
-            password = self.config.identity.nonadmin_user1_password
-            tenant_name = self.config.identity.nonadmin_user1_tenant_name
+        username = username or self.config.compute.username
+        password = password or self.config.compute.password
+        tenant_name = tenant_name or self.config.compute.tenant_name
 
-        if None in [username, password, tenant_name]:
-            # We can't find any usable credentials, fail early
-            raise exceptions.InvalidConfiguration(message="Missing complete \
-                                                  user credentials.")
+        if None in (username, password, tenant_name):
+            msg = ("Missing required credentials. "
+                   "username: %(username)s, password: %(password)s, "
+                   "tenant_name: %(tenant_name)s") % locals()
+            raise exceptions.InvalidConfiguration(msg)
+
         auth_url = self.config.identity.auth_url
 
         if self.config.identity.strategy == 'keystone':
@@ -50,6 +81,35 @@
         self.volumes_client = VolumesClient(*client_args)
 
 
+class AltManager(Manager):
+
+    """
+    Manager object that uses the alt_XXX credentials for its
+    managed client objects
+    """
+
+    def __init__(self):
+        conf = tempest.config.TempestConfig()
+        super(AltManager, self).__init__(conf.compute.alt_username,
+                                         conf.compute.alt_password,
+                                         conf.compute.alt_tenant_name)
+
+
+class AdminManager(Manager):
+
+    """
+    Manager object that uses the alt_XXX credentials for its
+    managed client objects
+    """
+
+    def __init__(self):
+        conf = tempest.config.TempestConfig()
+        super(AdminManager, self).__init__(conf.compute_admin.username,
+                                           conf.compute_admin.password,
+                                           conf.compute_admin.tenant_name)
+        # TODO(jaypipes): Add Admin-Only client class construction below...
+
+
 class ServiceManager(object):
 
     """
diff --git a/tempest/services/image/service.py b/tempest/services/image/service.py
index 2cf053f..901e497 100644
--- a/tempest/services/image/service.py
+++ b/tempest/services/image/service.py
@@ -42,14 +42,14 @@
             creds = {
                 'username': config.images.username,
                 'password': config.images.password,
-                'tenant': config.images.tenant,
-                'auth_url': config.images.auth_url,
-                'strategy': 'keystone'
+                'tenant': config.images.tenant_name,
+                # rstrip() is necessary here because Glance client
+                # automatically adds the tokens/ part...
+                'auth_url': config.identity.auth_url.rstrip('/tokens'),
+                'strategy': config.identity.strategy
             }
-            service_token = config.images.service_token
             self._client = client.Client(config.images.host,
                                          config.images.port,
-                                         auth_tok=service_token,
                                          creds=creds)
         else:
             raise NotImplementedError
diff --git a/tempest/services/nova/json/volumes_client.py b/tempest/services/nova/json/volumes_client.py
index 95131f2..c935621 100644
--- a/tempest/services/nova/json/volumes_client.py
+++ b/tempest/services/nova/json/volumes_client.py
@@ -1,5 +1,7 @@
+from tempest import exceptions
 from tempest.common import rest_client
 import json
+import time
 
 
 class VolumesClient(object):
@@ -9,6 +11,10 @@
         catalog_type = self.config.compute.catalog_type
         self.client = rest_client.RestClient(config, username, key, auth_url,
                                              catalog_type, tenant_name)
+        self.build_interval = self.config.compute.build_interval
+        self.build_timeout = self.config.compute.build_timeout
+        self.headers = {'Content-Type': 'application/json',
+                        'Accept': 'application/json'}
 
     def list_volumes(self, params=None):
         """List all the volumes created"""
@@ -24,6 +30,20 @@
         body = json.loads(body)
         return resp, body['volumes']
 
+    def list_volumes_with_detail(self, params=None):
+        """List all the details of volumes"""
+        url = 'os-volumes/detail'
+        if params != None:
+            param_list = []
+            for param, value in params.iteritems():
+                param_list.append("%s=%s&" % (param, value))
+
+            url = '?' + ' '.join(param_list)
+
+        resp, body = self.client.get(url)
+        body = json.loads(body)
+        return resp, body['volumes']
+
     def get_volume(self, volume_id):
         """Returns the details of a single volume"""
         url = "os-volumes/%s" % str(volume_id)
@@ -31,6 +51,45 @@
         body = json.loads(body)
         return resp, body['volume']
 
+    def create_volume(self, size, **kwargs):
+        """
+        Creates a new Volume.
+        size(Required): Size of volume in GB.
+        Following optional keyword arguments are accepted:
+        display_name: Optional Volume Name.
+        metadata: A dictionary of values to be used as metadata.
+        """
+        post_body = {
+            'size': size,
+            'display_name': kwargs.get('display_name'),
+            'metadata': kwargs.get('metadata'),
+            }
+
+        post_body = json.dumps({'volume': post_body})
+        resp, body = self.client.post('os-volumes', post_body, self.headers)
+        body = json.loads(body)
+        return resp, body['volume']
+
     def delete_volume(self, volume_id):
         """Deletes the Specified Volume"""
         return self.client.delete("os-volumes/%s" % str(volume_id))
+
+    def wait_for_volume_status(self, volume_id, status):
+        """Waits for a Volume to reach a given status"""
+        resp, body = self.get_volume(volume_id)
+        volume_name = body['displayName']
+        volume_status = body['status']
+        start = int(time.time())
+
+        while volume_status != status:
+            time.sleep(self.build_interval)
+            resp, body = self.get_volume(volume_id)
+            volume_status = body['status']
+            if volume_status == 'error':
+                raise exceptions.BuildErrorException(volume_id=volume_id)
+
+            if int(time.time()) - start >= self.build_timeout:
+                message = 'Volume %s failed to reach %s status within '\
+                'the required time (%s s).' % (volume_name, status,
+                                              self.build_timeout)
+                raise exceptions.TimeoutException(message)
diff --git a/tempest/tests/image/test_images.py b/tempest/tests/image/test_images.py
index 8f75f08..0e515dc 100644
--- a/tempest/tests/image/test_images.py
+++ b/tempest/tests/image/test_images.py
@@ -109,7 +109,6 @@
         self.assertEqual(1024, results['size'])
 
     @attr(type='image')
-    @unittest.skip('Skipping until Glance Bug 912897 is fixed')
     def test_register_remote_image(self):
         """Register a new remote image"""
         meta = {
@@ -141,7 +140,6 @@
     def setUpClass(cls):
         if not GLANCE_INSTALLED:
             raise SkipTest('Glance not installed')
-        raise SkipTest('Skipping until Glance Bug 912897 is fixed')
         cls.os = openstack.ServiceManager()
         cls.client = cls.os.images.get_client()
         cls.created_images = []
@@ -149,7 +147,7 @@
 
         # We add a few images here to test the listing functionality of
         # the images API
-        for x in xrange(1, 10):
+        for x in xrange(0, 10):
             # We make even images remote and odd images standard
             if x % 2 == 0:
                 cls.created_images.append(cls._create_remote_image(x))
diff --git a/tempest/tests/test_authorization.py b/tempest/tests/test_authorization.py
index f71073d..5b2eeca 100644
--- a/tempest/tests/test_authorization.py
+++ b/tempest/tests/test_authorization.py
@@ -4,8 +4,10 @@
 from nose.tools import raises
 
 from tempest import openstack
+from tempest.services.nova.json.images_client import ImagesClient
+from tempest.services.nova.json.servers_client import ServersClient
 from tempest.common.utils.data_utils import rand_name
-from tempest.exceptions import NotFound, ComputeFault, BadRequest, Unauthorized
+from tempest import exceptions
 from tempest.tests import utils
 
 
@@ -23,35 +25,38 @@
         cls.flavor_ref_alt = cls.config.compute.flavor_ref_alt
 
         # Verify the second user is not the same as the first and is configured
-        cls.user1 = cls.config.identity.nonadmin_user1
-        cls.user2 = cls.config.identity.nonadmin_user2
-        cls.user2_password = cls.config.identity.nonadmin_user2_password
-        cls.user2_tenant_name = cls.config.identity.nonadmin_user2_tenant_name
+        cls.user1 = cls.config.compute.username
+        cls.user2 = cls.config.compute.alt_username
+        cls.user2_password = cls.config.compute.alt_password
+        cls.user2_tenant_name = cls.config.compute.alt_tenant_name
         cls.multi_user = False
 
         if  (cls.user2 != None and cls.user1 != cls.user2
             and cls.user2_password != None
             and cls.user2_tenant_name != None):
 
-            # Setup a client instance for the second user
-            cls.multi_user = True
-            cls.os_other = openstack.Manager(cls.user2, cls.user2_password,
-                                             cls.user2_tenant_name)
-            cls.other_client = cls.os_other.servers_client
-            cls.other_images_client = cls.os_other.images_client
+            try:
+                cls.other_manager = openstack.AltManager()
+                cls.other_client = cls.other_manager.servers_client
+                cls.other_images_client = cls.other_manager.images_client
+            except exceptions.AuthenticationFailure:
+                # multi_user is already set to false, just fall through
+                pass
+            else:
+                cls.multi_user = True
 
-            name = rand_name('server')
-            resp, server = cls.client.create_server(name, cls.image_ref,
-                                                    cls.flavor_ref)
-            cls.client.wait_for_server_status(server['id'], 'ACTIVE')
-            resp, cls.server = cls.client.get_server(server['id'])
+                name = rand_name('server')
+                resp, server = cls.client.create_server(name, cls.image_ref,
+                                                        cls.flavor_ref)
+                cls.client.wait_for_server_status(server['id'], 'ACTIVE')
+                resp, cls.server = cls.client.get_server(server['id'])
 
-            name = rand_name('image')
-            resp, body = cls.client.create_image(server['id'], name)
-            image_id = cls._parse_image_id(resp['location'])
-            cls.images_client.wait_for_image_resp_code(image_id, 200)
-            cls.images_client.wait_for_image_status(image_id, 'ACTIVE')
-            resp, cls.image = cls.images_client.get_image(image_id)
+                name = rand_name('image')
+                resp, body = cls.client.create_image(server['id'], name)
+                image_id = cls._parse_image_id(resp['location'])
+                cls.images_client.wait_for_image_resp_code(image_id, 200)
+                cls.images_client.wait_for_image_status(image_id, 'ACTIVE')
+                resp, cls.image = cls.images_client.get_image(image_id)
 
     @classmethod
     def tearDownClass(cls):
@@ -59,35 +64,35 @@
             cls.client.delete_server(cls.server['id'])
             cls.images_client.delete_image(cls.image['id'])
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_get_server_for_other_account_fails(self):
         """A GET request for a server on another user's account should fail"""
         self.other_client.get_server(self.server['id'])
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_delete_server_for_other_account_fails(self):
         """A DELETE request for another user's server should fail"""
         self.other_client.delete_server(self.server['id'])
 
-    @raises(ComputeFault)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_update_server_for_other_account_fails(self):
         """An update server request for another user's server should fail"""
         self.other_client.update_server(self.server['id'], name='test')
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_list_server_addresses_for_other_account_fails(self):
         """A list addresses request for another user's server should fail"""
         self.other_client.list_addresses(self.server['id'])
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_list_server_addresses_by_network_for_other_account_fails(self):
@@ -97,42 +102,42 @@
         server_id = self.server['id']
         self.other_client.list_addresses_by_network(server_id, 'public')
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_change_password_for_other_account_fails(self):
         """A change password request for another user's server should fail"""
         self.other_client.change_password(self.server['id'], 'newpass')
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_reboot_server_for_other_account_fails(self):
         """A reboot request for another user's server should fail"""
         self.other_client.reboot(self.server['id'], 'HARD')
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_rebuild_server_for_other_account_fails(self):
         """A rebuild request for another user's server should fail"""
         self.other_client.rebuild(self.server['id'], self.image_ref_alt)
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_resize_server_for_other_account_fails(self):
         """A resize request for another user's server should fail"""
         self.other_client.resize(self.server['id'], self.flavor_ref_alt)
 
-    @raises(NotFound)
+    @raises(exceptions.NotFound)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_create_image_for_other_account_fails(self):
         """A create image request for another user's server should fail"""
         self.other_images_client.create_image(self.server['id'], 'testImage')
 
-    @raises(BadRequest)
+    @raises(exceptions.BadRequest)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_create_server_with_unauthorized_image(self):
@@ -140,7 +145,7 @@
         self.other_client.create_server('test', self.image['id'],
                                          self.flavor_ref)
 
-    @raises(Unauthorized)
+    @raises(exceptions.BadRequest)
     @attr(type='negative')
     @utils.skip_unless_attr('multi_user', 'Second user not configured')
     def test_create_server_fails_when_tenant_incorrect(self):
@@ -148,11 +153,15 @@
         A create server request should fail if the tenant id does not match
         the current user
         """
-        os_other = openstack.Manager(self.user2, self.user2_password,
-                                     self.user2_tenant_name)
-        os_other.servers_client.client.base_url = self.client.client.base_url
-        os_other.servers_client.create_server('test', self.image['id'],
-                                              self.flavor_ref)
+        try:
+            saved_base_url = self.other_client.client.base_url
+            # Change the base URL to impersonate another user
+            self.other_client.client.base_url = self.client.client.base_url
+            self.other_client.create_server('test', self.image['id'],
+                                                    self.flavor_ref)
+        finally:
+            # Reset the base_url...
+            self.other_client.client.base_url = saved_base_url
 
     @classmethod
     def _parse_image_id(self, image_ref):
diff --git a/tempest/tests/test_flavors.py b/tempest/tests/test_flavors.py
index ac58dba..187fac7 100644
--- a/tempest/tests/test_flavors.py
+++ b/tempest/tests/test_flavors.py
@@ -7,8 +7,6 @@
 
 class FlavorsTest(unittest.TestCase):
 
-    release = tempest.config.TempestConfig().compute.release_name
-
     @classmethod
     def setUpClass(cls):
         cls.os = openstack.Manager()
@@ -44,7 +42,6 @@
         self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
                           999)
 
-    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive', bug='lp912922')
     def test_list_flavors_limit_results(self):
         """Only the expected number of flavors should be returned"""
@@ -52,7 +49,6 @@
         resp, flavors = self.client.list_flavors(params)
         self.assertEqual(1, len(flavors))
 
-    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive', bug='lp912922')
     def test_list_flavors_detailed_limit_results(self):
         """Only the expected number of flavors (detailed) should be returned"""
@@ -60,8 +56,7 @@
         resp, flavors = self.client.list_flavors_with_detail(params)
         self.assertEqual(1, len(flavors))
 
-    @unittest.expectedFailure
-    @attr(type='positive', bug='lp912922')
+    @attr(type='positive')
     def test_list_flavors_using_marker(self):
         """The list of flavors should start from the provided marker"""
         resp, flavors = self.client.list_flavors()
@@ -72,8 +67,7 @@
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
                         'The list of flavors did not start after the marker.')
 
-    @unittest.expectedFailure
-    @attr(type='positive', bug='lp912922')
+    @attr(type='positive')
     def test_list_flavors_detailed_using_marker(self):
         """The list of flavors should start from the provided marker"""
         resp, flavors = self.client.list_flavors_with_detail()
@@ -84,7 +78,6 @@
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
                         'The list of flavors did not start after the marker.')
 
-    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive')
     def test_list_flavors_detailed_filter_by_min_disk(self):
         """The detailed list of flavors should be filtered by disk space"""
@@ -96,7 +89,6 @@
         resp, flavors = self.client.list_flavors_with_detail(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
-    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive')
     def test_list_flavors_detailed_filter_by_min_ram(self):
         """The detailed list of flavors should be filtered by RAM"""
@@ -108,7 +100,6 @@
         resp, flavors = self.client.list_flavors_with_detail(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
-    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive')
     def test_list_flavors_filter_by_min_disk(self):
         """The list of flavors should be filtered by disk space"""
@@ -120,7 +111,6 @@
         resp, flavors = self.client.list_flavors(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
-    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive')
     def test_list_flavors_filter_by_min_ram(self):
         """The list of flavors should be filtered by RAM"""
diff --git a/tempest/tests/test_keypairs.py b/tempest/tests/test_keypairs.py
index 9f17922..baedbb6 100644
--- a/tempest/tests/test_keypairs.py
+++ b/tempest/tests/test_keypairs.py
@@ -8,8 +8,6 @@
 
 class KeyPairsTest(unittest.TestCase):
 
-    release = tempest.config.TempestConfig().compute.release_name
-
     @classmethod
     def setUpClass(cls):
         cls.os = openstack.Manager()
@@ -138,7 +136,6 @@
         else:
             self.fail('empty string')
 
-    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='negative')
     def test_create_keypair_with_long_keynames(self):
         """Keypairs with name longer than 255 chars should not be created"""
diff --git a/tempest/tests/test_servers_negative.py b/tempest/tests/test_servers_negative.py
index f8de727..6c738ea 100644
--- a/tempest/tests/test_servers_negative.py
+++ b/tempest/tests/test_servers_negative.py
@@ -6,8 +6,6 @@
 
 
 class ServersNegativeTest(unittest.TestCase):
-    release = tempest.config.TempestConfig().\
-            compute.release_name
 
     @classmethod
     def setUpClass(cls):
@@ -16,7 +14,6 @@
         cls.config = cls.os.config
         cls.image_ref = cls.config.compute.image_ref
         cls.flavor_ref = cls.config.compute.flavor_ref
-        cls.ssh_timeout = cls.config.compute.ssh_timeout
 
     def test_server_name_blank(self):
         """Create a server with name parameter empty"""
@@ -63,7 +60,6 @@
         else:
             self.fail('Cannot create a server with an invalid flavor')
 
-    @unittest.skipIf(release == 'diablo', 'Bug in Diablo, lp#891264')
     def test_invalid_access_ip_v4_address(self):
         """An access IPv4 address must match a valid address pattern"""
         accessIPv4 = '1.1.1.1.1.1'
@@ -78,7 +74,6 @@
         else:
             self.fail('Access IPv4 address must match the correct format')
 
-    @unittest.skipIf(release == 'diablo', 'Bug in Diablo, lp#891264')
     def test_invalid_ip_v6_address(self):
         """An access IPv6 address must match a valid address pattern"""
         accessIPv6 = 'notvalid'
diff --git a/tempest/tests/test_volumes_list.py b/tempest/tests/test_volumes_list.py
new file mode 100644
index 0000000..4a4c7d1
--- /dev/null
+++ b/tempest/tests/test_volumes_list.py
@@ -0,0 +1,59 @@
+from nose.plugins.attrib import attr
+import unittest2 as unittest
+from tempest import openstack
+from tempest.common.utils.data_utils import rand_name
+
+
+class VolumesTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = openstack.Manager()
+        cls.client = cls.os.volumes_client
+        #Create 3 Volumes
+        cls.volume_list = list()
+        cls.volume_id_list = list()
+        for i in range(3):
+            v_name = rand_name('Name-')
+            metadata = {'Type': 'work'}
+            resp, volume = cls.client.create_volume(size=1,
+                                                     display_name=v_name,
+                                                     metadata=metadata)
+            cls.client.wait_for_volume_status(volume['id'],
+                                               'available')
+            resp, volume = cls.client.get_volume(volume['id'])
+            cls.volume_list.append(volume)
+            cls.volume_id_list.append(volume['id'])
+
+    def teardown(cls):
+        #Delete the created Volumes
+        for i in range(3):
+            resp, _ = cls.client.delete_volume(cls.volume_id_list[i])
+
+    @attr(type='smoke')
+    def test_volume_list(self):
+        """Should return the list of Volumes"""
+        #Fetch all Volumes
+        resp, fetched_list = self.client.list_volumes()
+        self.assertEqual(200, resp.status)
+        #Now check if all the Volumes created in setup are in fetched list
+        missing_volumes =\
+        [v for v in self.volume_list if v not in fetched_list]
+        self.assertFalse(missing_volumes,
+                         "Failed to find volume %s in fetched list"
+                         % ', '.join(m_vol['displayName']
+                                        for m_vol in missing_volumes))
+
+    @attr(type='smoke')
+    def test_volume_list_with_details(self):
+        """Should return the list of Volumes with details"""
+        #Fetch all Volumes
+        resp, fetched_list = self.client.list_volumes_with_detail()
+        self.assertEqual(200, resp.status)
+        #Now check if all the Volumes created in setup are in fetched list
+        missing_volumes =\
+        [v for v in self.volume_list if v not in fetched_list]
+        self.assertFalse(missing_volumes,
+                         "Failed to find volume %s in fetched list"
+                         % ', '.join(m_vol['displayName']
+                                        for m_vol in missing_volumes))