Merge "Connect to default ports if none are specified"
diff --git a/.stestr.conf b/.stestr.conf
index e3201c1..818c743 100644
--- a/.stestr.conf
+++ b/.stestr.conf
@@ -1,4 +1,3 @@
 [DEFAULT]
 test_path=./tempest/test_discover
 group_regex=([^\.]*\.)*
-
diff --git a/.testr.conf b/.testr.conf
deleted file mode 100644
index 95a4fb4..0000000
--- a/.testr.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-[DEFAULT]
-test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
-             OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
-             OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
-             OS_TEST_LOCK_PATH=${OS_TEST_LOCK_PATH:-${TMPDIR:-'/tmp'}} \
-             ${PYTHON:-python} -m subunit.run discover -t ${OS_TOP_LEVEL:-./} ${OS_TEST_PATH:-./tempest/test_discover} $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
-group_regex=([^\.]*\.)*
diff --git a/.zuul.yaml b/.zuul.yaml
index fbe158a..4f17959 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -29,11 +29,11 @@
 - job:
     name: tempest-full
     parent: devstack-tempest
-    # This currently only works on the master branch.
-    # NOTE(andreaf) Only run on master for now.
-    # The negative lookup is redudant but it's a
-    # reminder that we don't want the job running there.
-    branches: ^(?!driverfixes/)master$
+    # This currently works from stable/pike on.
+    branches:
+      - master
+      - stable/queens
+      - stable/pike
     description: |
       Base integration test with Neutron networking and py27.
       Former names for this job where:
@@ -47,7 +47,9 @@
 - job:
     name: tempest-full-py3
     parent: devstack-tempest
-    branches: ^(?!driverfixes/)master$
+    branches:
+      - master
+      - stable/queens
     description: |
       Base integration test with Neutron networking and py3.
       Former names for this job where:
@@ -77,6 +79,11 @@
     override-checkout: stable/queens
 
 - job:
+    name: tempest-full-pike
+    parent: tempest-full
+    override-checkout: stable/pike
+
+- job:
     name: tempest-tox-plugin-sanity-check
     parent: tox
     description: |
@@ -86,6 +93,13 @@
       tox_envlist: plugin-sanity-check
     voting: false
     timeout: 5000
+    irrelevant-files:
+      - ^.*\.rst$
+      - ^doc/.*$
+      - ^etc/.*$
+      - ^releasenotes/.*$
+      - ^tempest/hacking/.*$
+      - ^tempest/tests/.*$
     required-projects:
       - openstack/almanach
       - openstack/aodh
@@ -162,7 +176,6 @@
               - ^.zuul.yaml$
         - nova-multiattach
         - tempest-full-queens:
-            voting: false
             irrelevant-files:
               - ^(test-|)requirements.txt$
               - ^.*\.rst$
@@ -173,7 +186,16 @@
               - ^tempest/hacking/.*$
               - ^tempest/tests/.*$
         - tempest-full-queens-py3:
-            voting: false
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - tempest-full-pike:
             irrelevant-files:
               - ^(test-|)requirements.txt$
               - ^.*\.rst$
@@ -191,3 +213,4 @@
       jobs:
         - tempest-full-queens
         - tempest-full-queens-py3
+        - tempest-full-pike
diff --git a/README.rst b/README.rst
index 242f4d5..f8059ae 100644
--- a/README.rst
+++ b/README.rst
@@ -2,7 +2,7 @@
 Team and repository tags
 ========================
 
-.. image:: https://governance.openstack.org/badges/tempest.svg
+.. image:: https://governance.openstack.org/tc/badges/tempest.svg
     :target: https://governance.openstack.org/tc/reference/tags/index.html
 
 .. Change things from this point on
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 555b7d2..597b54e 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -1,6 +1,6 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-openstackdocstheme>=1.17.0 # Apache-2.0
+openstackdocstheme>=1.18.1 # Apache-2.0
 reno>=2.5.0 # Apache-2.0
 sphinx!=1.6.6,>=1.6.2 # BSD
diff --git a/playbooks/post-tempest.yaml b/playbooks/post-tempest.yaml
index ab7a1bb..4dde2c9 100644
--- a/playbooks/post-tempest.yaml
+++ b/playbooks/post-tempest.yaml
@@ -1,7 +1,6 @@
 - hosts: all
   become: true
   roles:
-    - role: process-test-results
-      test_results_dir: '{{ devstack_base_dir }}/tempest'
-      tox_envdir: tempest
+    - role: fetch-subunit-output
+      zuul_work_dir: '{{ devstack_base_dir }}/tempest'
     - role: process-stackviz
