329 lines
9.6 KiB
Text
329 lines
9.6 KiB
Text
|
#!/usr/bin/python2
|
||
|
|
||
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
# TODO:
|
||
|
# Ability to set CPU/Memory reservations
|
||
|
|
||
|
try:
|
||
|
import json
|
||
|
except ImportError:
|
||
|
import simplejson as json
|
||
|
|
||
|
DOCUMENTATION = '''
|
||
|
---
|
||
|
module: vsphere_client
|
||
|
short_description: Creates a virtual guest on vsphere.
|
||
|
description:
|
||
|
- Communicates with vsphere, creating a new virtual guest OS based on
|
||
|
the specifications you specify to the module.
|
||
|
version_added: "1.1"
|
||
|
options:
|
||
|
vcenter_hostname:
|
||
|
description:
|
||
|
- The hostname of the vcenter server the module will connect to, to create the guest.
|
||
|
required: true
|
||
|
default: null
|
||
|
aliases: []
|
||
|
user:
|
||
|
description:
|
||
|
- username of the user to connect to vcenter as.
|
||
|
required: true
|
||
|
default: null
|
||
|
password:
|
||
|
description:
|
||
|
- password of the user to connect to vcenter as.
|
||
|
required: true
|
||
|
default: null
|
||
|
resource_pool:
|
||
|
description:
|
||
|
- The name of the resource_pool to create the VM in.
|
||
|
required: false
|
||
|
default: None
|
||
|
cluster:
|
||
|
description:
|
||
|
- The name of the cluster to create the VM in. By default this is derived from the host you tell the module to build the guest on.
|
||
|
required: false
|
||
|
default: None
|
||
|
datacenter:
|
||
|
description:
|
||
|
- The name of the datacenter to create the VM in.
|
||
|
required: true
|
||
|
default: null
|
||
|
datastore:
|
||
|
description:
|
||
|
- The datastore to store the VMs config files in. (Hard-disk locations are specified separately.)
|
||
|
required: true
|
||
|
default: null
|
||
|
esxi_hostname:
|
||
|
description:
|
||
|
- The hostname of the esxi host you want the VM to be created on.
|
||
|
required: true
|
||
|
default: null
|
||
|
power_on:
|
||
|
description:
|
||
|
- Whether or not to power on the VM after creation.
|
||
|
required: false
|
||
|
default: no
|
||
|
choices: [yes, no]
|
||
|
vm_name:
|
||
|
description:
|
||
|
- The name you want to call the VM.
|
||
|
required: true
|
||
|
default: null
|
||
|
vm_memory_mb:
|
||
|
description:
|
||
|
- How much memory in MB to give the VM.
|
||
|
required: false
|
||
|
default: 1024
|
||
|
vm_num_cpus:
|
||
|
description:
|
||
|
- How many vCPUs to give the VM.
|
||
|
required: false
|
||
|
default: 1
|
||
|
vm_scsi:
|
||
|
description:
|
||
|
- The type of scsi controller to add to the VM.
|
||
|
required: false
|
||
|
default: "paravirtual"
|
||
|
choices: [paravirtual, lsi, lsi_sas, bus_logic]
|
||
|
vm_disk:
|
||
|
description:
|
||
|
- A key, value list of disks and their sizes and which datastore to keep it in.
|
||
|
required: false
|
||
|
default: null
|
||
|
vm_nic:
|
||
|
description:
|
||
|
- A key, value list of nics, their types and what network to put them on.
|
||
|
required: false
|
||
|
default: null
|
||
|
choices: [vmxnet3, vmxnet2, vmxnet, e1000, e1000e, pcnet32]
|
||
|
vm_notes:
|
||
|
description:
|
||
|
- Any notes that you want to show up in the VMs Annotations field.
|
||
|
required: false
|
||
|
default: null
|
||
|
vm_cdrom:
|
||
|
description:
|
||
|
- A path, including datastore, to an ISO you want the CDROM device on the VM to have.
|
||
|
required: false
|
||
|
default: null
|
||
|
vm_extra_config:
|
||
|
description:
|
||
|
- A key, value pair of any extra values you want set or changed in the vmx file of the VM. Useful to set advanced options on the VM.
|
||
|
required: false
|
||
|
default: null
|
||
|
guestosid:
|
||
|
description:
|
||
|
- "A vmware guest needs to have a specific OS identifier set on it
|
||
|
during creation. You can find your os guestosid at the following URL:
|
||
|
http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.GuestOsDescriptor.GuestOsIdentifier.html"
|
||
|
required: true
|
||
|
default: null
|
||
|
# informational: requirements for nodes
|
||
|
requirements: [ pysphere ]
|
||
|
author: Romeo Theriault
|
||
|
'''
|
||
|
|
||
|
|
||
|
def power_state(vm, state, force):
|
||
|
|
||
|
power_status = vm.get_status()
|
||
|
|
||
|
check_status = ' '.join(state.split("_")).upper()
|
||
|
|
||
|
# Need Force
|
||
|
if not force and power_status in [
|
||
|
'SUSPENDED', 'POWERING ON',
|
||
|
'RESETTING', 'BLOCKED ON MSG'
|
||
|
]:
|
||
|
|
||
|
return "VM is in %s power state. Force is required!" % power_status
|
||
|
|
||
|
# State is already true
|
||
|
if power_status == check_status:
|
||
|
return False
|
||
|
|
||
|
else:
|
||
|
try:
|
||
|
if state == 'powered_off':
|
||
|
vm.power_off(sync_run=True)
|
||
|
|
||
|
elif state == 'powered_on':
|
||
|
vm.power_on(sync_run=True)
|
||
|
|
||
|
elif state == 'restarted':
|
||
|
if power_status in ('POWERED ON', 'POWERING ON', 'RESETTING'):
|
||
|
vm.reset(sync_run=False)
|
||
|
else:
|
||
|
return "Cannot restart VM in the current state %s" \
|
||
|
% power_status
|
||
|
return True
|
||
|
|
||
|
except Exception, e:
|
||
|
return e
|
||
|
|
||
|
return False
|
||
|
|
||
|
|
||
|
def gather_facts(vm):
|
||
|
"""
|
||
|
Gather facts for VM directly from vsphere.
|
||
|
"""
|
||
|
vm.get_properties()
|
||
|
facts = {
|
||
|
'module_hw': True,
|
||
|
'hw_name': vm.properties.name,
|
||
|
'hw_guest_full_name': vm.properties.config.guestFullName,
|
||
|
'hw_guest_id': vm.properties.config.guestId,
|
||
|
'hw_product_uuid': vm.properties.config.uuid,
|
||
|
'hw_processor_count': vm.properties.config.hardware.numCPU,
|
||
|
'hw_memtotal_mb': vm.properties.config.hardware.memoryMB,
|
||
|
}
|
||
|
|
||
|
ifidx = 0
|
||
|
for entry in vm.properties.config.hardware.device:
|
||
|
|
||
|
if not hasattr(entry, 'macAddress'):
|
||
|
continue
|
||
|
|
||
|
factname = 'hw_eth' + str(ifidx)
|
||
|
facts[factname] = {
|
||
|
'addresstype': entry.addressType,
|
||
|
'label': entry.deviceInfo.label,
|
||
|
'macaddress': entry.macAddress,
|
||
|
'macaddress_dash': entry.macAddress.replace(':', '-'),
|
||
|
'summary': entry.deviceInfo.summary,
|
||
|
}
|
||
|
|
||
|
ifidx += 1
|
||
|
|
||
|
return facts
|
||
|
|
||
|
|
||
|
def main():
|
||
|
|
||
|
vm = None
|
||
|
|
||
|
module = AnsibleModule(
|
||
|
argument_spec=dict(
|
||
|
vcenter_hostname=dict(required=True, type='str'),
|
||
|
username=dict(required=True, type='str'),
|
||
|
password=dict(required=True, type='str'),
|
||
|
state=dict(
|
||
|
required=False,
|
||
|
choices=[
|
||
|
'powered_on',
|
||
|
'powered_off',
|
||
|
'present',
|
||
|
'absent',
|
||
|
'restarted',
|
||
|
'reconfigured'
|
||
|
],
|
||
|
default='present'),
|
||
|
vmware_guest_facts=dict(required=False, choices=BOOLEANS),
|
||
|
guest=dict(required=True, type='str'),
|
||
|
guest_config=dict(required=False, type='dict', default={}),
|
||
|
force=dict(required=False, choices=BOOLEANS, default=False),
|
||
|
|
||
|
),
|
||
|
supports_check_mode=False,
|
||
|
mutually_exclusive=[['state', 'vmware_guest_facts']],
|
||
|
required_together=[
|
||
|
['state', 'force'],
|
||
|
['state', 'guest_config']
|
||
|
],
|
||
|
)
|
||
|
|
||
|
try:
|
||
|
from pysphere import VIServer, VIProperty, MORTypes
|
||
|
from pysphere.resources import VimService_services as VI
|
||
|
from pysphere.vi_task import VITask
|
||
|
from pysphere import VIException, VIApiException, FaultTypes
|
||
|
except ImportError, e:
|
||
|
module.fail_json(msg='pysphere module required')
|
||
|
|
||
|
vcenter_hostname = module.params['vcenter_hostname']
|
||
|
username = module.params['username']
|
||
|
password = module.params['password']
|
||
|
vmware_guest_facts = module.params['vmware_guest_facts']
|
||
|
state = module.params['state']
|
||
|
guest = module.params['guest']
|
||
|
force = module.params['force']
|
||
|
guest_config = module.params['guest_config']
|
||
|
|
||
|
# CONNECT TO THE SERVER
|
||
|
viserver = VIServer()
|
||
|
try:
|
||
|
viserver.connect(vcenter_hostname, username, password)
|
||
|
except VIApiException, err:
|
||
|
module.fail_json(msg="Cannot connect to %s: %s" %
|
||
|
(vcenter_hostname, err))
|
||
|
|
||
|
# Check if the VM exists before continuing
|
||
|
try:
|
||
|
vm = viserver.get_vm_by_name(guest)
|
||
|
|
||
|
# Run for facts only
|
||
|
if vmware_guest_facts:
|
||
|
try:
|
||
|
module.exit_json(ansible_facts=gather_facts(vm))
|
||
|
except Exception, e:
|
||
|
module.fail_json(
|
||
|
msg="Fact gather failed with exception %s" % e)
|
||
|
|
||
|
# Power Changes
|
||
|
elif state in ['powered_on', 'powered_off', 'restarted']:
|
||
|
state_result = power_state(vm, state, force)
|
||
|
|
||
|
# Failure
|
||
|
if isinstance(state_result, basestring):
|
||
|
module.fail_json(msg=state_result)
|
||
|
else:
|
||
|
module.exit_json(changed=state_result)
|
||
|
|
||
|
# Just check if there
|
||
|
elif state == 'present' and not len(guest_config):
|
||
|
module.exit_json(changed=False)
|
||
|
|
||
|
# Fail on reconfig without params
|
||
|
elif state == 'reconfigured':
|
||
|
if not len(guest_config):
|
||
|
module.fail_json(
|
||
|
msg="guest_config is required to reconfigure a VM")
|
||
|
# do it
|
||
|
else:
|
||
|
pass
|
||
|
|
||
|
# VM doesn't exist
|
||
|
except Exception:
|
||
|
|
||
|
# Fail for fact gather task
|
||
|
if vmware_guest_facts:
|
||
|
module.fail_json(
|
||
|
msg="No such VM %s. Fact gathering requires an existing vm"
|
||
|
% guest)
|
||
|
if state not in ['absent', 'present']:
|
||
|
module.fail_json(
|
||
|
msg="No such VM %s. States [powered_on, powered_off, "
|
||
|
"restarted, reconfigured] required an existing VM" % guest)
|
||
|
elif state == 'absent':
|
||
|
module.exit_json(changed=False, msg="vm %s not present" % guest)
|
||
|
|
||
|
# Create the VM
|
||
|
elif state == 'present':
|
||
|
pass
|
||
|
|
||
|
if vm:
|
||
|
# If the vm already exists, lets get some info from it, pass back the
|
||
|
# vm's vmware_guest_facts and then exit.
|
||
|
viserver.disconnect()
|
||
|
module.exit_json(
|
||
|
changed=False,
|
||
|
vcenter=vcenter_hostname)
|
||
|
|
||
|
|
||
|
# this is magic, see lib/ansible/module_common.py
|
||
|
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
||
|
main()
|