diff --git a/.zuul.yaml b/.zuul.yaml
index 6f481a1408d3a4f0d35ce29c12b42ae39917287d..b48a96c45d762e2165b74427fbef35bdf7377a89 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -111,6 +111,7 @@
         user_group
         user_role
         volume
+        volume_backup
       # failing tags
       # neutron_rbac
 
diff --git a/ci/roles/volume/tasks/main.yml b/ci/roles/volume/tasks/main.yml
index de0686a1be494561d3dd1699ccbf6218d329d4fe..fa4a54efd04d23311b5032cefa5cbefd83a2235c 100644
--- a/ci/roles/volume/tasks/main.yml
+++ b/ci/roles/volume/tasks/main.yml
@@ -46,35 +46,6 @@
      description: Test volume
   register: vol
 
-- name: Create volume backup
-  openstack.cloud.volume_backup:
-    cloud: "{{ cloud }}"
-    state: present
-    name: ansible_volume_backup
-    volume: ansible_volume
-  register: vol_backup
-
-- name: Get backup info
-  openstack.cloud.volume_backup_info:
-    cloud: "{{ cloud }}"
-    name: ansible_volume_backup
-  register: backup_info
-
-- debug: var=vol
-
-- debug: var=vol_backup
-
-- debug: var=backup_info
-
-- debug: var=snap_info
-
-- name: Delete volume backup
-  openstack.cloud.volume_backup:
-    cloud: "{{ cloud }}"
-    name: ansible_volume_backup
-    wait: false
-    state: absent
-
 - name: Delete volume snapshot
   openstack.cloud.volume_snapshot:
     cloud: "{{ cloud }}"
diff --git a/ci/roles/volume_backup/defaults/main.yml b/ci/roles/volume_backup/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7091e286e5b566b8a99751bf138cab5fe4f7d399
--- /dev/null
+++ b/ci/roles/volume_backup/defaults/main.yml
@@ -0,0 +1,22 @@
+expected_fields:
+  - availability_zone
+  - container
+  - created_at
+  - data_timestamp
+  - description
+  - fail_reason
+  - force
+  - has_dependent_backups
+  - id
+  - is_incremental
+  - links
+  - metadata
+  - name
+  - object_count
+  - project_id
+  - size
+  - snapshot_id
+  - status
+  - updated_at
+  - user_id
+  - volume_id
diff --git a/ci/roles/volume_backup/tasks/main.yml b/ci/roles/volume_backup/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bc1b319544e3e84d56337779f64b87226683cfd3
--- /dev/null
+++ b/ci/roles/volume_backup/tasks/main.yml
@@ -0,0 +1,89 @@
+---
+- name: Get existing backups
+  openstack.cloud.volume_backup_info:
+    cloud: "{{ cloud }}"
+  register: info
+
+- name: Assert volume_backup_info
+  assert:
+    that:
+      - info.volume_backups|length == 0
+
+- name: Get non-existing backup
+  openstack.cloud.volume_backup_info:
+    cloud: "{{ cloud }}"
+    name: non-existing-backup
+  register: info
+
+- name: Assert volume_backup_info
+  assert:
+    that:
+      - info.volume_backups|length == 0
+
+- name: Create volume
+  openstack.cloud.volume:
+     cloud: "{{ cloud }}"
+     state: present
+     size: 1
+     name: ansible_volume
+  register: volume
+
+- name: Create volume backup
+  openstack.cloud.volume_backup:
+    cloud: "{{ cloud }}"
+    state: present
+    name: ansible_volume_backup
+    volume: ansible_volume
+    # TODO: Uncomment code when https://storyboard.openstack.org/#!/story/2010395 has been solved.
+    #metadata:
+    #  key1: value1
+    #  key2: value2
+  register: backup
+
+- name: Assert volume_backup
+  assert:
+    that:
+      - backup.volume_backup.name == "ansible_volume_backup"
+      - backup.volume_backup.volume_id == volume.volume.id
+      # TODO: Uncomment code when https://storyboard.openstack.org/#!/story/2010395 has been solved.
+      #- backup.volume_backup.metadata.keys()|sort == ['key1', 'key2']
+      #- backup.volume_backup.metadata['key1'] == 'value1'
+      #- backup.volume_backup.metadata['key2'] == 'value2'
+
+- name: Assert return values of volume_backup module
+  assert:
+    that:
+      # allow new fields to be introduced but prevent fields from being removed
+      - expected_fields|difference(backup.volume_backup.keys())|length == 0
+
+- name: Get backup info
+  openstack.cloud.volume_backup_info:
+    cloud: "{{ cloud }}"
+    name: ansible_volume_backup
+  register: info
+
+- name: Assert volume_backup_info
+  assert:
+    that:
+      - info.volume_backups|length == 1
+      - info.volume_backups[0].id == backup.backup.id
+      - info.volume_backups[0].volume_id == volume.volume.id
+
+- name: Assert return values of volume_info module
+  assert:
+    that:
+      # allow new fields to be introduced but prevent fields from being removed
+      - expected_fields|difference(info.volume_backups[0].keys())|length == 0
+
+- name: Delete volume backup
+  openstack.cloud.volume_backup:
+    cloud: "{{ cloud }}"
+    name: ansible_volume_backup
+    wait: false
+    state: absent
+
+- name: Delete volume
+  openstack.cloud.volume:
+     cloud: "{{ cloud }}"
+     state: absent
+     name: ansible_volume
diff --git a/ci/run-collection.yml b/ci/run-collection.yml
index 70c37784445c920e95f816ef8d9a6024397d7c72..d02bc480cdfe4398d27508f86dcde5e4b67df10a 100644
--- a/ci/run-collection.yml
+++ b/ci/run-collection.yml
@@ -65,6 +65,7 @@
     - { role: user_group, tags: user_group }
     - { role: user_role, tags: user_role }
     - { role: volume, tags: volume }
