Merge "Integrated gate storage/swift: blacklist more tests"
diff --git a/.zuul.yaml b/.zuul.yaml
index 80d49d8..09a5897 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -447,6 +447,11 @@
           USE_PYTHON3: true
 
 - job:
+    name: tempest-full-ussuri-py3
+    parent: tempest-full-py3
+    override-checkout: stable/ussuri
+
+- job:
     name: tempest-full-train-py3
     parent: tempest-full-py3
     override-checkout: stable/train
@@ -457,12 +462,6 @@
     override-checkout: stable/stein
 
 - job:
-    name: tempest-full-rocky-py3
-    parent: tempest-full-py3
-    nodeset: openstack-single-node-xenial
-    override-checkout: stable/rocky
-
-- job:
     name: tempest-tox-plugin-sanity-check
     parent: tox
     description: |
@@ -532,11 +531,11 @@
       run on neutron gate only.
     check:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-networking
     gate:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-networking
 
 - project-template:
@@ -548,11 +547,11 @@
       run on Nova gate only.
     check:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-compute
     gate:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-compute
 
 - project-template:
@@ -564,11 +563,11 @@
       run on Placement gate only.
     check:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-placement
     gate:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-placement
 
 - project-template:
@@ -580,11 +579,11 @@
       run on Cinder and Glance gate only.
     check:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-storage
     gate:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-storage
 
 - project-template:
@@ -596,11 +595,11 @@
       run on swift gate only.
     check:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-object-storage
     gate:
       jobs:
-        - grenade-py3
+        - grenade
         - tempest-integrated-object-storage
 
 - project:
@@ -644,12 +643,12 @@
         - tempest-full-py3-ipv6:
             voting: false
             irrelevant-files: *tempest-irrelevant-files
+        - tempest-full-ussuri-py3:
+            irrelevant-files: *tempest-irrelevant-files
         - tempest-full-train-py3:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-stein-py3:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-rocky-py3:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-multinode-full-py3:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-tox-plugin-sanity-check:
@@ -678,7 +677,7 @@
             irrelevant-files: *tempest-irrelevant-files
         - neutron-grenade-multinode:
             irrelevant-files: *tempest-irrelevant-files
-        - grenade-py3:
+        - grenade:
             irrelevant-files: *tempest-irrelevant-files
         - puppet-openstack-integration-4-scenario001-tempest-centos-7:
             voting: false
@@ -712,7 +711,7 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-py3:
             irrelevant-files: *tempest-irrelevant-files
-        - grenade-py3:
+        - grenade:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-ipv6-only:
             irrelevant-files: *tempest-irrelevant-files-2
@@ -736,9 +735,9 @@
             irrelevant-files: *tempest-irrelevant-files
     periodic-stable:
       jobs:
+        - tempest-full-ussuri-py3
         - tempest-full-train-py3
         - tempest-full-stein-py3
-        - tempest-full-rocky-py3
     periodic:
       jobs:
         - tempest-all
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index a89ad94..2300763 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -1,17 +1,19 @@
-If you would like to contribute to the development of OpenStack, you must
-follow the steps in this page:
+The source repository for this project can be found at:
 
-   https://docs.openstack.org/infra/manual/developers.html
+   https://opendev.org/openstack/tempest
 
-If you already have a good understanding of how the system works and your
-OpenStack accounts are set up, you can skip to the development workflow
-section of this documentation to learn how changes to OpenStack should be
-submitted for review via the Gerrit tool:
+Pull requests submitted through GitHub are not monitored.
 
-   https://docs.openstack.org/infra/manual/developers.html#development-workflow
+To start contributing to OpenStack, follow the steps in the contribution guide
+to set up and use Gerrit:
 
-Pull requests submitted through GitHub will be ignored.
+   https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
 
-Bugs should be filed on Launchpad, not GitHub:
+Bugs should be filed on Launchpad:
 
    https://bugs.launchpad.net/tempest
