1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

vmware_guest: use the datacenter as a context for clone specs (#26511)

* Slight refactor on vmware_guest to fix path searching and vcsim compatibility.
* Clean up pep8 errors
* Fix more pep8 issues
* Add assertions
* Remove debug failure
* Update docsting for folder with examples
* Revise _get_vm_prop based on suggestions
* Implement folder walker to find a folder path
* More enhancements for datacenter->datacenter cloning
* Fix a few pep8 issues
* Remove useless check for subclass of None
* Fix a few suggestions
* Move serialize_spec to a util function
* Group exceptions together

Fixes #25011
This commit is contained in:
jctanner 2017-07-11 23:07:00 -04:00 committed by GitHub
parent 5b898a7732
commit 10fc4417f7
6 changed files with 364 additions and 37 deletions

View file

@ -213,6 +213,17 @@ def find_host_portgroup_by_name(host, portgroup_name):
return None return None
def _get_vm_prop(vm, attributes):
"""Safely get a property or return None"""
result = vm
for attribute in attributes:
try:
result = getattr(result, attribute)
except (AttributeError, IndexError):
return None
return result
def gather_vm_facts(content, vm): def gather_vm_facts(content, vm):
""" Gather facts from vim.VirtualMachine object. """ """ Gather facts from vim.VirtualMachine object. """
facts = { facts = {
@ -225,8 +236,8 @@ def gather_vm_facts(content, vm):
'hw_processor_count': vm.config.hardware.numCPU, 'hw_processor_count': vm.config.hardware.numCPU,
'hw_memtotal_mb': vm.config.hardware.memoryMB, 'hw_memtotal_mb': vm.config.hardware.memoryMB,
'hw_interfaces': [], 'hw_interfaces': [],
'guest_tools_status': vm.guest.toolsRunningStatus, 'guest_tools_status': _get_vm_prop(vm, ('guest', 'toolsRunningStatus')),
'guest_tools_version': vm.guest.toolsVersion, 'guest_tools_version': _get_vm_prop(vm, ('guest', 'toolsVersion')),
'ipv4': None, 'ipv4': None,
'ipv6': None, 'ipv6': None,
'annotation': vm.config.annotation, 'annotation': vm.config.annotation,
@ -249,8 +260,10 @@ def gather_vm_facts(content, vm):
facts['customvalues'][kn] = value_obj.value facts['customvalues'][kn] = value_obj.value
net_dict = {} net_dict = {}
for device in vm.guest.net: vmnet = _get_vm_prop(vm, ('guest', 'net'))
net_dict[device.macAddress] = list(device.ipAddress) if vmnet:
for device in vmnet:
net_dict[device.macAddress] = list(device.ipAddress)
for k, v in iteritems(net_dict): for k, v in iteritems(net_dict):
for ipaddress in v: for ipaddress in v:
@ -317,6 +330,9 @@ def get_current_snap_obj(snapshots, snapob):
def list_snapshots(vm): def list_snapshots(vm):
result = {} result = {}
snapshot = _get_vm_prop(vm, ('vm', 'snapshot'))
if not snapshot:
return result
if vm.snapshot is None: if vm.snapshot is None:
return result return result
@ -360,7 +376,7 @@ def connect_to_api(module, disconnect_atexit=True):
service_instance = connect.SmartConnect(host=hostname, user=username, pwd=password, sslContext=context) service_instance = connect.SmartConnect(host=hostname, user=username, pwd=password, sslContext=context)
else: else:
module.fail_json(msg="Unable to connect to vCenter or ESXi API on TCP/443.", apierror=str(connection_error)) module.fail_json(msg="Unable to connect to vCenter or ESXi API on TCP/443.", apierror=str(connection_error))
except Exception as e: except:
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.verify_mode = ssl.CERT_NONE context.verify_mode = ssl.CERT_NONE
service_instance = connect.SmartConnect(host=hostname, user=username, pwd=password, sslContext=context) service_instance = connect.SmartConnect(host=hostname, user=username, pwd=password, sslContext=context)
@ -533,3 +549,58 @@ def run_command_in_guest(content, vm, username, password, program_path, program_
result['failed'] = True result['failed'] = True
return result return result
def serialize_spec(clonespec):
"""Serialize a clonespec or a relocation spec"""
data = {}
attrs = dir(clonespec)
attrs = [x for x in attrs if not x.startswith('_')]
for x in attrs:
xo = getattr(clonespec, x)
if callable(xo):
continue
xt = type(xo)
if xo is None:
data[x] = None
elif issubclass(xt, list):
data[x] = []
for xe in xo:
data[x].append(serialize_spec(xe))
elif issubclass(xt, str):
data[x] = xo
elif issubclass(xt, unicode):
data[x] = xo
elif issubclass(xt, int):
data[x] = xo
elif issubclass(xt, float):
data[x] = xo
elif issubclass(xt, long):
data[x] = xo
elif issubclass(xt, bool):
data[x] = xo
elif issubclass(xt, dict):
data[x] = {}
for k, v in xo.items():
data[x][k] = serialize_spec(v)
elif isinstance(xo, vim.vm.ConfigSpec):
data[x] = serialize_spec(xo)
elif isinstance(xo, vim.vm.RelocateSpec):
data[x] = serialize_spec(xo)
elif isinstance(xo, vim.vm.device.VirtualDisk):
data[x] = serialize_spec(xo)
elif isinstance(xo, vim.Description):
data[x] = {
'dynamicProperty': serialize_spec(xo.dynamicProperty),
'dynamicType': serialize_spec(xo.dynamicType),
'label': serialize_spec(xo.label),
'summary': serialize_spec(xo.summary),
}
elif hasattr(xo, 'name'):
data[x] = str(xo) + ':' + xo.name
elif isinstance(xo, vim.vm.ProfileSpec):
pass
else:
data[x] = str(xt)
return data

View file

@ -74,8 +74,21 @@ options:
version_added: '2.3' version_added: '2.3'
folder: folder:
description: description:
- Destination folder, absolute path to find an existing guest or create the new guest. - Destination folder, absolute or relative path to find an existing guest or create the new guest.
default: / - The folder should include the datacenter. ESX's datacenter is ha-datacenter
- 'Examples:'
- ' folder: /ha-datacenter/vm'
- ' folder: ha-datacenter/vm'
- ' folder: /datacenter1/vm'
- ' folder: datacenter1/vm'
- ' folder: /datacenter1/vm/folder1'
- ' folder: datacenter1/vm/folder1'
- ' folder: /folder1/datacenter1/vm'
- ' folder: folder1/datacenter1/vm'
- ' folder: /folder1/datacenter1/vm/folder2'
- ' folder: vm/folder2'
- ' fodler: folder2'
default: /vm
hardware: hardware:
description: description:
- Manage some VM hardware attributes. - Manage some VM hardware attributes.
@ -303,9 +316,8 @@ import time
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.pycompat24 import get_exception from ansible.module_utils.pycompat24 import get_exception
from ansible.module_utils.six import iteritems
from ansible.module_utils.urls import fetch_url
from ansible.module_utils.vmware import connect_to_api, find_obj, gather_vm_facts, get_all_objs from ansible.module_utils.vmware import connect_to_api, find_obj, gather_vm_facts, get_all_objs
from ansible.module_utils.vmware import serialize_spec
try: try:
import json import json
@ -424,30 +436,77 @@ class PyVmomiDeviceHelper(object):
class PyVmomiCache(object): class PyVmomiCache(object):
""" This class caches references to objects which are requested multiples times but not modified """ """ This class caches references to objects which are requested multiples times but not modified """
def __init__(self, content): def __init__(self, content, dc_name=None):
self.content = content self.content = content
self.dc_name = dc_name
self.networks = {} self.networks = {}
self.clusters = {} self.clusters = {}
self.esx_hosts = {} self.esx_hosts = {}
self.parent_datacenters = {}
def find_obj(self, content, types, name, confine_to_datacenter=True):
""" Wrapper around find_obj to set datacenter context """
result = find_obj(content, types, name)
if result and confine_to_datacenter:
if self.get_parent_datacenter(result).name != self.dc_name:
objects = self.get_all_objs(content, types, confine_to_datacenter=True)
for obj in objects:
if obj.name == name:
return obj
return result
def get_all_objs(self, content, types, confine_to_datacenter=True):
""" Wrapper around get_all_objs to set datacenter context """
objects = get_all_objs(content, [vim.Datastore])
if confine_to_datacenter:
if hasattr(objects, 'items'):
# resource pools come back as a dictionary
for k, v in objects.items():
parent_dc = self.get_parent_datacenter(k)
if parent_dc.name != self.dc_name:
objects.pop(k, None)
else:
# everything else should be a list
objects = [x for x in objects if self.get_parent_datacenter(x).name == self.dc_name]
return objects
def get_network(self, network): def get_network(self, network):
if network not in self.networks: if network not in self.networks:
self.networks[network] = find_obj(self.content, [vim.Network], network) self.networks[network] = self.find_obj(self.content, [vim.Network], network)
return self.networks[network] return self.networks[network]
def get_cluster(self, cluster): def get_cluster(self, cluster):
if cluster not in self.clusters: if cluster not in self.clusters:
self.clusters[cluster] = find_obj(self.content, [vim.ClusterComputeResource], cluster) self.clusters[cluster] = self.find_obj(self.content, [vim.ClusterComputeResource], cluster)
return self.clusters[cluster] return self.clusters[cluster]
def get_esx_host(self, host): def get_esx_host(self, host):
if host not in self.esx_hosts: if host not in self.esx_hosts:
self.esx_hosts[host] = find_obj(self.content, [vim.HostSystem], host) self.esx_hosts[host] = self.find_obj(self.content, [vim.HostSystem], host)
return self.esx_hosts[host] return self.esx_hosts[host]
def get_parent_datacenter(self, obj):
""" Walk the parent tree to find the objects datacenter """
if isinstance(obj, vim.Datacenter):
return obj
if obj in self.parent_datacenters:
return self.parent_datacenters[obj]
datacenter = None
while True:
if not hasattr(obj, 'parent'):
break
obj = obj.parent
if isinstance(obj, vim.Datacenter):
datacenter = obj
break
self.parent_datacenters[obj] = datacenter
return datacenter
class PyVmomiHelper(object): class PyVmomiHelper(object):
def __init__(self, module): def __init__(self, module):
@ -463,7 +522,7 @@ class PyVmomiHelper(object):
self.change_detected = False self.change_detected = False
self.customspec = None self.customspec = None
self.current_vm_obj = None self.current_vm_obj = None
self.cache = PyVmomiCache(self.content) self.cache = PyVmomiCache(self.content, dc_name=self.params['datacenter'])
def getvm(self, name=None, uuid=None, folder=None): def getvm(self, name=None, uuid=None, folder=None):
@ -476,19 +535,21 @@ class PyVmomiHelper(object):
if uuid: if uuid:
vm = self.content.searchIndex.FindByUuid(uuid=uuid, vmSearch=True) vm = self.content.searchIndex.FindByUuid(uuid=uuid, vmSearch=True)
elif folder: elif folder:
# Build the absolute folder path to pass into the search method # searchpaths do not need to be absolute
if not self.params['folder'].startswith('/'): searchpath = self.params['folder']
self.module.fail_json(msg="Folder %(folder)s needs to be an absolute path, starting with '/'." % self.params)
searchpath = '%(datacenter)s%(folder)s' % self.params
# get all objects for this path ... # get all objects for this path ...
f_obj = self.content.searchIndex.FindByInventoryPath(searchpath) f_obj = self.content.searchIndex.FindByInventoryPath(searchpath)
if f_obj: if f_obj:
if isinstance(f_obj, vim.Datacenter): if isinstance(f_obj, vim.Datacenter):
f_obj = f_obj.vmFolder f_obj = f_obj.vmFolder
for c_obj in f_obj.childEntity: for c_obj in f_obj.childEntity:
if not isinstance(c_obj, vim.VirtualMachine): if not isinstance(c_obj, vim.VirtualMachine):
continue continue
if c_obj.name == name: if c_obj.name == name:
vm = c_obj vm = c_obj
if self.params['name_match'] == 'first': if self.params['name_match'] == 'first':
@ -650,7 +711,7 @@ class PyVmomiHelper(object):
self.module.fail_json(msg="Network '%(name)s' does not exists" % network) self.module.fail_json(msg="Network '%(name)s' does not exists" % network)
elif 'vlan' in network: elif 'vlan' in network:
dvps = get_all_objs(self.content, [vim.dvs.DistributedVirtualPortgroup]) dvps = self.cache.get_all_objs(self.content, [vim.dvs.DistributedVirtualPortgroup])
for dvp in dvps: for dvp in dvps:
if hasattr(dvp.config.defaultPortConfig, 'vlan') and dvp.config.defaultPortConfig.vlan.vlanId == network['vlan']: if hasattr(dvp.config.defaultPortConfig, 'vlan') and dvp.config.defaultPortConfig.vlan.vlanId == network['vlan']:
network['name'] = dvp.config.name network['name'] = dvp.config.name
@ -994,13 +1055,32 @@ class PyVmomiHelper(object):
return hostsystem return hostsystem
def autoselect_datastore(self):
datastore = None
datastore_name = None
datastores = self.cache.get_all_objs(self.content, [vim.Datastore])
if datastores is None or len(datastores) == 0:
self.module.fail_json(msg="Unable to find a datastore list when autoselecting")
datastore_freespace = 0
for ds in datastores:
if ds.summary.freeSpace > datastore_freespace:
datastore = ds
datastore_name = datastore.name
datastore_freespace = ds.summary.freeSpace
return datastore
def select_datastore(self, vm_obj=None): def select_datastore(self, vm_obj=None):
datastore = None datastore = None
datastore_name = None datastore_name = None
if len(self.params['disk']) != 0: if len(self.params['disk']) != 0:
# TODO: really use the datastore for newly created disks # TODO: really use the datastore for newly created disks
if 'autoselect_datastore' in self.params['disk'][0] and self.params['disk'][0]['autoselect_datastore']: if 'autoselect_datastore' in self.params['disk'][0] and self.params['disk'][0]['autoselect_datastore']:
datastores = get_all_objs(self.content, [vim.Datastore]) datastores = self.cache.get_all_objs(self.content, [vim.Datastore])
datastores = [x for x in datastores if self.cache.get_parent_datacenter(x).name == self.params['datacenter']]
if datastores is None or len(datastores) == 0: if datastores is None or len(datastores) == 0:
self.module.fail_json(msg="Unable to find a datastore list when autoselecting") self.module.fail_json(msg="Unable to find a datastore list when autoselecting")
@ -1019,14 +1099,23 @@ class PyVmomiHelper(object):
elif 'datastore' in self.params['disk'][0]: elif 'datastore' in self.params['disk'][0]:
datastore_name = self.params['disk'][0]['datastore'] datastore_name = self.params['disk'][0]['datastore']
datastore = find_obj(self.content, [vim.Datastore], datastore_name) datastore = self.cache.find_obj(self.content, [vim.Datastore], datastore_name)
else: else:
self.module.fail_json(msg="Either datastore or autoselect_datastore should be provided to select datastore") self.module.fail_json(msg="Either datastore or autoselect_datastore should be provided to select datastore")
if not datastore and self.params['template']: if not datastore and self.params['template']:
# use the template's existing DS # use the template's existing DS
disks = [x for x in vm_obj.config.hardware.device if isinstance(x, vim.vm.device.VirtualDisk)] disks = [x for x in vm_obj.config.hardware.device if isinstance(x, vim.vm.device.VirtualDisk)]
datastore = disks[0].backing.datastore if disks:
datastore_name = datastore.name datastore = disks[0].backing.datastore
datastore_name = datastore.name
# validation
if datastore:
dc = self.cache.get_parent_datacenter(datastore)
if dc.name != self.params['datacenter']:
datastore = self.autoselect_datastore()
datastore_name = datastore.name
if not datastore: if not datastore:
self.module.fail_json(msg="Failed to find a matching datastore") self.module.fail_json(msg="Failed to find a matching datastore")
@ -1045,13 +1134,13 @@ class PyVmomiHelper(object):
return False return False
def select_resource_pool_by_name(self, resource_pool_name): def select_resource_pool_by_name(self, resource_pool_name):
resource_pool = find_obj(self.content, [vim.ResourcePool], resource_pool_name) resource_pool = self.cache.find_obj(self.content, [vim.ResourcePool], resource_pool_name)
if resource_pool is None: if resource_pool is None:
self.module.fail_json(msg='Could not find resource_pool "%s"' % resource_pool_name) self.module.fail_json(msg='Could not find resource_pool "%s"' % resource_pool_name)
return resource_pool return resource_pool
def select_resource_pool_by_host(self, host): def select_resource_pool_by_host(self, host):
resource_pools = get_all_objs(self.content, [vim.ResourcePool]) resource_pools = self.cache.get_all_objs(self.content, [vim.ResourcePool])
for rp in resource_pools.items(): for rp in resource_pools.items():
if not rp[0]: if not rp[0]:
continue continue
@ -1082,6 +1171,39 @@ class PyVmomiHelper(object):
self.module.fail_json(msg="hardware.scsi attribute should be 'paravirtual' or 'lsilogic'") self.module.fail_json(msg="hardware.scsi attribute should be 'paravirtual' or 'lsilogic'")
return disk_controller_type return disk_controller_type
def find_folder(self, searchpath):
""" Walk inventory objects one position of the searchpath at a time """
# split the searchpath so we can iterate through it
paths = [x.replace('/', '') for x in searchpath.split('/')]
paths_total = len(paths) - 1
position = 0
# recursive walk while looking for next element in searchpath
root = self.content.rootFolder
while root and position <= paths_total:
change = False
if hasattr(root, 'childEntity'):
for child in root.childEntity:
if child.name == paths[position]:
root = child
position += 1
change = True
break
elif isinstance(root, vim.Datacenter):
if hasattr(root, 'vmFolder'):
if root.vmFolder.name == paths[position]:
root = root.vmFolder
position += 1
change = True
else:
root = None
if not change:
root = None
return root
def deploy_vm(self): def deploy_vm(self):
# https://github.com/vmware/pyvmomi-community-samples/blob/master/samples/clone_vm.py # https://github.com/vmware/pyvmomi-community-samples/blob/master/samples/clone_vm.py
# https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.vm.CloneSpec.html # https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.vm.CloneSpec.html
@ -1094,15 +1216,21 @@ class PyVmomiHelper(object):
# - static IPs # - static IPs
# datacenters = get_all_objs(self.content, [vim.Datacenter]) # datacenters = get_all_objs(self.content, [vim.Datacenter])
datacenter = 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)
destfolder = None destfolder = None
if not self.params['folder'].startswith('/'):
self.module.fail_json(msg="Folder %(folder)s needs to be an absolute path, starting with '/'." % self.params)
f_obj = self.content.searchIndex.FindByInventoryPath('/%(datacenter)s%(folder)s' % self.params) # make an attempt with findinventorybypath, although this works poorly
# when datacenters are nested under folders
f_obj = self.content.searchIndex.FindByInventoryPath('%(folder)s' % self.params)
# retry by walking down the object tree
if f_obj is None:
f_obj = self.find_folder(self.params['folder'])
# abort if no strategy was successful
if f_obj is None: if f_obj is None:
self.module.fail_json(msg='No folder matched the path: %(folder)s' % self.params) self.module.fail_json(msg='No folder matched the path: %(folder)s' % self.params)
destfolder = f_obj destfolder = f_obj
@ -1115,8 +1243,13 @@ class PyVmomiHelper(object):
else: else:
vm_obj = None vm_obj = None
if self.params['resource_pool']: # need a resource pool if cloning from template
resource_pool = self.select_resource_pool_by_name(self.params['resource_pool']) if self.params['resource_pool'] or self.params['template']:
if self.params['esxi_hostname']:
host = self.select_host()
resource_pool = self.select_resource_pool_by_host(host)
else:
resource_pool = self.select_resource_pool_by_name(self.params['resource_pool'])
if resource_pool is None: if resource_pool is None:
self.module.fail_json(msg='Unable to find resource pool "%(resource_pool)s"' % self.params) self.module.fail_json(msg='Unable to find resource pool "%(resource_pool)s"' % self.params)
@ -1143,6 +1276,8 @@ class PyVmomiHelper(object):
if len(self.params['customization']) > 0 or network_changes is True: if len(self.params['customization']) > 0 or network_changes is True:
self.customize_vm(vm_obj=vm_obj) self.customize_vm(vm_obj=vm_obj)
clonespec = None
clone_method = None
try: try:
if self.params['template']: if self.params['template']:
# create the relocation spec # create the relocation spec
@ -1153,8 +1288,9 @@ class PyVmomiHelper(object):
relospec.host = self.select_host() relospec.host = self.select_host()
relospec.datastore = datastore relospec.datastore = datastore
if self.params['resource_pool']: # https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.RelocateSpec.html
relospec.pool = resource_pool # > pool: For a clone operation from a template to a virtual machine, this argument is required.
relospec.pool = resource_pool
if self.params['snapshot_src'] is not None and self.params['linked_clone']: if self.params['snapshot_src'] is not None and self.params['linked_clone']:
relospec.diskMoveType = vim.vm.RelocateSpec.DiskMoveOptions.createNewChildDiskBacking relospec.diskMoveType = vim.vm.RelocateSpec.DiskMoveOptions.createNewChildDiskBacking
@ -1171,6 +1307,7 @@ class PyVmomiHelper(object):
clonespec.snapshot = snapshot[0].snapshot clonespec.snapshot = snapshot[0].snapshot
clonespec.config = self.configspec clonespec.config = self.configspec
clone_method = 'Clone'
task = vm_obj.Clone(folder=destfolder, name=self.params['name'], spec=clonespec) task = vm_obj.Clone(folder=destfolder, name=self.params['name'], spec=clonespec)
self.change_detected = True self.change_detected = True
else: else:
@ -1181,6 +1318,7 @@ class PyVmomiHelper(object):
suspendDirectory=None, suspendDirectory=None,
vmPathName="[" + datastore_name + "] " + self.params["name"]) vmPathName="[" + datastore_name + "] " + self.params["name"])
clone_method = 'CreateVM_Task'
task = destfolder.CreateVM_Task(config=self.configspec, pool=resource_pool) task = destfolder.CreateVM_Task(config=self.configspec, pool=resource_pool)
self.change_detected = True self.change_detected = True
self.wait_for_task(task) self.wait_for_task(task)
@ -1191,7 +1329,20 @@ class PyVmomiHelper(object):
if task.info.state == 'error': if task.info.state == 'error':
# https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2021361 # https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2021361
# https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2173 # https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2173
return {'changed': self.change_detected, 'failed': True, 'msg': task.info.error.msg}
# provide these to the user for debugging
clonespec_json = serialize_spec(clonespec)
configspec_json = serialize_spec(self.configspec)
kwargs = {
'changed': self.change_detected,
'failed': True,
'msg': task.info.error.msg,
'clonespec': clonespec_json,
'configspec': configspec_json,
'clone_method': clone_method
}
return kwargs
else: else:
# set annotation # set annotation
vm = task.info.result vm = task.info.result
@ -1340,9 +1491,8 @@ def main():
result = {'failed': False, 'changed': False} result = {'failed': False, 'changed': False}
# Prepend /vm if it was missing from the folder path, also strip trailing slashes # FindByInventoryPath() does not require an absolute path
if not module.params['folder'].startswith('/vm') and module.params['folder'].startswith('/'): # so we should leave the input folder path unmodified
module.params['folder'] = '/vm%(folder)s' % module.params
module.params['folder'] = module.params['folder'].rstrip('/') module.params['folder'] = module.params['folder'].rstrip('/')
pyv = PyVmomiHelper(module) pyv = PyVmomiHelper(module)

View file

@ -0,0 +1,3 @@
posix/ci/cloud/vcenter
cloud/vcenter
skip/python3

View file

@ -0,0 +1,12 @@
#- name: make sure pyvmomi is installed
# pip:
# name: pyvmomi
# state: latest
- name: store the vcenter container ip
set_fact:
vcsim: "{{ lookup('env', 'vcenter_host') }}"
- debug: var=vcsim
- include: poweroff_d1_c1_f0.yml
- include: poweroff_d1_c1_f1.yml

View file

@ -0,0 +1,40 @@
#- name: make sure pyvmomi is installed
# pip:
# name: pyvmomi
# state: latest
- name: kill vcsim
uri:
url: "{{ 'http://' + vcsim + ':5000/killall' }}"
- name: start vcsim with no folders
uri:
url: "{{ 'http://' + vcsim + ':5000/spawn?datacenter=1&cluster=1&folder=0' }}"
register: vcsim_instance
- name: get a list of VMS from vcsim
uri:
url: "{{ 'http://' + vcsim + ':5000/govc_find?filter=VM' }}"
register: vmlist
- debug: var=vcsim_instance
- debug: var=vmlist
- name: set state to poweroff on all VMs
vmware_guest:
validate_certs: False
hostname: "{{ vcsim }}"
username: "{{ vcsim_instance['json']['username'] }}"
password: "{{ vcsim_instance['json']['password'] }}"
name: "{{ item|basename }}"
datacenter: "{{ (item|basename).split('_')[0] }}"
state: poweredoff
folder: "{{ item|dirname }}"
with_items: "{{ vmlist['json'] }}"
register: poweroff_d1_c1_f0
- debug: var=poweroff_d1_c1_f0
- name: make sure no changes were made
assert:
that:
- "poweroff_d1_c1_f0.results|map(attribute='changed')|unique|list == [False]"

View file

@ -0,0 +1,51 @@
#- name: make sure pyvmomi is installed
# pip:
# name: pyvmomi
# state: latest
- name: store the vcenter container ip
set_fact:
vcsim: "{{ lookup('env', 'vcenter_host') }}"
- debug: var=vcsim
- name: kill vcsim
uri:
url: "{{ 'http://' + vcsim + ':5000/killall' }}"
- name: start vcsim with folders
uri:
url: "{{ 'http://' + vcsim + ':5000/spawn?datacenter=1&cluster=1&folder=1' }}"
register: vcsim_instance
- name: get a list of VMS from vcsim
uri:
url: "{{ 'http://' + vcsim + ':5000/govc_find?filter=VM' }}"
register: vmlist
- debug: var=vcsim_instance
- debug: var=vmlist
# https://github.com/ansible/ansible/issues/25011
# Sending "-folders 1" to vcsim nests the datacenter under
# the folder so that the path prefix is no longer /vm
#
# /F0/DC0/vm/F0/DC0_H0_VM0
- name: set state to poweredoff on all VMs
vmware_guest:
validate_certs: False
hostname: "{{ vcsim }}"
username: "{{ vcsim_instance['json']['username'] }}"
password: "{{ vcsim_instance['json']['password'] }}"
name: "{{ item|basename }}"
datacenter: "{{ (item|basename).split('_')[0] }}"
state: poweredoff
folder: "{{ item|dirname }}"
with_items: "{{ vmlist['json'] }}"
register: poweroff_d1_c1_f1
- debug: var=poweroff_d1_c1_f1
- name: make sure no changes were made
assert:
that:
- "poweroff_d1_c1_f1.results|map(attribute='changed')|unique|list == [False]"