diff --git a/releasenotes/notes/add-port-profile-config-option-2610b2fa67027960.yaml b/releasenotes/notes/add-port-profile-config-option-2610b2fa67027960.yaml
new file mode 100644
index 0000000..b54ee8b
--- /dev/null
+++ b/releasenotes/notes/add-port-profile-config-option-2610b2fa67027960.yaml
@@ -0,0 +1,11 @@
+---
+prelude: >
+    When using OVS HW offload feature we need to create
+    Neutron port with a certain capability. This is done
+    by creating Neutron port with binding profile. To be
+    able to test this we need profile capability support
+    in Tempest as well.
+features:
+  - A new config option 'port_profile' is added to the section
+    'network' to specify capabilities of the port.
+    By default this is set to {}.
diff --git a/releasenotes/notes/start-of-queens-support-fea9051ba1d85fc7.yaml b/releasenotes/notes/start-of-queens-support-fea9051ba1d85fc7.yaml
new file mode 100644
index 0000000..10b2300
--- /dev/null
+++ b/releasenotes/notes/start-of-queens-support-fea9051ba1d85fc7.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+    This release marks the start of Queens release support in Tempest.
+    This release also marks the end of support for Newton in Tempest.
+other:
+    - OpenStack Releases supported after this release are **Queens**, **Pike**,
+      and **Ocata**.
+
+      The release under current development of this tag is Rocky, meaning
+      that every Tempest commit is also tested against master during the Rocky
+      cycle. However, this does not necessarily mean that using Tempest as of
+      this tag will work against a Rocky (or future release) cloud.
diff --git a/releasenotes/notes/switch-to-stestr-8c9f834b3f5a55d8.yaml b/releasenotes/notes/switch-to-stestr-8c9f834b3f5a55d8.yaml
new file mode 100644
index 0000000..9e2f1ba
--- /dev/null
+++ b/releasenotes/notes/switch-to-stestr-8c9f834b3f5a55d8.yaml
@@ -0,0 +1,13 @@
+---
+features:
+- The Tempest CLI commands have switched from calling testrepository internally
+  to use stestr instead. This means that all of the features and bug fixes from
+  moving to stestr are available to the tempest commands.
+
+upgrade:
+- Tempest CLI commands will no long rely on anything from testr. This means any
+  data in existing testr internals that were being exposed are no longer
+  present. For example things like the .testr directories will be silently
+  ignored. There is a potential incompatibility for existing users who are
+  relying on test results being stored by testr. Anything relying on previous
+  testr behavior will need to be updated to handle stestr.
diff --git a/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml b/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml
new file mode 100644
index 0000000..265853d
--- /dev/null
+++ b/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    Adds a new CLI arg in tempest run, --black-regex, which is a regex to
+    exclude the tests that match it.
+fixes:
+  - |
+    Fixes tempest run CLI args mutually exclusive behavior which should not
+    be the case anymore (Bug#1751201).
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index df1de46..d968a44 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
     :maxdepth: 1
 
     unreleased
+    v18.0.0
     v17.0.0
     v16.1.0
     v16.0.0
diff --git a/releasenotes/source/v18.0.0.rst b/releasenotes/source/v18.0.0.rst
new file mode 100644
index 0000000..aa05db3
--- /dev/null
+++ b/releasenotes/source/v18.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v18.0.0 Release Notes
+=====================
+
+.. release-notes:: 18.0.0 Release Notes
+   :version: 18.0.0
diff --git a/requirements.txt b/requirements.txt
index c02cd05..a74042f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,19 +7,18 @@
 testtools>=2.2.0 # MIT
 paramiko>=2.0.0 # LGPLv2.1+
 netaddr>=0.7.18 # BSD
-testrepository>=0.0.18 # Apache-2.0/BSD
 oslo.concurrency>=3.25.0 # Apache-2.0
 oslo.config>=5.1.0 # Apache-2.0
 oslo.log>=3.36.0 # Apache-2.0
+stestr>=1.0.0 # Apache-2.0
 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
 oslo.utils>=3.33.0 # Apache-2.0
 six>=1.10.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
-PyYAML>=3.10 # MIT
+PyYAML>=3.12 # MIT
 python-subunit>=1.0.0 # Apache-2.0/BSD
 stevedore>=1.20.0 # Apache-2.0
 PrettyTable<0.8,>=0.7.1 # BSD
-os-testr>=1.0.0 # Apache-2.0
 urllib3>=1.21.1 # MIT
 debtcollector>=1.2.0 # Apache-2.0
 unittest2>=1.1.0 # BSD
diff --git a/roles/process-stackviz/README.rst b/roles/process-stackviz/README.rst
index 54c217b..a8447d2 100644
--- a/roles/process-stackviz/README.rst
+++ b/roles/process-stackviz/README.rst
@@ -16,7 +16,7 @@
    The stage directory where the input data can be found and
    the output will be produced.
 
-.. zuul:rolevar:: test_results_stage_name
-   :default: test_results
+.. zuul:rolevar:: zuul_work_dir
+   :default: {{ devstack_base_dir }}/tempest
 
-   The name of the subunit file to be used as input.
+   Directory to work in. It has to be a fully qualified path.
diff --git a/roles/process-stackviz/defaults/main.yaml b/roles/process-stackviz/defaults/main.yaml
index c6a64d1..f3bc32b 100644
--- a/roles/process-stackviz/defaults/main.yaml
+++ b/roles/process-stackviz/defaults/main.yaml
@@ -1,3 +1,3 @@
 devstack_base_dir: /opt/stack
 stage_dir: "{{ ansible_user_dir }}"
-test_results_stage_name: test_results
+zuul_work_dir: "{{ devstack_base_dir }}/tempest"
diff --git a/roles/process-stackviz/tasks/main.yaml b/roles/process-stackviz/tasks/main.yaml
index 09de606..82f8f3d 100644
--- a/roles/process-stackviz/tasks/main.yaml
+++ b/roles/process-stackviz/tasks/main.yaml
@@ -9,11 +9,11 @@
 
 - name: Check if subunit data exists
   stat:
-    path: "{{ stage_dir }}/{{ test_results_stage_name }}.subunit"
+    path: "{{ zuul_work_dir }}/testrepository.subunit"
   register: subunit_input
 
 - debug:
-    msg: "Subunit file could not be found at {{ stage_dir }}/{{ test_results_stage_name }}.subunit"
+    msg: "Subunit file could not be found at {{ zuul_work_dir }}/testrepository.subunit"
   when: not subunit_input.stat.exists
 
 - name: Install stackviz
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index 33dcce9..384ca38 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -29,6 +29,12 @@
                # Line with only a comment.
                (tempest\.(api|scenario|thirdparty)).*$    # Run only api scenario and third party
 
+.. zuul:rolevar:: tempest_test_blacklist
+
+   Specifies a blacklist file to skip tests that are not needed.
+
+   Pass a full path to the file.
+
 .. zuul:rolevar:: tox_envlist
    :default: smoke
 
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 87898db..b68507a 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -20,8 +20,22 @@
     default_concurrency: "{{ num_cores|int // 2 }}"
   when: num_cores|int > 3
 
+- when:
+    - tempest_test_blacklist is defined
+  block:
+    - name: Check for test blacklist file
+      stat:
+        path: "{{ tempest_test_blacklist }}"
+      register:
+        blacklist_stat
+
+    - name: Build blacklist option
+      set_fact:
+        blacklist_option: "--blacklist-file={{ tempest_test_blacklist|quote }}"
+      when: blacklist_stat.stat.exists
+
 - name: Run Tempest
-  command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
+  command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
   args:
     chdir: "{{devstack_base_dir}}/tempest"
   become: true
diff --git a/setup.cfg b/setup.cfg
index 04bb29f..c981370 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -49,7 +49,8 @@
 
 [build_sphinx]
 all-files = 1
-warning-is-error = 1
+# warning can be generated by using GENERATE_TEMPEST_PLUGIN_LIST='False'
+warning-is-error = 0
 build-dir = doc/build
 source-dir = doc/source
 
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
index c4d5768..e8011a6 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -148,9 +148,7 @@
     def test_server_create_no_allocate(self):
         """Tests that no networking is allocated for the server."""
         # create the server with no networking
-        server, _ = compute.create_test_server(
-            self.os_primary, networks='none', wait_until='ACTIVE')
-        self.addCleanup(self.delete_server, server['id'])
+        server = self.create_test_server(networks='none', wait_until='ACTIVE')
         # get the server ips
         addresses = self.servers_client.list_addresses(
             server['id'])['addresses']
diff --git a/tempest/api/compute/admin/test_servers_on_multinodes.py b/tempest/api/compute/admin/test_servers_on_multinodes.py
index 18c974a..d32a5b4 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -43,6 +43,28 @@
         return cls.os_admin.servers_client.show_server(
             server_id)['server']['OS-EXT-SRV-ATTR:host']
 
+    def _create_servers_with_group(self, policy):
+        group_id = self.create_test_server_group(policy=[policy])['id']
+        hints = {'group': group_id}
+        reservation_id = self.create_test_server(
+            scheduler_hints=hints, wait_until='ACTIVE', min_count=2,
+            return_reservation_id=True)['reservation_id']
+
+        # Get the servers using the reservation_id.
+        servers = self.servers_client.list_servers(
+            detail=True, reservation_id=reservation_id)['servers']
+        self.assertEqual(2, len(servers))
+
+        # Assert the servers are in the group.
+        server_group = self.server_groups_client.show_server_group(
+            group_id)['server_group']
+        hosts = {}
+        for server in servers:
+            self.assertIn(server['id'], server_group['members'])
+            hosts[server['id']] = self._get_host(server['id'])
+
+        return hosts
+
     @decorators.idempotent_id('26a9d5df-6890-45f2-abc4-a659290cb130')
     @testtools.skipUnless(
         compute.is_scheduler_filter_enabled("SameHostFilter"),
@@ -87,28 +109,8 @@
         Creates two servers in an anti-affinity server group and
         asserts the servers are in the group and on different hosts.
         """
-        group_id = self.create_test_server_group(
-            policy=['anti-affinity'])['id']
-        hints = {'group': group_id}
-        reservation_id = self.create_test_server(
-            scheduler_hints=hints, wait_until='ACTIVE', min_count=2,
-            return_reservation_id=True)['reservation_id']
-
-        # Get the servers using the reservation_id.
-        servers = self.servers_client.list_servers(
-            detail=True, reservation_id=reservation_id)['servers']
-        self.assertEqual(2, len(servers))
-
-        # Assert the servers are in the group.
-        server_group = self.server_groups_client.show_server_group(
-            group_id)['server_group']
-        hosts = {}
-        for server in servers:
-            self.assertIn(server['id'], server_group['members'])
-            hosts[server['id']] = self._get_host(server['id'])
-
-        # Assert the servers are on different hosts.
-        hostnames = list(hosts.values())
+        hosts = self._create_servers_with_group('anti-affinity')
+        hostnames = hosts.values()
         self.assertNotEqual(hostnames[0], hostnames[1],
                             'Servers are on the same host: %s' % hosts)
 
@@ -122,26 +124,7 @@
         Creates two servers in an affinity server group and
         asserts the servers are in the group and on same host.
         """
-        group_id = self.create_test_server_group(policy=['affinity'])['id']
-        hints = {'group': group_id}
-        reservation_id = self.create_test_server(
-            scheduler_hints=hints, wait_until='ACTIVE', min_count=2,
-            return_reservation_id=True)['reservation_id']
-
-        # Get the servers using the reservation_id.
-        servers = self.servers_client.list_servers(
-            detail=True, reservation_id=reservation_id)['servers']
-        self.assertEqual(2, len(servers))
-
-        # Assert the servers are in the group.
-        server_group = self.server_groups_client.show_server_group(
-            group_id)['server_group']
-        hosts = {}
-        for server in servers:
-            self.assertIn(server['id'], server_group['members'])
-            hosts[server['id']] = self._get_host(server['id'])
-
-        # Assert the servers are on same host.
+        hosts = self._create_servers_with_group('affinity')
         hostnames = hosts.values()
         self.assertEqual(hostnames[0], hostnames[1],
                          'Servers are on the different hosts: %s' % hosts)
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 99bad8f..ed8cf20 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -126,13 +126,13 @@
         self.assertIn(volume1['id'], vol_attachments[0]['volumeId'])
 
 
-class AttachVolumeMultiAttachTest(TestVolumeSwapBase):
+class TestMultiAttachVolumeSwap(TestVolumeSwapBase):
     min_microversion = '2.60'
     max_microversion = 'latest'
 
     @classmethod
     def skip_checks(cls):
-        super(AttachVolumeMultiAttachTest, cls).skip_checks()
+        super(TestMultiAttachVolumeSwap, cls).skip_checks()
         if not CONF.compute_feature_enabled.volume_multiattach:
             raise cls.skipException('Volume multi-attach is not available.')
 
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 0e8f681..7509ac6 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -185,7 +185,7 @@
 
     @decorators.idempotent_id('73fe8f02-590d-4bf1-b184-e9ca81065051')
     @utils.services('network')
-    def test_create_list_show_delete_interfaces(self):
+    def test_create_list_show_delete_interfaces_by_network_port(self):
         server, ifs = self._create_server_get_interfaces()
         interface_count = len(ifs)
         self.assertGreater(interface_count, 0)
@@ -206,6 +206,42 @@
         iface = self._test_create_interface_by_port_id(server, ifs)
         ifs.append(iface)
 
+        _ifs = (self.interfaces_client.list_interfaces(server['id'])
+                ['interfaceAttachments'])
+        self._compare_iface_list(ifs, _ifs)
+
+        self._test_show_interface(server, ifs)
+
+        _ifs = self._test_delete_interface(server, ifs)
+        self.assertEqual(len(ifs) - 1, len(_ifs))
+
+    @decorators.idempotent_id('d290c06c-f5b3-11e7-8ec8-002293781009')
+    @utils.services('network')
+    def test_create_list_show_delete_interfaces_by_fixed_ip(self):
+        # NOTE(zhufl) By default only project that is admin or network owner
+        # or project with role advsvc is authorised to create interfaces with
+        # fixed-ip, so if we don't create network for each project, do not
+        # test _test_create_interface_by_fixed_ips.
+        if not (CONF.auth.use_dynamic_credentials and
+                CONF.auth.create_isolated_networks and
+                not CONF.network.shared_physical_network):
+                raise self.skipException("Only owner network supports "
+                                         "creating interface by fixed ip.")
+
+        server, ifs = self._create_server_get_interfaces()
+        interface_count = len(ifs)
+        self.assertGreater(interface_count, 0)
+
+        try:
+            iface = self._test_create_interface(server)
+        except lib_exc.BadRequest as e:
+            msg = ('Multiple possible networks found, use a Network ID to be '
+                   'more specific.')
+            if not CONF.compute.fixed_network_name and six.text_type(e) == msg:
+                raise
+        else:
+            ifs.append(iface)
+
         iface = self._test_create_interface_by_fixed_ips(server, ifs)
         ifs.append(iface)
 
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index ca6b03e..97a1f36 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -18,6 +18,7 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions
 
 CONF = config.CONF
 
@@ -115,6 +116,26 @@
         domains_list = [d['id'] for d in body]
         self.assertNotIn(domain['id'], domains_list)
 
+    @decorators.idempotent_id('d8d318b7-d1b3-4c37-94c5-3c5ba0b121ea')
+    def test_domain_delete_cascades_content(self):
+        # Create a domain with a user and a group in it
+        domain = self.setup_test_domain()
+        user = self.create_test_user(domain_id=domain['id'])
+        group = self.groups_client.create_group(
+            name=data_utils.rand_name('group'),
+            domain_id=domain['id'])['group']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.groups_client.delete_group, group['id'])
+        # Delete the domain
+        self.delete_domain(domain['id'])
+        # Check the domain, its users and groups are gone
+        self.assertRaises(exceptions.NotFound,
+                          self.domains_client.show_domain, domain['id'])
+        self.assertRaises(exceptions.NotFound,
+                          self.users_client.show_user, user['id'])
+        self.assertRaises(exceptions.NotFound,
+                          self.groups_client.show_group, group['id'])
+
     @decorators.idempotent_id('036df86e-bb5d-42c0-a7c2-66b9db3a6046')
     def test_create_domain_with_disabled_status(self):
         # Create domain with enabled status as false
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 4911ba4..ae7b3e4 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -184,3 +184,13 @@
                                          disk_format='raw')
         self.addCleanup(self.client.delete_image, image['id'])
         return image['id']
