Merge "Add scenario test of instance snapshot and boot"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index db6a7bd..0af8e9b 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -229,6 +229,10 @@
 multi_backend_enabled = false
 backend1_name = BACKEND_1
 backend2_name = BACKEND_2
+# Protocol and vendor of volume backend to target when testing volume-types.
+# You should update to reflect those exported by configured backend driver.
+storage_protocol = iSCSI
+vendor_name = Open Source
 
 [object-storage]
 # This section contains configuration options used when executing tests
@@ -332,8 +336,8 @@
 # ssh username for the image file
 ssh_user = cirros
 
-[CLI]
+[cli]
 # Enable cli tests
 enabled = True
 # directory where python client binaries are located
-cli_dir = /usr/local/bin/
+cli_dir = /usr/local/bin
diff --git a/requirements.txt b/requirements.txt
index df9951d..606d7ae 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,4 +19,3 @@
 oslo.config>=1.1.0
 # Needed for whitebox testing
 sqlalchemy
-MySQL-python
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index f201cf7..34f96ba 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -15,7 +15,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
@@ -51,6 +54,10 @@
 class FixedIPsTestJson(FixedIPsBase):
     _interface = 'json'
 
+    CONF = config.TempestConfig()
+
+    @testtools.skipIf(CONF.network.quantum_available, "This feature is not" +
+                      "implemented by Quantum. See bug: #1194569")
     @attr(type='gate')
     def test_list_fixed_ip_details(self):
         resp, fixed_ip = self.client.get_fixed_ip_details(self.ip)
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index f960ca4..12c646d 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -15,8 +15,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common.utils.data_utils import rand_name
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
@@ -155,6 +158,8 @@
                           self.client.create_security_group, s_name,
                           s_description)
 
+    @testtools.skipIf(config.TempestConfig().network.quantum_available,
+                      "Quantum allows duplicate names for security groups")
     @attr(type=['negative', 'gate'])
     def test_security_group_create_with_duplicate_name(self):
         # Negative test:Security Group with duplicate name should not
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index bbe489c..5f53080 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -236,11 +236,7 @@
         # Create a server with a nonexistent security group
 
         security_groups = [{'name': 'does_not_exist'}]
