Repository: ansible-collections/community.vmware

Issue:
vmware_guest_disk: add support for setting the sharing mode of virtual disks
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Describe the new feature/improvement briefly below -->
This feature should allow setting the sharing mode of the virtual disk.

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
plugins/modules/vmware_guest_disk.py

##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->
Setting the sharing to multi writer means that multiple virtual machines can write to the virtual disk. This sharing mode is allowed only for eagerly zeroed thick virtual disks[1].

References:

[1] vSphere WS API: https://vdc-download.vmware.com/vmwb-repository/dcr-public/b50dcbbf-051d-4204-a3e7-e1b618c1e384/538cf2ec-b34f-4bae-a332-3820ef9e7773/vim.vm.device.VirtualDisk.FlatVer2BackingInfo.html
<!--- Paste example playbooks or commands between quotes below -->
```yaml

```

<!--- HINT: You can also paste gist.github.com links for larger files -->



Discussion:

Resolving patch:
diff --git a/plugins/modules/vmware_guest_disk.py b/plugins/modules/vmware_guest_disk.py
--- a/plugins/modules/vmware_guest_disk.py
+++ b/plugins/modules/vmware_guest_disk.py
@@ -84,6 +84,9 @@
      - '     - C(persistent) Changes are immediately and permanently written to the virtual disk. This is default.'
      - '     - C(independent_persistent) Same as persistent, but not affected by snapshots.'
      - '     - C(independent_nonpersistent) Changes to virtual disk are made to a redo log and discarded at power off, but not affected by snapshots.'
+     - ' - C(sharing) (bool): The sharing mode of the virtual disk. The default value is no sharing.'
+     - '   Setting C(sharing) means that multiple virtual machines can write to the virtual disk.'
+     - '   Sharing can only be set if C(type) is C(eagerzeroedthick).'
      - ' - C(datastore) (string): Name of datastore or datastore cluster to be used for the disk.'
      - ' - C(autoselect_datastore) (bool): Select the less used datastore. Specify only if C(datastore) is not specified.'
      - ' - C(scsi_controller) (integer): SCSI controller number. Valid value range from 0 to 3.'
@@ -344,13 +347,14 @@ def create_scsi_controller(self, scsi_type, scsi_bus_number):
         return scsi_ctl
 
     @staticmethod
-    def create_scsi_disk(scsi_ctl_key, disk_index, disk_mode, disk_filename):
+    def create_scsi_disk(scsi_ctl_key, disk_index, disk_mode, disk_filename, sharing):
         """
         Create Virtual Device Spec for virtual disk
         Args:
             scsi_ctl_key: Unique SCSI Controller Key
             disk_index: Disk unit number at which disk needs to be attached
             disk_mode: Disk mode
+            sharing: Disk sharing mode
             disk_filename: Path to the disk file on the datastore
 
         Returns: Virtual Device Spec for virtual disk
@@ -361,6 +365,7 @@ def create_scsi_disk(scsi_ctl_key, disk_index, disk_mode, disk_filename):
         disk_spec.device = vim.vm.device.VirtualDisk()
         disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
         disk_spec.device.backing.diskMode = disk_mode
+        disk_spec.device.backing.sharing = sharing
         disk_spec.device.controllerKey = scsi_ctl_key
         disk_spec.device.unitNumber = disk_index
 
@@ -416,6 +421,28 @@ def get_ioandshares_diskconfig(self, disk_spec, disk):
             disk_spec.device.storageIOAllocation = io_disk_spec
         return disk_spec
 
+    def get_sharing(self, disk, disk_type, disk_index):
+        """
+        Get the sharing mode of the virtual disk
+        Args:
+            disk: Virtual disk data object
+            disk_type: Disk type of the virtual disk
+            disk_index: Disk unit number at which disk needs to be attached
+
+        Returns:
+            sharing_mode: The sharing mode of the virtual disk
+
+        """
+        sharing = disk.get('sharing')
+        if sharing and disk_type != 'eagerzeroedthick':
+            self.module.fail_json(msg="Invalid 'sharing' mode specified for disk index [%s]. 'disk_mode'"
+                                      " must be 'eagerzeroedthick' when 'sharing'." % disk_index)
+        if sharing:
+            sharing_mode = 'sharingMultiWriter'
+        else:
+            sharing_mode = 'sharingNone'
+        return sharing_mode
+
     def ensure_disks(self, vm_obj=None):
         """
         Manage internal state of virtual machine disks
@@ -466,7 +493,7 @@ def ensure_disks(self, vm_obj=None):
             scsi_controller = disk['scsi_controller'] + 1000  # VMware auto assign 1000 + SCSI Controller
             if disk['disk_unit_number'] not in current_scsi_info[scsi_controller]['disks'] and disk['state'] == 'present':
                 # Add new disk
-                disk_spec = self.create_scsi_disk(scsi_controller, disk['disk_unit_number'], disk['disk_mode'], disk['filename'])
+                disk_spec = self.create_scsi_disk(scsi_controller, disk['disk_unit_number'], disk['disk_mode'], disk['filename'], disk['sharing'])
                 if disk['filename'] is None:
                     disk_spec.device.capacityInKB = disk['size']
                 if disk['disk_type'] == 'thin':
@@ -480,6 +507,7 @@ def ensure_disks(self, vm_obj=None):
                 if disk['filename'] is not None:
                     disk_spec.device.backing.fileName = disk['filename']
                 disk_spec.device.backing.datastore = disk['datastore']