+
+
+class BaseV2ImageAdminTest(BaseV2ImageTest):
+
+    credentials = ['admin', 'primary']
+
+    @classmethod
+    def setup_clients(cls):
+        super(BaseV2ImageAdminTest, cls).setup_clients()
+        cls.admin_client = cls.os_admin.image_client_v2
diff --git a/tempest/api/image/v2/admin/__init__.py b/tempest/api/image/v2/admin/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/image/v2/admin/__init__.py
diff --git a/tempest/api/image/v2/admin/test_images.py b/tempest/api/image/v2/admin/test_images.py
new file mode 100644
index 0000000..dbb8c58
--- /dev/null
+++ b/tempest/api/image/v2/admin/test_images.py
@@ -0,0 +1,51 @@
+# Copyright 2018 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.image import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+
+class BasicOperationsImagesAdminTest(base.BaseV2ImageAdminTest):
+
+    @decorators.related_bug('1420008')
+    @decorators.idempotent_id('646a6eaa-135f-4493-a0af-12583021224e')
+    def test_create_image_owner_param(self):
+        # NOTE: Create image with owner different from tenant owner by
+        # using "owner" parameter requires an admin privileges.
+        random_id = data_utils.rand_uuid_hex()
+        image = self.admin_client.create_image(
+            container_format='bare', disk_format='raw', owner=random_id)
+        self.addCleanup(self.admin_client.delete_image, image['id'])
+        image_info = self.admin_client.show_image(image['id'])
+        self.assertEqual(random_id, image_info['owner'])
+
+    @decorators.related_bug('1420008')
+    @decorators.idempotent_id('525ba546-10ef-4aad-bba1-1858095ce553')
+    def test_update_image_owner_param(self):
+        random_id_1 = data_utils.rand_uuid_hex()
+        image = self.admin_client.create_image(
+            container_format='bare', disk_format='raw', owner=random_id_1)
+        self.addCleanup(self.admin_client.delete_image, image['id'])
+        created_image_info = self.admin_client.show_image(image['id'])
+
+        random_id_2 = data_utils.rand_uuid_hex()
+        self.admin_client.update_image(
+            image['id'], [dict(replace="/owner", value=random_id_2)])
+        updated_image_info = self.admin_client.show_image(image['id'])
+
+        self.assertEqual(random_id_2, updated_image_info['owner'])
+        self.assertNotEqual(created_image_info['owner'],
+                            updated_image_info['owner'])
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
index 69bebfe..482e808 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
@@ -49,7 +49,7 @@
         # List namespace tags
         body = self.namespace_tags_client.list_namespace_tags(
             namespace['namespace'])
