Merge "compute: Use wait_for_volume_attachment_remove when detaching multiattach volumes"
diff --git a/.zuul.yaml b/.zuul.yaml
index f749b74..02570a6 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -100,6 +100,7 @@
       devstack_localrc:
         ENABLE_FILE_INJECTION: true
         ENABLE_VOLUME_MULTIATTACH: true
+        USE_PYTHON3: False
       devstack_services:
         # NOTE(mriedem): Disable the cinder-backup service from tempest-full
         # since tempest-full is in the integrated-gate project template but
@@ -138,6 +139,9 @@
       - opendev.org/openstack/oslo.utils
       - opendev.org/openstack/oslo.versionedobjects
       - opendev.org/openstack/oslo.vmware
+    vars:
+      devstack_localrc:
+        USE_PYTHON3: True
 
 - job:
     name: tempest-full-parallel
@@ -146,11 +150,13 @@
     branches:
       - master
     description: |
-      Base integration test with Neutron networking and py27.
+      Base integration test with Neutron networking.
       It includes all scenarios as it was in the past.
       This job runs all scenario tests in parallel!
     vars:
       tox_envlist: full-parallel
+      devstack_localrc:
+        USE_PYTHON3: True
 
 - job:
     name: tempest-full-py3
@@ -345,6 +351,13 @@
       - stable/pike
       - stable/queens
       - stable/rocky
+    vars:
+      devstack_localrc:
+        USE_PYTHON3: False
+    group-vars:
+      subnode:
+        devstack_localrc:
+          USE_PYTHON3: False
 
 - job:
     name: tempest-multinode-full-py3
@@ -426,32 +439,16 @@
           USE_PYTHON3: true
 
 - job:
-    name: tempest-full-train
-    parent: tempest-full
-    override-checkout: stable/train
-
-- job:
     name: tempest-full-train-py3
     parent: tempest-full-py3
     override-checkout: stable/train
 
 - job:
-    name: tempest-full-stein
-    parent: tempest-full
-    override-checkout: stable/stein
-
-- job:
     name: tempest-full-stein-py3
     parent: tempest-full-py3
     override-checkout: stable/stein
 
 - job:
-    name: tempest-full-rocky
-    parent: tempest-full
-    nodeset: openstack-single-node-xenial
-    override-checkout: stable/rocky
-
-- job:
     name: tempest-full-rocky-py3
     parent: tempest-full-py3
     nodeset: openstack-single-node-xenial
@@ -511,12 +508,13 @@
     name: tempest-pg-full
     parent: tempest-full
     description: |
-      Base integration test with Neutron networking and py27 and PostgreSQL.
+      Base integration test with Neutron networking and PostgreSQL.
       Former name for this job was legacy-tempest-dsvm-neutron-pg-full.
     vars:
       devstack_localrc:
         ENABLE_FILE_INJECTION: true
         DATABASE_TYPE: postgresql
+        USE_PYTHON3: True
 
 - project-template:
     name: integrated-gate-networking
@@ -600,10 +598,9 @@
 - project:
     templates:
       - check-requirements
-      - integrated-gate
       - integrated-gate-py3
       - openstack-cover-jobs
-      - openstack-python3-train-jobs
+      - openstack-python3-ussuri-jobs
       - publish-openstack-docs-pti
       - release-notes-jobs-python3
     check:
@@ -639,22 +636,12 @@
         - tempest-full-py3-ipv6:
             voting: false
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-train:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-full-train-py3:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-stein:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-full-stein-py3:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-rocky:
-            irrelevant-files: *tempest-irrelevant-files
-            voting: false
         - tempest-full-rocky-py3:
             irrelevant-files: *tempest-irrelevant-files
-            voting: false
-        - tempest-multinode-full:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-multinode-full-py3:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-tox-plugin-sanity-check:
@@ -673,8 +660,6 @@
               # tools/ is not here since this relies on a script in tools/.
         - tempest-ipv6-only:
             irrelevant-files: *tempest-irrelevant-files-2