+    - { role: volume_backup, tags: volume_backup }
     - role: loadbalancer
       tags: loadbalancer
     - { role: quota, tags: quota }
diff --git a/plugins/modules/volume_backup.py b/plugins/modules/volume_backup.py
index ec6ae7e61ad870d6b2073a7dc35d793f06ce5257..709e2b47058f5e5d5de11c3651fe9e438c2f98be 100644
--- a/plugins/modules/volume_backup.py
+++ b/plugins/modules/volume_backup.py
@@ -4,28 +4,41 @@
 # Copyright (c) 2020 by Open Telekom Cloud, operated by T-Systems International GmbH
 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
 
-
-DOCUMENTATION = '''
+DOCUMENTATION = r'''
 ---
 module: volume_backup
 short_description: Add/Delete Volume backup
-extends_documentation_fragment: openstack.cloud.openstack
 author: OpenStack Ansible SIG
 description:
-  - Add or Remove Volume Backup in OTC.
+  - Add or Remove Volume Backup in OpenStack.
 options:
-  display_name:
+  description:
+    description:
+      - String describing the backup
+    type: str
+    aliases: ['display_description']
+  force:
+    description:
+      - Indicates whether to backup, even if the volume is attached.
+    type: bool
+    default: False
+  is_incremental:
+    description: The backup mode
+    type: bool
+    default: False
+    aliases: ['incremental']
+  metadata:
+    description: Metadata for the backup
+    type: dict
+  name:
     description:
       - Name that has to be given to the backup
     required: true
     type: str
-    aliases: ['name']
-  display_description:
-    description:
-      - String describing the backup
-    required: false
+    aliases: ['display_name']
+  snapshot:
+    description: Name or ID of the Snapshot to take backup of.
     type: str
-    aliases: ['description']
   state:
     description:
       - Should the resource be present or absent.
@@ -34,63 +47,117 @@ options:
     type: str
   volume:
     description:
-      - Name or ID of the volume. Required when state is True.
-    type: str
-    required: False
-  snapshot:
-    description: Name or ID of the Snapshot to take backup of
+      - Name or ID of the volume.
+      - Required when I(state) is C(present).
     type: str