-        self.assertTrue(3, len(body['tags']))
+        self.assertEqual(3, len(body['tags']))
         self.assertIn(body['tags'][0]['name'], self.tag_list)
         self.assertIn(body['tags'][1]['name'], self.tag_list)
         self.assertIn(body['tags'][2]['name'], self.tag_list)
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index 7634d9e..9a85d89 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -20,19 +20,15 @@
 from oslo_config import generator
 from oslo_log import log as logging
 from six import moves
-from testrepository import commands
+from stestr import commands
 
 from tempest.cmd import workspace
 
 LOG = logging.getLogger(__name__)
 
-TESTR_CONF = """[DEFAULT]
-test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \\
-    OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \\
-    OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \\
-    ${PYTHON:-python} -m subunit.run discover -t %s %s $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
+STESTR_CONF = """[DEFAULT]
+test_path=%s
+top_dir=%s
 group_regex=([^\.]*\.)*
 """
 
@@ -84,13 +80,13 @@
                                  "is ~/.tempest/workspace.yaml")
         return parser
 
-    def generate_testr_conf(self, local_path):
-        testr_conf_path = os.path.join(local_path, '.testr.conf')
+    def generate_stestr_conf(self, local_path):
+        stestr_conf_path = os.path.join(local_path, '.stestr.conf')
         top_level_path = os.path.dirname(os.path.dirname(__file__))
         discover_path = os.path.join(top_level_path, 'test_discover')
-        testr_conf = TESTR_CONF % (top_level_path, discover_path)
-        with open(testr_conf_path, 'w+') as testr_conf_file:
-            testr_conf_file.write(testr_conf)
+        stestr_conf = STESTR_CONF % (discover_path, top_level_path)
+        with open(stestr_conf_path, 'w+') as stestr_conf_file:
+            stestr_conf_file.write(stestr_conf)
 
     def get_configparser(self, conf_path):
         config_parse = moves.configparser.ConfigParser()
@@ -148,7 +144,7 @@
         etc_dir = os.path.join(local_dir, 'etc')
         config_path = os.path.join(etc_dir, 'tempest.conf')
         log_dir = os.path.join(local_dir, 'logs')
-        testr_dir = os.path.join(local_dir, '.testrepository')
+        stestr_dir = os.path.join(local_dir, '.stestr')
         # Create lock dir
         if not os.path.isdir(lock_dir):
             LOG.debug('Creating lock dir: %s', lock_dir)
@@ -163,12 +159,11 @@
         self.generate_sample_config(local_dir)
         # Update local confs to reflect local paths
         self.update_local_conf(config_path, lock_dir, log_dir)
-        # Generate a testr conf file
-        self.generate_testr_conf(local_dir)
-        # setup local testr working dir
-        if not os.path.isdir(testr_dir):
-            commands.run_argv(['testr', 'init', '-d', local_dir], sys.stdin,
-                              sys.stdout, sys.stderr)
+        # Generate a stestr conf file
+        self.generate_stestr_conf(local_dir)
+        # setup local stestr working dir
+        if not os.path.isdir(stestr_dir):
+            commands.init_command(repo_url=local_dir)
 
     def take_action(self, parsed_args):
         workspace_manager = workspace.WorkspaceManager(
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 6435717..72ee715 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -19,9 +19,11 @@
 ==============
 Tempest run has several options:
 
-* ``--regex, -r``: This is a selection regex like what testr uses. It will run
-  any tests that match on re.match() with the regex
-* ``--smoke, -s``: Run all the tests tagged as smoke
+ * **--regex/-r**: This is a selection regex like what stestr uses. It will run
+                   any tests that match on re.match() with the regex
+ * **--smoke/-s**: Run all the tests tagged as smoke
+ * **--black-regex**: It allows to do simple test exclusion via passing a
+                      rejection/black regexp
 
 There are also the ``--blacklist-file`` and ``--whitelist-file`` options that
 let you pass a filepath to tempest run with the file format being a line
@@ -32,17 +34,9 @@
     ^regex1 # Match these tests
     .*regex2 # Match those tests
 
-The blacklist file will be used to construct a negative lookahead regex and
-the whitelist file will simply OR all the regexes in the file. The whitelist
-and blacklist file options are mutually exclusive so you can't use them
-together. However, you can combine either with a normal regex or the *--smoke*
-flag. When used with a blacklist file the generated regex will be combined to
-something like::
-
-    ^((?!black_regex1|black_regex2).)*$cli_regex1
-
-When combined with a whitelist file all the regexes from the file and the CLI
-regexes will be ORed.
+These arguments are just passed into stestr, you can refer to the stestr
+selection docs for more details on how these operate:
+http://stestr.readthedocs.io/en/latest/MANUAL.html#test-selection
 
 You can also use the ``--list-tests`` option in conjunction with selection
 arguments to list which tests will be run.
@@ -74,9 +68,9 @@
 ---------------------
 Tempest run provides you with an option to execute tempest from anywhere on
 your system. You are required to provide a config file in this case with the
-``--config-file`` option. When run tempest will create a .testrepository
-directory and a .testr.conf file in your current working directory. This way
-you can use testr commands directly to inspect the state of the previous run.
+``--config-file`` option. When run tempest will create a .stestr
+directory and a .stestr.conf file in your current working directory. This way
+you can use stestr commands directly to inspect the state of the previous run.
 
 Test Output
 ===========
@@ -94,18 +88,13 @@
 the current run's results with the previous runs.
 """
 