-        - tempest-slow:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-slow-py3:
             irrelevant-files: *tempest-irrelevant-files
         - nova-live-migration:
@@ -701,8 +686,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - neutron-tempest-dvr:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full:
-            irrelevant-files: *tempest-irrelevant-files
         - interop-tempest-consistency:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-test-account-py3:
@@ -719,7 +702,7 @@
             irrelevant-files: *tempest-irrelevant-files
         - neutron-grenade-multinode:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full:
+        - tempest-full-py3:
             irrelevant-files: *tempest-irrelevant-files
         - grenade-py3:
             irrelevant-files: *tempest-irrelevant-files
@@ -747,11 +730,8 @@
             irrelevant-files: *tempest-irrelevant-files
     periodic-stable:
       jobs:
-        - tempest-full-train
         - tempest-full-train-py3
-        - tempest-full-stein
         - tempest-full-stein-py3
-        - tempest-full-rocky
         - tempest-full-rocky-py3
     periodic:
       jobs:
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 2194dc4..9f38ada 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -3,6 +3,5 @@
 # process, which may cause wedges in the gate later.
 openstackdocstheme>=1.20.0 # Apache-2.0
 reno>=2.5.0 # Apache-2.0
-sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
-sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2;python_version>='3.4' # BSD
+sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2 # BSD
 sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD
diff --git a/doc/source/supported_version.rst b/doc/source/supported_version.rst
index a97ec5b..4f65fd4 100644
--- a/doc/source/supported_version.rst
+++ b/doc/source/supported_version.rst
@@ -1,7 +1,7 @@
 Supported OpenStack Releases and Python Versions
 ================================================
 
-This Document list the officially supported OpenStack releases
+This document lists the officially supported OpenStack releases
 and python versions by Tempest.
 
 Compatible OpenStack Releases
@@ -24,13 +24,13 @@
 * https://releases.openstack.org/stein/index.html#stein-tempest
 
 How to use Tempest tag on Extended Maintenance stable branch:
-* https://review.opendev.org/#/c/681950/
+
+* https://review.opendev.org/#/c/705098/
 
 Supported Python Versions
 -------------------------
 
 Tempest master supports the below python versions:
 
-* Python 2.7
 * Python 3.6
 * Python 3.7
diff --git a/releasenotes/notes/drop-py-2-7-730baf411876d5d8.yaml b/releasenotes/notes/drop-py-2-7-730baf411876d5d8.yaml
new file mode 100644
index 0000000..a0ac244
--- /dev/null
+++ b/releasenotes/notes/drop-py-2-7-730baf411876d5d8.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    Python 2.7 support has been dropped. Last release of Tempest
+    to support python 2.7 is Temepst 23.0.0. The minimum version of Python now
+    supported by Tempest is Python 3.6.
diff --git a/releasenotes/notes/os_tenant_name-3ee175763bff455b.yaml b/releasenotes/notes/os_tenant_name-3ee175763bff455b.yaml
new file mode 100644
index 0000000..936bf1f
--- /dev/null
+++ b/releasenotes/notes/os_tenant_name-3ee175763bff455b.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - |
+    Remove the deprecated argument ``os-tenant-name`` or ``OS_TENANT_NAME`` in favour of
+    ``os-project-name`` argument.
diff --git a/releasenotes/notes/verify-tempest-command-8e88452c7a08dd77.yaml b/releasenotes/notes/verify-tempest-command-8e88452c7a08dd77.yaml
new file mode 100644
index 0000000..ce401ff
--- /dev/null
+++ b/releasenotes/notes/verify-tempest-command-8e88452c7a08dd77.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+  - |
+    Remove the deprecated CLI ``verify-tempest-config`` in favour of
+    ``tempest verify-config`` command.
+    You can use ``tempest verify-config`` CLI to verify the tempest
+    conf file.
diff --git a/setup.cfg b/setup.cfg
index dd30069..d246c68 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,6 +6,7 @@
 author = OpenStack
 author-email = openstack-discuss@lists.openstack.org
 home-page = https://docs.openstack.org/tempest/latest/
