VMware: Get virtual machine object using property (#33525)
This fixes get_vm method to use propertyCollector which can efficiently find the virtual machine from given VMware infrastructure using only name. * VMware: Refactor vmware_guest module * Add nested paths of datacenter * Add tchernomax suggestions Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
This commit is contained in:
parent
9106284c1c
commit
87f2893481
2 changed files with 176 additions and 39 deletions
|
@ -801,6 +801,9 @@ def set_vm_power_state(content, vm, state, force):
|
||||||
|
|
||||||
class PyVmomi(object):
|
class PyVmomi(object):
|
||||||
def __init__(self, module):
|
def __init__(self, module):
|
||||||
|
"""
|
||||||
|
Constructor
|
||||||
|
"""
|
||||||
if not HAS_PYVMOMI:
|
if not HAS_PYVMOMI:
|
||||||
module.fail_json(msg='PyVmomi Python module required. Install using "pip install PyVmomi"')
|
module.fail_json(msg='PyVmomi Python module required. Install using "pip install PyVmomi"')
|
||||||
|
|
||||||
|
@ -828,41 +831,174 @@ class PyVmomi(object):
|
||||||
elif api_type == 'HostAgent':
|
elif api_type == 'HostAgent':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_managed_objects_properties(self, vim_type, properties=None):
|
||||||
|
"""
|
||||||
|
Function to look up a Managed Object Reference in vCenter / ESXi Environment
|
||||||
|
:param vim_type: Type of vim object e.g, for datacenter - vim.Datacenter
|
||||||
|
:param properties: List of properties related to vim object e.g. Name
|
||||||
|
:return: local content object
|
||||||
|
"""
|
||||||
|
# Get Root Folder
|
||||||
|
root_folder = self.content.rootFolder
|
||||||
|
|
||||||
|
if properties is None:
|
||||||
|
properties = ['name']
|
||||||
|
|
||||||
|
# Create Container View with default root folder
|
||||||
|
mor = self.content.viewManager.CreateContainerView(root_folder, [vim_type], True)
|
||||||
|
|
||||||
|
# Create Traversal spec
|
||||||
|
traversal_spec = vmodl.query.PropertyCollector.TraversalSpec(
|
||||||
|
name="traversal_spec",
|
||||||
|
path='view',
|
||||||
|
skip=False,
|
||||||
|
type=vim.view.ContainerView
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create Property Spec
|
||||||
|
property_spec = vmodl.query.PropertyCollector.PropertySpec(
|
||||||
|
type=vim_type, # Type of object to retrieved
|
||||||
|
all=False,
|
||||||
|
pathSet=properties
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create Object Spec
|
||||||
|
object_spec = vmodl.query.PropertyCollector.ObjectSpec(
|
||||||
|
obj=mor,
|
||||||
|
skip=True,
|
||||||
|
selectSet=[traversal_spec]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create Filter Spec
|
||||||
|
filter_spec = vmodl.query.PropertyCollector.FilterSpec(
|
||||||
|
objectSet=[object_spec],
|
||||||
|
propSet=[property_spec],
|
||||||
|
reportMissingObjectsInResults=False
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.content.propertyCollector.RetrieveContents([filter_spec])
|
||||||
|
|
||||||
# Virtual Machine related functions
|
# Virtual Machine related functions
|
||||||
def get_vm(self):
|
def get_vm(self):
|
||||||
vm = None
|
"""
|
||||||
match_first = (self.params['name_match'] == 'first')
|
Function to find unique virtual machine either by UUID or Name.
|
||||||
|
Returns: virtual machine object if found, else None.
|
||||||
|
|
||||||
|
"""
|
||||||
|
vm_obj = None
|
||||||
|
user_desired_path = None
|
||||||
|
|
||||||
if self.params['uuid']:
|
if self.params['uuid']:
|
||||||
vm = find_vm_by_id(self.content, vm_id=self.params['uuid'], vm_id_type="uuid")
|
vm_obj = find_vm_by_id(self.content, vm_id=self.params['uuid'], vm_id_type="uuid")
|
||||||
elif self.params['folder'] and self.params['name']:
|
|
||||||
vm = find_vm_by_id(self.content, vm_id=self.params['name'], vm_id_type="inventory_path",
|
|
||||||
folder=self.params['folder'], match_first=match_first)
|
|
||||||
|
|
||||||
if vm:
|
elif self.params['name']:
|
||||||
self.current_vm_obj = vm
|
objects = self.get_managed_objects_properties(vim_type=vim.VirtualMachine, properties=['name'])
|
||||||
|
vms = []
|
||||||
|
|
||||||
return vm
|
for temp_vm_object in objects:
|
||||||
|
if len(temp_vm_object.propSet) != 1:
|
||||||
|
continue
|
||||||
|
for temp_vm_object_property in temp_vm_object.propSet:
|
||||||
|
if temp_vm_object_property.val == self.params['name']:
|
||||||
|
vms.append(temp_vm_object.obj)
|
||||||
|
break
|
||||||
|
|
||||||
|
# get_managed_objects_properties may return multiple virtual machine,
|
||||||
|
# following code tries to find user desired one depending upon the folder specified.
|
||||||
|
if len(vms) > 1:
|
||||||
|
# We have found multiple virtual machines, decide depending upon folder value
|
||||||
|
if self.params['folder'] is None:
|
||||||
|
self.module.fail_json(msg="Multiple virtual machines with same name [%s] found, "
|
||||||
|
"Folder value is a required parameter to find uniqueness "
|
||||||
|
"of the virtual machine" % self.params['name'],
|
||||||
|
details="Please see documentation of the vmware_guest module "
|
||||||
|
"for folder parameter.")
|
||||||
|
|
||||||
|
# Get folder path where virtual machine is located
|
||||||
|
# User provided folder where user thinks virtual machine is present
|
||||||
|
user_folder = self.params['folder']
|
||||||
|
# User defined datacenter
|
||||||
|
user_defined_dc = self.params['datacenter']
|
||||||
|
# User defined datacenter's object
|
||||||
|
datacenter_obj = find_datacenter_by_name(self.content, self.params['datacenter'])
|
||||||
|
# Get Path for Datacenter
|
||||||
|
dcpath = compile_folder_path_for_object(vobj=datacenter_obj)
|
||||||
|
|
||||||
|
# Nested folder does not return trailing /
|
||||||
|
if not dcpath.endswith('/'):
|
||||||
|
dcpath += '/'
|
||||||
|
|
||||||
|
if user_folder in [None, '', '/']:
|
||||||
|
# User provided blank value or
|
||||||
|
# User provided only root value, we fail
|
||||||
|
self.module.fail_json(msg="vmware_guest found multiple virtual machines with same "
|
||||||
|
"name [%s], please specify folder path other than blank "
|
||||||
|
"or '/'" % self.params['name'])
|
||||||
|
elif user_folder.startswith('/vm/'):
|
||||||
|
# User provided nested folder under VMware default vm folder i.e. folder = /vm/india/finance
|
||||||
|
user_desired_path = "%s%s%s" % (dcpath, user_defined_dc, user_folder)
|
||||||
|
else:
|
||||||
|
# User defined datacenter is not nested i.e. dcpath = '/' , or
|
||||||
|
# User defined datacenter is nested i.e. dcpath = '/F0/DC0' or
|
||||||
|
# User provided folder starts with / and datacenter i.e. folder = /ha-datacenter/ or
|
||||||
|
# User defined folder starts with datacenter without '/' i.e.
|
||||||
|
# folder = DC0/vm/india/finance or
|
||||||
|
# folder = DC0/vm
|
||||||
|
user_desired_path = user_folder
|
||||||
|
|
||||||
|
for vm in vms:
|
||||||
|
# Check if user has provided same path as virtual machine
|
||||||
|
actual_vm_folder_path = self.get_vm_path(content=self.content, vm_name=vm)
|
||||||
|
if not actual_vm_folder_path.startswith("%s%s" % (dcpath, user_defined_dc)):
|
||||||
|
continue
|
||||||
|
if user_desired_path in actual_vm_folder_path:
|
||||||
|
vm_obj = vm
|
||||||
|
break
|
||||||
|
elif vms:
|
||||||
|
# Unique virtual machine found.
|
||||||
|
vm_obj = vms[0]
|
||||||
|
|
||||||
|
if vm_obj:
|
||||||
|
self.current_vm_obj = vm_obj
|
||||||
|
|
||||||
|
return vm_obj
|
||||||
|
|
||||||
def gather_facts(self, vm):
|
def gather_facts(self, vm):
|
||||||
|
"""
|
||||||
|
Function to gather facts of virtual machine.
|
||||||
|
Args:
|
||||||
|
vm: Name of virtual machine.
|
||||||
|
|
||||||
|
Returns: Facts dictionary of the given virtual machine.
|
||||||
|
|
||||||
|
"""
|
||||||
return gather_vm_facts(self.content, vm)
|
return gather_vm_facts(self.content, vm)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_vm_path(content, vm):
|
def get_vm_path(content, vm_name):
|
||||||
foldername = None
|
"""
|
||||||
folder = vm.parent
|
Function to find the path of virtual machine.
|
||||||
|
Args:
|
||||||
|
content: VMware content object
|
||||||
|
vm_name: virtual machine managed object
|
||||||
|
|
||||||
|
Returns: Folder of virtual machine if exists, else None
|
||||||
|
|
||||||
|
"""
|
||||||
|
folder_name = None
|
||||||
|
folder = vm_name.parent
|
||||||
if folder:
|
if folder:
|
||||||
foldername = folder.name
|
folder_name = folder.name
|
||||||
fp = folder.parent
|
fp = folder.parent
|
||||||
# climb back up the tree to find our path, stop before the root folder
|
# climb back up the tree to find our path, stop before the root folder
|
||||||
while fp is not None and fp.name is not None and fp != content.rootFolder:
|
while fp is not None and fp.name is not None and fp != content.rootFolder:
|
||||||
foldername = fp.name + '/' + foldername
|
folder_name = fp.name + '/' + folder_name
|
||||||
try:
|
try:
|
||||||
fp = fp.parent
|
fp = fp.parent
|
||||||
except:
|
except:
|
||||||
break
|
break
|
||||||
foldername = '/' + foldername
|
folder_name = '/' + folder_name
|
||||||
return foldername
|
return folder_name
|
||||||
|
|
||||||
# Cluster related functions
|
# Cluster related functions
|
||||||
def find_cluster_by_name(self, cluster_name, datacenter_name=None):
|
def find_cluster_by_name(self, cluster_name, datacenter_name=None):
|
||||||
|
|
|
@ -78,7 +78,6 @@ options:
|
||||||
- ' folder: /folder1/datacenter1/vm'
|
- ' folder: /folder1/datacenter1/vm'
|
||||||
- ' folder: folder1/datacenter1/vm'
|
- ' folder: folder1/datacenter1/vm'
|
||||||
- ' folder: /folder1/datacenter1/vm/folder2'
|
- ' folder: /folder1/datacenter1/vm/folder2'
|
||||||
default: /vm
|
|
||||||
hardware:
|
hardware:
|
||||||
description:
|
description:
|
||||||
- Manage some VM hardware attributes.
|
- Manage some VM hardware attributes.
|
||||||
|
@ -1433,16 +1432,19 @@ class PyVmomiHelper(PyVmomi):
|
||||||
# - multiple templates by the same name
|
# - multiple templates by the same name
|
||||||
# - static IPs
|
# - static IPs
|
||||||
|
|
||||||
# datacenters = get_all_objs(self.content, [vim.Datacenter])
|
self.folder = self.params.get('folder', None)
|
||||||
|
if self.folder is None:
|
||||||
|
self.module.fail_json(msg="Folder is required parameter while deploying new virtual machine")
|
||||||
|
|
||||||
|
# Prepend / if it was missing from the folder path, also strip trailing slashes
|
||||||
|
if not self.folder.startswith('/'):
|
||||||
|
self.folder = '/%(folder)s' % self.params
|
||||||
|
self.folder = self.folder.rstrip('/')
|
||||||
|
|
||||||
datacenter = self.cache.find_obj(self.content, [vim.Datacenter], self.params['datacenter'])
|
datacenter = self.cache.find_obj(self.content, [vim.Datacenter], self.params['datacenter'])
|
||||||
if datacenter is None:
|
if datacenter is None:
|
||||||
self.module.fail_json(msg='No datacenter named %(datacenter)s was found' % self.params)
|
self.module.fail_json(msg='No datacenter named %(datacenter)s was found' % self.params)
|
||||||
|
|
||||||
# Prepend / if it was missing from the folder path, also strip trailing slashes
|
|
||||||
if not self.params['folder'].startswith('/'):
|
|
||||||
self.params['folder'] = '/%(folder)s' % self.params
|
|
||||||
self.params['folder'] = self.params['folder'].rstrip('/')
|
|
||||||
|
|
||||||
dcpath = compile_folder_path_for_object(datacenter)
|
dcpath = compile_folder_path_for_object(datacenter)
|
||||||
|
|
||||||
# Nested folder does not have trailing /
|
# Nested folder does not have trailing /
|
||||||
|
@ -1450,15 +1452,15 @@ class PyVmomiHelper(PyVmomi):
|
||||||
dcpath += '/'
|
dcpath += '/'
|
||||||
|
|
||||||
# Check for full path first in case it was already supplied
|
# Check for full path first in case it was already supplied
|
||||||
if (self.params['folder'].startswith(dcpath + self.params['datacenter'] + '/vm') or
|
if (self.folder.startswith(dcpath + self.params['datacenter'] + '/vm') or
|
||||||
self.params['folder'].startswith(dcpath + '/' + self.params['datacenter'] + '/vm')):
|
self.folder.startswith(dcpath + '/' + self.params['datacenter'] + '/vm')):
|
||||||
fullpath = self.params['folder']
|
fullpath = self.folder
|
||||||
elif self.params['folder'].startswith('/vm/') or self.params['folder'] == '/vm':
|
elif self.folder.startswith('/vm/') or self.folder == '/vm':
|
||||||
fullpath = "%s%s%s" % (dcpath, self.params['datacenter'], self.params['folder'])
|
fullpath = "%s%s%s" % (dcpath, self.params['datacenter'], self.folder)
|
||||||
elif self.params['folder'].startswith('/'):
|
elif self.folder.startswith('/'):
|
||||||
fullpath = "%s%s/vm%s" % (dcpath, self.params['datacenter'], self.params['folder'])
|
fullpath = "%s%s/vm%s" % (dcpath, self.params['datacenter'], self.folder)
|
||||||
else:
|
else:
|
||||||
fullpath = "%s%s/vm/%s" % (dcpath, self.params['datacenter'], self.params['folder'])
|
fullpath = "%s%s/vm/%s" % (dcpath, self.params['datacenter'], self.folder)
|
||||||
|
|
||||||
f_obj = self.content.searchIndex.FindByInventoryPath(fullpath)
|
f_obj = self.content.searchIndex.FindByInventoryPath(fullpath)
|
||||||
|
|
||||||
|
@ -1468,10 +1470,10 @@ class PyVmomiHelper(PyVmomi):
|
||||||
details = {
|
details = {
|
||||||
'datacenter': datacenter.name,
|
'datacenter': datacenter.name,
|
||||||
'datacenter_path': dcpath,
|
'datacenter_path': dcpath,
|
||||||
'folder': self.params['folder'],
|
'folder': self.folder,
|
||||||
'full_search_path': fullpath,
|
'full_search_path': fullpath,
|
||||||
}
|
}
|
||||||
self.module.fail_json(msg='No folder %s matched in the search path : %s' % (self.params['folder'], fullpath),
|
self.module.fail_json(msg='No folder %s matched in the search path : %s' % (self.folder, fullpath),
|
||||||
details=details)
|
details=details)
|
||||||
|
|
||||||
destfolder = f_obj
|
destfolder = f_obj
|
||||||
|
@ -1751,10 +1753,10 @@ def main():
|
||||||
is_template=dict(type='bool', default=False),
|
is_template=dict(type='bool', default=False),
|
||||||
annotation=dict(type='str', aliases=['notes']),
|
annotation=dict(type='str', aliases=['notes']),
|
||||||
customvalues=dict(type='list', default=[]),
|
customvalues=dict(type='list', default=[]),
|
||||||
name=dict(type='str', required=True),
|
name=dict(type='str'),
|
||||||
name_match=dict(type='str', choices=['first', 'last'], default='first'),
|
name_match=dict(type='str', choices=['first', 'last'], default='first'),
|
||||||
uuid=dict(type='str'),
|
uuid=dict(type='str'),
|
||||||
folder=dict(type='str', default='/vm'),
|
folder=dict(type='str'),
|
||||||
guest_id=dict(type='str'),
|
guest_id=dict(type='str'),
|
||||||
disk=dict(type='list', default=[]),
|
disk=dict(type='list', default=[]),
|
||||||
cdrom=dict(type='dict', default={}),
|
cdrom=dict(type='dict', default={}),
|
||||||
|
@ -1776,14 +1778,13 @@ def main():
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
['cluster', 'esxi_hostname'],
|
['cluster', 'esxi_hostname'],
|
||||||
],
|
],
|
||||||
|
required_one_of=[
|
||||||
|
['name', 'uuid'],
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
result = {'failed': False, 'changed': False}
|
result = {'failed': False, 'changed': False}
|
||||||
|
|
||||||
# FindByInventoryPath() does not require an absolute path
|
|
||||||
# so we should leave the input folder path unmodified
|
|
||||||
module.params['folder'] = module.params['folder'].rstrip('/')
|
|
||||||
|
|
||||||
pyv = PyVmomiHelper(module)
|
pyv = PyVmomiHelper(module)
|
||||||
|
|
||||||
# Check if the VM exists before continuing
|
# Check if the VM exists before continuing
|
||||||
|
|
Loading…
Reference in a new issue