Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support to mount disk to multiple VMs #867

Merged
merged 2 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 99 additions & 24 deletions plugins/modules/azure_rm_manageddisk.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,26 @@
- Name of an existing virtual machine with which the disk is or will be associated, this VM should be in the same resource group.
- To detach a disk from a vm, explicitly set to ''.
- If this option is unset, the value will not be changed.
managed_by_extended:
description:
- List of name and resource group of the VMs that have the disk attached.
- I(max_shares) should be set to a value greater than one for disks to allow attaching them to multiple VMs.
type: list
elements: dict
suboptions:
resource_group:
description:
- The resource group of the attache VM.
type: str
name:
description:
- The name of the attache VM.
type: str
max_shares:
description:
- The maximum number of VMs that can attach to the disk at the same time.
- Value greater than one indicates a disk that can be mounted on multiple VMs at the same time.
type: int
attach_caching:
description:
- Disk caching policy controlled by VM. Will be used when attached to the VM defined by C(managed_by).
Expand All @@ -94,10 +114,6 @@
- ''
- read_only
- read_write
tags:
description:
- Tags to assign to the managed disk.
- Format tags as 'key' or 'key:value'.
zone:
description:
- The Azure managed disk's zone.
Expand Down Expand Up @@ -147,6 +163,20 @@
managed_by: testvm001
attach_caching: read_only

- name: Mount the managed disk to multiple VMs
azure_rm_manageddisk:
resource_group: myResourceGroup
name: freddisk04
max_shares: 4
disk_size_gb: 1024
storage_account_type: Premium_LRS
managed_by_extended:
- resource_group: myResourceGroup01
name: testVM01
- resource_group: myResourceGroup02
name: testVM02
zone: 1

- name: Unmount the managed disk to VM
azure_rm_manageddisk:
name: mymanageddisk
Expand All @@ -168,7 +198,7 @@
description:
- Current state of the managed disk.
returned: always
type: dict
type: complex
contains:
id:
description:
Expand Down Expand Up @@ -210,6 +240,17 @@
description:
- Name of an existing virtual machine with which the disk is or will be associated, this VM should be in the same resource group.
type: str
max_shares:
description:
- The maximum number of VMs that can attach to the disk at the same time.
- Value greater than one indicates a disk that can be mounted on multiple VMs at the same time.
type: int
sample: 3
managed_by_extended:
description:
- List ID of an existing virtual machine with which the disk is or will be associated.
type: list
sample: ["/subscriptions/xxx-xxx/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/testVM"]
tags:
description:
- Tags to assign to the managed disk.
Expand All @@ -234,6 +275,12 @@
pass


managed_by_extended_spec = dict(
resource_group=dict(type='str'),
name=dict(type='str')
)


# duplicated in azure_rm_manageddisk_facts
def managed_disk_to_dict(managed_disk):
create_data = managed_disk.creation_data
Expand All @@ -248,6 +295,8 @@ def managed_disk_to_dict(managed_disk):
os_type=managed_disk.os_type.lower() if managed_disk.os_type else None,
storage_account_type=managed_disk.sku.name if managed_disk.sku else None,
managed_by=managed_disk.managed_by,
max_shares=managed_disk.max_shares,
managed_by_extended=managed_disk.managed_by_extended,
zone=managed_disk.zones[0] if managed_disk.zones and len(managed_disk.zones) > 0 else ''
)

Expand Down Expand Up @@ -305,6 +354,14 @@ def __init__(self):
),
lun=dict(
type='int'
),
max_shares=dict(
type='int'
),
managed_by_extended=dict(
type='list',
elements='dict',
options=managed_by_extended_spec
)
)
required_if = [
Expand All @@ -329,10 +386,16 @@ def __init__(self):
self.managed_by = None
self.attach_caching = None
self.lun = None
self.max_shares = None
self.managed_by_extended = None

mutually_exclusive = [['managed_by_extended', 'managed_by']]

super(AzureRMManagedDisk, self).__init__(
derived_arg_spec=self.module_arg_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
supports_tags=True)

def exec_module(self, **kwargs):
Expand Down Expand Up @@ -360,19 +423,27 @@ def exec_module(self, **kwargs):
else:
result = True

# Mount the disk to multiple VM
if self.managed_by_extended:
if not self.check_mode:
for vm_item in self.managed_by_extended:
vm_name_id = self.compute_client.virtual_machines.get(vm_item['resource_group'], vm_item['name'])
if result['managed_by_extended'] is None or vm_name_id.id not in result['managed_by_extended']:
changed = True
self.attach(vm_item['resource_group'], vm_item['name'], result)
result = self.get_managed_disk()

# unmount from the old virtual machine and mount to the new virtual machine
if self.managed_by or self.managed_by == '':
vm_name = parse_resource_id(disk_instance.get('managed_by', '')).get('name') if disk_instance else None
vm_name = vm_name or ''
vm_old = None
if self.managed_by != vm_name or self.is_attach_caching_option_different(vm_name, result):
changed = True
if not self.check_mode:
if vm_name:
vm_old = self._get_vm(vm_name)
self.detach(vm_name, result)
self.detach(self.resource_group, vm_name, result)
if self.managed_by:
self.attach(self.managed_by, result, vm_old)
self.attach(self.resource_group, self.managed_by, result)
result = self.get_managed_disk()

if self.state == 'absent' and disk_instance:
Expand All @@ -385,8 +456,8 @@ def exec_module(self, **kwargs):
self.results['state'] = result
return self.results

def attach(self, vm_name, disk, vm_old):
vm = self._get_vm(vm_name)
def attach(self, resource_group, vm_name, disk):
vm = self._get_vm(resource_group, vm_name)
# find the lun
if self.lun:
lun = self.lun
Expand All @@ -398,10 +469,9 @@ def attach(self, vm_name, disk, vm_old):
if lun not in luns:
break
lun = lun + 1
if vm_old is not None:
for item in vm_old.storage_profile.data_disks:
if item.name == self.name:
lun = item.lun
for item in vm.storage_profile.data_disks:
if item.name == self.name:
lun = item.lun

# prepare the data disk
params = self.compute_models.ManagedDiskParameters(id=disk.get('id'), storage_account_type=disk.get('storage_account_type'))
Expand All @@ -412,26 +482,26 @@ def attach(self, vm_name, disk, vm_old):
managed_disk=params,
caching=caching_options)
vm.storage_profile.data_disks.append(data_disk)
self._update_vm(vm_name, vm)
self._update_vm(resource_group, vm_name, vm)