-        if self.config.network.quantum_available:
-            expected_exception = exceptions.NotFound
-        else:
-            expected_exception = exceptions.BadRequest
-        self.assertRaises(expected_exception,
+        self.assertRaises(exceptions.BadRequest,
                           self.create_server,
                           security_groups=security_groups)
 
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 3119643..9073aeb 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -16,9 +16,11 @@
 #    under the License.
 
 import netaddr
+import testtools
 
 from tempest.api.compute import base
 from tempest.common.utils.data_utils import rand_name
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
@@ -26,6 +28,8 @@
 class VirtualInterfacesTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
+    CONF = config.TempestConfig()
+
     @classmethod
     def setUpClass(cls):
         super(VirtualInterfacesTestJSON, cls).setUpClass()
@@ -33,6 +37,8 @@
         resp, server = cls.create_server(wait_until='ACTIVE')
         cls.server_id = server['id']
 
+    @testtools.skipIf(CONF.network.quantum_available, "This feature is not " +
+                      "implemented by Quantum. See bug: #1183436")
     @attr(type='gate')
     def test_list_virtual_interfaces(self):
         # Positive test:Should be able to GET the virtual interfaces list
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 4131d3e..3c4b5d8 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -55,8 +55,10 @@
             volume = {}
             vol_name = rand_name("volume-")
             vol_type_name = rand_name("volume-type-")
-            extra_specs = {"storage_protocol": "iSCSI",
-                           "vendor_name": "Open Source"}
+            proto = self.config.volume.storage_protocol
+            vendor = self.config.volume.vendor_name
+            extra_specs = {"storage_protocol": proto,
+                           "vendor_name": vendor}
             body = {}
             resp, body = self.client.create_volume_type(
                 vol_type_name,
diff --git a/tempest/cli/README.rst b/tempest/cli/README.rst
index 76b05a3..3eae492 100644
--- a/tempest/cli/README.rst
+++ b/tempest/cli/README.rst
@@ -36,7 +36,7 @@
 If a test is validating the cli for bad data, it should do it with
 assertRaises.
 
-A reasonable example of an existing test is as follows:
+A reasonable example of an existing test is as follows::
 
     def test_admin_list(self):
         self.nova('list')
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 413990d..5bbedfd 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -16,6 +16,7 @@
 #    under the License.
 
 import logging
+import os
 import shlex
 import subprocess
 
@@ -99,7 +100,7 @@
     def cmd(self, cmd, action, flags='', params='', fail_ok=False,
             merge_stderr=False):
         """Executes specified command for the given action."""
-        cmd = ' '.join([CONF.cli.cli_dir + cmd,
+        cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
                         flags, action, params])
         LOG.info("running: '%s'" % cmd)
         cmd = shlex.split(cmd)
@@ -109,7 +110,7 @@
             else:
                 with open('/dev/null', 'w') as devnull:
                     result = self.check_output(cmd, stderr=devnull)
-        except subprocess.CalledProcessError, e:
+        except subprocess.CalledProcessError as e:
             LOG.error("command output:\n%s" % e.output)
             raise
         return result
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index d19d216..cd33a22 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -304,14 +304,14 @@
         if self.cert_file:
             try:
                 self.context.use_certificate_file(self.cert_file)
-            except Exception, e:
+            except Exception as e:
                 msg = 'Unable to load cert from "%s" %s' % (self.cert_file, e)
                 raise exc.SSLConfigurationError(msg)
             if self.key_file is None:
                 # We support having key and cert in same file
                 try:
                     self.context.use_privatekey_file(self.cert_file)
-                except Exception, e:
+                except Exception as e:
                     msg = ('No key file specified and unable to load key '
                            'from "%s" %s' % (self.cert_file, e))
                     raise exc.SSLConfigurationError(msg)
@@ -319,14 +319,14 @@
         if self.key_file:
             try:
                 self.context.use_privatekey_file(self.key_file)
-            except Exception, e:
+            except Exception as e:
                 msg = 'Unable to load key from "%s" %s' % (self.key_file, e)
                 raise exc.SSLConfigurationError(msg)
 
         if self.cacert:
             try:
                 self.context.load_verify_locations(self.cacert)
-            except Exception, e:
+            except Exception as e:
                 msg = 'Unable to load CA from "%s"' % (self.cacert, e)
                 raise exc.SSLConfigurationError(msg)
         else:
diff --git a/tempest/common/log.py b/tempest/common/log.py
index 9b35723..2159bfe 100644
--- a/tempest/common/log.py
+++ b/tempest/common/log.py
@@ -55,7 +55,7 @@
     log_config = os.path.join(conf_dir, conf_file)
     try:
         logging.config.fileConfig(log_config)
-    except ConfigParser.Error, exc:
+    except ConfigParser.Error as exc:
         raise cfg.ConfigFileParseError(log_config, str(exc))
     return True
 
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 531dfc8..e94455d 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -144,8 +144,8 @@
             try:
                 auth_data = json.loads(resp_body)['access']
                 token = auth_data['token']['id']
-            except Exception, e:
-                print "Failed to obtain token for user: %s" % e
+            except Exception as e:
+                print("Failed to obtain token for user: %s" % e)
                 raise
 
             mgmt_url = None
diff --git a/tempest/config.py b/tempest/config.py
index 7196078..8795b33 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -330,6 +330,12 @@
     cfg.StrOpt('backend2_name',
                default='BACKEND_2',
                help="Name of the backend2 (must be declared in cinder.conf)"),
+    cfg.StrOpt('storage_protocol',
+               default='iSCSI',
+               help='Backend protocol to target when creating volume types'),
+    cfg.StrOpt('vendor_name',
+               default='Open Source',
+               help='Backend vendor to target when creating volume types'),
 ]
 
 
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index f0b1c28..dac77a2 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -88,7 +88,7 @@
                 obj_size = obj.tell()
                 obj.seek(0)
                 return obj_size
-            except IOError, e:
+            except IOError as e:
                 if e.errno == errno.ESPIPE:
                     # Illegal seek. This means the user is trying
                     # to pipe image data to the client, e.g.
diff --git a/tempest/whitebox/manager.py b/tempest/whitebox/manager.py
index aa58ab6..3bd057c 100644
--- a/tempest/whitebox/manager.py
+++ b/tempest/whitebox/manager.py
@@ -128,7 +128,7 @@
             meta = MetaData()
             meta.reflect(bind=engine)
 
-        except Exception, e:
+        except Exception as e:
             raise exceptions.SQLException(message=e)
 
         return connection, meta
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index 3129484..0ce1500 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -110,7 +110,7 @@
 
 
 def usage():
-    print """
+    print("""
 Usage: find_stack_traces.py <logurl>
 
 Hunts for stack traces in a devstack run. Must provide it a base log url
@@ -118,20 +118,20 @@
 
 Returns a report listing stack traces out of the various files where
 they are found.
-"""
+""")
     sys.exit(0)
 
 
 def print_stats(items, fname, verbose=False):
     errors = len(filter(lambda x: x.level == "ERROR", items))
     traces = len(filter(lambda x: x.level == "TRACE", items))
