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:
Abhijeet Kasurde 2018-01-19 09:50:20 +05:30 committed by GitHub
parent 9106284c1c
commit 87f2893481
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 176 additions and 39 deletions

View file

@ -801,6 +801,9 @@ def set_vm_power_state(content, vm, state, force):
class PyVmomi(object):
def __init__(self, module):
"""
Constructor
"""
if not HAS_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':
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
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']:
vm = 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)
vm_obj = find_vm_by_id(self.content, vm_id=self.params['uuid'], vm_id_type="uuid")
if vm:
self.current_vm_obj = vm
elif self.params['name']:
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):
"""
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)
@staticmethod
def get_vm_path(content, vm):
foldername = None
folder = vm.parent
def get_vm_path(content, vm_name):
"""
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:
foldername = folder.name
folder_name = folder.name
fp = folder.parent
# 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:
foldername = fp.name + '/' + foldername
folder_name = fp.name + '/' + folder_name
try:
fp = fp.parent
except:
break
foldername = '/' + foldername
return foldername
folder_name = '/' + folder_name
return folder_name
# Cluster related functions
def find_cluster_by_name(self, cluster_name, datacenter_name=None):

View file

@ -78,7 +78,6 @@ options:
- ' folder: /folder1/datacenter1/vm'
- ' folder: folder1/datacenter1/vm'
- ' folder: /folder1/datacenter1/vm/folder2'
default: /vm
hardware:
description:
- Manage some VM hardware attributes.
@ -1433,16 +1432,19 @@ class PyVmomiHelper(PyVmomi):
# - multiple templates by the same name
# - 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'])
if datacenter is None:
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)
# Nested folder does not have trailing /
@ -1450,15 +1452,15 @@ class PyVmomiHelper(PyVmomi):
dcpath += '/'
# Check for full path first in case it was already supplied
if (self.params['folder'].startswith(dcpath + self.params['datacenter'] + '/vm') or
self.params['folder'].startswith(dcpath + '/' + self.params['datacenter'] + '/vm')):
fullpath = self.params['folder']
elif self.params['folder'].startswith('/vm/') or self.params['folder'] == '/vm':
fullpath = "%s%s%s" % (dcpath, self.params['datacenter'], self.params['folder'])
elif self.params['folder'].startswith('/'):
fullpath = "%s%s/vm%s" % (dcpath, self.params['datacenter'], self.params['folder'])
if (self.folder.startswith(dcpath + self.params['datacenter'] + '/vm') or
self.folder.startswith(dcpath + '/' + self.params['datacenter'] + '/vm')):
fullpath = self.folder
elif self.folder.startswith('/vm/') or self.folder == '/vm':
fullpath = "%s%s%s" % (dcpath, self.params['datacenter'], self.folder)
elif self.folder.startswith('/'):
fullpath = "%s%s/vm%s" % (dcpath, self.params['datacenter'], self.folder)
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)
@ -1468,10 +1470,10 @@ class PyVmomiHelper(PyVmomi):
details = {
'datacenter': datacenter.name,
'datacenter_path': dcpath,
'folder': self.params['folder'],
'folder': self.folder,
'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)
destfolder = f_obj
@ -1751,10 +1753,10 @@ def main():
is_template=dict(type='bool', default=False),
annotation=dict(type='str', aliases=['notes']),
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'),
uuid=dict(type='str'),
folder=dict(type='str', default='/vm'),
folder=dict(type='str'),
guest_id=dict(type='str'),
disk=dict(type='list', default=[]),
cdrom=dict(type='dict', default={}),
@ -1776,14 +1778,13 @@ def main():
mutually_exclusive=[
['cluster', 'esxi_hostname'],
],
required_one_of=[
['name', 'uuid'],
],
)
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)
# Check if the VM exists before continuing