def detach(self, vm_name, disk):
vm = self._get_vm(vm_name)
def detach(self, resource_group, vm_name, disk):
vm = self._get_vm(resource_group, vm_name)
leftovers = [d for d in vm.storage_profile.data_disks if d.name.lower() != disk.get('name').lower()]
if len(vm.storage_profile.data_disks) == len(leftovers):
self.fail("No disk with the name '{0}' was found".format(disk.get('name')))
vm.storage_profile.data_disks = leftovers
self._update_vm(vm_name, vm)
self._update_vm(resource_group, vm_name, vm)

def _update_vm(self, name, params):
def _update_vm(self, resource_group, name, params):
try:
poller = self.compute_client.virtual_machines.begin_create_or_update(self.resource_group, name, params)
poller = self.compute_client.virtual_machines.begin_create_or_update(resource_group, name, params)
self.get_poller_result(poller)
except Exception as exc:
self.fail("Error updating virtual machine {0} - {1}".format(name, str(exc)))

def _get_vm(self, name):
def _get_vm(self, resource_group, name):
try:
return self.compute_client.virtual_machines.get(self.resource_group, name, expand='instanceview')
return self.compute_client.virtual_machines.get(resource_group, name, expand='instanceview')
except Exception as exc:
self.fail("Error getting virtual machine {0} - {1}".format(name, str(exc)))

Expand All @@ -458,6 +528,8 @@ def generate_managed_disk_property(self):
disk_params['os_type'] = self.compute_models.OperatingSystemTypes(self.os_type.capitalize())
else:
disk_params['os_type'] = None
if self.max_shares:
disk_params['max_shares'] = self.max_shares
disk_params['creation_data'] = creation_data
return disk_params

Expand Down Expand Up @@ -491,6 +563,9 @@ def is_different(self, found_disk, new_disk):
if self.zone is not None:
if not found_disk['zone'] == self.zone:
resp = True
if self.max_shares is not None:
if not found_disk['max_shares'] == self.max_shares:
resp = True
return resp

def delete_managed_disk(self):
Expand All @@ -507,7 +582,7 @@ def get_managed_disk(self):
self.resource_group,
self.name)
return managed_disk_to_dict(resp)
except ResourceNotFoundError as e:
except ResourceNotFoundError:
self.log('Did not find managed disk')

def is_attach_caching_option_different(self, vm_name, disk):
Expand Down
14 changes: 14 additions & 0 deletions plugins/modules/azure_rm_manageddisk_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@
description:
- Name of an existing virtual machine with which the disk is or will be associated, this VM should be in the same resource group.
type: str
sample: "/subscriptions/xxx-xxx/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/testVM"
max_shares:
description:
- The maximum number of VMs that can attach to the disk at the same time.
- Value greater than one indicates a disk that can be mounted on multiple VMs at the same time.
type: int
sample: 3
managed_by_extended:
description:
- List ID of an existing virtual machine with which the disk is or will be associated.
type: list
sample: ["/subscriptions/xxx-xxx/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/testVM"]
tags:
description:
- Tags to assign to the managed disk.
Expand Down Expand Up @@ -241,6 +253,8 @@ def managed_disk_to_dict(self, managed_disk):
os_type=managed_disk.os_type.lower() if managed_disk.os_type else None,
storage_account_type=managed_disk.sku.name if managed_disk.sku else None,
managed_by=managed_disk.managed_by,
max_shares=managed_disk.max_shares,
managed_by_extended=managed_disk.managed_by_extended,
zone=managed_disk.zones[0] if managed_disk.zones and len(managed_disk.zones) > 0 else ''
)

Expand Down