Merge "Use addClassResourceCleanup to cleanup floatingips"
diff --git a/README.rst b/README.rst
index c087f29..242f4d5 100644
--- a/README.rst
+++ b/README.rst
@@ -193,6 +193,25 @@
 Alternatively, there are the py27 and py35 tox jobs which will run the unit
 tests with the corresponding version of python.
 
+One common activity is to just run a single test, you can do this with tox
+simply by specifying to just run py27 or py35 tests against a single test::
+
+    $ tox -e py27 -- -n tempest.tests.test_microversions.TestMicroversionsTestsClass.test_config_version_none_23
+
+Or all tests in the test_microversions.py file::
+
+    $ tox -e py27 -- -n tempest.tests.test_microversions
+
+You may also use regular expressions to run any matching tests::
+
+    $ tox -e py27 -- test_microversions
+
+Additionally, when running a single test, or test-file, the ``-n/--no-discover``
+argument is no longer required, however it may perform faster if included.
+
+For more information on these options and details about stestr, please see the
+`stestr documentation <http://stestr.readthedocs.io/en/latest/MANUAL.html>`_.
+
 Python 2.6
 ----------
 
diff --git a/playbooks/post-tempest.yaml b/playbooks/post-tempest.yaml
index 01784f1..820e4f6 100644
--- a/playbooks/post-tempest.yaml
+++ b/playbooks/post-tempest.yaml
@@ -8,13 +8,15 @@
     - role: process-test-results
       test_results_dir: '{{ logs_root }}/tempest'
       tox_envdir: tempest
+    - role: process-stackviz
     - role: stage-output
       zuul_copy_output:
         { '{{ logs_root }}/tempest/etc/tempest.conf': 'logs',
           '{{ logs_root }}/tempest/etc/accounts.yaml': 'logs',
           '{{ logs_root }}/tempest/tempest.log': 'logs',
           '{{ stage_dir }}/{{ test_results_stage_name }}.subunit': 'logs',
-          '{{ stage_dir }}/{{ test_results_stage_name }}.html': 'logs' }
+          '{{ stage_dir }}/{{ test_results_stage_name }}.html': 'logs',
+          '{{ stage_dir }}/stackviz': 'logs' }
       extensions_to_txt:
         - conf
         - log
