Merge "Add missing validation for the compute/positive"
diff --git a/.zuul.yaml b/.zuul.yaml
index 71bbeca..7d77b71 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -297,6 +297,16 @@
         c-bak: false
 
 - 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
@@ -514,6 +524,10 @@
         - tempest-full-py3-ipv6:
             voting: false
             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
         - tempest-full-rocky-py3:
@@ -569,8 +583,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - neutron-tempest-dvr:
             irrelevant-files: *tempest-irrelevant-files
-        - legacy-tempest-dsvm-neutron-full-ocata:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-full:
             irrelevant-files: *tempest-irrelevant-files
         - interop-tempest-consistency:
@@ -605,7 +617,7 @@
             irrelevant-files: *tempest-irrelevant-files
         - nova-cells-v1:
             irrelevant-files: *tempest-irrelevant-files
-        - legacy-tempest-dsvm-nova-v20-api:
+        - nova-tempest-v2-api:
             irrelevant-files: *tempest-irrelevant-files
         - legacy-tempest-dsvm-lvm-multibackend:
             irrelevant-files: *tempest-irrelevant-files
@@ -617,6 +629,8 @@
             irrelevant-files: *tempest-irrelevant-files
     periodic-stable:
       jobs:
+        - tempest-full-stein
+        - tempest-full-stein-py3
         - tempest-full-rocky
         - tempest-full-rocky-py3
         - tempest-full-queens
diff --git a/HACKING.rst b/HACKING.rst
index eb6551a..1559fc6 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -28,6 +28,8 @@
 - [T117] Check negative tests have ``@decorators.attr(type=['negative'])``
   applied.
 
+It is recommended to use ``tox -eautopep8`` before submitting a patch.
+
 Test Data/Configuration
 -----------------------
 - Assume nothing about existing test data
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 3be014f..e5d5bfe 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v20.0.0
    v19.0.0
    v18.0.0
    v17.2.0
diff --git a/releasenotes/source/v20.0.0.rst b/releasenotes/source/v20.0.0.rst
new file mode 100644
index 0000000..28c5431
--- /dev/null
+++ b/releasenotes/source/v20.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v20.0.0 Release Notes
+=====================
+
+.. release-notes:: 20.0.0 Release Notes
+   :version: 20.0.0
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index bea23d9..eeb58d6 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -331,6 +331,16 @@
     @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
     @utils.services('network')
     def test_add_remove_fixed_ip(self):
+        # NOTE(zhufl) By default only project that is admin or network owner
+        # or project with role advsvc is authorised to add interfaces with
+        # fixed-ip, so if we don't create network for each project, do not
+        # test
+        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.")
+
         # Add and Remove the fixed IP to server.
         server, ifs = self._create_server_get_interfaces()
         original_interface_count = len(ifs)  # This is the number of ports.
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index 56d973e..e8b1161 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -19,7 +19,6 @@
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -40,11 +39,7 @@
         # If an admin password is provided on server creation, the server's
         # root password should be set to that password.
         server = self.create_test_server(adminPass='testpassword')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
+        self.addCleanup(self.delete_server, server['id'])
 
         # Verify the password is set correctly in the response
         self.assertEqual('testpassword', server['adminPass'])
@@ -59,19 +54,11 @@
         server = self.create_test_server(name=server_name,
                                          wait_until='ACTIVE')
         id1 = server['id']
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, id1)
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, id1)
+        self.addCleanup(self.delete_server, id1)
         server = self.create_test_server(name=server_name,
                                          wait_until='ACTIVE')
         id2 = server['id']
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, id2)
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, id2)
+        self.addCleanup(self.delete_server, id2)
         self.assertNotEqual(id1, id2, "Did not create a new server")
         server = self.client.show_server(id1)['server']
         name1 = server['name']
@@ -87,13 +74,9 @@
         self.keypairs_client.create_keypair(name=key_name)
         self.addCleanup(self.keypairs_client.delete_keypair, key_name)
         self.keypairs_client.list_keypairs()
-        server = self.create_test_server(key_name=key_name)
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
-        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+        server = self.create_test_server(key_name=key_name,
+                                         wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, server['id'])
         server = self.client.show_server(server['id'])['server']
         self.assertEqual(key_name, server['key_name'])
 
@@ -115,11 +98,7 @@
     def test_update_server_name(self):
         # The server name should be changed to the provided value
         server = self.create_test_server(wait_until='ACTIVE')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
+        self.addCleanup(self.delete_server, server['id'])
         # Update instance name with non-ASCII characters
         prefix_name = u'\u00CD\u00F1st\u00E1\u00F1c\u00E9'
         self._update_server_name(server['id'], 'ACTIVE', prefix_name)
@@ -137,11 +116,7 @@
     def test_update_access_server_address(self):
         # The server's access addresses should reflect the provided values
         server = self.create_test_server(wait_until='ACTIVE')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