-import io
 import os
 import sys
-import tempfile
-import threading
 
 from cliff import command
-from os_testr import regex_builder
-from os_testr import subunit_trace
 from oslo_serialization import jsonutils as json
 import six
-from testrepository.commands import run_argv
+from stestr import commands
 
 from tempest import clients
 from tempest.cmd import cleanup_service
@@ -124,35 +113,27 @@
     def _set_env(self, config_file=None):
         if config_file:
             CONF.set_config_path(os.path.abspath(config_file))
-        # NOTE(mtreinish): This is needed so that testr doesn't gobble up any
+        # NOTE(mtreinish): This is needed so that stestr doesn't gobble up any
         # stacktraces on failure.
         if 'TESTR_PDB' in os.environ:
             return
         else:
             os.environ["TESTR_PDB"] = ""
-        # NOTE(dims): most of our .testr.conf try to test for PYTHON
+        # NOTE(dims): most of our .stestr.conf try to test for PYTHON
         # environment variable and fall back to "python", under python3
         # if it does not exist. we should set it to the python3 executable
         # to deal with this situation better for now.
         if six.PY3 and 'PYTHON' not in os.environ:
             os.environ['PYTHON'] = sys.executable
 
-    def _create_testrepository(self):
-        if not os.path.isdir('.testrepository'):
-            returncode = run_argv(['testr', 'init'], sys.stdin, sys.stdout,
-                                  sys.stderr)
-            if returncode:
-                sys.exit(returncode)
-
-    def _create_testr_conf(self):
+    def _create_stestr_conf(self):
         top_level_path = os.path.dirname(os.path.dirname(__file__))
         discover_path = os.path.join(top_level_path, 'test_discover')
-        file_contents = init.TESTR_CONF % (top_level_path, discover_path)
-        with open('.testr.conf', 'w+') as testr_conf_file:
-            testr_conf_file.write(file_contents)
+        file_contents = init.STESTR_CONF % (discover_path, top_level_path)
+        with open('.stestr.conf', 'w+') as stestr_conf_file:
+            stestr_conf_file.write(file_contents)
 
     def take_action(self, parsed_args):
-        returncode = 0
         if parsed_args.config_file:
             self._set_env(parsed_args.config_file)
         else:
@@ -169,52 +150,39 @@
                     "register the workspace." %
                     (parsed_args.workspace, workspace_mgr.path))
             os.chdir(path)
-            # NOTE(mtreinish): tempest init should create a .testrepository dir
-            # but since workspaces can be imported let's sanity check and
-            # ensure that one is created
-            self._create_testrepository()
-        # Local execution mode
-        elif os.path.isfile('.testr.conf'):
-            # If you're running in local execution mode and there is not a
-            # testrepository dir create one
-            self._create_testrepository()
+            if not os.path.isfile('.stestr.conf'):
+                self._create_stestr_conf()
         # local execution with config file mode
-        elif parsed_args.config_file:
-            self._create_testr_conf()
-            self._create_testrepository()
-        else:
-            print("No .testr.conf file was found for local execution")
+        elif parsed_args.config_file and not os.path.isfile('.stestr.conf'):
+            self._create_stestr_conf()
+        elif not os.path.isfile('.stestr.conf'):
+            print("No .stestr.conf file was found for local execution")
             sys.exit(2)
         if parsed_args.state:
             self._init_state()
         else:
             pass
 
-        if parsed_args.combine:
-            temp_stream = tempfile.NamedTemporaryFile()
-            return_code = run_argv(['tempest', 'last', '--subunit'], sys.stdin,
-                                   temp_stream, sys.stderr)
-            if return_code > 0:
-                sys.exit(return_code)
-
         regex = self._build_regex(parsed_args)
+        return_code = 0
         if parsed_args.list_tests:
-            argv = ['tempest', 'list-tests', regex]
-            returncode = run_argv(argv, sys.stdin, sys.stdout, sys.stderr)
-        else:
-            options = self._build_options(parsed_args)
-            returncode = self._run(regex, options)
-            if returncode > 0:
-                sys.exit(returncode)
+            return_code = commands.list_command(
+                filters=regex, whitelist_file=parsed_args.whitelist_file,
+                blacklist_file=parsed_args.blacklist_file,
+                black_regex=parsed_args.black_regex)
 
-        if parsed_args.combine:
-            return_code = run_argv(['tempest', 'last', '--subunit'], sys.stdin,
-                                   temp_stream, sys.stderr)
+        else:
+            serial = not parsed_args.parallel
+            return_code = commands.run_command(
+                filters=regex, subunit_out=parsed_args.subunit,
+                serial=serial, concurrency=parsed_args.concurrency,
+                blacklist_file=parsed_args.blacklist_file,
+                whitelist_file=parsed_args.whitelist_file,
+                black_regex=parsed_args.black_regex,
+                load_list=parsed_args.load_list, combine=parsed_args.combine)
             if return_code > 0:
                 sys.exit(return_code)
-            returncode = run_argv(['tempest', 'load', temp_stream.name],
-                                  sys.stdin, sys.stdout, sys.stderr)
-        sys.exit(returncode)
+        return return_code
 
     def get_description(self):
         return 'Run tempest'
@@ -262,23 +230,24 @@
         regex.add_argument('--smoke', '-s', action='store_true',
                            help="Run the smoke tests only")
         regex.add_argument('--regex', '-r', default='',
-                           help='A normal testr selection regex used to '
+                           help='A normal stestr selection regex used to '
                                 'specify a subset of tests to run')
-        list_selector = parser.add_mutually_exclusive_group()
-        list_selector.add_argument('--whitelist-file', '--whitelist_file',
-                                   help="Path to a whitelist file, this file "
-                                        "contains a separate regex on each "
-                                        "newline.")
-        list_selector.add_argument('--blacklist-file', '--blacklist_file',
-                                   help='Path to a blacklist file, this file '
-                                        'contains a separate regex exclude on '
-                                        'each newline')
-        list_selector.add_argument('--load-list', '--load_list',
-                                   help='Path to a non-regex whitelist file, '
-                                        'this file contains a seperate test '
-                                        'on each newline. This command'
-                                        'supports files created by the tempest'
-                                        'run ``--list-tests`` command')
+        parser.add_argument('--black-regex', dest='black_regex',
+                            help='A regex to exclude tests that match it')
+        parser.add_argument('--whitelist-file', '--whitelist_file',
+                            help="Path to a whitelist file, this file "
+                            "contains a separate regex on each "
+                            "newline.")
+        parser.add_argument('--blacklist-file', '--blacklist_file',
+                            help='Path to a blacklist file, this file '
+                                 'contains a separate regex exclude on '
+                                 'each newline')
+        parser.add_argument('--load-list', '--load_list',
+                            help='Path to a non-regex whitelist file, '
+                                 'this file contains a seperate test '
+                                 'on each newline. This command'
+                                 'supports files created by the tempest'
+                                 'run ``--list-tests`` command')
         # list only args
         parser.add_argument('--list-tests', '-l', action='store_true',
                             help='List tests',
@@ -305,62 +274,15 @@
         parser.add_argument("--combine", action='store_true',
                             help='Combine the output of this run with the '
                                  "previous run's as a combined stream in the "
-                                 "testr repository after it finish")
+                                 "stestr repository after it finish")
 
         parser.set_defaults(parallel=True)
         return parser
 
     def _build_regex(self, parsed_args):