+                disk_spec.device.backing.sharing = disk['sharing']
                 disk_spec = self.get_ioandshares_diskconfig(disk_spec, disk)
                 self.config_spec.deviceChange.append(disk_spec)
                 disk_change = True
@@ -552,7 +580,8 @@ def sanitize_disk_inputs(self):
                                 autoselect_datastore=True,
                                 disk_unit_number=0,
                                 scsi_controller=0,
-                                disk_mode='persistent')
+                                disk_mode='persistent',
+                                sharing=False)
             # Check state
             if 'state' in disk:
                 if disk['state'] not in ['absent', 'present']:
@@ -718,6 +747,9 @@ def sanitize_disk_inputs(self):
                                           " 'disk_mode' value from ['persistent', 'independent_persistent', 'independent_nonpersistent']." % disk_index)
             current_disk['disk_mode'] = temp_disk_mode
 
+            # Sharing mode of disk
+            current_disk['sharing'] = self.get_sharing(disk, disk_type, disk_index)
+
             # SCSI Controller Type
             scsi_contrl_type = disk.get('scsi_type', 'paravirtual').lower()
             if scsi_contrl_type not in self.scsi_device_type.keys():
@@ -807,6 +839,7 @@ def gather_disk_facts(vm_obj):
                     backing_filename=disk.backing.fileName,
                     backing_datastore=disk.backing.datastore.name,
                     backing_disk_mode=disk.backing.diskMode,
+                    backing_sharing=disk.backing.sharing,
                     backing_writethrough=disk.backing.writeThrough,
                     backing_thinprovisioned=disk.backing.thinProvisioned,
                     backing_eagerlyscrub=bool(disk.backing.eagerlyScrub),


Test patch:
diff --git a/tests/integration/targets/vmware_guest_disk/tasks/main.yml b/tests/integration/targets/vmware_guest_disk/tasks/main.yml
--- a/tests/integration/targets/vmware_guest_disk/tasks/main.yml
+++ b/tests/integration/targets/vmware_guest_disk/tasks/main.yml
@@ -86,7 +86,7 @@
     datacenter: "{{ dc1 }}"
     validate_certs: no
     name: "{{ virtual_machines[0].name }}"
-    disk: 
+    disk:
         - size_gb: 1
           type: eagerzeroedthick
           datastore: "{{ rw_datastore }}"
@@ -106,7 +106,7 @@
   assert:
      that:
          - test_custom_shares is changed
- 
+
 - name: create new disk with custom IO limits and shares in IO Limits
   vmware_guest_disk:
     hostname: "{{ vcenter_hostname }}"
@@ -115,7 +115,7 @@
     datacenter: "{{ dc1 }}"
     validate_certs: no
     name: "{{ virtual_machines[0].name }}"
-    disk: 
+    disk:
         - size_gb: 1
           type: eagerzeroedthick
           datastore: "{{ rw_datastore }}"
@@ -146,7 +146,7 @@
     datacenter: "{{ dc1 }}"
     validate_certs: no
     name: "{{ virtual_machines[0].name }}"
-    disk: 
+    disk:
         - size_gb: 2
           type: eagerzeroedthick
           datastore: "{{ rw_datastore }}"
@@ -177,7 +177,7 @@
     datacenter: "{{ dc1 }}"
     validate_certs: no
     name: "{{ virtual_machines[0].name }}"
-    disk: 
+    disk:
         - size_gb: 3
           type: eagerzeroedthick
           datastore: "{{ rw_datastore }}"
@@ -208,7 +208,7 @@
     datacenter: "{{ dc1 }}"
     validate_certs: no
     name: "{{ virtual_machines[0].name }}"
-    disk: 
+    disk:
         - size_gb: 4
           type: eagerzeroedthick
           datastore: "{{ rw_datastore }}"
@@ -280,3 +280,60 @@
   assert:
     that:
         - test_recreate_disk is changed
+
+- name: create new disk with sharing (multi-writer) mode
+  vmware_guest_disk:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    datacenter: "{{ dc1 }}"
+    validate_certs: no
+    name: "{{ virtual_machines[0].name }}"
+    disk:
+        - datastore: "{{ rw_datastore }}"
+          disk_mode: "independent_persistent"
+          scsi_controller: 0
+          scsi_type: 'paravirtual'
+          size_gb: 1
+          state: present
+          type: eagerzeroedthick
+          sharing: True
+          unit_number: 6
+  register: test_create_disk_sharing
+
+- debug:
+    msg: "{{ test_create_disk_sharing }}"
+
+- name: assert that changes were made
+  assert:
+    that:
+        - test_create_disk_sharing is changed
+
+- name: create new disk with invalid disk type for sharing (multi-writer) mode
+  vmware_guest_disk:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    datacenter: "{{ dc1 }}"
+    validate_certs: no
+    name: "{{ virtual_machines[0].name }}"
+    disk:
+        - datastore: "{{ rw_datastore }}"
+          disk_mode: "independent_persistent"
+          scsi_controller: 0
+          scsi_type: 'paravirtual'
+          size_gb: 1
+          state: present
+          type: thin
+          unit_number: 5
+          sharing: True
+  register: test_create_disk_sharing_invalid
+  ignore_errors: True
+
+- debug:
+    msg: "{{ test_create_disk_sharing_invalid }}"
+
+- name: assert that changes were not made
+  assert:
+    that:
+        - not(test_create_disk_sharing_invalid is changed)