+
+For more specific information about contributing to this repository, see the
+Tempest contributor guide:
+
+   https://docs.openstack.org/tempest/latest/contributor/contributing.html
diff --git a/doc/source/contributor/contributing.rst b/doc/source/contributor/contributing.rst
new file mode 100644
index 0000000..9c79a1f
--- /dev/null
+++ b/doc/source/contributor/contributing.rst
@@ -0,0 +1,59 @@
+============================
+So You Want to Contribute...
+============================
+
+For general information on contributing to OpenStack, please check out the
+`contributor guide <https://docs.openstack.org/contributors/>`_ to get started.
+It covers all the basics that are common to all OpenStack projects: the accounts
+you need, the basics of interacting with our Gerrit review system, how we
+communicate as a community, etc.
+
+Below will cover the more project specific information you need to get started
+with Tempest.
+
+Communication
+~~~~~~~~~~~~~
+* IRC channel ``#openstack-qa`` at FreeNode
+* Mailing list (prefix subjects with ``[qa]`` for faster responses)
+  http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
+
+Contacting the Core Team
+~~~~~~~~~~~~~~~~~~~~~~~~
+Please refer to the `Tempest Core Team
+<https://review.opendev.org/#/admin/groups/42,members>`_ contacts.
+
+New Feature Planning
+~~~~~~~~~~~~~~~~~~~~
+If you want to propose a new feature please read `Feature Proposal Process`_
+Tempest features are tracked on `Launchpad BP <https://blueprints.launchpad.net/tempest>`_.
+
+Task Tracking
+~~~~~~~~~~~~~
+We track our tasks in `Launchpad <https://bugs.launchpad.net/tempest>`_.
+
+If you're looking for some smaller, easier work item to pick up and get started
+on, search for the 'low-hanging-fruit' tag.
+
+Reporting a Bug
+~~~~~~~~~~~~~~~
+You found an issue and want to make sure we are aware of it? You can do so on
+`Launchpad <https://bugs.launchpad.net/tempest/+filebug>`__.
+More info about Launchpad usage can be found on `OpenStack docs page
+<https://docs.openstack.org/contributors/common/task-tracking.html#launchpad>`_
+
+Getting Your Patch Merged
+~~~~~~~~~~~~~~~~~~~~~~~~~
+All changes proposed to the Tempest require two ``Code-Review +2`` votes from
+Tempest core reviewers before one of the core reviewers can approve the patch by
+giving ``Workflow +1`` vote. More detailed guidelines for reviewers are available
+at :doc:`../REVIEWING`.
+
+Project Team Lead Duties
+~~~~~~~~~~~~~~~~~~~~~~~~
+All common PTL duties are enumerated in the `PTL guide
+<https://docs.openstack.org/project-team-guide/ptl.html>`_.
+
+The Release Process for QA is documented in `QA Release Process
+<https://wiki.openstack.org/wiki/QA/releases>`_.
+
+.. _Feature Proposal Process: https://wiki.openstack.org/wiki/QA#Feature_Proposal_.26_Design_discussions
diff --git a/doc/source/index.rst b/doc/source/index.rst
index a72e783..93fcc3a 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -56,6 +56,16 @@
 
    supported_version
 
+For Contributors
+================
+
+* If you are a new contributor to Tempest please refer: :doc:`contributor/contributing`
+
+.. toctree::
+   :hidden:
+
+   contributor/contributing
+
 Developers Guide
 ================
 
diff --git a/doc/source/supported_version.rst b/doc/source/supported_version.rst
index 4f65fd4..62faa1f 100644
--- a/doc/source/supported_version.rst
+++ b/doc/source/supported_version.rst
@@ -11,7 +11,6 @@
 
 * Train
 * Stein
-* Rocky
 
 For older OpenStack Release:
 