-        regex = ''
+        regex = None
         if parsed_args.smoke:
-            regex = 'smoke'
+            regex = ['smoke']
         elif parsed_args.regex:
-            regex = parsed_args.regex
-        if parsed_args.whitelist_file or parsed_args.blacklist_file:
-            regex = regex_builder.construct_regex(parsed_args.blacklist_file,
-                                                  parsed_args.whitelist_file,
-                                                  regex, False)
+            regex = parsed_args.regex.split()
         return regex
-
-    def _build_options(self, parsed_args):
-        options = []
-        if parsed_args.subunit:
-            options.append("--subunit")
-        if parsed_args.parallel:
-            options.append("--parallel")
-        if parsed_args.concurrency:
-            options.append("--concurrency=%s" % parsed_args.concurrency)
-        if parsed_args.load_list:
-            options.append("--load-list=%s" % parsed_args.load_list)
-        return options
-
-    def _run(self, regex, options):
-        returncode = 0
-        argv = ['tempest', 'run', regex] + options
-        if '--subunit' in options:
-            returncode = run_argv(argv, sys.stdin, sys.stdout, sys.stderr)
-        else:
-            argv.append('--subunit')
-            stdin = io.StringIO()
-            stdout_r, stdout_w = os.pipe()
-            subunit_w = os.fdopen(stdout_w, 'wt')
-            subunit_r = os.fdopen(stdout_r)
-            returncodes = {}
-
-            def run_argv_thread():
-                returncodes['testr'] = run_argv(argv, stdin, subunit_w,
-                                                sys.stderr)
-                subunit_w.close()
-
-            run_thread = threading.Thread(target=run_argv_thread)
-            run_thread.start()
-            returncodes['subunit-trace'] = subunit_trace.trace(
-                subunit_r, sys.stdout, post_fails=True, print_failures=True)
-            run_thread.join()
-            subunit_r.close()
-            # python version of pipefail
-            if returncodes['testr']:
-                returncode = returncodes['testr']
-            elif returncodes['subunit-trace']:
-                returncode = returncodes['subunit-trace']
-        return returncode
diff --git a/tempest/config.py b/tempest/config.py
index 340a27e..8a6370a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -609,10 +609,14 @@
                      " for subnet creation"),
     cfg.StrOpt('port_vnic_type',
                choices=[None, 'normal', 'direct', 'macvtap'],
-               help="vnic_type to use when Launching instances"
+               help="vnic_type to use when launching instances"
                     " with pre-configured ports."
                     " Supported ports are:"
                     " ['normal','direct','macvtap']"),