-  force:
-    description:
-      - Indicates whether to backup, even if the volume is attached.
-    type: bool
-    default: False
-  metadata:
-    description: Metadata for the backup
-    type: dict
-  incremental:
-    description: The backup mode
-    type: bool
-    default: False
-requirements: ["openstacksdk"]
+
+notes:
+    - This module does not support updates to existing backups.
+
+requirements:
+  - "python >= 3.6"
+  - "openstacksdk"
+
+extends_documentation_fragment:
+- openstack.cloud.openstack
 '''
 
-RETURN = '''
-id:
-    description: The Volume backup ID.
-    returned: On success when C(state=present)
-    type: str
-    sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
+RETURN = r'''
 backup:
-    description: Dictionary describing the Cluster.
+    description: Same as C(volume_backup), kept for backward compatibility.
+    returned: On success when C(state=present)
+    type: dict
+volume_backup:
+    description: Dictionary describing the volume backup.
     returned: On success when C(state=present)
-    type: complex
+    type: dict
     contains:
+        availability_zone:
+            description: Backup availability zone.
+            type: str
+        container:
+            description: The container name.
+            type: str
+        created_at:
+            description: Backup creation time.
+            type: str
+        data_timestamp:
+            description: The time when the data on the volume was first saved.
+                         If it is a backup from volume, it will be the same as
+                         C(created_at) for a backup. If it is a backup from a
+                         snapshot, it will be the same as created_at for the
+                         snapshot.
+            type: str
+        description:
+            description: Backup desciption.
+            type: str
+        fail_reason:
+            description: Backup fail reason.
+            type: str
+        force:
+            description: Force backup.
+            type: bool
+        has_dependent_backups:
+            description: If this value is true, there are other backups
+                         depending on this backup.
+            type: bool
         id:
             description: Unique UUID.
             type: str
             sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
+        is_incremental:
+            description: Backup incremental property.
+            type: bool
+        links:
+            description: A list of links associated with this volume.
+            type: list
+        metadata:
+            description: Backup metadata.
+            type: dict
         name:
-            description: Name given to the load balancer.
+            description: Backup Name.
+            type: str
+        object_count:
+            description: backup object count.
+            type: int
+        project_id:
+            description: The UUID of the owning project.
+            type: str
+        size:
+            description: The size of the volume, in gibibytes (GiB).
+            type: int
+        snapshot_id:
+            description: Snapshot ID.
+            type: str
+        status:
+            description: Backup status.
+            type: str
+        updated_at:
+            description: Backup update time.
+            type: str
+        user_id:
+            description: The UUID of the project owner.
+            type: str
+        volume_id:
+            description: Volume ID.
             type: str
-            sample: "elb_test"
 '''
 
-EXAMPLES = '''
+EXAMPLES = r'''
 - name: Create backup
   openstack.cloud.volume_backup:
-    display_name: test_volume_backup
+    name: test_volume_backup
     volume: "test_volume"
 
 - name: Create backup from snapshot
   openstack.cloud.volume_backup:
-    display_name: test_volume_backup
-    volume: "test_volume"
+    name: test_volume_backup
     snapshot: "test_snapshot"
+    volume: "test_volume"
 
 - name: Delete volume backup
   openstack.cloud.volume_backup:
-    display_name: test_volume_backup
+    name: test_volume_backup
     state: absent
 '''
 
@@ -98,18 +165,20 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
 
 
 class VolumeBackupModule(OpenStackModule):
-    module_min_sdk_version = '0.49.0'
 
     argument_spec = dict(
-        display_name=dict(required=True, aliases=['name']),
-        display_description=dict(aliases=['description']),
-        volume=dict(),
-        snapshot=dict(),
-        state=dict(default='present', choices=['absent', 'present']),
+        description=dict(aliases=['display_description']),
         force=dict(default=False, type='bool'),
+        is_incremental=dict(default=False,
+                            type='bool',
+                            aliases=['incremental']),
         metadata=dict(type='dict'),
-        incremental=dict(default=False, type='bool')
+        name=dict(required=True, aliases=['display_name']),
+        snapshot=dict(),
+        state=dict(default='present', choices=['absent', 'present']),
+        volume=dict(),
     )
+
     module_kwargs = dict(
         required_if=[
             ('state', 'present', ['volume'])
@@ -117,98 +186,79 @@ class VolumeBackupModule(OpenStackModule):
         supports_check_mode=True
     )
 
-    def _create_backup(self):
-        if self.ansible.check_mode:
-            self.exit_json(changed=True)
-
-        name = self.params['display_name']
-        description = self.params['display_description']
-        volume = self.params['volume']
-        snapshot = self.params['snapshot']
-        force = self.params['force']
-        is_incremental = self.params['incremental']
-        metadata = self.params['metadata']
+    def run(self):
+        name = self.params['name']
+        state = self.params['state']
 
-        changed = False
+        backup = self.conn.block_storage.find_backup(name)
 
-        cloud_volume = self.conn.block_storage.find_volume(volume)
-        cloud_snapshot_id = None
+        if self.ansible.check_mode:
+            self.exit_json(changed=self._will_change(state, backup))
+
+        if state == 'present' and not backup:
+            backup = self._create()
+            self.exit_json(changed=True,
+                           backup=backup.to_dict(computed=False),
+                           volume_backup=backup.to_dict(computed=False))
+
+        elif state == 'present' and backup:
+            # We do not support backup updates, because
+            # openstacksdk does not support it either
+            self.exit_json(changed=False,
+                           backup=backup.to_dict(computed=False),
+                           volume_backup=backup.to_dict(computed=False))
+
+        elif state == 'absent' and backup:
+            self._delete(backup)
+            self.exit_json(changed=True)
 
-        attrs = {
-            'name': name,
-            'volume_id': cloud_volume.id,
-            'force': force,
-            'is_incremental': is_incremental
-        }
+        else:  # state == 'absent' and not backup
+            self.exit_json(changed=False)
 
-        if snapshot:
-            cloud_snapshot_id = self.conn.block_storage.find_snapshot(
-                snapshot, ignore_missing=False).id
-            attrs['snapshot_id'] = cloud_snapshot_id
+    def _create(self):
+        args = dict()
+        for k in ['description', 'is_incremental', 'force', 'metadata',
+                  'name']:
+            if self.params[k] is not None:
+                args[k] = self.params[k]
 
-        if metadata:
-            attrs['metadata'] = metadata
+        volume_name_or_id = self.params['volume']
+        volume = self.conn.block_storage.find_volume(volume_name_or_id,
+                                                     ignore_missing=False)
+        args['volume_id'] = volume.id
 
-        if description:
-            attrs['description'] = description
+        snapshot_name_or_id = self.params['snapshot']
+        if snapshot_name_or_id:
+            snapshot = self.conn.block_storage.find_snapshot(
+                snapshot_name_or_id, ignore_missing=False)
+            args['snapshot_id'] = snapshot.id
 
-        backup = self.conn.block_storage.create_backup(**attrs)
-        changed = True
+        backup = self.conn.block_storage.create_backup(**args)
 
         if self.params['wait']:
-            try:
-                backup = self.conn.block_storage.wait_for_status(
-                    backup,
-                    status='available',
-                    wait=self.params['timeout'])
-                self.exit_json(
-                    changed=True, volume_backup=backup.to_dict(), id=backup.id
-                )
-            except self.sdk.exceptions.ResourceTimeout:
-                self.fail_json(
-                    msg='Timeout failure waiting for backup '
-                        'to complete'
-                )
-
-        self.exit_json(
-            changed=changed, volume_backup=backup.to_dict(), id=backup.id
-        )
-
-    def _delete_backup(self, backup):
-        if self.ansible.check_mode:
-            self.exit_json(changed=True)
+            backup = self.conn.block_storage.wait_for_status(
+                backup, status='available', wait=self.params['timeout'])
 
-        if backup:
-            self.conn.block_storage.delete_backup(backup)
-            if self.params['wait']:
-                try:
-                    self.conn.block_storage.wait_for_delete(
-                        backup,
-                        interval=2,
-                        wait=self.params['timeout'])
-                except self.sdk.exceptions.ResourceTimeout:
-                    self.fail_json(
-                        msg='Timeout failure waiting for backup '
-                            'to be deleted'
-                    )
-
-        self.exit_json(changed=True)
-
-    def run(self):
-        name = self.params['display_name']
+        return backup
 
-        backup = self.conn.block_storage.find_backup(name)
-
-        if self.params['state'] == 'present':
-            if not backup:
-                self._create_backup()
-            else:
-                # For the moment we do not support backup update, since SDK
-                # doesn't support it either => do nothing
-                self.exit_json(changed=False)
-
-        elif self.params['state'] == 'absent':
-            self._delete_backup(backup)
+    def _delete(self, backup):
+        self.conn.block_storage.delete_backup(backup)
+        if self.params['wait']:
+            self.conn.block_storage.wait_for_delete(
+                backup, wait=self.params['timeout'])
+
+    def _will_change(self, state, backup):
+        if state == 'present' and not backup:
+            return True
+        elif state == 'present' and backup:
+            # We do not support backup updates, because
+            # openstacksdk does not support it either
+            return False
+        elif state == 'absent' and backup:
+            return False
+        else:
+            # state == 'absent' and not backup:
+            return True
 
 
 def main():
diff --git a/plugins/modules/volume_backup_info.py b/plugins/modules/volume_backup_info.py
index 1fb549fd3b16a753774fe622b862689a69055d93..57f01fed0976f931027098e8fb5b9650c92b4ab2 100644
--- a/plugins/modules/volume_backup_info.py
+++ b/plugins/modules/volume_backup_info.py
@@ -4,8 +4,7 @@
 # Copyright (c) 2020 by Open Telekom Cloud, operated by T-Systems International GmbH
 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
 
-
-DOCUMENTATION = '''
+DOCUMENTATION = r'''
 ---
 module: volume_backup_info
 short_description: Get Backups
@@ -19,14 +18,18 @@ options:
     type: str
   volume:
     description:
-      - Name of the volume.
+      - Name or ID of the volume.
     type: str
-requirements: ["openstacksdk"]
+
+requirements:
+  - "python >= 3.6"
+  - "openstacksdk"
+
 extends_documentation_fragment:
 - openstack.cloud.openstack
 '''
 
