Add test case for update volume backup

Update volume backup is a new api implemented in volume v3 (since 3.9).
This patch is to add test case for this new feature.

Including:

[1] Add v3 backups_client as a library
[2] Add update backup api to v3 backups_client
[3] Add unit tests for update backup api
[4] Add test case: test_update_backup
[5] Add release note

Change-Id: Id50fcdbc41e9e170f3d467789f80c28bac01d434
diff --git a/releasenotes/notes/add-update-backup-api-to-v3-backups-client-e8465b2b66617dc0.yaml b/releasenotes/notes/add-update-backup-api-to-v3-backups-client-e8465b2b66617dc0.yaml
new file mode 100644
index 0000000..7cd6887
--- /dev/null
+++ b/releasenotes/notes/add-update-backup-api-to-v3-backups-client-e8465b2b66617dc0.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    Define v3 backups_client for the volume service as a library interface,
+    allowing other projects to use this module as a stable library without
+    maintenance changes.
+    Add update backup API to v3 backups_client library, min_microversion
+    of this API is 3.9.
+
+    * backups_client(v3)
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 394c453..ef69ba3 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -69,11 +69,14 @@
         if CONF.service_available.glance:
             cls.images_client = cls.os_primary.image_client_v2
 
-        cls.snapshots_client = cls.os_primary.snapshots_v2_client
-        cls.volumes_client = cls.os_primary.volumes_v2_client
         if cls._api_version == 3:
+            cls.backups_client = cls.os_primary.backups_v3_client
             cls.volumes_client = cls.os_primary.volumes_v3_client
-        cls.backups_client = cls.os_primary.backups_v2_client
+        else:
+            cls.backups_client = cls.os_primary.backups_v2_client
+            cls.volumes_client = cls.os_primary.volumes_v2_client
+
+        cls.snapshots_client = cls.os_primary.snapshots_v2_client
         cls.volumes_extension_client =\
             cls.os_primary.volumes_v2_extension_client
         cls.availability_zone_client = (
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 4b4aeec..1f91db6 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -140,3 +140,39 @@
             restored_volume_id)['volume']
 
         self.assertEqual('true', restored_volume_info['bootable'])
+
+
+class VolumesBackupsV39Test(base.BaseVolumeTest):
+
+    _api_version = 3
+    min_microversion = '3.9'
+    max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesBackupsV39Test, cls).skip_checks()
+        if not CONF.volume_feature_enabled.backup:
+            raise cls.skipException("Cinder backup feature disabled")
+
+    @decorators.idempotent_id('9b374cbc-be5f-4d37-8848-7efb8a873dcc')
+    def test_update_backup(self):
+        # Create volume and backup
+        volume = self.create_volume()
+        backup = self.create_backup(volume_id=volume['id'])
+
+        # Update backup and assert response body for update_backup method
+        update_kwargs = {
+            'name': data_utils.rand_name(self.__class__.__name__ + '-Backup'),
+            'description': data_utils.rand_name("volume-backup-description")
+        }
+        update_backup = self.backups_client.update_backup(
+            backup['id'], **update_kwargs)['backup']
+        self.assertEqual(backup['id'], update_backup['id'])
+        self.assertEqual(update_kwargs['name'], update_backup['name'])
+        self.assertIn('links', update_backup)
+
+        # Assert response body for show_backup method
+        retrieved_backup = self.backups_client.show_backup(
+            backup['id'])['backup']
+        for key in update_kwargs:
+            self.assertEqual(update_kwargs[key], retrieved_backup[key])
diff --git a/tempest/clients.py b/tempest/clients.py
index c3357bb..467ef9c 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -235,6 +235,7 @@
         self.volume_services_v2_client = self.volume_v2.ServicesClient()
         self.backups_client = self.volume_v1.BackupsClient()
         self.backups_v2_client = self.volume_v2.BackupsClient()
+        self.backups_v3_client = self.volume_v3.BackupsClient()
         self.encryption_types_client = self.volume_v1.EncryptionTypesClient()
         self.encryption_types_v2_client = \
             self.volume_v2.EncryptionTypesClient()
diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py
index 2b5e82d..a44ed0b 100644
--- a/tempest/lib/services/volume/v2/backups_client.py
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -17,9 +17,10 @@
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
 
 
-class BackupsClient(rest_client.RestClient):
+class BackupsClient(base_client.BaseClient):
     """Volume V2 Backups client"""
     api_version = "v2"
 
diff --git a/tempest/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
index a351d61..ff58fc2 100644
--- a/tempest/lib/services/volume/v3/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -12,6 +12,7 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from tempest.lib.services.volume.v3.backups_client import BackupsClient
 from tempest.lib.services.volume.v3.base_client import BaseClient
 from tempest.lib.services.volume.v3.group_types_client import GroupTypesClient
 from tempest.lib.services.volume.v3.groups_client import GroupsClient
@@ -19,5 +20,5 @@
 from tempest.lib.services.volume.v3.versions_client import VersionsClient
 from tempest.lib.services.volume.v3.volumes_client import VolumesClient
 
-__all__ = ['BaseClient', 'GroupsClient', 'GroupTypesClient',
+__all__ = ['BackupsClient', 'BaseClient', 'GroupsClient', 'GroupTypesClient',
            'MessagesClient', 'VersionsClient', 'VolumesClient']
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
new file mode 100644
index 0000000..e742e39
--- /dev/null
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -0,0 +1,37 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v2 import backups_client
+
+
+class BackupsClient(backups_client.BackupsClient):
+    """Volume V3 Backups client"""
+    api_version = "v3"
+
+    def update_backup(self, backup_id, **kwargs):
+        """Updates the specified volume backup.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#update-a-backup
+        """
+        put_body = json.dumps({'backup': kwargs})
+        resp, body = self.put('backups/%s' % backup_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/lib/services/volume/v3/test_backups_client.py b/tempest/tests/lib/services/volume/v3/test_backups_client.py
new file mode 100644
index 0000000..f1ce987
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_backups_client.py
@@ -0,0 +1,50 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v3 import backups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestBackupsClient(base.BaseServiceTest):
+
+    FAKE_BACKUP_UPDATE = {
+        "backup": {
+            "id": "4c65c15f-a5c5-464b-b92a-90e4c04636a7",
+            "name": "fake-backup-name",
+            "links": "fake-links"
+        }
+    }
+
+    def setUp(self):
+        super(TestBackupsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = backups_client.BackupsClient(fake_auth,
+                                                   'volume',
+                                                   'regionOne')
+
+    def _test_update_backup(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_backup,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_BACKUP_UPDATE,
+            bytes_body,
+            backup_id='4c65c15f-a5c5-464b-b92a-90e4c04636a7')
+
+    def test_update_backup_with_str_body(self):
+        self._test_update_backup()
+
+    def test_update_backup_with_bytes_body(self):
+        self._test_update_backup(bytes_body=True)