Merge "Fix attach_interfaces tests of Nova v3"
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 3211ef8..12fda92 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -64,6 +64,19 @@
                                                           status)
         super(SnapshotsActionsTest, self).tearDown()
 
+    def _create_reset_and_force_delete_temp_snapshot(self, status=None):
+        # Create snapshot, reset snapshot status,
+        # and force delete temp snapshot
+        temp_snapshot = self.create_snapshot(self.volume['id'])
+        if status:
+            resp, body = self.admin_snapshots_client.\
+                reset_snapshot_status(temp_snapshot['id'], status)
+            self.assertEqual(202, resp.status)
+        resp_delete, volume_delete = self.admin_snapshots_client.\
+            force_delete_snapshot(temp_snapshot['id'])
+        self.assertEqual(202, resp_delete.status)
+        self.client.wait_for_resource_deletion(temp_snapshot['id'])
+
     def _get_progress_alias(self):
         return 'os-extended-snapshot-attributes:progress'
 
@@ -99,6 +112,26 @@
         self.assertEqual(status, snapshot_get['status'])
         self.assertEqual(progress, snapshot_get[progress_alias])
 
+    @attr(type='gate')
+    def test_snapshot_force_delete_when_snapshot_is_creating(self):
+        # test force delete when status of snapshot is creating
+        self._create_reset_and_force_delete_temp_snapshot('creating')
+
+    @attr(type='gate')
+    def test_snapshot_force_delete_when_snapshot_is_deleting(self):
+        # test force delete when status of snapshot is deleting
+        self._create_reset_and_force_delete_temp_snapshot('deleting')
+
+    @attr(type='gate')
+    def test_snapshot_force_delete_when_snapshot_is_error(self):
+        # test force delete when status of snapshot is error
+        self._create_reset_and_force_delete_temp_snapshot('error')
+
+    @attr(type='gate')
+    def test_snapshot_force_delete_when_snapshot_is_error_deleting(self):
+        # test force delete when status of snapshot is error_deleting
+        self._create_reset_and_force_delete_temp_snapshot('error_deleting')
+
 
 class SnapshotsActionsTestXML(SnapshotsActionsTest):
     _interface = "xml"
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index be0e045..020a256 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -196,7 +196,8 @@
             floating_ip = self._create_floating_ip(server, public_network_id)
             self.floating_ips[floating_ip] = server
 
-    def _check_public_network_connectivity(self, should_connect=True):
+    def _check_public_network_connectivity(self, should_connect=True,
+                                           msg=None):
         # The target login is assumed to have been configured for
         # key-based authentication by cloud-init.
         ssh_login = CONF.compute.image_ssh_user
@@ -212,7 +213,10 @@
                                             private_key,
                                             should_connect=should_connect)
         except Exception:
-            LOG.exception('Public network connectivity check failed')
+            ex_msg = 'Public network connectivity check failed'
+            if msg:
+                ex_msg += ": " + msg
+            LOG.exception(ex_msg)
             self._log_console_output(servers=self.servers.keys())
             debug.log_ip_ns()
             raise
@@ -242,6 +246,10 @@
         self._check_tenant_network_connectivity()
         self._check_public_network_connectivity(should_connect=True)
         self._disassociate_floating_ips()
-        self._check_public_network_connectivity(should_connect=False)
+        self._check_public_network_connectivity(should_connect=False,
+                                                msg="after disassociate "
+                                                    "floating ip")
         self._reassociate_floating_ips()
-        self._check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True,
+                                                msg="after re-associate "
+                                                    "floating ip")
diff --git a/tempest/scenario/test_swift_basic_ops.py b/tempest/scenario/test_swift_basic_ops.py
index b367f7f..60df606 100644
--- a/tempest/scenario/test_swift_basic_ops.py
+++ b/tempest/scenario/test_swift_basic_ops.py
@@ -93,7 +93,7 @@
             for obj in not_present_obj:
                 self.assertNotIn(obj, object_list)
 
-    @services('object')
+    @services('object_storage')
     def test_swift_basic_ops(self):
         self._get_swift_stat()
         container_name = self._create_container()
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index a36083b..e8926bd 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -187,3 +187,10 @@
         url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
         resp, body = self.delete(url, self.headers)
         return resp, body
+
+    def force_delete_snapshot(self, snapshot_id):
+        """Force Delete Snapshot."""
+        post_body = json.dumps({'os-force_delete': {}})
+        resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body,
+                               self.headers)
+        return resp, body
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index 3e85041..5e62b6d 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -226,3 +226,12 @@
         """Delete metadata item for the snapshot."""
         url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
         return self.delete(url)
+
+    def force_delete_snapshot(self, snapshot_id):
+        """Force Delete Snapshot."""
+        post_body = Element("os-force_delete")
+        url = 'snapshots/%s/action' % str(snapshot_id)
+        resp, body = self.post(url, str(Document(post_body)), self.headers)
+        if body:
+            body = xml_to_json(etree.fromstring(body))
+        return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 3052f55..1a3ca0d 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -67,7 +67,7 @@
     exercised by a test case.
     """
     valid_service_list = ['compute', 'image', 'volume', 'orchestration',
-                          'network', 'identity', 'object', 'dashboard']
+                          'network', 'identity', 'object_storage', 'dashboard']
 
     def decorator(f):
         for service in args:
diff --git a/tools/verify_tempest_config.py b/tools/verify_tempest_config.py
index b393402..913c90b 100755
--- a/tools/verify_tempest_config.py
+++ b/tools/verify_tempest_config.py
@@ -127,11 +127,22 @@
                           "enabled extensions" % (service, extension))
 
 
+def check_service_availability(service):
+    if service == 'nova_v3':
+        service = 'nova'
+    return getattr(CONF.service_available, service)
+
+
 def main(argv):
     print('Running config verification...')
     os = clients.ComputeAdminManager(interface='json')
     results = {}
     for service in ['nova', 'nova_v3', 'cinder', 'neutron']:
+        # TODO(mtreinish) make this a keystone endpoint check for available
+        # services
+        if not check_service_availability(service):
+            print("%s is not available" % service)
+            continue
         results = verify_extensions(os, service, results)
     verify_glance_api_versions(os)
     verify_nova_api_versions(os)