Merge "Fix tests for snapshots extended_snapshot_attributes policy"
diff --git a/.zuul.yaml b/.zuul.yaml
index 387c042..089ba6e 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -1,7 +1,13 @@
 - job:
     name: patrole-base
-    parent: legacy-dsvm-base
+    parent: devstack-tempest
+    description: Patrole base job for admin and Member roles.
+    required-projects:
+      - name: openstack/tempest
+      - name: openstack/patrole
     timeout: 7800
+    roles:
+      - zuul: openstack-dev/devstack
     irrelevant-files:
       - ^(test-|)requirements.txt$
       - ^.*\.rst$
@@ -9,10 +15,17 @@
       - ^patrole/patrole_tempest_plugin/tests/unit/.*$
       - ^releasenotes/.*
       - ^setup.cfg$
-    required-projects:
-      - openstack-infra/devstack-gate
-      - openstack/patrole
-      - openstack/tempest
+    vars:
+      devstack_localrc:
+        TEMPEST_PLUGINS: "'{{ ansible_user_dir }}/src/git.openstack.org/openstack/patrole'"
+      devstack_plugins:
+        patrole: git://git.openstack.org/openstack/patrole.git
+      devstack_services:
+        tempest: true
+        neutron: true
+      tempest_concurrency: 2
+      tempest_test_regex: (?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)
+      tox_envlist: all-plugin
 
 - job:
     name: patrole-base-multinode
@@ -33,14 +46,18 @@
 - job:
     name: patrole-admin
     parent: patrole-base
-    run: playbooks/patrole-admin/run.yaml
-    post-run: playbooks/patrole-admin/post.yaml
+    description: Patrole job for admin role.
+    vars:
+      devstack_localrc:
+        RBAC_TEST_ROLE: admin
 
 - job:
     name: patrole-member
     parent: patrole-base
-    run: playbooks/patrole-member/run.yaml
-    post-run: playbooks/patrole-member/post.yaml
+    description: Patrole job for Member role.
+    vars:
+      devstack_localrc:
+        RBAC_TEST_ROLE: Member
 
 - job:
     name: patrole-multinode-admin
@@ -61,8 +78,20 @@
 - job:
     name: patrole-py35-member
     parent: patrole-base
-    run: playbooks/patrole-py35-member/run.yaml
-    post-run: playbooks/patrole-py35-member/post.yaml
+    description: Patrole py3 job for Member role.
+    vars:
+      devstack_localrc:
+        # Use Member for py3 because arguably negative testing is more
+        # important than admin, which is already covered by patrole-admin job.
+        RBAC_TEST_ROLE: Member
+        USE_PYTHON3: true
+      devstack_services:
+        s-account: false
+        s-container: false
+        s-object: false
+        s-proxy: false
+        # Without Swift, c-bak cannot run (in the gate at least).
+        c-bak: false
 
 - project:
     check:
@@ -72,8 +101,10 @@
         - patrole-py35-member
         - patrole-multinode-admin
         - patrole-multinode-member
+        - openstack-tox-lower-constraints
     gate:
       jobs:
         - patrole-admin
         - patrole-member
         - patrole-py35-member
+        - openstack-tox-lower-constraints
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 308c12c..be3264e 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -14,6 +14,7 @@
    installation
    configuration
    usage
+   testing
    sampleconf
 
 Developer's Guide
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index c244152..b9cc924 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -10,12 +10,13 @@
 At the command line::
 
     $ git clone http://git.openstack.org/openstack/patrole
-    $ sudo pip install patrole
+    $ sudo pip install ./patrole
 
 Or, if you have virtualenvwrapper installed::
 
-    $ mkvirtualenv patrole
-    $ sudo pip install patrole
+    $ mkvirtualenv patrole_env
+    $ workon patrole_env
+    $ pip install ./patrole
 
 Or to install from the source::
 
