Server IP : 85.214.239.14 / Your IP : 13.59.36.4 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/ansible_collections/community/vmware/plugins/modules/ |
Upload File : |
#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright: (c) 2015, Bede Carroll <bc+github () bedecarroll.com> # Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com> # Copyright: (c) 2018, Ansible Project # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = r''' --- module: vmware_vmotion short_description: Move a virtual machine using vMotion, and/or its vmdks using storage vMotion. description: - Using VMware vCenter, move a virtual machine using vMotion to a different host, and/or its vmdks to another datastore using storage vMotion. author: - Bede Carroll (@bedecarroll) - Olivier Boukili (@oboukili) options: vm_name: description: - Name of the VM to perform a vMotion on. - This is required parameter, if C(vm_uuid) is not set. - Version 2.6 onwards, this parameter is not a required parameter, unlike the previous versions. aliases: ['vm'] type: str vm_uuid: description: - UUID of the virtual machine to perform a vMotion operation on. - This is a required parameter, if C(vm_name) or C(moid) is not set. aliases: ['uuid'] type: str moid: description: - Managed Object ID of the instance to manage if known, this is a unique identifier only within a single vCenter instance. - This is required if C(vm_name) or C(vm_uuid) is not supplied. type: str use_instance_uuid: description: - Whether to use the VMware instance UUID rather than the BIOS UUID. default: false type: bool destination_host: description: - Name of the destination host the virtual machine should be running on. - Version 2.6 onwards, this parameter is not a required parameter, unlike the previous versions. aliases: ['destination'] type: str destination_cluster: version_added: '2.5.0' description: - Name of the destination cluster the virtual machine should be running on. - Only works if drs is enabled for this cluster. type: str destination_datastore_cluster: version_added: '2.5.0' description: - Name of the destination datastore cluster (storage pod) the virtual machine's vmdk should be moved on. - Only works if drs is enabled for the cluster the vm is running / should run. type: str destination_datastore: description: - Name of the destination datastore the virtual machine's vmdk should be moved on. aliases: ['datastore'] type: str destination_datacenter: description: - Name of the destination datacenter the datastore is located on. - Optional, required only when datastores are shared across datacenters. type: str destination_resourcepool: description: - Name of the destination resource pool where the virtual machine should be running. - Resource pool is required if vmotion is done between hosts which are part of different clusters or datacenters. - if not passed, resource_pool object will be retrived from host_obj parent. aliases: ['resource_pool'] type: str timeout: description: - The timeout in seconds. When the timeout is reached, the module will fail. type: int default: 3600 version_added: '3.4.0' extends_documentation_fragment: - community.vmware.vmware.documentation ''' EXAMPLES = r''' - name: Perform vMotion of virtual machine community.vmware.vmware_vmotion: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' vm_name: 'vm_name_as_per_vcenter' destination_host: 'destination_host_as_per_vcenter' delegate_to: localhost - name: Perform vMotion of virtual machine community.vmware.vmware_vmotion: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' moid: vm-42 destination_host: 'destination_host_as_per_vcenter' delegate_to: localhost - name: Perform vMotion of virtual machine to resource_pool community.vmware.vmware_vmotion: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' moid: vm-42 destination_host: 'destination_host_as_per_vcenter' destination_resourcepool: 'destination_resourcepool_as_per_vcenter' delegate_to: localhost - name: Perform storage vMotion of virtual machine community.vmware.vmware_vmotion: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' vm_name: 'vm_name_as_per_vcenter' destination_datastore: 'destination_datastore_as_per_vcenter' delegate_to: localhost - name: Perform storage vMotion and host vMotion of virtual machine community.vmware.vmware_vmotion: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' vm_name: 'vm_name_as_per_vcenter' destination_host: 'destination_host_as_per_vcenter' destination_datastore: 'destination_datastore_as_per_vcenter' delegate_to: localhost - name: Perform storage vMotion to a Storage Cluster and vMotion to a Cluster of virtual machine community.vmware.vmware_vmotion: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' vm_name: 'vm_name_as_per_vcenter' destination_cluster: 'destination_cluster_as_per_vcenter' destination_datastore_cluster: 'destination_datastore_cluster_as_per_vcenter' delegate_to: localhost ''' RETURN = r''' running_host: description: - List the host the virtual machine is registered to. - Only returned if there is asked for a vMotion (Cluster or Host). returned: changed or success type: str sample: 'host1.example.com' datastore: description: - List the datastore the virtual machine is on. - Only returned if there is asked for a Storage vMotion (Datastore or Datastore Cluster). returned: changed or success type: str sample: 'datastore1' ''' try: from pyVmomi import vim, VmomiSupport except ImportError: pass from ansible.module_utils._text import to_native from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.vmware.plugins.module_utils.vmware import ( PyVmomi, find_hostsystem_by_name, find_vm_by_id, find_datastore_by_name, find_resource_pool_by_name, find_datacenter_by_name, find_cluster_by_name, get_all_objs, vmware_argument_spec, wait_for_task, TaskError) class VmotionManager(PyVmomi): def __init__(self, module): super(VmotionManager, self).__init__(module) self.vm = None self.vm_uuid = self.params.get('vm_uuid', None) self.use_instance_uuid = self.params.get('use_instance_uuid', False) self.vm_name = self.params.get('vm_name', None) self.moid = self.params.get('moid') or None self.destination_datacenter = self.params.get('destination_datacenter', None) self.timeout = self.params.get('timeout') result = dict() self.get_vm() if self.vm is None: vm_id = self.vm_uuid or self.vm_name or self.moid self.module.fail_json(msg="Failed to find the virtual machine with %s" % vm_id) # Get Datacenter if specified by user dest_datacenter = self.destination_datacenter datacenter_object = None if dest_datacenter is not None: datacenter_object = find_datacenter_by_name(content=self.content, datacenter_name=dest_datacenter) if datacenter_object: dest_datacenter = datacenter_object # Get Destination Host System or Cluster if specified by user dest_host_name = self.params.get('destination_host', None) dest_cluster_name = self.params.get('destination_cluster', None) if dest_host_name and dest_cluster_name: self.module.fail_json(msg="Please only define one: destination_host or destination_cluster") self.host_object = None self.cluster_object = None self.cluster_hosts = None if dest_host_name is not None: self.host_object = find_hostsystem_by_name(content=self.content, hostname=dest_host_name) if self.host_object is None: self.module.fail_json(msg="Unable to find destination host %s" % dest_host_name) if dest_cluster_name is not None: self.cluster_object = find_cluster_by_name(content=self.content, cluster_name=dest_cluster_name, datacenter=datacenter_object) if self.cluster_object: self.cluster_hosts = [] for host in self.cluster_object.host: self.cluster_hosts.append(host) else: self.module.fail_json(msg="Unable to find destination cluster %s" % dest_cluster_name) # Get Destination Datastore or Datastore Cluster if specified by user dest_datastore = self.params.get('destination_datastore', None) dest_datastore_cluster = self.params.get('destination_datastore_cluster', None) if dest_datastore and dest_datastore_cluster: self.module.fail_json(msg="Please only define one: destination_datastore or destination_datastore_cluster") self.datastore_object = None self.datastore_cluster_object = None if dest_datastore is not None: self.datastore_object = find_datastore_by_name(content=self.content, datastore_name=dest_datastore, datacenter_name=dest_datacenter) if dest_datastore_cluster is not None: # unable to use find_datastore_cluster_by_name module data_store_clusters = get_all_objs(self.content, [vim.StoragePod], folder=self.content.rootFolder) for dsc in data_store_clusters: if dsc.name == dest_datastore_cluster: self.datastore_cluster_object = dsc # At-least one of datastore, datastore cluster, host system or cluster is required to migrate if self.datastore_object is None and self.datastore_cluster_object is None and self.host_object is None and self.cluster_object is None: self.module.fail_json(msg="Unable to find destination datastore, destination datastore cluster," " destination host system or destination cluster.") # Check if datastore is required, this check is required if destination # and source host system does not share same datastore. host_datastore_required = [] for vm_datastore in self.vm.datastore: if self.host_object and vm_datastore not in self.host_object.datastore: host_datastore_required.append(True) if self.cluster_object and vm_datastore not in self.cluster_object.datastore: host_datastore_required.append(True) else: host_datastore_required.append(False) if any(host_datastore_required) and (dest_datastore is None and dest_datastore_cluster is None): msg = "Destination host system or cluster does not share" \ " datastore ['%s'] with source host system ['%s'] on which" \ " virtual machine is located. Please specify destination_datastore or destination_datastore_cluster" \ " to rectify this problem." % ("', '".join([ds.name for ds in (self.host_object.datastore or self.cluster_object.datastore)]), "', '".join([ds.name for ds in self.vm.datastore])) self.module.fail_json(msg=msg) # Check for changes storage_vmotion_needed = True change_required = True vm_ds_name = self.vm.config.files.vmPathName.split(' ', 1)[0].replace('[', '').replace(']', '') if self.host_object and self.datastore_object: # We have both host system and datastore object if not self.datastore_object.summary.accessible: # Datastore is not accessible self.module.fail_json(msg='Destination datastore %s is' ' not accessible.' % dest_datastore) if self.datastore_object not in self.host_object.datastore: # Datastore is not associated with host system self.module.fail_json(msg="Destination datastore %s provided" " is not associated with destination" " host system %s. Please specify" " datastore value ['%s'] associated with" " the given host system." % (dest_datastore, dest_host_name, "', '".join([ds.name for ds in self.host_object.datastore]))) if self.vm.runtime.host.name == dest_host_name and dest_datastore in [ds.name for ds in self.vm.datastore]: change_required = False elif self.host_object and self.datastore_cluster_object: if not set(self.datastore_cluster_object.childEntity) <= set(self.host_object.datastore): self.module.fail_json(msg="Destination datastore cluster %s provided" " is not associated with destination" " host system %s. Please specify" " datastore value ['%s'] associated with" " the given host system." % (dest_datastore_cluster, dest_host_name, "', '".join([ds.name for ds in self.host_object.datastore]))) if self.vm.runtime.host.name == dest_host_name and vm_ds_name in [ds.name for ds in self.datastore_cluster_object.childEntity]: change_required = False elif self.cluster_object and self.datastore_object: if not self.datastore_object.summary.accessible: # Datastore is not accessible self.module.fail_json(msg='Destination datastore %s is' ' not accessible.' % dest_datastore) if self.datastore_object not in self.cluster_object.datastore: # Datastore is not associated with host system self.module.fail_json(msg="Destination datastore %s provided" " is not associated with destination" " cluster %s. Please specify" " datastore value ['%s'] associated with" " the given host system." % (dest_datastore, dest_cluster_name, "', '".join([ds.name for ds in self.cluster_object.datastore]))) if self.vm.runtime.host.name in [host.name for host in self.cluster_hosts] and \ dest_datastore in [ds.name for ds in self.vm.datastore]: change_required = False elif self.cluster_object and self.datastore_cluster_object: if not set(self.datastore_cluster_object.childEntity) <= set(self.cluster_object.datastore): self.module.fail_json(msg="Destination datastore cluster %s provided" " is not associated with destination" " cluster %s. Please specify" " datastore value ['%s'] associated with" " the given host system." % (dest_datastore_cluster, dest_cluster_name, "', '".join([ds.name for ds in self.cluster_object.datastore]))) if self.vm.runtime.host.name in [host.name for host in self.cluster_hosts] and \ vm_ds_name in [ds.name for ds in self.datastore_cluster_object.childEntity]: change_required = False elif (self.host_object and self.datastore_object is None) or ( self.host_object and self.datastore_cluster_object is None): if self.vm.runtime.host.name == dest_host_name: # VM is already located on same host change_required = False storage_vmotion_needed = False elif (self.cluster_object and self.datastore_object is None) or ( self.cluster_object and self.datastore_cluster_object is None): if self.vm.runtime.host.name in [host.name for host in self.cluster_hosts]: # VM is already located on this cluster change_required = False storage_vmotion_needed = False elif (self.datastore_object and self.host_object is None) or ( self.datastore_object and self.cluster_object is None): if self.datastore_object in self.vm.datastore: # VM is already located on same datastore change_required = False if not self.datastore_object.summary.accessible: # Datastore is not accessible self.module.fail_json(msg='Destination datastore %s is' ' not accessible.' % dest_datastore) elif (self.datastore_cluster_object and self.host_object is None) or ( self.datastore_cluster_object and self.cluster_object is None): if vm_ds_name in [ds.name for ds in self.datastore_cluster_object.childEntity]: # VM is already located on a datastore in the datastore cluster change_required = False if self.cluster_object or self.datastore_cluster_object: self.set_placement() # Get Destination resourcepool dest_resourcepool = self.params.get('destination_resourcepool', None) self.resourcepool_object = None if dest_resourcepool: self.resourcepool_object = find_resource_pool_by_name(content=self.content, resource_pool_name=dest_resourcepool) if self.resourcepool_object is None: self.module.fail_json(msg="Unable to find destination resource pool object for %s" % dest_resourcepool) elif not dest_resourcepool and self.host_object: self.resourcepool_object = self.host_object.parent.resourcePool if module.check_mode: if self.host_object: result['running_host'] = self.host_object.name if self.datastore_object: result['datastore'] = self.datastore_object.name result['changed'] = change_required module.exit_json(**result) if change_required: # Migrate VM and get Task object back task_object = self.migrate_vm() # Wait for task to complete try: wait_for_task(task_object, timeout=self.timeout) except TaskError as task_error: self.module.fail_json(msg=to_native(task_error)) # If task was a success the VM has moved, update running_host and complete module if task_object.info.state == vim.TaskInfo.State.success: # The storage layout is not automatically refreshed, so we trigger it to get coherent module return values if storage_vmotion_needed: self.vm.RefreshStorageInfo() if self.host_object: result['running_host'] = self.host_object.name if self.datastore_object: result['datastore'] = self.datastore_object.name result['changed'] = True module.exit_json(**result) else: msg = 'Unable to migrate virtual machine due to an error, please check vCenter' if task_object.info.error is not None: msg += " : %s" % task_object.info.error module.fail_json(msg=msg) else: try: if self.host_object: result['running_host'] = self.host_object.name if self.datastore_object: result['datastore'] = self.datastore_object.name except vim.fault.NoPermission: result['running_host'] = 'NA' result['datastore'] = 'NA' result['changed'] = False module.exit_json(**result) def migrate_vm(self): """ Migrate virtual machine and return the task. """ relocate_spec = vim.vm.RelocateSpec(host=self.host_object, datastore=self.datastore_object, pool=self.resourcepool_object) task_object = self.vm.Relocate(relocate_spec) return task_object def set_placement(self): """ Get the host from the cluster and/or the datastore from datastore cluster. """ if self.cluster_object is None: if self.host_object: self.cluster_object = self.host_object.parent else: self.cluster_object = self.vm.runtime.host.parent if not self.cluster_object.configuration.drsConfig.enabled: self.module.fail_json( msg='destination_cluster or destination_storage_cluster is only allowed for clusters with active drs.') relocate_spec = vim.vm.RelocateSpec(host=self.host_object, datastore=self.datastore_object) if self.datastore_cluster_object: storagePods = [self.datastore_cluster_object] else: storagePods = None placement_spec = vim.cluster.PlacementSpec(storagePods=storagePods, hosts=self.cluster_hosts, vm=self.vm, relocateSpec=relocate_spec) placement = self.cluster_object.PlaceVm(placement_spec) if self.host_object is None: self.host_object = placement.recommendations[0].action[0].targetHost if self.datastore_object is None: self.datastore_object = placement.recommendations[0].action[0].relocateSpec.datastore def get_vm(self): """ Find unique virtual machine either by UUID or Name. Returns: virtual machine object if found, else None. """ vms = [] if self.vm_uuid: if not self.use_instance_uuid: vm_obj = find_vm_by_id(self.content, vm_id=self.params['vm_uuid'], vm_id_type="uuid") elif self.use_instance_uuid: vm_obj = find_vm_by_id(self.content, vm_id=self.params['vm_uuid'], vm_id_type="instance_uuid") vms = [vm_obj] elif self.vm_name: objects = self.get_managed_objects_properties(vim_type=vim.VirtualMachine, properties=['name']) for temp_vm_object in objects: if len(temp_vm_object.propSet) != 1: continue if temp_vm_object.obj.name == self.vm_name: vms.append(temp_vm_object.obj) break elif self.moid: vm_obj = VmomiSupport.templateOf('VirtualMachine')(self.moid, self.si._stub) if vm_obj: vms.append(vm_obj) if len(vms) > 1: self.module.fail_json(msg="Multiple virtual machines with same name %s found." " Please specify vm_uuid instead of vm_name." % self.vm_name) if vms: self.vm = vms[0] def main(): argument_spec = vmware_argument_spec() argument_spec.update( dict( vm_name=dict(aliases=['vm']), vm_uuid=dict(aliases=['uuid']), moid=dict(type='str'), use_instance_uuid=dict(type='bool', default=False), destination_host=dict(aliases=['destination']), destination_resourcepool=dict(aliases=['resource_pool']), destination_datastore=dict(aliases=['datastore']), destination_datacenter=dict(type='str'), destination_cluster=dict(type='str'), destination_datastore_cluster=dict(type='str'), timeout=dict(type='int', default=3600) ) ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=[ ['destination_host', 'destination_datastore', 'destination_cluster', 'destination_datastore_cluster'], ['vm_uuid', 'vm_name', 'moid'], ], mutually_exclusive=[ ['vm_uuid', 'vm_name', 'moid'], ], ) VmotionManager(module) if __name__ == '__main__': main()