+        self.addCleanup(self.delete_server, server['id'])
 
         # Update the IPv4 and IPv6 access addresses
         self.client.update_server(server['id'],
@@ -157,13 +132,9 @@
     @decorators.idempotent_id('38fb1d02-c3c5-41de-91d3-9bc2025a75eb')
     def test_create_server_with_ipv6_addr_only(self):
         # Create a server without an IPv4 address(only IPv6 address).
-        server = self.create_test_server(accessIPv6='2001:2001::3')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
-        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+        server = self.create_test_server(accessIPv6='2001:2001::3',
+                                         wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, server['id'])
         server = self.client.show_server(server['id'])['server']
         self.assertEqual('2001:2001::3', server['accessIPv6'])
 
diff --git a/tempest/clients.py b/tempest/clients.py
index e5d5be1..0506646 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -44,6 +44,7 @@
         self._set_object_storage_clients()
         self._set_image_clients()
         self._set_network_clients()
+        self.placement_client = self.placement.PlacementClient()
         # TODO(andreaf) This is maintained for backward compatibility
         # with plugins, but it should removed eventually, since it was
         # never a stable interface and it's not useful anyways
diff --git a/tempest/tests/lib/services/registry_fixture.py b/tempest/tests/lib/services/registry_fixture.py
index 1da2112..07af68a 100644
--- a/tempest/tests/lib/services/registry_fixture.py
+++ b/tempest/tests/lib/services/registry_fixture.py
@@ -37,8 +37,9 @@
     def __init__(self):
         """Initialise the registry fixture"""
         self.services = set(['compute', 'identity.v2', 'identity.v3',
-                             'image.v1', 'image.v2', 'network', 'volume.v1',
-                             'volume.v2', 'volume.v3', 'object-storage'])
+                             'image.v1', 'image.v2', 'network', 'placement',
+                             'volume.v1', 'volume.v2', 'volume.v3',
+                             'object-storage'])
 
     def _setUp(self):
         # Cleanup the registry
diff --git a/tools/format.sh b/tools/format.sh
index adffb8c..dec8f1c 100755
--- a/tools/format.sh
+++ b/tools/format.sh
@@ -1,5 +1,29 @@
 #!/bin/bash
+
 cd $(dirname "$(readlink -f "$0")")
 
-autopep8 --exit-code --max-line-length=79 --experimental --in-place -r ../tempest ../setup.py && echo Formatting was not needed. >&2
+AUTOPEP8=`which autopep8 2>/dev/null`
 
+if [[ -z "$AUTOPEP8" ]]; then
+    AUTOPEP8=`which autopep8-3`
+fi
+
+if [[ -z "$AUTOPEP8" ]]; then
+    echo "Unable to locate autopep8" >&2
+    exit 2
+fi
+
+# isort is not compatible with the default flake8 (H306), maybe flake8-isort
+# isort -rc -sl -fss ../tempest ../setup.py
+$AUTOPEP8 --exit-code --max-line-length=79 --experimental --in-place -r ../tempest ../setup.py
+ERROR=$?
+
+if [[ $ERROR -eq 0 ]]; then
+    echo "Formatting was not needed." >&2
+    exit 0
+elif [[ $ERROR -eq 1 ]]; then
+    echo "Formatting failed.." >&2
+    exit 1
+else
+    echo "done" >&2
+fi
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index 111c9ce..17a4059 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -69,7 +69,7 @@
 i=0
 for plugin in ${sorted_plugins}; do
     i=$((i+1))
-    giturl="git://git.openstack.org/openstack/${plugin}"
+    giturl="https://git.openstack.org/openstack/${plugin}"
     gitlink="https://git.openstack.org/cgit/openstack/${plugin}"
     printf "%-3s %-${name_col_len}s %s\n" "$i" "${plugin}" "\`${giturl} <${gitlink}>\`__"
 done
diff --git a/tools/tempest-plugin-sanity.sh b/tools/tempest-plugin-sanity.sh
index 16e7b8c..703dce2 100644
--- a/tools/tempest-plugin-sanity.sh
+++ b/tools/tempest-plugin-sanity.sh
@@ -81,11 +81,11 @@
 function clone_project() {
     if [ -e /usr/zuul-env/bin/zuul-cloner ]; then
         /usr/zuul-env/bin/zuul-cloner --cache-dir /opt/git \
-        git://git.openstack.org \
+        https://git.openstack.org \
         openstack/"$1"
 
     elif [ -e /usr/bin/git ]; then
-        /usr/bin/git clone git://git.openstack.org/openstack/"$1" \
+        /usr/bin/git clone https://git.openstack.org/openstack/"$1" \
         openstack/"$1"
 
     fi
diff --git a/tox.ini b/tox.ini
index 433f168..230249f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -198,7 +198,7 @@
 
 [testenv:pep8]
 deps =
-    -r test-requirements.txt
+    -r{toxinidir}/test-requirements.txt
     autopep8
 basepython = python3
 commands =
@@ -210,7 +210,7 @@
 deps = autopep8
 basepython = python3
 commands =
-    autopep8 --max-line-length=79  --experimental --in-place -r tempest setup.py
+    {toxinidir}/tools/format.sh
 
 [testenv:uuidgen]
 commands =