diff --git a/doc/source/testing.rst b/doc/source/testing.rst
new file mode 100644
index 0000000..d61c78d
--- /dev/null
+++ b/doc/source/testing.rst
@@ -0,0 +1,51 @@
+.. _patrole-testing:
+
+===============
+Patrole Testing
+===============
+
+Testing Scope
+=============
+
+Patrole testing scope is strictly confined to Role-Based Access Control
+(RBAC). In OpenStack, ``oslo.policy`` is the RBAC library used by all
+major services. Thus, Patrole is concerned with validating that public API
+endpoints are correctly using ``oslo.policy`` for authorization.
+
+In other words, all tests in Patrole are RBAC tests.
+
+Stable Tests
+============
+
+In the discussion below, "correct" means that a test is consistent with
+a service's API-to-policy mapping and "stable" means that a test should
+require minimal maintenance for the supported releases.
+
+Present
+-------
+
+During the Queens release, a `governance spec`_ was pushed to support policy
+in code, which documents the mapping between APIs and each of their policies.
+
+This documentation is an important prerequisite for ensuring that Patrole
+tests for a given service are correct. This mapping can be referenced to
+confirm that Patrole's assumed mapping for a test is correct. For
+example, Nova has implemented policy in code which can be used to verify
+that Patrole's Nova RBAC tests use the same mapping.
+
+If a given service does not have policy in code, this implies that it is
+*more likely* that the RBAC tests for that service are inconsistent with the
+*intended* policy mapping. Until that service implements policy in code, it
+is difficult for Patrole maintainers to verify that tests for that service
+are correct.
+
+Future
+------
+
+Once all services that Patrole tests have implemented policy in code --
+and once Patrole has updated all its tests in accordance with the policy in
+code documentation -- then Patrole tests can guaranteed to be stable.
+
+This stability will be denoted with a 1.0 version release.
+
+.. _governance spec: https://governance.openstack.org/tc/goals/queens/policy-in-code.html
diff --git a/lower-constraints.txt b/lower-constraints.txt
new file mode 100644
index 0000000..a5ff1ca
--- /dev/null
+++ b/lower-constraints.txt
@@ -0,0 +1,84 @@
+alabaster==0.7.10
+appdirs==1.4.3
+asn1crypto==0.24.0
+Babel==2.5.3
+bcrypt==3.1.4
+certifi==2018.1.18
+cffi==1.11.5
+chardet==3.0.4
+cliff==2.11.0
+cmd2==0.8.1
+coverage==4.5.1
+cryptography==2.1.4
+debtcollector==1.19.0
+docutils==0.14
+dulwich==0.19.0
+extras==1.0.0
+fasteners==0.14.1
+fixtures==3.0.0
+flake8==2.5.5
+future==0.16.0
+hacking==1.0.0
+idna==2.6
+imagesize==1.0.0
+iso8601==0.1.12
+Jinja2==2.10
+jsonschema==2.6.0
+keystoneauth1==3.4.0
+linecache2==1.0.0
+MarkupSafe==1.0
+mccabe==0.2.1
+mock==2.0.0
+monotonic==1.4
+mox3==0.25.0
+msgpack==0.5.6
+netaddr==0.7.19
+netifaces==0.10.6
+nose==1.3.7
+nosexcover==1.0.11
+openstackdocstheme==1.20.0
+os-client-config==1.29.0
+oslo.concurrency==3.26.0
+oslo.config==5.2.0
+oslo.context==2.20.0
+oslo.i18n==3.20.0
+oslo.log==3.37.0
+oslo.policy==1.34.0
+oslo.serialization==2.25.0
+oslo.utils==3.36.0
+oslotest==3.3.0
+paramiko==2.4.1
+pbr==3.1.1
+pep8==1.5.7
+prettytable==0.7.2
+pyasn1==0.4.2
+pycparser==2.18
+pyflakes==0.8.1
+Pygments==2.2.0
+pyinotify==0.9.6
+PyNaCl==1.2.1
+pyparsing==2.2.0
+pyperclip==1.6.0
+python-dateutil==2.7.0
+python-mimeparse==1.6.0
+python-subunit==1.2.0
+pytz==2018.3
+PyYAML==3.12
+reno==2.7.0
+requests==2.18.4
+requestsexceptions==1.4.0
+rfc3986==1.1.0
+six==1.11.0
+snowballstemmer==1.2.1
+Sphinx==1.6.5
+sphinxcontrib-websupport==1.0.1
+stestr==2.0.0
+stevedore==1.28.0
+tempest==18.0.0
+testrepository==0.0.20
+testtools==2.3.0
+traceback2==1.4.0
+unittest2==1.1.0
+urllib3==1.22
+voluptuous==0.11.1
+wrapt==1.10.11
diff --git a/patrole_tempest_plugin/tests/api/network/test_networks_multiprovider_rbac.py b/patrole_tempest_plugin/tests/api/network/test_network_segments_rbac.py
similarity index 76%
rename from patrole_tempest_plugin/tests/api/network/test_networks_multiprovider_rbac.py
rename to patrole_tempest_plugin/tests/api/network/test_network_segments_rbac.py
index 9c65c14..1dee46b 100644
--- a/patrole_tempest_plugin/tests/api/network/test_networks_multiprovider_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_network_segments_rbac.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from oslo_log import log
+
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
@@ -26,18 +27,34 @@
 LOG = log.getLogger(__name__)
 
 