+    cfg.DictOpt('port_profile',
+                default={},
+                help="port profile to use when launching instances"
+                     " with pre-configured ports."),
     cfg.ListOpt('default_network',
                 default=["1.0.0.0/16", "2.0.0.0/16"],
                 help="List of ip pools"
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 06aa531..26ae848 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -139,13 +139,15 @@
             name = data_utils.rand_name(self.__class__.__name__ + "-server")
 
         vnic_type = CONF.network.port_vnic_type
+        profile = CONF.network.port_profile
 
         # If vnic_type is configured create port for
         # every network
         if vnic_type:
             ports = []
 
-            create_port_body = {'binding:vnic_type': vnic_type}
+            create_port_body = {'binding:vnic_type': vnic_type,
+                                'binding:profile': profile}
             if kwargs:
                 # Convert security group names to security group ids
                 # to pass to create_port
@@ -252,6 +254,8 @@
         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 create_volume_type(self, client=None, name=None, backend_name=None):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index ff8837f..fd9c985 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -304,16 +304,19 @@
 
         - ping internal gateway and DHCP port, implying in-tenant connectivity
         pinging both, because L3 and DHCP agents might be on different nodes
+        - ping internal compute port, implying connectivity to other VMs on
+        this network
         """
         floating_ip, server = self.floating_ip_tuple
         # get internal ports' ips:
-        # get all network ports in the new network
+        # get all network and compute ports in the new network
         internal_ips = (
             p['fixed_ips'][0]['ip_address'] for p in
             self.os_admin.ports_client.list_ports(
                 tenant_id=server['tenant_id'],
                 network_id=network['id'])['ports']
-            if p['device_owner'].startswith('network')
+            if p['device_owner'].startswith('network') or
+            p['device_owner'].startswith('compute')
         )
 
         self._check_server_connectivity(floating_ip,
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index 0485e14..6cc356e 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -21,6 +21,7 @@
 
 import fixtures
 import mock
+import six
 
 from tempest.cmd import run
 from tempest.tests import base
@@ -35,24 +36,13 @@
         super(TestTempestRun, self).setUp()
         self.run_cmd = run.TempestRun(None, None)
 
-    def test_build_options(self):
-        args = mock.Mock(spec=argparse.Namespace)
-        setattr(args, "subunit", True)
-        setattr(args, "parallel", False)
-        setattr(args, "concurrency", 10)
-        setattr(args, "load_list", '')
-        options = self.run_cmd._build_options(args)
-        self.assertEqual(['--subunit',
-                          '--concurrency=10'],
-                         options)
-
     def test__build_regex_default(self):
         args = mock.Mock(spec=argparse.Namespace)
         setattr(args, 'smoke', False)
         setattr(args, 'regex', '')
         setattr(args, 'whitelist_file', None)
         setattr(args, 'blacklist_file', None)
-        self.assertEqual('', self.run_cmd._build_regex(args))
+        self.assertIsNone(None, self.run_cmd._build_regex(args))
 
     def test__build_regex_smoke(self):
         args = mock.Mock(spec=argparse.Namespace)
@@ -60,7 +50,7 @@
         setattr(args, 'regex', '')
         setattr(args, 'whitelist_file', None)
         setattr(args, 'blacklist_file', None)
-        self.assertEqual('smoke', self.run_cmd._build_regex(args))
+        self.assertEqual(['smoke'], self.run_cmd._build_regex(args))
 
     def test__build_regex_regex(self):
         args = mock.Mock(spec=argparse.Namespace)
@@ -68,37 +58,9 @@
         setattr(args, "regex", 'i_am_a_fun_little_regex')
         setattr(args, 'whitelist_file', None)
         setattr(args, 'blacklist_file', None)
-        self.assertEqual('i_am_a_fun_little_regex',
+        self.assertEqual(['i_am_a_fun_little_regex'],
                          self.run_cmd._build_regex(args))
 
-    def test__build_whitelist_file(self):
-        args = mock.Mock(spec=argparse.Namespace)
-        setattr(args, 'smoke', False)
-        setattr(args, 'regex', None)
-        self.tests = tempfile.NamedTemporaryFile(
-            prefix='whitelist', delete=False)
-        self.tests.write(b"volume \n compute")
-        self.tests.close()
-        setattr(args, 'whitelist_file', self.tests.name)
-        setattr(args, 'blacklist_file', None)
-        self.assertEqual("volume|compute",
-                         self.run_cmd._build_regex(args))
-        os.unlink(self.tests.name)
-
-    def test__build_blacklist_file(self):
-        args = mock.Mock(spec=argparse.Namespace)
-        setattr(args, 'smoke', False)
-        setattr(args, 'regex', None)
-        self.tests = tempfile.NamedTemporaryFile(
-            prefix='blacklist', delete=False)
-        self.tests.write(b"volume \n compute")
-        self.tests.close()
-        setattr(args, 'whitelist_file', None)
-        setattr(args, 'blacklist_file', self.tests.name)
-        self.assertEqual("^((?!compute|volume).)*$",
-                         self.run_cmd._build_regex(args))
-        os.unlink(self.tests.name)
-
 
 class TestRunReturnCode(base.TestCase):
     def setUp(self):
@@ -109,13 +71,13 @@
         self.test_dir = os.path.join(self.directory, 'tests')
         os.mkdir(self.test_dir)
         # Setup Test files
-        self.testr_conf_file = os.path.join(self.directory, '.testr.conf')
+        self.stestr_conf_file = os.path.join(self.directory, '.stestr.conf')
         self.setup_cfg_file = os.path.join(self.directory, 'setup.cfg')
         self.passing_file = os.path.join(self.test_dir, 'test_passing.py')
         self.failing_file = os.path.join(self.test_dir, 'test_failing.py')
         self.init_file = os.path.join(self.test_dir, '__init__.py')
         self.setup_py = os.path.join(self.directory, 'setup.py')
-        shutil.copy('tempest/tests/files/testr-conf', self.testr_conf_file)
+        shutil.copy('tempest/tests/files/testr-conf', self.stestr_conf_file)
         shutil.copy('tempest/tests/files/passing-tests', self.passing_file)
         shutil.copy('tempest/tests/files/failing-tests', self.failing_file)
         shutil.copy('setup.py', self.setup_py)
@@ -132,29 +94,57 @@
         msg = ("Running %s got an unexpected returncode\n"
                "Stdout: %s\nStderr: %s" % (' '.join(cmd), out, err))
         self.assertEqual(p.returncode, expected, msg)
+        return out, err
 
     def test_tempest_run_passes(self):
-        # Git init is required for the pbr testr command. pbr requires a git
-        # version or an sdist to work. so make the test directory a git repo
-        # too.
-        subprocess.call(['git', 'init'], stderr=DEVNULL)
         self.assertRunExit(['tempest', 'run', '--regex', 'passing'], 0)
 
-    def test_tempest_run_passes_with_testrepository(self):
-        # Git init is required for the pbr testr command. pbr requires a git
-        # version or an sdist to work. so make the test directory a git repo
-        # too.
-        subprocess.call(['git', 'init'], stderr=DEVNULL)
-        subprocess.call(['testr', 'init'])
+    def test_tempest_run_passes_with_stestr_repository(self):
+        subprocess.call(['stestr', 'init'])
         self.assertRunExit(['tempest', 'run', '--regex', 'passing'], 0)
 
     def test_tempest_run_fails(self):
-        # Git init is required for the pbr testr command. pbr requires a git
-        # version or an sdist to work. so make the test directory a git repo
-        # too.
-        subprocess.call(['git', 'init'], stderr=DEVNULL)
         self.assertRunExit(['tempest', 'run'], 1)
 
+    def test_run_list(self):
+        subprocess.call(['stestr', 'init'])
+        out, err = self.assertRunExit(['tempest', 'run', '-l'], 0)
+        tests = out.split()
+        tests = sorted([six.text_type(x.rstrip()) for x in tests if x])
+        result = [
+            six.text_type('tests.test_failing.FakeTestClass.test_pass'),
+            six.text_type('tests.test_failing.FakeTestClass.test_pass_list'),
+            six.text_type('tests.test_passing.FakeTestClass.test_pass'),
+            six.text_type('tests.test_passing.FakeTestClass.test_pass_list'),
+        ]
+        # NOTE(mtreinish): on python 3 the subprocess prints b'' around
+        # stdout.
+        if six.PY3:
+            result = ["b\'" + x + "\'" for x in result]
+        self.assertEqual(result, tests)
+
+    def test_tempest_run_with_whitelist(self):
+        fd, path = tempfile.mkstemp()
+        self.addCleanup(os.remove, path)
+        whitelist_file = os.fdopen(fd, 'wb', 0)
+        self.addCleanup(whitelist_file.close)
+        whitelist_file.write('passing'.encode('utf-8'))
+        self.assertRunExit(['tempest', 'run', '--whitelist-file=%s' % path], 0)
+
+    def test_tempest_run_with_whitelist_with_regex(self):
+        fd, path = tempfile.mkstemp()
+        self.addCleanup(os.remove, path)
+        whitelist_file = os.fdopen(fd, 'wb', 0)
+        self.addCleanup(whitelist_file.close)
+        whitelist_file.write('passing'.encode('utf-8'))
+        self.assertRunExit(['tempest', 'run', '--whitelist-file=%s' % path,
+                            '--regex', 'fail'], 1)
+
+    def test_tempest_run_passes_with_config_file(self):
+        self.assertRunExit(['tempest', 'run',
+                            '--config-file', self.stestr_conf_file,
+                            '--regex', 'passing'], 0)
+
 
 class TestTakeAction(base.TestCase):
     def test_workspace_not_registered(self):
@@ -183,3 +173,27 @@
         self.assertRaises(Exception_, tempest_run.take_action, parsed_args)
         exit_msg = m_exit.call_args[0][0]
         self.assertIn(workspace, exit_msg)
+
+    def test_config_file_specified(self):
+        # Setup test dirs
+        self.directory = tempfile.mkdtemp(prefix='tempest-unit')
+        self.addCleanup(shutil.rmtree, self.directory)
+        self.test_dir = os.path.join(self.directory, 'tests')
+        os.mkdir(self.test_dir)
+        # Change directory, run wrapper and check result
+        self.addCleanup(os.chdir, os.path.abspath(os.curdir))
+        os.chdir(self.directory)
+
+        tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+        parsed_args = mock.Mock()
+        parsed_args.config_file = []
+
+        parsed_args.workspace = None
+        parsed_args.state = None
+        parsed_args.list_tests = False
+        parsed_args.config_file = '.stestr.conf'
+
+        with mock.patch('stestr.commands.run_command') as m:
+            m.return_value = 0
+            self.assertEqual(0, tempest_run.take_action(parsed_args))
+            m.assert_called()
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
index 79510be..5f39ac9 100644
--- a/tempest/tests/cmd/test_tempest_init.py
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -27,16 +27,16 @@
         conf_dir = self.useFixture(fixtures.TempDir())
 
         init_cmd = init.TempestInit(None, None)
-        init_cmd.generate_testr_conf(conf_dir.path)
+        init_cmd.generate_stestr_conf(conf_dir.path)
 
         # Generate expected file contents
         top_level_path = os.path.dirname(os.path.dirname(init.__file__))
         discover_path = os.path.join(top_level_path, 'test_discover')
-        testr_conf_file = init.TESTR_CONF % (top_level_path, discover_path)
+        stestr_conf_file = init.STESTR_CONF % (discover_path, top_level_path)
 
-        conf_path = conf_dir.join('.testr.conf')
+        conf_path = conf_dir.join('.stestr.conf')
         with open(conf_path, 'r') as conf_file:
-            self.assertEqual(conf_file.read(), testr_conf_file)
+            self.assertEqual(conf_file.read(), stestr_conf_file)
 
     def test_generate_sample_config(self):
         local_dir = self.useFixture(fixtures.TempDir())
@@ -125,18 +125,18 @@
         lock_path = os.path.join(fake_local_dir.path, 'tempest_lock')
         etc_dir = os.path.join(fake_local_dir.path, 'etc')
         log_dir = os.path.join(fake_local_dir.path, 'logs')
-        testr_dir = os.path.join(fake_local_dir.path, '.testrepository')
+        stestr_dir = os.path.join(fake_local_dir.path, '.stestr')
         self.assertTrue(os.path.isdir(lock_path))
         self.assertTrue(os.path.isdir(etc_dir))
         self.assertTrue(os.path.isdir(log_dir))
-        self.assertTrue(os.path.isdir(testr_dir))
+        self.assertTrue(os.path.isdir(stestr_dir))
         # Assert file creation
         fake_file_moved = os.path.join(etc_dir, 'conf_file.conf')
         local_conf_file = os.path.join(etc_dir, 'tempest.conf')
-        local_testr_conf = os.path.join(fake_local_dir.path, '.testr.conf')
+        local_stestr_conf = os.path.join(fake_local_dir.path, '.stestr.conf')
         self.assertTrue(os.path.isfile(fake_file_moved))
         self.assertTrue(os.path.isfile(local_conf_file))
-        self.assertTrue(os.path.isfile(local_testr_conf))
+        self.assertTrue(os.path.isfile(local_stestr_conf))
 
     def test_take_action_fails(self):
         class ParsedArgs(object):
diff --git a/tempest/tests/files/setup.cfg b/tempest/tests/files/setup.cfg
index f6f9f73..bd68708 100644
--- a/tempest/tests/files/setup.cfg
+++ b/tempest/tests/files/setup.cfg
@@ -4,7 +4,7 @@
 summary = Fake Project for testing wrapper scripts
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
-home-page = http://www.openstack.org/
+home-page = https://docs.openstack.org/tempest/latest/
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
diff --git a/tempest/tests/files/testr-conf b/tempest/tests/files/testr-conf
index d5ad083..63b3c44 100644
--- a/tempest/tests/files/testr-conf
+++ b/tempest/tests/files/testr-conf
@@ -1,5 +1,3 @@
 [DEFAULT]
-test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./tests $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
+test_path=./tests
 group_regex=([^\.]*\.)*
diff --git a/tempest/tests/services/object_storage/test_object_client.py b/tempest/tests/services/object_storage/test_object_client.py
deleted file mode 100644
index 86535f9..0000000
--- a/tempest/tests/services/object_storage/test_object_client.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# Copyright 2016 IBM Corp.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-
-import mock
-
-from tempest.lib import exceptions
-from tempest.services.object_storage import object_client
-from tempest.tests import base
-from tempest.tests.lib import fake_auth_provider
-
-
-class TestObjectClient(base.TestCase):
-
-    def setUp(self):
-        super(TestObjectClient, self).setUp()
-        self.fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.url = self.fake_auth.base_url(None)
-        self.object_client = object_client.ObjectClient(self.fake_auth,
-                                                        'swift', 'region1')
-
-    @mock.patch.object(object_client, '_create_connection')
-    def test_create_object_continue_no_data(self, mock_poc):
-        self._validate_create_object_continue(None, mock_poc)
-
-    @mock.patch.object(object_client, '_create_connection')
-    def test_create_object_continue_with_data(self, mock_poc):
-        self._validate_create_object_continue('hello', mock_poc)
-
-    @mock.patch.object(object_client, '_create_connection')
-    def test_create_continue_with_no_continue_received(self, mock_poc):
-        self._validate_create_object_continue('hello', mock_poc,
-                                              initial_status=201)
-
-    def _validate_create_object_continue(self, req_data,
-                                         mock_poc, initial_status=100):
-
-        expected_hdrs = {
-            'X-Auth-Token': self.fake_auth.get_token(),
-            'content-length': 0 if req_data is None else len(req_data),
-            'Expect': '100-continue'}
-
-        # Setup the Mocks prior to invoking the object creation
-        mock_resp_cls = mock.Mock()
-        mock_resp_cls._read_status.return_value = ("1", initial_status, "OK")
-
-        mock_poc.return_value.response_class.return_value = mock_resp_cls
-
-        # This is the final expected return value
-        mock_poc.return_value.getresponse.return_value.status = 201
-        mock_poc.return_value.getresponse.return_value.reason = 'OK'
-
-        # Call method to PUT object using expect:100-continue
-        cnt = "container1"
-        obj = "object1"
-        path = "/%s/%s" % (cnt, obj)
-
-        # If the expected initial status is not 100, then an exception
-        # should be thrown and the connection closed
-        if initial_status is 100:
-            status, reason = \
-                self.object_client.create_object_continue(cnt, obj, req_data)
-        else:
-            self.assertRaises(exceptions.UnexpectedResponseCode,
-                              self.object_client.create_object_continue, cnt,
-                              obj, req_data)
-            mock_poc.return_value.close.assert_called_once_with()
-
-        # Verify that putrequest is called 1 time with the appropriate values
-        mock_poc.return_value.putrequest.assert_called_once_with('PUT', path)
-
-        # Verify that headers were written, including "Expect:100-continue"
-        calls = []
-
-        for header, value in expected_hdrs.items():
-            calls.append(mock.call(header, value))
-
-        mock_poc.return_value.putheader.assert_has_calls(calls, False)
-        mock_poc.return_value.endheaders.assert_called_once_with()
-
-        # The following steps are only taken if the initial status is 100
-        if initial_status is 100:
-            # Verify that the method returned what it was supposed to
-            self.assertEqual(status, 201)
-
-            # Verify that _safe_read was called once to remove the CRLF
-            # after the 100 response
-            mock_rc = mock_poc.return_value.response_class.return_value
-            mock_rc._safe_read.assert_called_once_with(2)
-
-            # Verify the actual data was written via send
-            mock_poc.return_value.send.assert_called_once_with(req_data)
-
-            # Verify that the getresponse method was called to receive
-            # the final
-            mock_poc.return_value.getresponse.assert_called_once_with()
diff --git a/tox.ini b/tox.ini
index 892b6f4..9103175 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,6 +16,9 @@
 setenv =
     VIRTUAL_ENV={envdir}
     OS_LOG_CAPTURE=1
+    OS_STDOUT_CAPTURE=1
+    OS_STDERR_CAPTURE=1
+    OS_TEST_TIMEOUT=160
     PYTHONWARNINGS=default::DeprecationWarning
 passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST
 usedevelop = True
@@ -143,7 +146,7 @@
   -r{toxinidir}/doc/requirements.txt
 commands =
   rm -rf doc/build
-  sphinx-build -b html doc/source doc/build/html
+  sphinx-build -W -b html doc/source doc/build/html
 whitelist_externals = rm
 
 [testenv:pep8]