vmware_guest: Various fixes and changes

Changes missing from a poor merge probably, but also a few new things.
- Reordered the examples from important (often used) to less important
  (fewer used)
- Remove the new_name: option and replace it with the uuid/name
  combination for renaming + added example
- Added an example using the VM uuid instead of the VM name
- Also check whether the password is non-empty (rogue merge)
- Wait for all tasks to finish (to be sure the new facts reflect the
  state)
- Ensure that on failure we still set the change-bit
- Moved a set of functions that are unused (related to transfering
  files to guest, or running commands) to module_utils
This commit is contained in:
Dag Wieers 2017-01-11 01:08:17 +01:00 committed by Brian Coca
parent efda0323be
commit 20df62ab31
2 changed files with 216 additions and 207 deletions

View file

@ -233,3 +233,153 @@ def get_all_objs(content, vimtype, folder=None, recurse=True):
for managed_object_ref in container.view:
obj.update({managed_object_ref: managed_object_ref.name})
return obj
def fetch_file_from_guest(content, vm, username, password, src, dest):
""" Use VMWare's filemanager api to fetch a file over http """
result = {'failed': False}
tools_status = vm.guest.toolsStatus
if tools_status == 'toolsNotInstalled' or tools_status == 'toolsNotRunning':
result['failed'] = True
result['msg'] = "VMwareTools is not installed or is not running in the guest"
return result
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/NamePasswordAuthentication.rst
creds = vim.vm.guest.NamePasswordAuthentication(
username=username, password=password
)
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/FileManager/FileTransferInformation.rst
fti = content.guestOperationsManager.fileManager. \
InitiateFileTransferFromGuest(vm, creds, src)
result['size'] = fti.size
result['url'] = fti.url
# Use module_utils to fetch the remote url returned from the api
rsp, info = fetch_url(self.module, fti.url, use_proxy=False,
force=True, last_mod_time=None,
timeout=10, headers=None)
# save all of the transfer data
for k, v in iteritems(info):
result[k] = v
# exit early if xfer failed
if info['status'] != 200:
result['failed'] = True
return result
# attempt to read the content and write it
try:
with open(dest, 'wb') as f:
f.write(rsp.read())
except Exception as e:
result['failed'] = True
result['msg'] = str(e)
return result
def push_file_to_guest(content, vm, username, password, src, dest, overwrite=True):
""" Use VMWare's filemanager api to fetch a file over http """
result = {'failed': False}
tools_status = vm.guest.toolsStatus
if tools_status == 'toolsNotInstalled' or tools_status == 'toolsNotRunning':
result['failed'] = True
result['msg'] = "VMwareTools is not installed or is not running in the guest"
return result
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/NamePasswordAuthentication.rst
creds = vim.vm.guest.NamePasswordAuthentication(
username=username, password=password
)
# the api requires a filesize in bytes
fdata = None
try:
# filesize = os.path.getsize(src)
filesize = os.stat(src).st_size
with open(src, 'rb') as f:
fdata = f.read()
result['local_filesize'] = filesize
except Exception as e:
result['failed'] = True
result['msg'] = "Unable to read src file: %s" % str(e)
return result
# https://www.vmware.com/support/developer/converter-sdk/conv60_apireference/vim.vm.guest.FileManager.html#initiateFileTransferToGuest
file_attribute = vim.vm.guest.FileManager.FileAttributes()
url = content.guestOperationsManager.fileManager. \
InitiateFileTransferToGuest(vm, creds, dest, file_attribute,
filesize, overwrite)
# PUT the filedata to the url ...
rsp, info = fetch_url(self.module, url, method="put", data=fdata,
use_proxy=False, force=True, last_mod_time=None,
timeout=10, headers=None)
result['msg'] = str(rsp.read())
# save all of the transfer data
for k, v in iteritems(info):
result[k] = v
return result
def run_command_in_guest(content, vm, username, password, program_path, program_args, program_cwd, program_env):
result = {'failed': False}
tools_status = vm.guest.toolsStatus
if (tools_status == 'toolsNotInstalled' or
tools_status == 'toolsNotRunning'):
result['failed'] = True
result['msg'] = "VMwareTools is not installed or is not running in the guest"
return result
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/NamePasswordAuthentication.rst
creds = vim.vm.guest.NamePasswordAuthentication(
username=username, password=password
)
try:
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/ProcessManager.rst
pm = content.guestOperationsManager.processManager
# https://www.vmware.com/support/developer/converter-sdk/conv51_apireference/vim.vm.guest.ProcessManager.ProgramSpec.html
ps = vim.vm.guest.ProcessManager.ProgramSpec(
# programPath=program,
# arguments=args
programPath=program_path,
arguments=program_args,
workingDirectory=program_cwd,
)
res = pm.StartProgramInGuest(vm, creds, ps)
result['pid'] = res
pdata = pm.ListProcessesInGuest(vm, creds, [res])
# wait for pid to finish
while not pdata[0].endTime:
time.sleep(1)
pdata = pm.ListProcessesInGuest(vm, creds, [res])
result['owner'] = pdata[0].owner
result['startTime'] = pdata[0].startTime.isoformat()
result['endTime'] = pdata[0].endTime.isoformat()
result['exitCode'] = pdata[0].exitCode
if result['exitCode'] != 0:
result['failed'] = True
result['msg'] = "program exited non-zero"
else:
result['msg'] = "program completed successfully"
except Exception as e:
result['msg'] = str(e)
result['failed'] = True
return result