+requires-python = >=3.6
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
@@ -13,11 +14,11 @@
     License :: OSI Approved :: Apache Software License
     Operating System :: POSIX :: Linux
     Programming Language :: Python
-    Programming Language :: Python :: 2
-    Programming Language :: Python :: 2.7
     Programming Language :: Python :: 3
     Programming Language :: Python :: 3.6
     Programming Language :: Python :: 3.7
+    Programming Language :: Python :: 3 :: Only
+    Programming Language :: Python :: Implementation :: CPython
 
 [files]
 packages =
@@ -27,7 +28,6 @@
 
 [entry_points]
 console_scripts =
-    verify-tempest-config = tempest.cmd.verify_tempest_config:main
     tempest-account-generator = tempest.cmd.account_generator:main
     tempest = tempest.cmd.main:main
     skip-tracker = tempest.lib.cmd.skip_tracker:main
diff --git a/setup.py b/setup.py
index 566d844..f63cc23 100644
--- a/setup.py
+++ b/setup.py
@@ -16,14 +16,6 @@
 # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
 import setuptools
 
-# In python < 2.7.4, a lazy loading of package `pbr` will break
-# setuptools if some other modules registered functions in `atexit`.
-# solution from: http://bugs.python.org/issue15881#msg170215
-try:
-    import multiprocessing  # noqa
-except ImportError:
-    pass
-
 setuptools.setup(
     setup_requires=['pbr>=2.0.0'],
     pbr=True)
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 8aab574..1f7eb7b 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -358,7 +358,8 @@
             validation_resources=validation_resources,
             config_drive=config_drive_enabled,
             name=data_utils.rand_name('device-tagging-server'),
-            networks=[{'uuid': self.get_tenant_network()['id']}])
+            networks=[{'uuid': self.get_tenant_network()['id']}],
+            wait_until='ACTIVE')
         self.addCleanup(self.delete_server, server['id'])
 
         # NOTE(mgoddard): Get detailed server to ensure addresses are present
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 7ea0099..1535786 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -46,7 +46,6 @@
 Username ``--os-username``            OS_USERNAME
 Password ``--os-password``            OS_PASSWORD
 Project  ``--os-project-name``        OS_PROJECT_NAME
-Tenant   ``--os-tenant-name`` (depr.) OS_TENANT_NAME
 Domain   ``--os-domain-name``         OS_DOMAIN_NAME
 ======== ============================ ====================
 
@@ -75,9 +74,6 @@
 * ``--os-project-name <auth-project-name>`` (Optional) Project to request
   authorization on. Defaults to env[OS_PROJECT_NAME].
 
-* ``--os-tenant-name <auth-tenant-name>`` (Optional, deprecated) Tenant to
-  request authorization on. Defaults to env[OS_TENANT_NAME].
-
 * ``--os-domain-name <auth-domain-name>`` (Optional) Domain the user and
   project belong to. Defaults to env[OS_DOMAIN_NAME].
 
@@ -139,7 +135,7 @@
                          'dhcp': True}
     admin_creds_dict = {'username': opts.os_username,
                         'password': opts.os_password}
-    _project_name = opts.os_project_name or opts.os_tenant_name
+    _project_name = opts.os_project_name
     if opts.identity_version == 3:
         admin_creds_dict['project_name'] = _project_name
         admin_creds_dict['domain_name'] = opts.os_domain_name or 'Default'
@@ -221,10 +217,6 @@
                         metavar='<auth-project-name>',
                         default=os.environ.get('OS_PROJECT_NAME'),
                         help='Defaults to env[OS_PROJECT_NAME].')