-    print "%d ERRORS found in %s" % (errors, fname)
-    print "%d TRACES found in %s" % (traces, fname)
+    print("%d ERRORS found in %s" % (errors, fname))
+    print("%d TRACES found in %s" % (traces, fname))
 
     if verbose:
         for item in items:
-            print item
-        print "\n\n"
+            print(item)
+        print("\n\n")
 
 
 def main():
diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py
index fd9076f..42a44e8 100644
--- a/tools/install_venv_common.py
+++ b/tools/install_venv_common.py
@@ -1,6 +1,6 @@
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 
-# Copyright 2013 OpenStack, LLC
+# Copyright 2013 OpenStack Foundation
 # Copyright 2013 IBM Corp.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -18,10 +18,15 @@
 """Provides methods needed by installation script for OpenStack development
 virtual environments.
 
+Since this script is used to bootstrap a virtualenv from the system's Python
+environment, it should be kept strictly compatible with Python 2.6.
+
 Synced in from openstack-common
 """
 
-import argparse
+from __future__ import print_function
+
+import optparse
 import os
 import subprocess
 import sys
@@ -39,7 +44,7 @@
         self.project = project
 
     def die(self, message, *args):
-        print >> sys.stderr, message % args
+        print(message % args, file=sys.stderr)
         sys.exit(1)
 
     def check_python_version(self):