diff --git a/releasenotes/notes/account_generator-6eb03f664a448c35.yaml b/releasenotes/notes/account_generator-6eb03f664a448c35.yaml
new file mode 100644
index 0000000..ade632f
--- /dev/null
+++ b/releasenotes/notes/account_generator-6eb03f664a448c35.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+  - |
+    Remove the deprecated CLI ``tempest-account-generator`` in favor of
+    ``tempest account-generator`` command.
+    You can use ``tempest account-generator`` CLI to generate the accounts
+    yaml file.
diff --git a/releasenotes/notes/tempest-ussuri-release-72b5770a3b97678f.yaml b/releasenotes/notes/tempest-ussuri-release-72b5770a3b97678f.yaml
new file mode 100644
index 0000000..37e56bb
--- /dev/null
+++ b/releasenotes/notes/tempest-ussuri-release-72b5770a3b97678f.yaml
@@ -0,0 +1,16 @@
+---
+prelude: >
+    This release is to tag the Tempest for OpenStack Ussuri release.
+    This release marks the start of Ussuri release support in Tempest.
+    After this release, Tempest will support below OpenStack Releases:
+
+    * Ussuri
+    * Train
+    * Stein
+
+    Current development of Tempest is for OpenStack Victoria development
+    cycle. Every Tempest commit is also tested against master during
+    the Victoria cycle. However, this does not necessarily mean that using
+    Tempest as of this tag will work against a Ussuri (or future release)
+    cloud.
+    To be on safe side, use this tag to test the OpenStack Ussuri release.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index bfd8b2d..d8702f9 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v24.0.0
    v23.0.0
    v22.1.0
    v22.0.0
diff --git a/releasenotes/source/v24.0.0.rst b/releasenotes/source/v24.0.0.rst
new file mode 100644
index 0000000..8131975
--- /dev/null
+++ b/releasenotes/source/v24.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v24.0.0 Release Notes
+=====================
+
+.. release-notes:: 24.0.0 Release Notes
+   :version: 24.0.0
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index 91b0b5f..3643edb 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -1,5 +1,8 @@
 Run Tempest
 
+The result of the tempest run is stored in the `tempest_run_result`
+variable (through the `register` statement).
+
 **Role Variables**
 
 .. zuul:rolevar:: devstack_base_dir
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 8686f9a..1de3725 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -27,7 +27,8 @@
 
 - name: Use stable branch upper-constraints till stable/rocky
   set_fact:
-    tempest_tox_environment: "{{ tempest_tox_environment | combine({'UPPER_CONSTRAINTS_FILE': stable_constraints_file}) }}"
+    # TOX_CONSTRAINTS_FILE is new name, UPPER_CONSTRAINTS_FILE is old one, best to set both
+    tempest_tox_environment: "{{ tempest_tox_environment | combine({'UPPER_CONSTRAINTS_FILE': stable_constraints_file}) | combine({'TOX_CONSTRAINTS_FILE': stable_constraints_file}) }}"
   when: target_branch in ["stable/ocata", "stable/pike", "stable/queens", "stable/rocky"]
 
 - name: Set OS_TEST_TIMEOUT if requested
@@ -55,6 +56,7 @@
             --black-regex={{tempest_black_regex|quote}}
   args:
     chdir: "{{devstack_base_dir}}/tempest"
+  register: tempest_run_result
   become: true
   become_user: tempest
   environment: "{{ tempest_tox_environment }}"
diff --git a/setup.cfg b/setup.cfg
index 04511e1..f57a805 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -28,7 +28,6 @@
 
 [entry_points]
 console_scripts =
-    tempest-account-generator = tempest.cmd.account_generator:main
     tempest = tempest.cmd.main:main
     skip-tracker = tempest.lib.cmd.skip_tracker:main
     check-uuid = tempest.lib.cmd.check_uuid:run
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index eab2a8d..74570ce 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -109,6 +109,7 @@
         if CONF.service_available.cinder:
             cls.volumes_client = cls.os_primary.volumes_client_latest
             cls.attachments_client = cls.os_primary.attachments_client_latest