-RETURN = '''
+RETURN = r'''
 volume_backups:
     description: List of dictionaries describing volume backups.
     type: list
@@ -36,12 +39,32 @@ volume_backups:
         availability_zone:
             description: Backup availability zone.
             type: str
+        container:
+            description: The container name.
+            type: str
         created_at:
             description: Backup creation time.
             type: str
+        data_timestamp:
+            description: The time when the data on the volume was first saved.
+                         If it is a backup from volume, it will be the same as
+                         C(created_at) for a backup. If it is a backup from a
+                         snapshot, it will be the same as created_at for the
+                         snapshot.
+            type: str
         description:
             description: Backup desciption.
             type: str
+        fail_reason:
+            description: Backup fail reason.
+            type: str
+        force:
+            description: Force backup.
+            type: bool
+        has_dependent_backups:
+            description: If this value is true, there are other backups
+                         depending on this backup.
+            type: bool
         id:
             description: Unique UUID.
             type: str
@@ -49,12 +72,24 @@ volume_backups:
         is_incremental:
             description: Backup incremental property.
             type: bool
+        links:
+            description: A list of links associated with this volume.
+            type: list
         metadata:
             description: Backup metadata.
             type: dict
         name:
             description: Backup Name.
             type: str
+        object_count:
+            description: backup object count.
+            type: int
+        project_id:
+            description: The UUID of the owning project.
+            type: str
+        size:
+            description: The size of the volume, in gibibytes (GiB).
+            type: int
         snapshot_id:
             description: Snapshot ID.
             type: str
@@ -64,57 +99,56 @@ volume_backups:
         updated_at:
             description: Backup update time.
             type: str
+        user_id:
+            description: The UUID of the project owner.
+            type: str
         volume_id:
             description: Volume ID.
             type: str
-
 '''
 
-EXAMPLES = '''
-# Get backups.
-- openstack.cloud.volume_backup_info:
-  register: backup
+EXAMPLES = r'''
+- name: Get all backups
+  openstack.cloud.volume_backup_info:
 
-- openstack.cloud.volume_backup_info:
+- name: Get backup 'my_fake_backup'
+  openstack.cloud.volume_backup_info:
     name: my_fake_backup
-  register: backup
 '''
 
 from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
 
 
 class VolumeBackupInfoModule(OpenStackModule):
-    module_min_sdk_version = '0.49.0'
 
     argument_spec = dict(
         name=dict(),
         volume=dict()
     )
+
     module_kwargs = dict(
         supports_check_mode=True
     )
 
     def run(self):
-        name_filter = self.params['name']
-        volume = self.params['volume']
-
-        data = []
-        attrs = {}
-
-        if name_filter:
-            attrs['name'] = name_filter
-        if volume:
-            attrs['volume_id'] = self.conn.block_storage.find_volume(volume)
-
-        for raw in self.conn.block_storage.backups(**attrs):
-            dt = raw.to_dict()
-            dt.pop('location')
-            data.append(dt)
-
-        self.exit_json(
-            changed=False,
-            volume_backups=data
-        )
+        kwargs = dict((k, self.params[k])
+                      for k in ['name']
+                      if self.params[k] is not None)
+
+        volume_name_or_id = self.params['volume']
+        volume = None
+        if volume_name_or_id:
+            volume = self.conn.block_storage.find_volume(volume_name_or_id)
+            if volume:
+                kwargs['volume_id'] = volume.id
+
+        if volume_name_or_id and not volume:
+            backups = []
+        else:
+            backups = [b.to_dict(computed=False)
+                       for b in self.conn.block_storage.backups(**kwargs)]
+
+        self.exit_json(changed=False, volume_backups=backups)
 
 
 def main():