diff --git a/roles/process-stackviz/README.rst b/roles/process-stackviz/README.rst
new file mode 100644
index 0000000..b05326d
--- /dev/null
+++ b/roles/process-stackviz/README.rst
@@ -0,0 +1,22 @@
+Generate stackviz report.
+
+Generate stackviz report using subunit and dstat data, using
+the stackviz archive embedded in test images.
+
+**Role Variables**
+
+.. zuul:rolevar:: devstack_base_dir
+   :default: /opt/stack
+
+   The devstack base directory.
+
+.. zuul:rolevar:: stage_dir
+   :default: /opt/stack/logs
+
+   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
+
+   The name of the subunit file to be used as input.
diff --git a/roles/process-stackviz/defaults/main.yaml b/roles/process-stackviz/defaults/main.yaml
new file mode 100644
index 0000000..b1eb8d9
--- /dev/null
+++ b/roles/process-stackviz/defaults/main.yaml
@@ -0,0 +1,3 @@
+devstack_base_dir: /opt/stack
+stage_dir: /opt/stack/
+test_results_stage_name: test_results
diff --git a/roles/process-stackviz/tasks/main.yaml b/roles/process-stackviz/tasks/main.yaml
new file mode 100644
index 0000000..09de606
--- /dev/null
+++ b/roles/process-stackviz/tasks/main.yaml
@@ -0,0 +1,63 @@
+- name: Check if stackviz archive exists
+  stat:
+    path: "/opt/cache/files/stackviz-latest.tar.gz"
+  register: stackviz_archive
+
+- debug:
+    msg: "Stackviz archive could not be found in /opt/cache/files/stackviz-latest.tar.gz"
+  when: not stackviz_archive.stat.exists
+
+- name: Check if subunit data exists
+  stat:
+    path: "{{ stage_dir }}/{{ test_results_stage_name }}.subunit"
+  register: subunit_input
+
+- debug:
+    msg: "Subunit file could not be found at {{ stage_dir }}/{{ test_results_stage_name }}.subunit"
+  when: not subunit_input.stat.exists
+
+- name: Install stackviz
+  pip:
+    name: "file://{{ stackviz_archive.stat.path }}"
+    virtualenv: /tmp/stackviz
+    extra_args: -U
+  when:
+    - stackviz_archive.stat.exists
+    - subunit_input.stat.exists
+
+- name: Deploy stackviz static html+js
+  command: cp -pR /tmp/stackviz/share/stackviz-html {{ stage_dir }}/stackviz
+  when:
+    - stackviz_archive.stat.exists
+    - subunit_input.stat.exists
+
+- name: Check if dstat data exists
+  stat:
+    path: "{{ devstack_base_dir }}/logs/dstat-csv.log"
+  register: dstat_input
+  when:
+    - stackviz_archive.stat.exists
+    - subunit_input.stat.exists
+
+- name: Run stackviz with dstat
+  shell: |
+    cat {{ subunit_input.stat.path }} | \
+      /tmp/stackviz/bin/stackviz-export \
+        --dstat "{{ devstack_base_dir }}/logs/dstat-csv.log" \
+        --env --stdin \
+        {{ stage_dir }}/stackviz/data
+  when:
+    - stackviz_archive.stat.exists
+    - subunit_input.stat.exists
+    - dstat_input.stat.exists
+
+- name: Run stackviz without dstat
+  shell: |
+    cat {{ subunit_input.stat.path }} | \
+      /tmp/stackviz/bin/stackviz-export \
+        --env --stdin \
+        {{ stage_dir }}/stackviz/data
+  when:
+    - stackviz_archive.stat.exists
+    - subunit_input.stat.exists
+    - not dstat_input.stat.exists
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 705814c..9ee8858 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -434,7 +434,7 @@
             # the compute API will return a 400 response.
             if volume['status'] == 'in-use':
                 self.servers_client.detach_volume(server['id'], volume['id'])
-        except exceptions.NotFound:
+        except lib_exc.NotFound:
             # Ignore 404s on detach in case the server is deleted or the volume
             # is already detached.
             pass
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index bce7524..e2be249 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -326,10 +326,22 @@
         server = self.create_test_server(
             volume_backed=True, wait_until='ACTIVE')
         self._test_resize_server_confirm(server['id'])
-        # Now do something interactive with the guest like get its console
-        # output; we don't actually care about the output, just that it doesn't
-        # raise an error.
-        self.client.get_console_output(server['id'])
+        if CONF.compute_feature_enabled.console_output:
+            # Now do something interactive with the guest like get its console
+            # output; we don't actually care about the output,
+            # just that it doesn't raise an error.
+            self.client.get_console_output(server['id'])
+        if CONF.validation.run_validation:
+            validation_resources = self.get_class_validation_resources(
+                self.os_primary)
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server, validation_resources),
+                self.ssh_user,
+                password=None,
+                pkey=validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.client)
+            linux_client.validate_authentication()
 
     @decorators.idempotent_id('138b131d-66df-48c9-a171-64f45eb92962')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index de28a30..b73bdf2 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -18,6 +18,7 @@
 import testtools
 
 from tempest.api.volume import base
+from tempest.common import utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
@@ -104,6 +105,7 @@
     @decorators.idempotent_id('301f5a30-1c6f-4ea0-be1a-91fd28d44354')
     @testtools.skipUnless(CONF.volume_feature_enabled.extend_attached_volume,
                           "Attached volume extend is disabled.")
+    @utils.services('compute')
     def test_extend_attached_volume(self):
         """This is a happy path test which does the following:
 
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 6332c6d..ff8837f 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -421,6 +421,10 @@
     def test_mtu_sized_frames(self):
         """Validate that network MTU sized frames fit through."""
         self._setup_network_and_servers()
+        # first check that connectivity works in general for the instance
+        self.check_public_network_connectivity(should_connect=True)
+        # now that we checked general connectivity, test that full size frames
+        # can also pass between nodes
         self.check_public_network_connectivity(
             should_connect=True, mtu=self.network['mtu'])