+            cls.snapshots_client = cls.os_primary.snapshots_client_latest
         if CONF.service_available.glance:
             if CONF.image_feature_enabled.api_v1:
                 cls.images_client = cls.os_primary.image_client
@@ -578,6 +579,25 @@
                                                 volume['id'], 'in-use')
         return attachment
 
+    def create_volume_snapshot(self, volume_id, name=None, description=None,
+                               metadata=None, force=False):
+        name = name or data_utils.rand_name(
+            self.__class__.__name__ + '-snapshot')
+        snapshot = self.snapshots_client.create_snapshot(
+            volume_id=volume_id,
+            force=force,
+            display_name=name,
+            description=description,
+            metadata=metadata)['snapshot']
+        self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
+                        snapshot['id'])
+        self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                snapshot['id'], 'available')
+        snapshot = self.snapshots_client.show_snapshot(
+            snapshot['id'])['snapshot']
+        return snapshot
+
     def assert_flavor_equal(self, flavor_id, server_flavor):
         """Check whether server_flavor equals to flavor.
 
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 1f7eb7b..8879369 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -12,6 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from json import decoder as json_decoder
+
 from oslo_log import log as logging
 from oslo_serialization import jsonutils as json
 
@@ -110,7 +112,11 @@
     max_microversion = '2.32'
 
     def verify_device_metadata(self, md_json):
-        md_dict = json.loads(md_json)
+        try:
+            md_dict = json.loads(md_json)
+        except (json_decoder.JSONDecodeError, TypeError):
+            return False
+
         for d in md_dict['devices']:
             if d['type'] == 'nic':
                 if d['mac'] == self.port1['mac_address']:
@@ -310,7 +316,11 @@
             raise cls.skipException('Metadata API must be enabled')
 
     def verify_device_metadata(self, md_json):
-        md_dict = json.loads(md_json)
+        try:
+            md_dict = json.loads(md_json)
+        except (json_decoder.JSONDecodeError, TypeError):
+            return False
+
         found_devices = [d['tags'][0] for d in md_dict['devices']
                          if d.get('tags')]
         try:
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 1ac9516..3fa859e 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -25,6 +25,7 @@
 
 
 class ServerRescueTestBase(base.BaseV2ComputeTest):
+    create_default_network = True
 
     @classmethod
     def skip_checks(cls):
@@ -105,11 +106,12 @@
                                                   name=sg['name'])
 
 
-class ServerStableDeviceRescueTest(base.BaseV2ComputeTest):
+class BaseServerStableDeviceRescueTest(base.BaseV2ComputeTest):
+    create_default_network = True
 
     @classmethod
     def skip_checks(self):
-        super(ServerStableDeviceRescueTest, self).skip_checks()
+        super(BaseServerStableDeviceRescueTest, self).skip_checks()
         if not CONF.compute_feature_enabled.rescue:
             msg = "Server rescue not available."
             raise self.skipException(msg)
@@ -118,8 +120,15 @@
             raise self.skipException(msg)
 
     def _create_server_and_rescue_image(self, hw_rescue_device=None,
-                                        hw_rescue_bus=None):
-        server_id = self.create_test_server(wait_until='ACTIVE')['id']
+                                        hw_rescue_bus=None,
+                                        block_device_mapping_v2=None):
+        if block_device_mapping_v2:
+            server_id = self.create_test_server(
+                wait_until='ACTIVE',
+                block_device_mapping_v2=block_device_mapping_v2)['id']
+        else:
+            server_id = self.create_test_server(wait_until='ACTIVE')['id']
+
         image_id = self.create_image_from_server(server_id,
                                                  wait_until='ACTIVE')['id']
         if hw_rescue_bus:
@@ -141,6 +150,9 @@
         waiters.wait_for_server_status(
             self.servers_client, server_id, 'ACTIVE')
 
+
+class ServerStableDeviceRescueTest(BaseServerStableDeviceRescueTest):
+
     @decorators.idempotent_id('947004c3-e8ef-47d9-9f00-97b74f9eaf96')
     def test_stable_device_rescue_cdrom_ide(self):
         server_id, rescue_image_id = self._create_server_and_rescue_image(
@@ -175,3 +187,49 @@
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'in-use')
         self._test_stable_device_rescue(server_id, rescue_image_id)
+
+
+class ServerBootFromVolumeStableRescueTest(BaseServerStableDeviceRescueTest):
+
+    min_microversion = '2.87'
+
+    @decorators.idempotent_id('48f123cb-922a-4065-8db6-b9a9074a556b')
+    def test_stable_device_rescue_bfv_blank_volume(self):
+        block_device_mapping_v2 = [{
+            "boot_index": "0",
+            "source_type": "blank",
+            "volume_size": "1",
+            "destination_type": "volume"}]
+        server_id, rescue_image_id = self._create_server_and_rescue_image(
+            hw_rescue_device='disk', hw_rescue_bus='virtio',
+            block_device_mapping_v2=block_device_mapping_v2)
+        self._test_stable_device_rescue(server_id, rescue_image_id)
+
+    @decorators.idempotent_id('e4636333-c928-40fc-98b7-70a23eef4224')
+    def test_stable_device_rescue_bfv_image_volume(self):
+        block_device_mapping_v2 = [{
+            "boot_index": "0",
+            "source_type": "image",
+            "volume_size": "1",
+            "uuid": CONF.compute.image_ref,
+            "destination_type": "volume"}]
+        server_id, rescue_image_id = self._create_server_and_rescue_image(
+            hw_rescue_device='disk', hw_rescue_bus='virtio',
+            block_device_mapping_v2=block_device_mapping_v2)
+        self._test_stable_device_rescue(server_id, rescue_image_id)
+
+    @decorators.idempotent_id('7fcc5d2c-130e-4750-95f5-7343f9d0a2f3')
+    def test_stable_device_rescue_bfv_snapshot_volume(self):
+        volume_id = self.create_volume()['id']
+        self.volumes_client.set_bootable_volume(volume_id, bootable=True)
+        snapshot_id = self.create_volume_snapshot(volume_id)['id']
+        block_device_mapping_v2 = [{
+            "boot_index": "0",
+            "source_type": "snapshot",
+            "volume_size": "1",
+            "uuid": snapshot_id,
+            "destination_type": "volume"}]
+        server_id, rescue_image_id = self._create_server_and_rescue_image(
+            hw_rescue_device='disk', hw_rescue_bus='virtio',
+            block_device_mapping_v2=block_device_mapping_v2)
+        self._test_stable_device_rescue(server_id, rescue_image_id)
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 1535786..b230615 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -96,7 +96,7 @@
 To see help on specific argument, please do: ``tempest account-generator
 [OPTIONS] <accounts_file.yaml> -h``.
 """