-    parser.add_argument('--os-tenant-name',
-                        metavar='<auth-tenant-name>',
-                        default=os.environ.get('OS_TENANT_NAME'),
-                        help='Defaults to env[OS_TENANT_NAME].')
     parser.add_argument('--os-domain-name',
                         metavar='<auth-domain-name>',
                         default=os.environ.get('OS_DOMAIN_NAME'),
@@ -301,10 +293,6 @@
     if log_warning:
         LOG.warning("Use of: 'tempest-account-generator' is deprecated, "
                     "please use: 'tempest account-generator'")
-    if opts.os_tenant_name:
-        LOG.warning("'os-tenant-name' and 'OS_TENANT_NAME' are both "
-                    "deprecated, please use 'os-project-name' or "
-                    "'OS_PROJECT_NAME' instead")
     resources = []
     for count in range(opts.concurrency):
         # Use N different cred_providers to obtain different sets of creds
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index d25d3ca..8d5bdbd 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -433,11 +433,6 @@
 
 
 def main(opts=None):
-    print('Running config verification...')
-    if opts is None:
-        print("Use of: 'verify-tempest-config' is deprecated, "
-              "please use: 'tempest verify-config'")
-        opts = parse_args()
     update = opts.update
     replace = opts.replace_ext
     global CONF_PARSER
@@ -497,7 +492,3 @@
             LOG.exception("Failure verifying configuration.")
             traceback.print_exc()
             raise
-
-
-if __name__ == "__main__":
-    main()
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
index b349bba..a962e37 100644
--- a/tempest/tests/cmd/test_account_generator.py
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -28,7 +28,6 @@
         self.os_username = 'fake_user'
         self.os_password = 'fake_password'
         self.os_project_name = 'fake_project_name'
-        self.os_tenant_name = None
         self.os_domain_name = 'fake_domain'
         self.tag = 'fake'
         self.concurrency = 2
@@ -100,15 +99,6 @@
         self.assertEqual(self.opts.os_password, admin_creds.password)
         self.assertFalse(hasattr(admin_creds, 'domain_name'))
 
-    def test_get_credential_provider_with_tenant(self):
-        self.opts.os_project_name = None
-        self.opts.os_tenant_name = 'fake_tenant'
-        cp = account_generator.get_credential_provider(self.opts)
-        admin_creds = cp.default_admin_creds
-        self.assertEqual(self.opts.os_tenant_name, admin_creds.tenant_name)
-        self.assertEqual(self.opts.os_username, admin_creds.username)
-        self.assertEqual(self.opts.os_password, admin_creds.password)
-
 
 class TestAccountGeneratorV3(TestAccountGeneratorV2):
 
diff --git a/tempest/tests/lib/services/network/test_agents_client.py b/tempest/tests/lib/services/network/test_agents_client.py
index aabc6ce..8904882 100644
--- a/tempest/tests/lib/services/network/test_agents_client.py
+++ b/tempest/tests/lib/services/network/test_agents_client.py
@@ -22,12 +22,145 @@
 
     FAKE_AGENT_ID = "d32019d3-bc6e-4319-9c1d-6123f4135a88"
 
+    FAKE_LIST_DATA = {
+        "agents": [
+            {
+                "binary": "neutron-dhcp-agent",
+                "description": None,
+                "availability_zone": "nova",
+                "heartbeat_timestamp": "2017-09-12 19:39:56",
+                "admin_state_up": True,
+                "alive": True,
+                "id": "840d5d68-5759-4e9e-812f",
+                "topic": "dhcp_agent",
+                "host": "agenthost1",
+                "agent_type": "DHCP agent",
+                "started_at": "2017-09-12 19:35:36",
+                "created_at": "2017-09-12 19:35:36",
+                "resources_synced": None,
+                "configurations": {
+                    "subnets": 2,
+                    "dhcp_lease_duration": 86400,
+                    "dhcp_driver": "neutron.agent",
+                    "networks": 1,
+                    "log_agent_heartbeats": False,
+                    "ports": 3
+                }
+            }
+        ]
+    }
+
+    FAKE_SHOW_DATA = {
+        "agent": {
+            "binary": "neutron-openvswitch-agent",
+            "description": None,
+            "availability_zone": None,
+            "heartbeat_timestamp": "2017-09-12 19:40:38",
+            "admin_state_up": True,
+            "alive": True,
+            "id": "04c62b91-b799-48b7-9cd5-2982db6df9c6",
+            "topic": "N/A",
+            "host": "agenthost1",
+            "agent_type": "Open vSwitch agent",
+            "started_at": "2017-09-12 19:35:38",
+            "created_at": "2017-09-12 19:35:38",
+            "resources_synced": True,
+            "configurations": {
+                "ovs_hybrid_plug": True,
+                "in_distributed_mode": False,
+                "datapath_type": "system",
+                "vhostuser_socket_dir": "/var/run/openvswitch",
+                "tunneling_ip": "172.16.78.191",
+                "arp_responder_enabled": False,
+                "devices": 0,
+                "ovs_capabilities": {
+                    "datapath_types": [
+                        "netdev",
+                        "system"
+                    ],
+                    "iface_types": [
+                        "geneve",
+                        "gre",
+                        "internal",
+                        "ipsec_gre",
+                        "lisp",
+                        "patch",
+                        "stt",
+                        "system",
+                        "tap",
+                        "vxlan"
+                    ]
+                },
+                "log_agent_heartbeats": False,
+                "l2_population": False,
+                "tunnel_types": [
+                    "vxlan"
+                ],
+                "extensions": [],
+                "enable_distributed_routing": False,
+                "bridge_mappings": {
+                    "public": "br-ex"
+                }
+            }
+        }
+    }
+
+    FAKE_UPDATE_DATA = {
+        "agent": {
+            "description": "My OVS agent for OpenStack"
+        }
+    }
+
     def setUp(self):
         super(TestAgentsClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
         self.agents_client = agents_client.AgentsClient(
             fake_auth, "network", "regionOne")
 
+    def _test_show_agent(self, bytes_body=False):
+        self.check_service_client_function(
+            self.agents_client.show_agent,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_SHOW_DATA,
+            bytes_body,
+            status=200,
+            agent_id=self.FAKE_AGENT_ID)
+
+    def _test_update_agent(self, bytes_body=False):
+        self.check_service_client_function(
+            self.agents_client.update_agent,
+            "tempest.lib.common.rest_client.RestClient.put",
+            self.FAKE_UPDATE_DATA,
+            bytes_body,
+            status=200,
+            agent_id=self.FAKE_AGENT_ID)
+
+    def _test_list_agents(self, bytes_body=False):
+        self.check_service_client_function(
+            self.agents_client.list_agents,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_LIST_DATA,
+            bytes_body,
+            status=200)
+
+    def test_show_agent_with_str_body(self):
+        self._test_show_agent()
+
+    def test_show_agent_with_bytes_body(self):
+        self._test_show_agent(bytes_body=True)
+
+    def test_update_agent_with_str_body(self):
+        self._test_update_agent()
+
+    def test_update_agent_with_bytes_body(self):
+        self._test_update_agent(bytes_body=True)
+
+    def test_list_agent_with_str_body(self):
+        self._test_list_agents()
+
+    def test_list_agent_with_bytes_body(self):
+        self._test_list_agents(bytes_body=True)
+
     def test_delete_agent(self):
         self.check_service_client_function(
             self.agents_client.delete_agent,
diff --git a/tox.ini b/tox.ini
index 401bbfe..64921ef 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = pep8,py36,py37,py27,bashate,pip-check-reqs
+envlist = pep8,py36,py37,bashate,pip-check-reqs
 minversion = 3.1.1
 skipsdist = True
 ignore_basepython_conflict = True