-class NetworksMultiProviderRbacTest(base.BaseNetworkRbacTest):
+class NetworkSegmentsRbacTest(base.BaseNetworkRbacTest):
 
     @classmethod
     def skip_checks(cls):
-        super(NetworksMultiProviderRbacTest, cls).skip_checks()
+        super(NetworkSegmentsRbacTest, cls).skip_checks()
         if not utils.is_extension_enabled('multi-provider', 'network'):
             msg = "multi-provider extension not enabled."
             raise cls.skipException(msg)
 
+    @classmethod
+    def resource_setup(cls):
+        super(NetworkSegmentsRbacTest, cls).resource_setup()
+        # Find the network type that is supported by the current cloud by
+        # checking which network type other networks currently have. This is
+        # done because there is no tempest.conf option enumerating supported
+        # network types.
+        networks = cls.networks_client.list_networks()['networks']
+        network_types = [n['provider:network_type'] for n in networks
+                         if n['provider:network_type'] != 'flat']
+        if not network_types:
+            raise cls.skipException(
+                'Could not find network with provider:network_type that is '
+                'not "flat".')
+        cls.network_type = network_types[0]
+
     def _create_network_segments(self):
-        segments = [{"provider:network_type": "gre"},
-                    {"provider:network_type": "gre"}]
+        segments = [{'provider:network_type': self.network_type},
+                    {'provider:network_type': self.network_type}]
 
         body = self.networks_client.create_network(
             name=data_utils.rand_name(self.__class__.__name__),
@@ -68,7 +85,7 @@
         RBAC test for the neutron update_network:segments policy
         """
         network = self._create_network_segments()
-        new_segments = [{"provider:network_type": "gre"}]
+        new_segments = [{'provider:network_type': self.network_type}]
 
         with self.rbac_utils.override_role(self):
             self.networks_client.update_network(network['id'],
@@ -92,7 +109,7 @@
         # If user does not have access to the network segments attribute,
         # no NotFound or Forbidden exception are thrown.  Instead,
         # the response will have an empty network body only.
-        if len(response_network) == 0:
+        if not response_network:
             LOG.info("NotFound or Forbidden exception are not thrown when "
                      "role doesn't have access to the endpoint. Instead, "
                      "the response will have an empty network body.")
diff --git a/playbooks/patrole-admin/post.yaml b/playbooks/patrole-admin/post.yaml
deleted file mode 100644
index dac8753..0000000
--- a/playbooks/patrole-admin/post.yaml
+++ /dev/null
@@ -1,80 +0,0 @@
-- hosts: primary
-  tasks:
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*nose_results.html
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*testr_results.html.gz
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/.testrepository/tmp*
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*testrepository.subunit.gz
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}/tox'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/.tox/*/log/*
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/logs/**
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
diff --git a/playbooks/patrole-admin/run.yaml b/playbooks/patrole-admin/run.yaml
deleted file mode 100644
index 57f208d..0000000
--- a/playbooks/patrole-admin/run.yaml
+++ /dev/null
@@ -1,60 +0,0 @@
-- hosts: all
-  name: Autoconverted job legacy-tempest-dsvm-patrole-admin from old job gate-tempest-dsvm-patrole-admin-ubuntu-xenial
-  tasks:
-
-    - name: Ensure legacy workspace directory
-      file:
-        path: '{{ ansible_user_dir }}/workspace'
-        state: directory
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          cat > clonemap.yaml << EOF
-          clonemap:
-            - name: openstack-infra/devstack-gate
-              dest: devstack-gate
-          EOF
-          /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
-              git://git.openstack.org \
-              openstack-infra/devstack-gate
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          cat << 'EOF' >>"/tmp/dg-local.conf"
-          [[local|localrc]]
-          enable_plugin patrole git://git.openstack.org/openstack/patrole
-          TEMPEST_PLUGINS='/opt/stack/new/patrole'
-          # Needed by Patrole devstack plugin
-          RBAC_TEST_ROLE=admin
-          EOF
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          export PYTHONUNBUFFERED=true
-          export DEVSTACK_GATE_TEMPEST=1
-          export DEVSTACK_GATE_NEUTRON=1
-          export DEVSTACK_GATE_TEMPEST_REGEX='(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
-          export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
-          export TEMPEST_CONCURRENCY=2
-          export PROJECTS="openstack/patrole $PROJECTS"
-          export BRANCH_OVERRIDE=default
-          if [ "$BRANCH_OVERRIDE" != "default" ] ; then
-              export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
-          fi
-          cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
-          ./safe-devstack-vm-gate-wrap.sh
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
diff --git a/playbooks/patrole-member/post.yaml b/playbooks/patrole-member/post.yaml
deleted file mode 100644
index dac8753..0000000
--- a/playbooks/patrole-member/post.yaml
+++ /dev/null
@@ -1,80 +0,0 @@
-- hosts: primary
-  tasks:
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*nose_results.html
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*testr_results.html.gz
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/.testrepository/tmp*
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*testrepository.subunit.gz
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}/tox'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/.tox/*/log/*
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/logs/**
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
diff --git a/playbooks/patrole-member/run.yaml b/playbooks/patrole-member/run.yaml
deleted file mode 100644
index b95467f..0000000
--- a/playbooks/patrole-member/run.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-- hosts: all
-  name: Autoconverted job legacy-tempest-dsvm-patrole-member from old job gate-tempest-dsvm-patrole-member-ubuntu-xenial
-  tasks:
-
-    - name: Ensure legacy workspace directory
-      file:
-        path: '{{ ansible_user_dir }}/workspace'
-        state: directory
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          cat > clonemap.yaml << EOF
-          clonemap:
-            - name: openstack-infra/devstack-gate
-              dest: devstack-gate
-          EOF
-          /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
-              git://git.openstack.org \
-              openstack-infra/devstack-gate
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          cat << 'EOF' >>"/tmp/dg-local.conf"
-          [[local|localrc]]
-          enable_plugin patrole git://git.openstack.org/openstack/patrole
-          TEMPEST_PLUGINS='/opt/stack/new/patrole'
-          # Needed by Patrole devstack plugin
-          RBAC_TEST_ROLE=member
-          EOF
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          export PYTHONUNBUFFERED=true
-          export DEVSTACK_GATE_TEMPEST=1
-          export DEVSTACK_GATE_NEUTRON=1
-          export DEVSTACK_GATE_TEMPEST_REGEX='(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
-          export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
-          export TEMPEST_CONCURRENCY=2
-          export PROJECTS="openstack/patrole $PROJECTS"
-          export BRANCH_OVERRIDE=default
-          if [ "$BRANCH_OVERRIDE" != "default" ] ; then
-              export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
-          fi
-          cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
-          ./safe-devstack-vm-gate-wrap.sh
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
-
diff --git a/playbooks/patrole-py35-member/post.yaml b/playbooks/patrole-py35-member/post.yaml
deleted file mode 100644
index dac8753..0000000
--- a/playbooks/patrole-py35-member/post.yaml
+++ /dev/null
@@ -1,80 +0,0 @@
-- hosts: primary
-  tasks:
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*nose_results.html
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*testr_results.html.gz
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/.testrepository/tmp*
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=**/*testrepository.subunit.gz
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}/tox'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/.tox/*/log/*
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
-
-    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
-      synchronize:
-        src: '{{ ansible_user_dir }}/workspace/'
-        dest: '{{ zuul.executor.log_root }}'
-        mode: pull
-        copy_links: true
-        verify_host: true
-        rsync_opts:
-          - --include=/logs/**
-          - --include=*/
-          - --exclude=*
-          - --prune-empty-dirs
diff --git a/playbooks/patrole-py35-member/run.yaml b/playbooks/patrole-py35-member/run.yaml
deleted file mode 100644
index e895702..0000000
--- a/playbooks/patrole-py35-member/run.yaml
+++ /dev/null
@@ -1,70 +0,0 @@
-- hosts: all
-  name: Autoconverted job legacy-tempest-dsvm-patrole-py35-member from old job gate-tempest-dsvm-patrole-py35-member-ubuntu-xenial
-  tasks:
-
-    - name: Ensure legacy workspace directory
-      file:
-        path: '{{ ansible_user_dir }}/workspace'
-        state: directory
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          cat > clonemap.yaml << EOF
-          clonemap:
-            - name: openstack-infra/devstack-gate
-              dest: devstack-gate
-          EOF
-          /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
-              git://git.openstack.org \
-              openstack-infra/devstack-gate
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          cat << 'EOF' >>"/tmp/dg-local.conf"
-          [[local|localrc]]
-          enable_plugin patrole git://git.openstack.org/openstack/patrole
-          TEMPEST_PLUGINS='/opt/stack/new/patrole'
-          # Needed by Patrole devstack plugin
-          RBAC_TEST_ROLE=member
-          # Swift is not ready for python3 yet
-          disable_service s-account
-          disable_service s-container
-          disable_service s-object
-          disable_service s-proxy
-          # Without Swift, c-bak cannot run (in the Gate at least)
-          disable_service c-bak
-          EOF
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
-
-    - shell:
-        cmd: |
-          set -e
-          set -x
-          export PYTHONUNBUFFERED=true
-          export DEVSTACK_GATE_USE_PYTHON3=True
-          # Ensure that tempest set up is executed, but do not automatically
-          # execute tempest tests; they are executed in post_test_hook.
-          export DEVSTACK_GATE_TEMPEST=1
-          export DEVSTACK_GATE_NEUTRON=1
-          export DEVSTACK_GATE_TEMPEST_REGEX='(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
-          export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
-          export TEMPEST_CONCURRENCY=2
-          export PROJECTS="openstack/patrole $PROJECTS"
-          export BRANCH_OVERRIDE=default
-          if [ "$BRANCH_OVERRIDE" != "default" ] ; then
-              export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
-          fi
-          cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
-          ./safe-devstack-vm-gate-wrap.sh
-        executable: /bin/bash
-        chdir: '{{ ansible_user_dir }}/workspace'
-      environment: '{{ zuul | zuul_legacy_vars }}'
diff --git a/requirements.txt b/requirements.txt
index 35c6038..cc13aa9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@
 # process, which may cause wedges in the gate later.
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
 oslo.log>=3.36.0 # Apache-2.0
-oslo.config>=5.1.0 # Apache-2.0
+oslo.config>=5.2.0 # Apache-2.0
 oslo.policy>=1.30.0 # Apache-2.0
 tempest>=17.1.0 # Apache-2.0
 stevedore>=1.20.0 # Apache-2.0
diff --git a/test-requirements.txt b/test-requirements.txt
index add2388..475d1e5 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,7 +3,7 @@
 # process, which may cause wedges in the gate later.
 hacking>=1.0.0 # Apache-2.0
 
-sphinx!=1.6.6,>=1.6.2 # BSD
+sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
 openstackdocstheme>=1.18.1 # Apache-2.0
 reno>=2.5.0 # Apache-2.0
 fixtures>=3.0.0 # Apache-2.0/BSD
diff --git a/tox.ini b/tox.ini
index d5e3b91..3c2c8ad 100644
--- a/tox.ini
+++ b/tox.ini
@@ -78,3 +78,10 @@
 
 [hacking]
 local-check-factory = patrole_tempest_plugin.hacking.checks.factory
+
+[testenv:lower-constraints]
+basepython = python3
+deps =
+  -c{toxinidir}/lower-constraints.txt
+  -r{toxinidir}/test-requirements.txt
+  -r{toxinidir}/requirements.txt