View file

@ -29,8 +29,7 @@ short_description: Manages virtual machines in vcenter
description:
- Create new virtual machines (from templates or not)
- Power on/power off/restart a virtual machine
- Modify an existing virtual machine
- Remove a virtual machine
- Modify, rename or remove a virtual machine
version_added: 2.2
author:
- James Tanner (@jctanner) <tanner.jc@gmail.com>
@ -51,12 +50,6 @@ options:
description:
- Name of the VM to work with
required: True
new_name:
description:
- Rename a VM
- New Name of the exising VM
required: False
version_added: "2.3"
name_match:
description:
- If multiple VMs matching the name, use the first or last found
@ -185,6 +178,17 @@ extends_documentation_fragment: vmware.documentation
'''
EXAMPLES = '''
# Gather facts only
- name: gather the VM facts
vmware_guest:
hostname: 192.168.1.209
username: administrator@vsphere.local
password: vmware
validate_certs: no
esxi_hostname: 192.168.1.117
uuid: 421e4592-c069-924d-ce20-7e7533fab926
register: facts
# Create a VM from a template
- name: create the VM
vmware_guest:
@ -214,32 +218,7 @@ EXAMPLES = '''
wait_for_ip_address: yes
register: deploy
# Create a VM template
- name: create a VM template
vmware_guest:
hostname: 192.0.2.88
username: administrator@vsphere.local
password: vmware
validate_certs: no
datacenter: datacenter1
cluster: vmware_cluster_esx
resource_pool: highperformance_pool
folder: testvms
name: testvm_6
is_template: yes
guest_id: debian6_64Guest
disk:
- size_gb: 10
type: thin
datastore: g73_datastore
hardware:
memory_mb: 512
num_cpus: 1
scsi: lsilogic
wait_for_ip_address: yes
register: deploy
# Clone Template and customize
# Clone a VM from Template and customize
- name: Clone template and customize
vmware_guest:
hostname: 192.168.1.209
@ -270,21 +249,52 @@ EXAMPLES = '''
runonce:
- powershell.exe -ExecutionPolicy Unrestricted -File C:\Windows\Temp\Enable-WinRM.ps1 -ForceNewSSLCert
# Gather facts only
- name: gather the VM facts
# Create a VM template
- name: create a VM template
vmware_guest:
hostname: 192.168.1.209
hostname: 192.0.2.88
username: administrator@vsphere.local
password: vmware
validate_certs: no
name: testvm_2
esxi_hostname: 192.168.1.117
state: gatherfacts
register: facts
datacenter: datacenter1
cluster: vmware_cluster_esx
resource_pool: highperformance_pool
folder: testvms
name: testvm_6
is_template: yes
guest_id: debian6_64Guest
disk:
- size_gb: 10
type: thin
datastore: g73_datastore
hardware:
memory_mb: 512
num_cpus: 1
scsi: lsilogic
wait_for_ip_address: yes
register: deploy
# Rename a VM (requires the VM's uuid)
- vmware_guest:
hostname: 192.168.1.209
username: administrator@vsphere.local
password: vmware
uuid: 421e4592-c069-924d-ce20-7e7533fab926
name: new_name
state: present
# Remove a VM by uuid
- vmware_guest:
hostname: 192.168.1.209
username: administrator@vsphere.local
password: vmware
uuid: 421e4592-c069-924d-ce20-7e7533fab926
state: absent
### Snapshot Operations
###
### BEWARE: This functionality will move into vmware_guest_snapshot before release !
# BEWARE: This functionality will move into vmware_guest_snapshot before release !
# Create snapshot
- vmware_guest:
hostname: 192.168.1.209
@ -1044,12 +1054,12 @@ class PyVmomiHelper(object):
ident.identification = vim.vm.customization.Identification()
if self.params['customization'].get('password'):
if self.params['customization'].get('password', '') != '':
ident.guiUnattended.password = vim.vm.customization.Password()
ident.guiUnattended.password.value = str(self.params['customization']['password'])
ident.guiUnattended.password.plainText = True
else:
self.module.fail_json(msg="A 'password' entry is mandatory in the 'customization' section.")
self.module.fail_json(msg="The 'customization' section requires 'password' entry, which cannot be empty.")
if 'productid' in self.params['customization']:
ident.userData.orgName = str(self.params['customization']['productid'])
@ -1372,7 +1382,7 @@ class PyVmomiHelper(object):
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=2173
return {'changed': False, 'failed': True, 'msg': task.info.error.msg}
return {'changed': self.change_detected, 'failed': True, 'msg': task.info.error.msg}
else:
# set annotation
vm = task.info.result
@ -1421,26 +1431,26 @@ class PyVmomiHelper(object):
if self.change_detected:
task = self.current_vm_obj.ReconfigVM_Task(spec=self.configspec)
self.wait_for_task(task)
change_applied = True
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=2173
return {'changed': False, 'failed': True, 'msg': task.info.error.msg}
return {'changed': change_applied, 'failed': True, 'msg': task.info.error.msg}
# Rename VM
if self.params['uuid'] and self.params['name'] and self.params['name'] != vm.config.name:
task = self.current_vm_obj.Rename_Task(self.params['name'])
self.wait_for_task(task)
change_applied = True
if task.info.state == 'error':
return {'changed': change_applied, 'failed': True, 'msg': task.info.error.msg}
# Mark VM as Template
if self.params['is_template']:
task = self.current_vm_obj.MarkAsTemplate()
change_applied = True
# Rename VM
if self.params['new_name']:
task = self.current_vm_obj.Rename_Task(self.params['new_name'])
if task.info.state == 'error':
return {'changed': False, 'failed': True, 'msg': task.info.error.msg}
self.wait_for_task(task)
change_applied = True
vm_facts = self.gather_facts(self.current_vm_obj)
@ -1469,156 +1479,6 @@ class PyVmomiHelper(object):
return facts
def fetch_file_from_guest(self, vm, username, password, src, dest):
""" Use VMWare's filemanager api to fetch a file over http """
result = {'failed': False}
tools_status = vm.guest.toolsStatus
if tools_status == 'toolsNotInstalled' or tools_status == 'toolsNotRunning':
result['failed'] = True
result['msg'] = "VMwareTools is not installed or is not running in the guest"
return result
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/NamePasswordAuthentication.rst
creds = vim.vm.guest.NamePasswordAuthentication(
username=username, password=password
)
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/FileManager/FileTransferInformation.rst
fti = self.content.guestOperationsManager.fileManager. \
InitiateFileTransferFromGuest(vm, creds, src)
result['size'] = fti.size
result['url'] = fti.url
# Use module_utils to fetch the remote url returned from the api
rsp, info = fetch_url(self.module, fti.url, use_proxy=False,
force=True, last_mod_time=None,
timeout=10, headers=None)
# save all of the transfer data
for k, v in iteritems(info):
result[k] = v
# exit early if xfer failed
if info['status'] != 200:
result['failed'] = True
return result
# attempt to read the content and write it
try:
with open(dest, 'wb') as f:
f.write(rsp.read())
except Exception as e:
result['failed'] = True
result['msg'] = str(e)
return result
def push_file_to_guest(self, vm, username, password, src, dest, overwrite=True):
""" Use VMWare's filemanager api to fetch a file over http """
result = {'failed': False}
tools_status = vm.guest.toolsStatus
if tools_status == 'toolsNotInstalled' or tools_status == 'toolsNotRunning':
result['failed'] = True
result['msg'] = "VMwareTools is not installed or is not running in the guest"
return result
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/NamePasswordAuthentication.rst
creds = vim.vm.guest.NamePasswordAuthentication(
username=username, password=password
)
# the api requires a filesize in bytes
fdata = None
try:
# filesize = os.path.getsize(src)
filesize = os.stat(src).st_size
with open(src, 'rb') as f:
fdata = f.read()
result['local_filesize'] = filesize
except Exception as e:
result['failed'] = True
result['msg'] = "Unable to read src file: %s" % str(e)
return result
# https://www.vmware.com/support/developer/converter-sdk/conv60_apireference/vim.vm.guest.FileManager.html#initiateFileTransferToGuest
file_attribute = vim.vm.guest.FileManager.FileAttributes()
url = self.content.guestOperationsManager.fileManager. \
InitiateFileTransferToGuest(vm, creds, dest, file_attribute,
filesize, overwrite)
# PUT the filedata to the url ...
rsp, info = fetch_url(self.module, url, method="put", data=fdata,
use_proxy=False, force=True, last_mod_time=None,
timeout=10, headers=None)
result['msg'] = str(rsp.read())
# save all of the transfer data
for k, v in iteritems(info):
result[k] = v
return result
def run_command_in_guest(self, vm, username, password, program_path, program_args, program_cwd, program_env):
result = {'failed': False}
tools_status = vm.guest.toolsStatus
if (tools_status == 'toolsNotInstalled' or
tools_status == 'toolsNotRunning'):
result['failed'] = True
result['msg'] = "VMwareTools is not installed or is not running in the guest"
return result
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/NamePasswordAuthentication.rst
creds = vim.vm.guest.NamePasswordAuthentication(
username=username, password=password
)
try:
# https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/ProcessManager.rst
pm = self.content.guestOperationsManager.processManager
# https://www.vmware.com/support/developer/converter-sdk/conv51_apireference/vim.vm.guest.ProcessManager.ProgramSpec.html
ps = vim.vm.guest.ProcessManager.ProgramSpec(
# programPath=program,
# arguments=args
programPath=program_path,
arguments=program_args,
workingDirectory=program_cwd,
)
res = pm.StartProgramInGuest(vm, creds, ps)
result['pid'] = res
pdata = pm.ListProcessesInGuest(vm, creds, [res])
# wait for pid to finish
while not pdata[0].endTime:
time.sleep(1)
pdata = pm.ListProcessesInGuest(vm, creds, [res])
result['owner'] = pdata[0].owner
result['startTime'] = pdata[0].startTime.isoformat()
result['endTime'] = pdata[0].endTime.isoformat()
result['exitCode'] = pdata[0].exitCode
if result['exitCode'] != 0:
result['failed'] = True
result['msg'] = "program exited non-zero"
else:
result['msg'] = "program completed successfully"
except Exception as e:
result['msg'] = str(e)
result['failed'] = True
return result
def list_snapshots_recursively(self, snapshots):
snapshot_data = []
for snapshot in snapshots:
@ -1778,7 +1638,6 @@ def main():
annotation=dict(required=False, type='str', aliases=['notes']),
customvalues=dict(required=False, type='list', default=[]),
name=dict(required=True, type='str'),
new_name=dict(required=False, type='str'),
name_match=dict(required=False, type='str', default='first'),
snapshot_op=dict(required=False, type='dict', default={}),
uuid=dict(required=False, type='str'),