@@ -86,20 +91,20 @@
         virtual environment.
         """
         if not os.path.isdir(self.venv):
-            print 'Creating venv...',
+            print('Creating venv...', end=' ')
             if no_site_packages:
                 self.run_command(['virtualenv', '-q', '--no-site-packages',
                                  self.venv])
             else:
                 self.run_command(['virtualenv', '-q', self.venv])
-            print 'done.'
-            print 'Installing pip in venv...',
+            print('done.')
+            print('Installing pip in venv...', end=' ')
             if not self.run_command(['tools/with_venv.sh', 'easy_install',
                                     'pip>1.0']).strip():
                 self.die("Failed to install pip.")
-            print 'done.'
+            print('done.')
         else:
-            print "venv already exists..."
+            print("venv already exists...")
             pass
 
     def pip_install(self, *args):
@@ -108,7 +113,7 @@
                          redirect_output=False)
 
     def install_dependencies(self):
-        print 'Installing dependencies with pip (this can take a while)...'
+        print('Installing dependencies with pip (this can take a while)...')
 
         # First things first, make sure our venv has the latest pip and
         # distribute.
@@ -131,12 +136,12 @@
 
     def parse_args(self, argv):
         """Parses command-line arguments."""
-        parser = argparse.ArgumentParser()
-        parser.add_argument('-n', '--no-site-packages',
-                            action='store_true',
-                            help="Do not inherit packages from global Python "
-                                 "install")
-        return parser.parse_args(argv[1:])
+        parser = optparse.OptionParser()
+        parser.add_option('-n', '--no-site-packages',
+                          action='store_true',
+                          help="Do not inherit packages from global Python "
+                               "install")
+        return parser.parse_args(argv[1:])[0]
 
 
 class Distro(InstallVenv):
@@ -150,12 +155,12 @@
             return
 
         if self.check_cmd('easy_install'):
-            print 'Installing virtualenv via easy_install...',
+            print('Installing virtualenv via easy_install...', end=' ')
             if self.run_command(['easy_install', 'virtualenv']):
-                print 'Succeeded'
+                print('Succeeded')
                 return
             else:
-                print 'Failed'
+                print('Failed')
 
         self.die('ERROR: virtualenv not found.\n\n%s development'
                  ' requires virtualenv, please install it using your'
@@ -180,19 +185,16 @@
         return self.run_command_with_code(['rpm', '-q', pkg],
                                           check_exit_code=False)[1] == 0
 
-    def yum_install(self, pkg, **kwargs):
-        print "Attempting to install '%s' via yum" % pkg
-        self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
-
     def apply_patch(self, originalfile, patchfile):
-        self.run_command(['patch', originalfile, patchfile])
+        self.run_command(['patch', '-N', originalfile, patchfile],
+                         check_exit_code=False)
 
     def install_virtualenv(self):
         if self.check_cmd('virtualenv'):
             return
 
         if not self.check_pkg('python-virtualenv'):
-            self.yum_install('python-virtualenv', check_exit_code=False)
+            self.die("Please install 'python-virtualenv'.")
 
         super(Fedora, self).install_virtualenv()
 
@@ -205,12 +207,13 @@
         This can be removed when the fix is applied upstream.
 
         Nova: https://bugs.launchpad.net/nova/+bug/884915
-        Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
+        Upstream: https://bitbucket.org/eventlet/eventlet/issue/89
+        RHEL: https://bugzilla.redhat.com/958868
         """
 
         # Install "patch" program if it's not there
         if not self.check_pkg('patch'):
-            self.yum_install('patch')
+            self.die("Please install 'patch'.")
 
         # Apply the eventlet patch
         self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index c7b0033..1ed6961 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -118,8 +118,8 @@
 
     unskips = sorted(set(unskips))
     if unskips:
-        print "The following bugs have been fixed and the corresponding skips"
-        print "should be removed from the test cases:"
-        print
+        print("The following bugs have been fixed and the corresponding skips")
+        print("should be removed from the test cases:")
+        print()
         for bug in unskips:
-            print "  %7s" % bug
+            print("  %7s" % bug)
diff --git a/tools/tempest_coverage.py b/tools/tempest_coverage.py
index 5b926f9..ef2eacd 100755
--- a/tools/tempest_coverage.py
+++ b/tools/tempest_coverage.py
@@ -12,7 +12,7 @@
 #    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
+#    under the License.
 
 import json
 import os
@@ -151,14 +151,14 @@
     elif CLI.command == 'stop':
         resp, body = coverage_client.stop_coverage()
         if not resp['status'] == '200':
-            print 'coverage stop failed with: %s:' % (resp['status'] + ': '
-                                                      + body)
+            print('coverage stop failed with: %s:' % (resp['status'] + ': '
+                                                      + body))
             exit(int(resp['status']))
         path = body['path']
         if CLI.output:
             shutil.copytree(path, CLI.output)
         else:
-            print "Data files located at: %s" % path
+            print("Data files located at: %s" % path)
 
     elif CLI.command == 'report':
         if CLI.xml:
@@ -169,8 +169,8 @@
         else:
             resp, body = coverage_client.report_coverage(file=CLI.filename)
         if not resp['status'] == '200':
-            print 'coverage report failed with: %s:' % (resp['status'] + ': '
-                                                        + body)
+            print('coverage report failed with: %s:' % (resp['status'] + ': '
+                                                        + body))
             exit(int(resp['status']))
         path = body['path']
         if CLI.output:
@@ -182,10 +182,10 @@
         else:
             if not CLI.html:
                 path = os.path.dirname(path)
-            print 'Report files located at: %s' % path
+            print('Report files located at: %s' % path)
 
     else:
-        print 'Invalid command'
+        print('Invalid command')
         exit(1)