-import argparse
+
 import os
 import traceback
 
@@ -248,21 +248,6 @@
                         help='Output accounts yaml file')
 
 
-def get_options():
-    usage_string = ('tempest account-generator [-h] <ARG> ...\n\n'
-                    'To see help on specific argument, do:\n'
-                    'tempest account-generator <ARG> -h')
-    parser = argparse.ArgumentParser(
-        description=DESCRIPTION,
-        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
-        usage=usage_string
-    )
-
-    _parser_add_args(parser)
-    opts = parser.parse_args()
-    return opts
-
-
 class TempestAccountGenerator(command.Command):
 
     def get_parser(self, prog_name):
@@ -272,7 +257,19 @@
 
     def take_action(self, parsed_args):
         try:
-            main(parsed_args)
+            if parsed_args.config_file:
+                config.CONF.set_config_path(parsed_args.config_file)
+            setup_logging()
+            resources = []
+            for count in range(parsed_args.concurrency):
+                # Use N different cred_providers to obtain different
+                # sets of creds
+                cred_provider = get_credential_provider(parsed_args)
+                resources.extend(generate_resources(cred_provider,
+                                                    parsed_args.admin))
+            dump_accounts(resources, parsed_args.identity_version,
+                          parsed_args.accounts)
+
         except Exception:
             LOG.exception("Failure generating test accounts.")
             traceback.print_exc()
