Merge "Make list methods consistent for container client"
diff --git a/releasenotes/notes/add-reset-group-status-api-to-v3-groups-client-9aa048617c66756a.yaml b/releasenotes/notes/add-reset-group-status-api-to-v3-groups-client-9aa048617c66756a.yaml
new file mode 100644
index 0000000..a39c23b
--- /dev/null
+++ b/releasenotes/notes/add-reset-group-status-api-to-v3-groups-client-9aa048617c66756a.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add reset group status API to v3 groups_client library, min_microversion
+    of this API is 3.20. This feature enables the possibility to reset group
+    status.
diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py
index baea37b..d4b2faa 100644
--- a/tempest/api/volume/admin/test_groups.py
+++ b/tempest/api/volume/admin/test_groups.py
@@ -23,10 +23,7 @@
 CONF = config.CONF
 
 
-class GroupsTest(base.BaseVolumeAdminTest):
-    _api_version = 3
-    min_microversion = '3.14'
-    max_microversion = 'latest'
+class BaseGroupsTest(base.BaseVolumeAdminTest):
 
     def _delete_group(self, grp_id, delete_volumes=True):
         self.groups_client.delete_group(grp_id, delete_volumes)
@@ -37,8 +34,7 @@
         self.groups_client.wait_for_resource_deletion(grp_id)
 
     def _delete_group_snapshot(self, group_snapshot_id, grp_id):
-        self.group_snapshots_client.delete_group_snapshot(
-            group_snapshot_id)
+        self.group_snapshots_client.delete_group_snapshot(group_snapshot_id)
         vols = self.volumes_client.list_volumes(detail=True)['volumes']
         snapshots = self.snapshots_client.list_snapshots(
             detail=True)['snapshots']
@@ -65,6 +61,12 @@
         self.assertEqual(grp_name, grp['name'])
         return grp
 
+
+class GroupsTest(BaseGroupsTest):
+    min_microversion = '3.14'
+    max_microversion = 'latest'
+    _api_version = 3
+
     @decorators.idempotent_id('4b111d28-b73d-4908-9bd2-03dc2992e4d4')
     def test_group_create_show_list_delete(self):
         # Create volume type
@@ -126,8 +128,7 @@
         self._delete_group(grp1_id)
         # grp2 is empty so delete_volumes flag can be set to False
         self._delete_group(grp2_id, delete_volumes=False)
-        grps = self.groups_client.list_groups(
-            detail=True)['groups']
+        grps = self.groups_client.list_groups(detail=True)['groups']
         self.assertEmpty(grps)
 
     @decorators.idempotent_id('1298e537-f1f0-47a3-a1dd-8adec8168897')
@@ -151,6 +152,9 @@
             self.group_snapshots_client.create_group_snapshot(
                 group_id=grp['id'],
                 name=group_snapshot_name)['group_snapshot'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._delete_group_snapshot,
+                        group_snapshot['id'], grp['id'])
         snapshots = self.snapshots_client.list_snapshots(
             detail=True)['snapshots']
         for snap in snapshots:
@@ -212,14 +216,12 @@
                 waiters.wait_for_volume_resource_status(
                     self.snapshots_client, snap['id'], 'available')
         waiters.wait_for_volume_resource_status(
-            self.group_snapshots_client,
-            group_snapshot['id'], 'available')
+            self.group_snapshots_client, group_snapshot['id'], 'available')
 
         # Create Group from Group snapshot
         grp_name2 = data_utils.rand_name('Group_from_snap')
         grp2 = self.groups_client.create_group_from_source(
-            group_snapshot_id=group_snapshot['id'],
-            name=grp_name2)['group']
+            group_snapshot_id=group_snapshot['id'], name=grp_name2)['group']
         self.addCleanup(self._delete_group, grp2['id'])
         self.assertEqual(grp_name2, grp2['name'])
         vols = self.volumes_client.list_volumes(detail=True)['volumes']
@@ -250,8 +252,7 @@
             source_group_id=grp['id'], name=grp_name2)['group']
         self.addCleanup(self._delete_group, grp2['id'])
         self.assertEqual(grp_name2, grp2['name'])
-        vols = self.volumes_client.list_volumes(
-            detail=True)['volumes']
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
         for vol in vols:
             if vol['group_id'] == grp2['id']:
                 waiters.wait_for_volume_resource_status(
@@ -298,10 +299,7 @@
         # Get volumes in the group
         vols = self.volumes_client.list_volumes(
             detail=True)['volumes']
-        grp_vols = []
-        for vol in vols:
-            if vol['group_id'] == grp['id']:
-                grp_vols.append(vol)
+        grp_vols = [v for v in vols if v['group_id'] == grp['id']]
         self.assertEqual(1, len(grp_vols))
 
         # Add a volume to the group
@@ -313,10 +311,33 @@
             self.groups_client, grp['id'], 'available')
 
         # Get volumes in the group
-        vols = self.volumes_client.list_volumes(
-            detail=True)['volumes']
-        grp_vols = []
-        for vol in vols:
-            if vol['group_id'] == grp['id']:
-                grp_vols.append(vol)
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
+        grp_vols = [v for v in vols if v['group_id'] == grp['id']]
         self.assertEqual(2, len(grp_vols))
