Merge "Update port profile only if defined"
diff --git a/.zuul.yaml b/.zuul.yaml
index 67d1f5e..4f17959 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -93,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
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/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/source/index.rst b/releasenotes/source/index.rst
index df1de46..2518703 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -2,19 +2,22 @@
  Tempest Release Notes
 ===========================
 
- .. toctree::
-    :maxdepth: 1
+.. toctree::
+   :maxdepth: 1
 
-    unreleased
-    v17.0.0
-    v16.1.0
-    v16.0.0
-    v15.0.0
-    v14.0.0
-    v13.0.0
-    v12.0.0
-    v11.0.0
-    v10.0.0
+   unreleased
+   v18.0.0
+   v17.2.0
+   v17.1.0
+   v17.0.0
+   v16.1.0
+   v16.0.0
+   v15.0.0
+   v14.0.0
+   v13.0.0
+   v12.0.0
+   v11.0.0
+   v10.0.0
 
 Indices and tables
 ==================
diff --git a/releasenotes/source/v17.1.0.rst b/releasenotes/source/v17.1.0.rst
new file mode 100644
index 0000000..b8fd570
--- /dev/null
+++ b/releasenotes/source/v17.1.0.rst
@@ -0,0 +1,6 @@
+=====================
+v17.1.0 Release Notes
+=====================
+
+.. release-notes:: 17.1.0 Release Notes
+   :version: 17.1.0
diff --git a/releasenotes/source/v17.2.0.rst b/releasenotes/source/v17.2.0.rst
new file mode 100644
index 0000000..8566ae4
--- /dev/null
+++ b/releasenotes/source/v17.2.0.rst
@@ -0,0 +1,6 @@
+=====================
+v17.2.0 Release Notes
+=====================
+
+.. release-notes:: 17.2.0 Release Notes
+   :version: 17.2.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 76db574..a74042f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,7 +15,7 @@
 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
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_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/image/v2/admin/test_images.py b/tempest/api/image/v2/admin/test_images.py
index 4ff0268..dbb8c58 100644
--- a/tempest/api/image/v2/admin/test_images.py
+++ b/tempest/api/image/v2/admin/test_images.py
@@ -31,3 +31,21 @@
         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/common/compute.py b/tempest/common/compute.py
index 638ad9b..68c4a10 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -289,7 +289,12 @@
 
 def create_websocket(url):
     url = urlparse.urlparse(url)
-    for res in socket.getaddrinfo(url.hostname, url.port,
+
+    # NOTE(mnaser): It is possible that there is no port specified, so fall
+    #               back to the default port based on the scheme.
+    port = url.port or (443 if url.scheme == 'https' else 80)
+
+    for res in socket.getaddrinfo(url.hostname, port,
                                   socket.AF_UNSPEC, socket.SOCK_STREAM):
         af, socktype, proto, _, sa = res
         client_socket = socket.socket(af, socktype, proto)
@@ -382,7 +387,12 @@
         """Upgrade the HTTP connection to a WebSocket and verify."""
         # The real request goes to the /websockify URI always
         reqdata = 'GET /websockify HTTP/1.1\r\n'
-        reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
+        reqdata += 'Host: %s' % url.hostname
+        # Add port only if we have one specified
+        if url.port:
+            reqdata += ':%s' % url.port
+        # Line-ending for Host header
+        reqdata += '\r\n'
         # Tell the HTTP Server to Upgrade the connection to a WebSocket
         reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
         # The token=xxx is sent as a Cookie not in the URI
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 57fb1b2..c78646f 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -259,6 +259,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/tox.ini b/tox.ini
index 5644641..9103175 100644
--- a/tox.ini
+++ b/tox.ini
@@ -146,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]