@@ -280,26 +277,3 @@
 
     def get_description(self):
         return DESCRIPTION
-
-
-def main(opts=None):
-    log_warning = False
-    if not opts:
-        log_warning = True
-        opts = get_options()
-    if opts.config_file:
-        config.CONF.set_config_path(opts.config_file)
-    setup_logging()
-    if log_warning:
-        LOG.warning("Use of: 'tempest-account-generator' is deprecated, "
-                    "please use: 'tempest account-generator'")
-    resources = []
-    for count in range(opts.concurrency):
-        # Use N different cred_providers to obtain different sets of creds
-        cred_provider = get_credential_provider(opts)
-        resources.extend(generate_resources(cred_provider, opts.admin))
-    dump_accounts(resources, opts.identity_version, opts.accounts)
-
-
-if __name__ == "__main__":
-    main()
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index cd85ede..edb9d16 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -400,9 +400,24 @@
         """Upgrade the HTTP connection to a WebSocket and verify."""
         # It is possible to pass the path as a query parameter in the request,
         # so use it if present
+        # Given noVNC format
+        # https://x.com/vnc_auto.html?path=%3Ftoken%3Dxxx,
+        # url format is
+        # ParseResult(scheme='https', netloc='x.com',
+        # path='/vnc_auto.html', params='',
+        # query='path=%3Ftoken%3Dxxx', fragment='').
+        # qparams format is {'path': ['?token=xxx']}
         qparams = urlparse.parse_qs(url.query)
-        path = qparams['path'][0] if 'path' in qparams else '/websockify'
-        reqdata = 'GET %s HTTP/1.1\r\n' % path
+        # according to references
+        # https://docs.python.org/3/library/urllib.parse.html
+        # https://tools.ietf.org/html/rfc3986#section-3.4
+        # qparams['path'][0] format is '?token=xxx' without / prefix
+        # remove / in /websockify to comply to references.
+        path = qparams['path'][0] if 'path' in qparams else 'websockify'
+        # Fix websocket request format by adding / prefix.
+        # Updated request format: GET /?token=xxx HTTP/1.1
+        # or GET /websockify HTTP/1.1
+        reqdata = 'GET /%s HTTP/1.1\r\n' % path
         reqdata += 'Host: %s' % url.hostname
         # Add port only if we have one specified
         if url.port:
diff --git a/tempest/config.py b/tempest/config.py
index 1699c7d..204d977 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -833,7 +833,8 @@
                help="User name used to authenticate to an instance."),
     cfg.StrOpt('image_ssh_password',
                default="password",
-               help="Password used to authenticate to an instance."),
+               help="Password used to authenticate to an instance.",
+               secret=True),
     cfg.StrOpt('ssh_shell_prologue',
                default="set -eu -o pipefail; PATH=$$PATH:/sbin:/usr/sbin;",
                help="Shell fragments to use before executing a command "
diff --git a/tools/tempest-plugin-sanity.sh b/tools/tempest-plugin-sanity.sh
index b484a41..2ff4aea 100644
--- a/tools/tempest-plugin-sanity.sh
+++ b/tools/tempest-plugin-sanity.sh
@@ -60,8 +60,8 @@
     fi
 }
 
-: ${UPPER_CONSTRAINTS_FILE:="https://releases.openstack.org/constraints/upper/master"}
-DEPS="-c${UPPER_CONSTRAINTS_FILE}"
+: ${TOX_CONSTRAINTS_FILE:="https://releases.openstack.org/constraints/upper/master"}
+DEPS="-c${TOX_CONSTRAINTS_FILE}"
 
 # function to create virtualenv to perform sanity operation
 function prepare_workspace {