+
+
+class GroupsV320Test(BaseGroupsTest):
+    _api_version = 3
+    min_microversion = '3.20'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('b20c696b-0cbc-49a5-8b3a-b1fb9338f45c')
+    def test_reset_group_status(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create group
+        group = self._create_group(group_type, volume_type)
+
+        # Reset group status
+        self.addCleanup(waiters.wait_for_volume_resource_status,
+                        self.groups_client, group['id'], 'available')
+        self.addCleanup(self.admin_groups_client.reset_group_status,
+                        group['id'], 'available')
+        for status in ['creating', 'available', 'error']:
+            self.admin_groups_client.reset_group_status(group['id'], status)
+            waiters.wait_for_volume_resource_status(
+                self.groups_client, group['id'], status)
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index c4d3a40..d56f1de 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -101,7 +101,7 @@
 
         volume = self.create_volume()
         self.addCleanup(self.delete_volume,
-                        self.admin_volume_client, volume['id'])
+                        self.volumes_client, volume['id'])
 
         new_quota_usage = self.admin_quotas_client.show_quota_set(
             self.demo_tenant_id, params={'usage': True})['quota_set']
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
index b463fdf..e2e477d 100644
--- a/tempest/lib/services/volume/v3/groups_client.py
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -109,6 +109,17 @@
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def reset_group_status(self, group_id, status_to_set):
+        """Resets group status.
+
+        For more information, please refer to the official API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#reset-group-status
+        """
+        post_body = json.dumps({'reset_status': {'status': status_to_set}})
+        resp, body = self.post('groups/%s/action' % group_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def is_resource_deleted(self, id):
         try:
             self.show_group(id)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 680efce..4a947fa 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -858,22 +858,6 @@
                         floating_ip['id'])
         return floating_ip
 
-    def _associate_floating_ip(self, floating_ip, server):
-        port_id, _ = self._get_server_port_id_and_ip4(server)
-        kwargs = dict(port_id=port_id)
-        floating_ip = self.floating_ips_client.update_floatingip(
-            floating_ip['id'], **kwargs)['floatingip']
-        self.assertEqual(port_id, floating_ip['port_id'])
-        return floating_ip
-
-    def _disassociate_floating_ip(self, floating_ip):
-        """:param floating_ip: floating_ips_client.create_floatingip"""
-        kwargs = dict(port_id=None)
-        floating_ip = self.floating_ips_client.update_floatingip(
-            floating_ip['id'], **kwargs)['floatingip']
-        self.assertIsNone(floating_ip['port_id'])
-        return floating_ip
-
     def check_floating_ip_status(self, floating_ip, status):
         """Verifies floatingip reaches the given status
 
@@ -1183,12 +1167,6 @@
                         router['id'])
         return router
 
-    def _update_router_admin_state(self, router, admin_state_up):
-        kwargs = dict(admin_state_up=admin_state_up)
-        router = self.routers_client.update_router(
-            router['id'], **kwargs)['router']
-        self.assertEqual(admin_state_up, router['admin_state_up'])
-
     def create_networks(self, networks_client=None,
                         routers_client=None, subnets_client=None,
                         tenant_id=None, dns_nameservers=None,
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 2d38b06..0c3bf23 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -213,17 +213,20 @@
 
     def _disassociate_floating_ips(self):
         floating_ip, _ = self.floating_ip_tuple
-        self._disassociate_floating_ip(floating_ip)
-        self.floating_ip_tuple = Floating_IP_tuple(
-            floating_ip, None)
+        floating_ip = self.floating_ips_client.update_floatingip(
+            floating_ip['id'], port_id=None)['floatingip']
+        self.assertIsNone(floating_ip['port_id'])
+        self.floating_ip_tuple = Floating_IP_tuple(floating_ip, None)
 
     def _reassociate_floating_ips(self):
         floating_ip, server = self.floating_ip_tuple
         # create a new server for the floating ip
         server = self._create_server(self.network)
-        self._associate_floating_ip(floating_ip, server)
-        self.floating_ip_tuple = Floating_IP_tuple(
-            floating_ip, server)
+        port_id, _ = self._get_server_port_id_and_ip4(server)
+        floating_ip = self.floating_ips_client.update_floatingip(
+            floating_ip['id'], port_id=port_id)['floatingip']
+        self.assertEqual(port_id, floating_ip['port_id'])
+        self.floating_ip_tuple = Floating_IP_tuple(floating_ip, server)
 
     def _create_new_network(self, create_gateway=False):
         self.new_net = self._create_network()
@@ -355,6 +358,12 @@
             self.check_remote_connectivity(ssh_source, remote_ip,
                                            should_connect)
 
+    def _update_router_admin_state(self, router, admin_state_up):
+        kwargs = dict(admin_state_up=admin_state_up)
+        router = self.routers_client.update_router(
+            router['id'], **kwargs)['router']
+        self.assertEqual(admin_state_up, router['admin_state_up'])
+
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('f323b3ba-82f8-4db7-8ea6-6a895869ec49')
     @utils.services('compute', 'network')
diff --git a/tempest/tests/lib/services/volume/v3/test_groups_client.py b/tempest/tests/lib/services/volume/v3/test_groups_client.py
index 0884e5a..918e958 100644
--- a/tempest/tests/lib/services/volume/v3/test_groups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_groups_client.py
@@ -184,3 +184,12 @@
             group_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
             status=202,
             **self.FAKE_UPDATE_GROUP['group'])
+
+    def test_reset_group_status(self):
+        self.check_service_client_function(
+            self.client.reset_group_status,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            group_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
+            status_to_set='error')