#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# TODO:
# Ability to set CPU/Memory reservations
try :
import json
except ImportError :
import simplejson as json
HAS_PYSPHERE = False
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
HAS_PYSPHERE = True
except ImportError :
pass
import ssl
DOCUMENTATION = '''
- - -
module : vsphere_guest
short_description : Create / delete / manage a guest VM through VMware vSphere .
description :
- Create / delete / reconfigure a guest VM through VMware vSphere . This module has a dependency on pysphere > = 1.7
version_added : " 1.6 "
options :
vcenter_hostname :
description :
- The hostname of the vcenter server the module will connect to , to create the guest .
required : true
default : null
aliases : [ ]
validate_certs :
description :
- Validate SSL certs . Note , if running on python without SSLContext
support ( typically , python < 2.7 .9 ) you will have to set this to C ( no )
as pysphere does not support validating certificates on older python .
Prior to 2.1 , this module would always validate on python > = 2.7 .9 and
never validate on python < = 2.7 .8 .
required : false
default : yes
choices : [ ' yes ' , ' no ' ]
version_added : 2.1
guest :
description :
- The virtual server name you wish to manage .
required : true
username :
description :
- Username 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
esxi :
description :
- Dictionary which includes datacenter and hostname on which the VM should be created . For standalone ESXi hosts , ha - datacenter should be used as the datacenter name
required : false
default : null
state :
description :
- Indicate desired state of the vm . ' reconfigured ' only applies changes to ' memory_mb ' and ' num_cpus ' in vm_hardware parameter , and only when hot - plugging is enabled for the guest .
default : present
choices : [ ' present ' , ' powered_off ' , ' absent ' , ' powered_on ' , ' restarted ' , ' reconfigured ' ]
from_template :
version_added : " 1.9 "
description :
- Specifies if the VM should be deployed from a template ( mutually exclusive with ' state ' parameter ) . No guest customization changes to hardware such as CPU , RAM , NICs or Disks can be applied when launching from template .
default : no
choices : [ ' yes ' , ' no ' ]
template_src :
version_added : " 1.9 "
description :
- Name of the source template to deploy from
default : None
snapshot_to_clone :
description :
- A string that when specified , will create a linked clone copy of the VM . Snapshot must already be taken in vCenter .
version_added : " 2.0 "
required : false
default : none
power_on_after_clone :
description :
- Specifies if the VM should be powered on after the clone .
required : false
default : yes
choices : [ ' yes ' , ' no ' ]
vm_disk :
description :
- A key , value list of disks and their sizes and which datastore to keep it in .
required : false
default : null
vm_hardware :
description :
- A key , value list of VM config settings . Must include [ ' memory_mb ' , ' num_cpus ' , ' osid ' , ' scsi ' ] .
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
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
vm_hw_version :
description :
- Desired hardware version identifier ( for example , " vmx-08 " for vms that needs to be managed with vSphere Client ) . Note that changing hardware version of existing vm is not supported .
required : false
default : null
version_added : " 1.7 "
vmware_guest_facts :
description :
- Gather facts from vCenter on a particular VM
required : false
default : null
force :
description :
- Boolean . Allows you to run commands which may alter the running state of a guest . Also used to reconfigure and destroy .
default : " no "
choices : [ " yes " , " no " ]
notes :
- This module should run from a system that can access vSphere directly .
Either by using local_action , or using delegate_to .
author : " Richard Hoop (@rhoop) <wrhoop@gmail.com> "
requirements :
- " python >= 2.6 "
- pysphere
'''
EXAMPLES = '''
# Create a new VM on an ESX server
# Returns changed = False when the VM already exists
# Returns changed = True and a adds ansible_facts from the new VM
# State will set the power status of a guest upon creation. Use powered_on to create and boot.
# Options ['state', 'vm_extra_config', 'vm_disk', 'vm_nic', 'vm_hardware', 'esxi'] are required together
# Note: vm_floppy support added in 2.0
- vsphere_guest :
vcenter_hostname : vcenter . mydomain . local
username : myuser
password : mypass
guest : newvm001
state : powered_on
vm_extra_config :
vcpu . hotadd : yes
mem . hotadd : yes
notes : This is a test VM
folder : MyFolder
vm_disk :
disk1 :
size_gb : 10
type : thin
datastore : storage001
# VMs can be put into folders. The value given here is either the full path
# to the folder (e.g. production/customerA/lamp) or just the last component
# of the path (e.g. lamp):
folder : production / customerA / lamp
vm_nic :
nic1 :
type : vmxnet3
network : VM Network
network_type : standard
nic2 :
type : vmxnet3
network : dvSwitch Network
network_type : dvs
vm_hardware :
memory_mb : 2048
num_cpus : 2
osid : centos64Guest
scsi : paravirtual
vm_cdrom :
type : " iso "
iso_path : " DatastoreName/cd-image.iso "
vm_floppy :
type : " image "
image_path : " DatastoreName/floppy-image.flp "
esxi :
datacenter : MyDatacenter
hostname : esx001 . mydomain . local
# Reconfigure the CPU and Memory on the newly created VM
# Will return the changes made
- vsphere_guest :
vcenter_hostname : vcenter . mydomain . local
username : myuser
password : mypass
guest : newvm001
state : reconfigured
vm_extra_config :
vcpu . hotadd : yes
mem . hotadd : yes
notes : This is a test VM
vm_disk :
disk1 :
size_gb : 10
type : thin
datastore : storage001
vm_nic :
nic1 :
type : vmxnet3
network : VM Network
network_type : standard
vm_hardware :
memory_mb : 4096
num_cpus : 4
osid : centos64Guest
scsi : paravirtual
esxi :
datacenter : MyDatacenter
hostname : esx001 . mydomain . local
# Deploy a guest from a template
- vsphere_guest :
vcenter_hostname : vcenter . mydomain . local
username : myuser
password : mypass
guest : newvm001
from_template : yes
template_src : centosTemplate
cluster : MainCluster
resource_pool : " /Resources "
vm_extra_config :
folder : MyFolder
# Task to gather facts from a vSphere cluster only if the system is a VMWare guest
- vsphere_guest :
vcenter_hostname : vcenter . mydomain . local
username : myuser
password : mypass
guest : newvm001
vmware_guest_facts : yes
# Typical output of a vsphere_facts run on a guest
# If vmware tools is not installed, ipadresses with return None
- hw_eth0 :
- addresstype : " assigned "
label : " Network adapter 1 "
macaddress : " 00:22:33:33:44:55 "
macaddress_dash : " 00-22-33-33-44-55 "
ipaddresses : [ ' 192.0.2.100 ' , ' 2001:DB8:56ff:feac:4d8a ' ]
summary : " VM Network "
hw_guest_full_name : " newvm001 "
hw_guest_id : " rhel6_64Guest "
hw_memtotal_mb : 2048
hw_name : " centos64Guest "
hw_power_status : " POWERED ON " ,
hw_processor_count : 2
hw_product_uuid : " ef50bac8-2845-40ff-81d9-675315501dac "
hw_power_status will be one of the following values :
- POWERED ON
- POWERED OFF
- SUSPENDED
- POWERING ON
- POWERING OFF
- SUSPENDING
- RESETTING
- BLOCKED ON MSG
- REVERTING TO SNAPSHOT
- UNKNOWN
as seen in the VMPowerState - Class of PySphere : http : / / git . io / vlwOq
# Remove a vm from vSphere
# The VM must be powered_off or you need to use force to force a shutdown
- vsphere_guest :
vcenter_hostname : vcenter . mydomain . local
username : myuser
password : mypass
guest : newvm001
state : absent
force : yes
'''
def add_scsi_controller ( module , s , config , devices , type = " paravirtual " , bus_num = 0 , disk_ctrl_key = 1 ) :
# add a scsi controller
scsi_ctrl_spec = config . new_deviceChange ( )
scsi_ctrl_spec . set_element_operation ( ' add ' )
if type == " lsi " :
# For RHEL5
scsi_ctrl = VI . ns0 . VirtualLsiLogicController_Def ( " scsi_ctrl " ) . pyclass ( )
elif type == " paravirtual " :
# For RHEL6
scsi_ctrl = VI . ns0 . ParaVirtualSCSIController_Def ( " scsi_ctrl " ) . pyclass ( )
elif type == " lsi_sas " :
scsi_ctrl = VI . ns0 . VirtualLsiLogicSASController_Def (
" scsi_ctrl " ) . pyclass ( )
elif type == " bus_logic " :
scsi_ctrl = VI . ns0 . VirtualBusLogicController_Def ( " scsi_ctrl " ) . pyclass ( )
else :
s . disconnect ( )
module . fail_json (
msg = " Error adding scsi controller to vm spec. No scsi controller "
" type of: %s " % ( type ) )
scsi_ctrl . set_element_busNumber ( int ( bus_num ) )
scsi_ctrl . set_element_key ( int ( disk_ctrl_key ) )
scsi_ctrl . set_element_sharedBus ( " noSharing " )
scsi_ctrl_spec . set_element_device ( scsi_ctrl )
# Add the scsi controller to the VM spec.
devices . append ( scsi_ctrl_spec )
return disk_ctrl_key
def add_disk ( module , s , config_target , config , devices , datastore , type = " thin " , size = 200000 , disk_ctrl_key = 1 , disk_number = 0 , key = 0 ) :
# add a vmdk disk
# Verify the datastore exists
datastore_name , ds = find_datastore ( module , s , datastore , config_target )
# create a new disk - file based - for the vm
disk_spec = config . new_deviceChange ( )
disk_spec . set_element_fileOperation ( " create " )
disk_spec . set_element_operation ( " add " )
disk_ctlr = VI . ns0 . VirtualDisk_Def ( " disk_ctlr " ) . pyclass ( )
disk_backing = VI . ns0 . VirtualDiskFlatVer2BackingInfo_Def (
" disk_backing " ) . pyclass ( )
disk_backing . set_element_fileName ( datastore_name )
disk_backing . set_element_diskMode ( " persistent " )
if type != " thick " :
disk_backing . set_element_thinProvisioned ( 1 )
disk_ctlr . set_element_key ( key )
disk_ctlr . set_element_controllerKey ( int ( disk_ctrl_key ) )
disk_ctlr . set_element_unitNumber ( int ( disk_number ) )
disk_ctlr . set_element_backing ( disk_backing )
disk_ctlr . set_element_capacityInKB ( int ( size ) )
disk_spec . set_element_device ( disk_ctlr )
devices . append ( disk_spec )
def add_cdrom ( module , s , config_target , config , devices , default_devs , type = " client " , vm_cd_iso_path = None ) :
# Add a cd-rom
# Make sure the datastore exists.
if vm_cd_iso_path :
iso_location = vm_cd_iso_path . split ( ' / ' , 1 )
datastore , ds = find_datastore (
module , s , iso_location [ 0 ] , config_target )
iso_path = iso_location [ 1 ]
# find ide controller
ide_ctlr = None
for dev in default_devs :
if dev . typecode . type [ 1 ] == " VirtualIDEController " :
ide_ctlr = dev
# add a cdrom based on a physical device
if ide_ctlr :
cd_spec = config . new_deviceChange ( )
cd_spec . set_element_operation ( ' add ' )
cd_ctrl = VI . ns0 . VirtualCdrom_Def ( " cd_ctrl " ) . pyclass ( )
if type == " iso " :
iso = VI . ns0 . VirtualCdromIsoBackingInfo_Def ( " iso " ) . pyclass ( )
ds_ref = iso . new_datastore ( ds )
ds_ref . set_attribute_type ( ds . get_attribute_type ( ) )
iso . set_element_datastore ( ds_ref )
iso . set_element_fileName ( " %s %s " % ( datastore , iso_path ) )
cd_ctrl . set_element_backing ( iso )
cd_ctrl . set_element_key ( 20 )
cd_ctrl . set_element_controllerKey ( ide_ctlr . get_element_key ( ) )
cd_ctrl . set_element_unitNumber ( 0 )
cd_spec . set_element_device ( cd_ctrl )
elif type == " client " :
client = VI . ns0 . VirtualCdromRemoteAtapiBackingInfo_Def (
" client " ) . pyclass ( )
client . set_element_deviceName ( " " )
cd_ctrl . set_element_backing ( client )
cd_ctrl . set_element_key ( 20 )
cd_ctrl . set_element_controllerKey ( ide_ctlr . get_element_key ( ) )
cd_ctrl . set_element_unitNumber ( 0 )
cd_spec . set_element_device ( cd_ctrl )
else :
s . disconnect ( )
module . fail_json (
msg = " Error adding cdrom of type %s to vm spec. "
" cdrom type can either be iso or client " % ( type ) )
devices . append ( cd_spec )
def add_floppy ( module , s , config_target , config , devices , default_devs , type = " image " , vm_floppy_image_path = None ) :
# Add a floppy
# Make sure the datastore exists.
if vm_floppy_image_path :
image_location = vm_floppy_image_path . split ( ' / ' , 1 )
datastore , ds = find_datastore (
module , s , image_location [ 0 ] , config_target )
image_path = image_location [ 1 ]
floppy_spec = config . new_deviceChange ( )
floppy_spec . set_element_operation ( ' add ' )
floppy_ctrl = VI . ns0 . VirtualFloppy_Def ( " floppy_ctrl " ) . pyclass ( )
if type == " image " :
image = VI . ns0 . VirtualFloppyImageBackingInfo_Def ( " image " ) . pyclass ( )
ds_ref = image . new_datastore ( ds )
ds_ref . set_attribute_type ( ds . get_attribute_type ( ) )
image . set_element_datastore ( ds_ref )
image . set_element_fileName ( " %s %s " % ( datastore , image_path ) )
floppy_ctrl . set_element_backing ( image )
floppy_ctrl . set_element_key ( 3 )
floppy_spec . set_element_device ( floppy_ctrl )
elif type == " client " :
client = VI . ns0 . VirtualFloppyRemoteDeviceBackingInfo_Def (
" client " ) . pyclass ( )
client . set_element_deviceName ( " /dev/fd0 " )
floppy_ctrl . set_element_backing ( client )
floppy_ctrl . set_element_key ( 3 )
floppy_spec . set_element_device ( floppy_ctrl )
else :
s . disconnect ( )
module . fail_json (
msg = " Error adding floppy of type %s to vm spec. "
" floppy type can either be image or client " % ( type ) )
devices . append ( floppy_spec )
def add_nic ( module , s , nfmor , config , devices , nic_type = " vmxnet3 " , network_name = " VM Network " , network_type = " standard " ) :
# add a NIC
# Different network card types are: "VirtualE1000",
# "VirtualE1000e","VirtualPCNet32", "VirtualVmxnet", "VirtualNmxnet2",
# "VirtualVmxnet3"
nic_spec = config . new_deviceChange ( )
nic_spec . set_element_operation ( " add " )
if nic_type == " e1000 " :
nic_ctlr = VI . ns0 . VirtualE1000_Def ( " nic_ctlr " ) . pyclass ( )
elif nic_type == " e1000e " :
nic_ctlr = VI . ns0 . VirtualE1000e_Def ( " nic_ctlr " ) . pyclass ( )
elif nic_type == " pcnet32 " :
nic_ctlr = VI . ns0 . VirtualPCNet32_Def ( " nic_ctlr " ) . pyclass ( )
elif nic_type == " vmxnet " :
nic_ctlr = VI . ns0 . VirtualVmxnet_Def ( " nic_ctlr " ) . pyclass ( )
elif nic_type == " vmxnet2 " :
nic_ctlr = VI . ns0 . VirtualVmxnet2_Def ( " nic_ctlr " ) . pyclass ( )
elif nic_type == " vmxnet3 " :
nic_ctlr = VI . ns0 . VirtualVmxnet3_Def ( " nic_ctlr " ) . pyclass ( )
else :
s . disconnect ( )
module . fail_json (
msg = " Error adding nic to vm spec. No nic type of: %s " %
( nic_type ) )
if network_type == " standard " :
nic_backing = VI . ns0 . VirtualEthernetCardNetworkBackingInfo_Def (
" nic_backing " ) . pyclass ( )
nic_backing . set_element_deviceName ( network_name )
elif network_type == " dvs " :
# Get the portgroup key
portgroupKey = find_portgroup_key ( module , s , nfmor , network_name )
# Get the dvswitch uuid
dvswitch_uuid = find_dvswitch_uuid ( module , s , nfmor , portgroupKey )
nic_backing_port = VI . ns0 . DistributedVirtualSwitchPortConnection_Def (
" nic_backing_port " ) . pyclass ( )
nic_backing_port . set_element_switchUuid ( dvswitch_uuid )
nic_backing_port . set_element_portgroupKey ( portgroupKey )
nic_backing = VI . ns0 . VirtualEthernetCardDistributedVirtualPortBackingInfo_Def (
" nic_backing " ) . pyclass ( )
nic_backing . set_element_port ( nic_backing_port )
else :
s . disconnect ( )
module . fail_json (
msg = " Error adding nic backing to vm spec. No network type of: "
" %s " % ( network_type ) )
nic_ctlr . set_element_addressType ( " generated " )
nic_ctlr . set_element_backing ( nic_backing )
nic_ctlr . set_element_key ( 4 )
nic_spec . set_element_device ( nic_ctlr )
devices . append ( nic_spec )
def find_datastore ( module , s , datastore , config_target ) :
# Verify the datastore exists and put it in brackets if it does.
ds = None
if config_target :
for d in config_target . Datastore :
if ( d . Datastore . Accessible and
( datastore and d . Datastore . Name == datastore )
or ( not datastore ) ) :
ds = d . Datastore . Datastore
datastore = d . Datastore . Name
break
else :
for ds_mor , ds_name in server . get_datastores ( ) . items ( ) :
ds_props = VIProperty ( s , ds_mor )
if ( ds_props . summary . accessible and ( datastore and ds_name == datastore )
or ( not datastore ) ) :
ds = ds_mor
datastore = ds_name
if not ds :
s . disconnect ( )
module . fail_json ( msg = " Datastore: %s does not appear to exist " %
( datastore ) )
datastore_name = " [ %s ] " % datastore
return datastore_name , ds
def find_portgroup_key ( module , s , nfmor , network_name ) :
# Find a portgroups key given the portgroup name.
# Grab all the distributed virtual portgroup's names and key's.
dvpg_mors = s . _retrieve_properties_traversal (
property_names = [ ' name ' , ' key ' ] ,
from_node = nfmor , obj_type = ' DistributedVirtualPortgroup ' )
# Get the correct portgroup managed object.
dvpg_mor = None
for dvpg in dvpg_mors :
if dvpg_mor :
break
for p in dvpg . PropSet :
if p . Name == " name " and p . Val == network_name :
dvpg_mor = dvpg
if dvpg_mor :
break
# If dvpg_mor is empty we didn't find the named portgroup.
if dvpg_mor is None :
s . disconnect ( )
module . fail_json (
msg = " Could not find the distributed virtual portgroup named "
" %s " % network_name )
# Get the portgroup key
portgroupKey = None
for p in dvpg_mor . PropSet :
if p . Name == " key " :
portgroupKey = p . Val
return portgroupKey
def find_dvswitch_uuid ( module , s , nfmor , portgroupKey ) :
# Find a dvswitch's uuid given a portgroup key.
# Function searches all dvswitches in the datacenter to find the switch
# that has the portgroup key.
# Grab the dvswitch uuid and portgroup properties
dvswitch_mors = s . _retrieve_properties_traversal (
property_names = [ ' uuid ' , ' portgroup ' ] ,
from_node = nfmor , obj_type = ' DistributedVirtualSwitch ' )
dvswitch_mor = None
# Get the dvswitches managed object
for dvswitch in dvswitch_mors :
if dvswitch_mor :
break
for p in dvswitch . PropSet :
if p . Name == " portgroup " :
pg_mors = p . Val . ManagedObjectReference
for pg_mor in pg_mors :
if dvswitch_mor :
break
key_mor = s . _get_object_properties (
pg_mor , property_names = [ ' key ' ] )
for key in key_mor . PropSet :
if key . Val == portgroupKey :
dvswitch_mor = dvswitch
# Get the switches uuid
dvswitch_uuid = None
for p in dvswitch_mor . PropSet :
if p . Name == " uuid " :
dvswitch_uuid = p . Val
return dvswitch_uuid
def spec_singleton ( spec , request , vm ) :
if not spec :
_this = request . new__this ( vm . _mor )
_this . set_attribute_type ( vm . _mor . get_attribute_type ( ) )
request . set_element__this ( _this )
spec = request . new_spec ( )
return spec
def vmdisk_id ( vm , current_datastore_name ) :
id_list = [ ]
for vm_disk in vm . _disks :
if current_datastore_name in vm_disk [ ' descriptor ' ] :
id_list . append ( vm_disk [ ' device ' ] [ ' key ' ] )
return id_list
def deploy_template ( vsphere_client , guest , resource_pool , template_src , esxi , module , cluster_name , snapshot_to_clone , power_on_after_clone , vm_extra_config ) :
vmTemplate = vsphere_client . get_vm_by_name ( template_src )
vmTarget = None
if esxi :
datacenter = esxi [ ' datacenter ' ]
esxi_hostname = esxi [ ' hostname ' ]
# Datacenter managed object reference
dclist = [ k for k ,
v in vsphere_client . get_datacenters ( ) . items ( ) if v == datacenter ]
if dclist :
dcmor = dclist [ 0 ]
else :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find datacenter named: %s " % datacenter )
dcprops = VIProperty ( vsphere_client , dcmor )
# hostFolder managed reference
hfmor = dcprops . hostFolder . _obj
# Grab the computerResource name and host properties
crmors = vsphere_client . _retrieve_properties_traversal (
property_names = [ ' name ' , ' host ' ] ,
from_node = hfmor ,
obj_type = ' ComputeResource ' )
# Grab the host managed object reference of the esxi_hostname
try :
hostmor = [ k for k ,
v in vsphere_client . get_hosts ( ) . items ( ) if v == esxi_hostname ] [ 0 ]
except IndexError , e :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find esx host named: %s " % esxi_hostname )
# Grab the computeResource managed object reference of the host we are
# creating the VM on.
crmor = None
for cr in crmors :
if crmor :
break
for p in cr . PropSet :
if p . Name == " host " :
for h in p . Val . get_element_ManagedObjectReference ( ) :
if h == hostmor :
crmor = cr . Obj
break
if crmor :
break
crprops = VIProperty ( vsphere_client , crmor )
rpmor = crprops . resourcePool . _obj
elif resource_pool :
try :
cluster = [ k for k ,
v in vsphere_client . get_clusters ( ) . items ( ) if v == cluster_name ] [ 0 ] if cluster_name else None
except IndexError , e :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find Cluster named: %s " %
cluster_name )
try :
rpmor = [ k for k , v in vsphere_client . get_resource_pools (
from_mor = cluster ) . items ( )
if v == resource_pool ] [ 0 ]
except IndexError , e :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find Resource Pool named: %s " %
resource_pool )
else :
module . fail_json ( msg = " You need to specify either esxi:[datacenter,hostname] or [cluster,resource_pool] " )
try :
vmTarget = vsphere_client . get_vm_by_name ( guest )
except Exception :
pass
if not vmTemplate . is_powered_off ( ) :
module . fail_json (
msg = " Source %s must be powered off " % template_src
)
try :
if not vmTarget :
cloneArgs = dict ( resourcepool = rpmor , power_on = power_on_after_clone )
if snapshot_to_clone is not None :
#check if snapshot_to_clone is specified, Create a Linked Clone instead of a full clone.
cloneArgs [ " linked " ] = True
cloneArgs [ " snapshot " ] = snapshot_to_clone
if vm_extra_config . get ( " folder " ) is not None :
# if a folder is specified, clone the VM into it
cloneArgs [ " folder " ] = vm_extra_config . get ( " folder " )
vmTemplate . clone ( guest , * * cloneArgs )
changed = True
else :
changed = False
vsphere_client . disconnect ( )
module . exit_json ( changed = changed )
except Exception as e :
module . fail_json (
msg = " Could not clone selected machine: %s " % e
)
# example from https://github.com/kalazzerx/pysphere/blob/master/examples/pysphere_create_disk_and_add_to_vm.py
# was used.
def update_disks ( vsphere_client , vm , module , vm_disk , changes ) :
request = VI . ReconfigVM_TaskRequestMsg ( )
changed = False
for cnf_disk in vm_disk :
disk_id = re . sub ( " disk " , " " , cnf_disk )
found = False
for dev_key in vm . _devices :
if vm . _devices [ dev_key ] [ ' type ' ] == ' VirtualDisk ' :
hdd_id = vm . _devices [ dev_key ] [ ' label ' ] . split ( ) [ 2 ]
if disk_id == hdd_id :
found = True
continue
if not found :
it = VI . ReconfigVM_TaskRequestMsg ( )
_this = request . new__this ( vm . _mor )
_this . set_attribute_type ( vm . _mor . get_attribute_type ( ) )
request . set_element__this ( _this )
spec = request . new_spec ( )
dc = spec . new_deviceChange ( )
dc . Operation = " add "
dc . FileOperation = " create "
hd = VI . ns0 . VirtualDisk_Def ( " hd " ) . pyclass ( )
hd . Key = - 100
hd . UnitNumber = int ( disk_id )
hd . CapacityInKB = int ( vm_disk [ cnf_disk ] [ ' size_gb ' ] ) * 1024 * 1024
hd . ControllerKey = 1000
# module.fail_json(msg="peos : %s" % vm_disk[cnf_disk])
backing = VI . ns0 . VirtualDiskFlatVer2BackingInfo_Def ( " backing " ) . pyclass ( )
backing . FileName = " [ %s ] " % vm_disk [ cnf_disk ] [ ' datastore ' ]
backing . DiskMode = " persistent "
backing . Split = False
backing . WriteThrough = False
backing . ThinProvisioned = False
backing . EagerlyScrub = False
hd . Backing = backing
dc . Device = hd
spec . DeviceChange = [ dc ]
request . set_element_spec ( spec )
ret = vsphere_client . _proxy . ReconfigVM_Task ( request ) . _returnval
# Wait for the task to finish
task = VITask ( ret , vsphere_client )
status = task . wait_for_state ( [ task . STATE_SUCCESS ,
task . STATE_ERROR ] )
if status == task . STATE_SUCCESS :
changed = True
changes [ cnf_disk ] = vm_disk [ cnf_disk ]
elif status == task . STATE_ERROR :
module . fail_json (
msg = " Error reconfiguring vm: %s , [ %s ] " % (
task . get_error_message ( ) ,
vm_disk [ cnf_disk ] ) )
return changed , changes
def reconfigure_vm ( vsphere_client , vm , module , esxi , resource_pool , cluster_name , guest , vm_extra_config , vm_hardware , vm_disk , vm_nic , state , force ) :
spec = None
changed = False
changes = { }
request = None
shutdown = False
poweron = vm . is_powered_on ( )
memoryHotAddEnabled = bool ( vm . properties . config . memoryHotAddEnabled )
cpuHotAddEnabled = bool ( vm . properties . config . cpuHotAddEnabled )
cpuHotRemoveEnabled = bool ( vm . properties . config . cpuHotRemoveEnabled )
changed , changes = update_disks ( vsphere_client , vm ,
module , vm_disk , changes )
request = VI . ReconfigVM_TaskRequestMsg ( )
# Change Memory
if ' memory_mb ' in vm_hardware :
if int ( vm_hardware [ ' memory_mb ' ] ) != vm . properties . config . hardware . memoryMB :
spec = spec_singleton ( spec , request , vm )
if vm . is_powered_on ( ) :
if force :
# No hot add but force
if not memoryHotAddEnabled :
shutdown = True
elif int ( vm_hardware [ ' memory_mb ' ] ) < vm . properties . config . hardware . memoryMB :
shutdown = True
else :
# Fail on no hot add and no force
if not memoryHotAddEnabled :
module . fail_json (
msg = " memoryHotAdd is not enabled. force is "
" required for shutdown " )
# Fail on no force and memory shrink
elif int ( vm_hardware [ ' memory_mb ' ] ) < vm . properties . config . hardware . memoryMB :
module . fail_json (
msg = " Cannot lower memory on a live VM. force is "
" required for shutdown " )
# set the new RAM size
spec . set_element_memoryMB ( int ( vm_hardware [ ' memory_mb ' ] ) )
changes [ ' memory ' ] = vm_hardware [ ' memory_mb ' ]
# ===( Reconfigure Network )====#
if vm_nic :
changed = reconfigure_net ( vsphere_client , vm , module , esxi , resource_pool , guest , vm_nic , cluster_name )
# ====( Config Memory )====#
if ' num_cpus ' in vm_hardware :
if int ( vm_hardware [ ' num_cpus ' ] ) != vm . properties . config . hardware . numCPU :
spec = spec_singleton ( spec , request , vm )
if vm . is_powered_on ( ) :
if force :
# No hot add but force
if not cpuHotAddEnabled :
shutdown = True
elif int ( vm_hardware [ ' num_cpus ' ] ) < vm . properties . config . hardware . numCPU :
if not cpuHotRemoveEnabled :
shutdown = True
else :
# Fail on no hot add and no force
if not cpuHotAddEnabled :
module . fail_json (
msg = " cpuHotAdd is not enabled. force is "
" required for shutdown " )
# Fail on no force and cpu shrink without hot remove
elif int ( vm_hardware [ ' num_cpus ' ] ) < vm . properties . config . hardware . numCPU :
if not cpuHotRemoveEnabled :
module . fail_json (
msg = " Cannot lower CPU on a live VM without "
" cpuHotRemove. force is required for shutdown " )
spec . set_element_numCPUs ( int ( vm_hardware [ ' num_cpus ' ] ) )
changes [ ' cpu ' ] = vm_hardware [ ' num_cpus ' ]
if len ( changes ) :
if shutdown and vm . is_powered_on ( ) :
try :
vm . power_off ( sync_run = True )
vm . get_status ( )
except Exception , e :
module . fail_json (
msg = ' Failed to shutdown vm %s : %s ' % ( guest , e )
)
request . set_element_spec ( spec )
ret = vsphere_client . _proxy . ReconfigVM_Task ( request ) . _returnval
# Wait for the task to finish
task = VITask ( ret , vsphere_client )
status = task . wait_for_state ( [ task . STATE_SUCCESS , task . STATE_ERROR ] )
if status == task . STATE_SUCCESS :
changed = True
elif status == task . STATE_ERROR :
module . fail_json (
msg = " Error reconfiguring vm: %s " % task . get_error_message ( ) )
if vm . is_powered_off ( ) and poweron :
try :
vm . power_on ( sync_run = True )
except Exception , e :
module . fail_json (
msg = ' Failed to power on vm %s : %s ' % ( guest , e )
)
vsphere_client . disconnect ( )
if changed :
module . exit_json ( changed = True , changes = changes )
module . exit_json ( changed = False )
def reconfigure_net ( vsphere_client , vm , module , esxi , resource_pool , guest , vm_nic , cluster_name = None ) :
s = vsphere_client
nics = { }
request = VI . ReconfigVM_TaskRequestMsg ( )
_this = request . new__this ( vm . _mor )
_this . set_attribute_type ( vm . _mor . get_attribute_type ( ) )
request . set_element__this ( _this )
nic_changes = [ ]
datacenter = esxi [ ' datacenter ' ]
# Datacenter managed object reference
dclist = [ k for k ,
v in vsphere_client . get_datacenters ( ) . items ( ) if v == datacenter ]
if dclist :
dcmor = dclist [ 0 ]
else :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find datacenter named: %s " % datacenter )
dcprops = VIProperty ( vsphere_client , dcmor )
nfmor = dcprops . networkFolder . _obj
for k , v in vm_nic . iteritems ( ) :
nicNum = k [ len ( k ) - 1 ]
if vm_nic [ k ] [ ' network_type ' ] == ' dvs ' :
portgroupKey = find_portgroup_key ( module , s , nfmor , vm_nic [ k ] [ ' network ' ] )
todvs = True
elif vm_nic [ k ] [ ' network_type ' ] == ' standard ' :
todvs = False
# Detect cards that need to be changed and network type (and act accordingly)
for dev in vm . properties . config . hardware . device :
if dev . _type in [ " VirtualE1000 " , " VirtualE1000e " ,
" VirtualPCNet32 " , " VirtualVmxnet " ,
" VirtualNmxnet2 " , " VirtualVmxnet3 " ] :
devNum = dev . deviceInfo . label [ len ( dev . deviceInfo . label ) - 1 ]
if devNum == nicNum :
fromdvs = dev . deviceInfo . summary . split ( ' : ' ) [ 0 ] == ' DVSwitch '
if todvs and fromdvs :
if dev . backing . port . _obj . get_element_portgroupKey ( ) != portgroupKey :
nics [ k ] = ( dev , portgroupKey , 1 )
elif fromdvs and not todvs :
nics [ k ] = ( dev , ' ' , 2 )
elif not fromdvs and todvs :
nics [ k ] = ( dev , portgroupKey , 3 )
elif not fromdvs and not todvs :
if dev . backing . _obj . get_element_deviceName ( ) != vm_nic [ k ] [ ' network ' ] :
nics [ k ] = ( dev , ' ' , 2 )
else :
pass
else :
module . exit_json ( )
if len ( nics ) > 0 :
for nic , obj in nics . iteritems ( ) :
"""
1 , 2 and 3 are used to mark which action should be taken
1 = from a distributed switch to a distributed switch
2 = to a standard switch
3 = to a distributed switch
"""
dev = obj [ 0 ]
pgKey = obj [ 1 ]
dvsKey = obj [ 2 ]
if dvsKey == 1 :
dev . backing . port . _obj . set_element_portgroupKey ( pgKey )
dev . backing . port . _obj . set_element_portKey ( ' ' )
if dvsKey == 3 :
dvswitch_uuid = find_dvswitch_uuid ( module , s , nfmor , pgKey )
nic_backing_port = VI . ns0 . DistributedVirtualSwitchPortConnection_Def (
" nic_backing_port " ) . pyclass ( )
nic_backing_port . set_element_switchUuid ( dvswitch_uuid )
nic_backing_port . set_element_portgroupKey ( pgKey )
nic_backing_port . set_element_portKey ( ' ' )
nic_backing = VI . ns0 . VirtualEthernetCardDistributedVirtualPortBackingInfo_Def (
" nic_backing " ) . pyclass ( )
nic_backing . set_element_port ( nic_backing_port )
dev . _obj . set_element_backing ( nic_backing )
if dvsKey == 2 :
nic_backing = VI . ns0 . VirtualEthernetCardNetworkBackingInfo_Def (
" nic_backing " ) . pyclass ( )
nic_backing . set_element_deviceName ( vm_nic [ nic ] [ ' network ' ] )
dev . _obj . set_element_backing ( nic_backing )
for nic , obj in nics . iteritems ( ) :
dev = obj [ 0 ]
spec = request . new_spec ( )
nic_change = spec . new_deviceChange ( )
nic_change . set_element_device ( dev . _obj )
nic_change . set_element_operation ( " edit " )
nic_changes . append ( nic_change )
spec . set_element_deviceChange ( nic_changes )
request . set_element_spec ( spec )
ret = vsphere_client . _proxy . ReconfigVM_Task ( request ) . _returnval
task = VITask ( ret , vsphere_client )
status = task . wait_for_state ( [ task . STATE_SUCCESS , task . STATE_ERROR ] )
if status == task . STATE_SUCCESS :
return ( True )
elif status == task . STATE_ERROR :
module . fail_json ( msg = " Could not change network %s " % task . get_error_message ( ) )
elif len ( nics ) == 0 :
return ( False )
def _build_folder_tree ( nodes , parent ) :
tree = { }
for node in nodes :
if node [ ' parent ' ] == parent :
tree [ node [ ' name ' ] ] = dict . copy ( node )
tree [ node [ ' name ' ] ] [ ' subfolders ' ] = _build_folder_tree ( nodes , node [ ' id ' ] )
del tree [ node [ ' name ' ] ] [ ' parent ' ]
return tree
def _find_path_in_tree ( tree , path ) :
for name , o in tree . iteritems ( ) :
if name == path [ 0 ] :
if len ( path ) == 1 :
return o
else :
return _find_path_in_tree ( o [ ' subfolders ' ] , path [ 1 : ] )
return None
def _get_folderid_for_path ( vsphere_client , datacenter , path ) :
content = vsphere_client . _retrieve_properties_traversal ( property_names = [ ' name ' , ' parent ' ] , obj_type = MORTypes . Folder )
if not content : return { }
node_list = [
{
' id ' : o . Obj ,
' name ' : o . PropSet [ 0 ] . Val ,
' parent ' : ( o . PropSet [ 1 ] . Val if len ( o . PropSet ) > 1 else None )
} for o in content
]
tree = _build_folder_tree ( node_list , datacenter )
tree = _find_path_in_tree ( tree , [ ' vm ' ] ) [ ' subfolders ' ]
folder = _find_path_in_tree ( tree , path . split ( ' / ' ) )
return folder [ ' id ' ] if folder else None
def create_vm ( vsphere_client , module , esxi , resource_pool , cluster_name , guest , vm_extra_config , vm_hardware , vm_disk , vm_nic , vm_hw_version , state ) :
datacenter = esxi [ ' datacenter ' ]
esxi_hostname = esxi [ ' hostname ' ]
# Datacenter managed object reference
dclist = [ k for k ,
v in vsphere_client . get_datacenters ( ) . items ( ) if v == datacenter ]
if dclist :
dcmor = dclist [ 0 ]
else :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find datacenter named: %s " % datacenter )
dcprops = VIProperty ( vsphere_client , dcmor )
# hostFolder managed reference
hfmor = dcprops . hostFolder . _obj
# virtualmachineFolder managed object reference
if vm_extra_config . get ( ' folder ' ) :
# try to find the folder by its full path, e.g. 'production/customerA/lamp'
vmfmor = _get_folderid_for_path ( vsphere_client , dcmor , vm_extra_config . get ( ' folder ' ) )
# try the legacy behaviour of just matching the folder name, so 'lamp' alone matches 'production/customerA/lamp'
if vmfmor is None :
for mor , name in vsphere_client . _get_managed_objects ( MORTypes . Folder ) . iteritems ( ) :
if name == vm_extra_config [ ' folder ' ] :
vmfmor = mor
# if neither of strategies worked, bail out
if vmfmor is None :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find folder named: %s " % vm_extra_config [ ' folder ' ] )
else :
vmfmor = dcprops . vmFolder . _obj
# networkFolder managed object reference
nfmor = dcprops . networkFolder . _obj
# Grab the computerResource name and host properties
crmors = vsphere_client . _retrieve_properties_traversal (
property_names = [ ' name ' , ' host ' ] ,
from_node = hfmor ,
obj_type = ' ComputeResource ' )
# Grab the host managed object reference of the esxi_hostname
try :
hostmor = [ k for k ,
v in vsphere_client . get_hosts ( ) . items ( ) if v == esxi_hostname ] [ 0 ]
except IndexError , e :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find esx host named: %s " % esxi_hostname )
# Grab the computerResource managed object reference of the host we are
# creating the VM on.
crmor = None
for cr in crmors :
if crmor :
break
for p in cr . PropSet :
if p . Name == " host " :
for h in p . Val . get_element_ManagedObjectReference ( ) :
if h == hostmor :
crmor = cr . Obj
break
if crmor :
break
crprops = VIProperty ( vsphere_client , crmor )
# Get resource pool managed reference
# Requires that a cluster name be specified.
if resource_pool :
try :
cluster = [ k for k ,
v in vsphere_client . get_clusters ( ) . items ( ) if v == cluster_name ] [ 0 ] if cluster_name else None
except IndexError , e :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find Cluster named: %s " %
cluster_name )
try :
rpmor = [ k for k , v in vsphere_client . get_resource_pools (
from_mor = cluster ) . items ( )
if v == resource_pool ] [ 0 ]
except IndexError , e :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Cannot find Resource Pool named: %s " %
resource_pool )
else :
rpmor = crprops . resourcePool . _obj
# CREATE VM CONFIGURATION
# get config target
request = VI . QueryConfigTargetRequestMsg ( )
_this = request . new__this ( crprops . environmentBrowser . _obj )
_this . set_attribute_type (
crprops . environmentBrowser . _obj . get_attribute_type ( ) )
request . set_element__this ( _this )
h = request . new_host ( hostmor )
h . set_attribute_type ( hostmor . get_attribute_type ( ) )
request . set_element_host ( h )
config_target = vsphere_client . _proxy . QueryConfigTarget ( request ) . _returnval
# get default devices
request = VI . QueryConfigOptionRequestMsg ( )
_this = request . new__this ( crprops . environmentBrowser . _obj )
_this . set_attribute_type (
crprops . environmentBrowser . _obj . get_attribute_type ( ) )
request . set_element__this ( _this )
h = request . new_host ( hostmor )
h . set_attribute_type ( hostmor . get_attribute_type ( ) )
request . set_element_host ( h )
config_option = vsphere_client . _proxy . QueryConfigOption ( request ) . _returnval
default_devs = config_option . DefaultDevice
# add parameters to the create vm task
create_vm_request = VI . CreateVM_TaskRequestMsg ( )
config = create_vm_request . new_config ( )
if vm_hw_version :
config . set_element_version ( vm_hw_version )
vmfiles = config . new_files ( )
datastore_name , ds = find_datastore (
module , vsphere_client , vm_disk [ ' disk1 ' ] [ ' datastore ' ] , config_target )
vmfiles . set_element_vmPathName ( datastore_name )
config . set_element_files ( vmfiles )
config . set_element_name ( guest )
if ' notes ' in vm_extra_config :
config . set_element_annotation ( vm_extra_config [ ' notes ' ] )
config . set_element_memoryMB ( int ( vm_hardware [ ' memory_mb ' ] ) )
config . set_element_numCPUs ( int ( vm_hardware [ ' num_cpus ' ] ) )
config . set_element_guestId ( vm_hardware [ ' osid ' ] )
devices = [ ]
# Attach all the hardware we want to the VM spec.
# Add a scsi controller to the VM spec.
disk_ctrl_key = add_scsi_controller (
module , vsphere_client , config , devices , vm_hardware [ ' scsi ' ] )
if vm_disk :
disk_num = 0
disk_key = 0
for disk in sorted ( vm_disk . iterkeys ( ) ) :
try :
datastore = vm_disk [ disk ] [ ' datastore ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. datastore needs to be "
" specified. " % disk )
try :
disksize = int ( vm_disk [ disk ] [ ' size_gb ' ] )
# Convert the disk size to kiloboytes
disksize = disksize * 1024 * 1024
except ( KeyError , ValueError ) :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Error on %s definition. size needs to be specified as an integer. " % disk )
try :
disktype = vm_disk [ disk ] [ ' type ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. type needs to be "
" specified. " % disk )
# Add the disk to the VM spec.
add_disk (
module , vsphere_client , config_target , config ,
devices , datastore , disktype , disksize , disk_ctrl_key ,
disk_num , disk_key )
disk_num = disk_num + 1
disk_key = disk_key + 1
if ' vm_cdrom ' in vm_hardware :
cdrom_iso_path = None
cdrom_type = None
try :
cdrom_type = vm_hardware [ ' vm_cdrom ' ] [ ' type ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. cdrom type needs to be "
" specified. " % vm_hardware [ ' vm_cdrom ' ] )
if cdrom_type == ' iso ' :
try :
cdrom_iso_path = vm_hardware [ ' vm_cdrom ' ] [ ' iso_path ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. cdrom iso_path needs "
" to be specified. " % vm_hardware [ ' vm_cdrom ' ] )
# Add a CD-ROM device to the VM.
add_cdrom ( module , vsphere_client , config_target , config , devices ,
default_devs , cdrom_type , cdrom_iso_path )
if ' vm_floppy ' in vm_hardware :
floppy_image_path = None
floppy_type = None
try :
floppy_type = vm_hardware [ ' vm_floppy ' ] [ ' type ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. floppy type needs to be "
" specified. " % vm_hardware [ ' vm_floppy ' ] )
if floppy_type == ' image ' :
try :
floppy_image_path = vm_hardware [ ' vm_floppy ' ] [ ' image_path ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. floppy image_path needs "
" to be specified. " % vm_hardware [ ' vm_floppy ' ] )
# Add a floppy to the VM.
add_floppy ( module , vsphere_client , config_target , config , devices ,
default_devs , floppy_type , floppy_image_path )
if vm_nic :
for nic in sorted ( vm_nic . iterkeys ( ) ) :
try :
nictype = vm_nic [ nic ] [ ' type ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. type needs to be "
" specified. " % nic )
try :
network = vm_nic [ nic ] [ ' network ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. network needs to be "
" specified. " % nic )
try :
network_type = vm_nic [ nic ] [ ' network_type ' ]
except KeyError :
vsphere_client . disconnect ( )
module . fail_json (
msg = " Error on %s definition. network_type needs to be "
" specified. " % nic )
# Add the nic to the VM spec.
add_nic ( module , vsphere_client , nfmor , config , devices ,
nictype , network , network_type )
config . set_element_deviceChange ( devices )
create_vm_request . set_element_config ( config )
folder_mor = create_vm_request . new__this ( vmfmor )
folder_mor . set_attribute_type ( vmfmor . get_attribute_type ( ) )
create_vm_request . set_element__this ( folder_mor )
rp_mor = create_vm_request . new_pool ( rpmor )
rp_mor . set_attribute_type ( rpmor . get_attribute_type ( ) )
create_vm_request . set_element_pool ( rp_mor )
host_mor = create_vm_request . new_host ( hostmor )
host_mor . set_attribute_type ( hostmor . get_attribute_type ( ) )
create_vm_request . set_element_host ( host_mor )
# CREATE THE VM
taskmor = vsphere_client . _proxy . CreateVM_Task ( create_vm_request ) . _returnval
task = VITask ( taskmor , vsphere_client )
task . wait_for_state ( [ task . STATE_SUCCESS , task . STATE_ERROR ] )
if task . get_state ( ) == task . STATE_ERROR :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Error creating vm: %s " %
task . get_error_message ( ) )
else :
# We always need to get the vm because we are going to gather facts
vm = vsphere_client . get_vm_by_name ( guest )
# VM was created. If there is any extra config options specified, set
# them here , disconnect from vcenter, then exit.
if vm_extra_config :
vm . set_extra_config ( vm_extra_config )
# Power on the VM if it was requested
power_state ( vm , state , True )
vmfacts = gather_facts ( vm )
vsphere_client . disconnect ( )
module . exit_json (
ansible_facts = vmfacts ,
changed = True ,
changes = " Created VM %s " % guest )
def delete_vm ( vsphere_client , module , guest , vm , force ) :
try :
if vm . is_powered_on ( ) :
if force :
try :
vm . power_off ( sync_run = True )
vm . get_status ( )
except Exception , e :
module . fail_json (
msg = ' Failed to shutdown vm %s : %s ' % ( guest , e ) )
else :
module . fail_json (
msg = ' You must use either shut the vm down first or '
' use force ' )
# Invoke Destroy_Task
request = VI . Destroy_TaskRequestMsg ( )
_this = request . new__this ( vm . _mor )
_this . set_attribute_type ( vm . _mor . get_attribute_type ( ) )
request . set_element__this ( _this )
ret = vsphere_client . _proxy . Destroy_Task ( request ) . _returnval
task = VITask ( ret , vsphere_client )
# Wait for the task to finish
status = task . wait_for_state (
[ task . STATE_SUCCESS , task . STATE_ERROR ] )
if status == task . STATE_ERROR :
vsphere_client . disconnect ( )
module . fail_json ( msg = " Error removing vm: %s %s " %
task . get_error_message ( ) )
module . exit_json ( changed = True , changes = " VM %s deleted " % guest )
except Exception , e :
module . fail_json (
msg = ' Failed to delete vm %s : %s ' % ( guest , e ) )
def power_state ( vm , state , force ) :
"""
Correctly set the power status for a VM determined by the current and
requested states . force is forceful
"""
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_power_status ' : vm . get_status ( ) ,
' 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 ,
' hw_interfaces ' : [ ] ,
}
netInfo = vm . get_property ( ' net ' )
netDict = { }
if netInfo :
for net in netInfo :
netDict [ net [ ' mac_address ' ] ] = net [ ' ip_addresses ' ]
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 ,
' ipaddresses ' : netDict . get ( entry . macAddress , None ) ,
' macaddress_dash ' : entry . macAddress . replace ( ' : ' , ' - ' ) ,
' summary ' : entry . deviceInfo . summary ,
}
facts [ ' hw_interfaces ' ] . append ( ' eth ' + str ( ifidx ) )
ifidx + = 1
return facts
class DefaultVMConfig ( object ) :
"""
Shallow and deep dict comparison for interfaces
"""
def __init__ ( self , check_dict , interface_dict ) :
self . check_dict , self . interface_dict = check_dict , interface_dict
self . set_current , self . set_past = set (
check_dict . keys ( ) ) , set ( interface_dict . keys ( ) )
self . intersect = self . set_current . intersection ( self . set_past )
self . recursive_missing = None
def shallow_diff ( self ) :
return self . set_past - self . intersect
def recursive_diff ( self ) :
if not self . recursive_missing :
self . recursive_missing = [ ]
for key , value in self . interface_dict . items ( ) :
if isinstance ( value , dict ) :
for k , v in value . items ( ) :
if k in self . check_dict [ key ] :
if not isinstance ( self . check_dict [ key ] [ k ] , v ) :
try :
if v == int :
self . check_dict [ key ] [ k ] = int ( self . check_dict [ key ] [ k ] )
elif v == basestring :
self . check_dict [ key ] [ k ] = str ( self . check_dict [ key ] [ k ] )
else :
raise ValueError
except ValueError :
self . recursive_missing . append ( ( k , v ) )
else :
self . recursive_missing . append ( ( k , v ) )
return self . recursive_missing
def config_check ( name , passed , default , module ) :
"""
Checks that the dict passed for VM configuration matches the required
interface declared at the top of __main__
"""
diff = DefaultVMConfig ( passed , default )
if len ( diff . shallow_diff ( ) ) :
module . fail_json (
msg = " Missing required key/pair [ %s ]. %s must contain %s " %
( ' , ' . join ( diff . shallow_diff ( ) ) , name , default ) )
if diff . recursive_diff ( ) :
module . fail_json (
msg = " Config mismatch for %s on %s " %
( name , diff . recursive_diff ( ) ) )
return True
def main ( ) :
vm = None
proto_vm_hardware = {
' memory_mb ' : int ,
' num_cpus ' : int ,
' scsi ' : basestring ,
' osid ' : basestring
}
proto_vm_disk = {
' disk1 ' : {
' datastore ' : basestring ,
' size_gb ' : int ,
' type ' : basestring
}
}
proto_vm_nic = {
' nic1 ' : {
' type ' : basestring ,
' network ' : basestring ,
' network_type ' : basestring
}
}
proto_esxi = {
' datacenter ' : basestring ,
' hostname ' : basestring
}
module = AnsibleModule (
argument_spec = dict (
vcenter_hostname = dict ( required = True , type = ' str ' ) ,
username = dict ( required = True , type = ' str ' ) ,
password = dict ( required = True , type = ' str ' , no_log = True ) ,
state = dict (
required = False ,
choices = [
' powered_on ' ,
' powered_off ' ,
' present ' ,
' absent ' ,
' restarted ' ,
' reconfigured '
] ,
default = ' present ' ) ,
vmware_guest_facts = dict ( required = False , type = ' bool ' ) ,
from_template = dict ( required = False , type = ' bool ' ) ,
template_src = dict ( required = False , type = ' str ' ) ,
snapshot_to_clone = dict ( required = False , default = None , type = ' str ' ) ,
guest = dict ( required = True , type = ' str ' ) ,
vm_disk = dict ( required = False , type = ' dict ' , default = { } ) ,
vm_nic = dict ( required = False , type = ' dict ' , default = { } ) ,
vm_hardware = dict ( required = False , type = ' dict ' , default = { } ) ,
vm_extra_config = dict ( required = False , type = ' dict ' , default = { } ) ,
vm_hw_version = dict ( required = False , default = None , type = ' str ' ) ,
resource_pool = dict ( required = False , default = None , type = ' str ' ) ,
cluster = dict ( required = False , default = None , type = ' str ' ) ,
force = dict ( required = False , type = ' bool ' , default = False ) ,
esxi = dict ( required = False , type = ' dict ' , default = { } ) ,
validate_certs = dict ( required = False , type = ' bool ' , default = True ) ,
power_on_after_clone = dict ( required = False , type = ' bool ' , default = True )
) ,
supports_check_mode = False ,
mutually_exclusive = [ [ ' state ' , ' vmware_guest_facts ' ] , [ ' state ' , ' from_template ' ] ] ,
required_together = [
[ ' state ' , ' force ' ] ,
[
' state ' ,
' vm_disk ' ,
' vm_nic ' ,
' vm_hardware ' ,
' esxi '
] ,
[ ' from_template ' , ' template_src ' ] ,
] ,
)
if not HAS_PYSPHERE :
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 ' ]
vm_disk = module . params [ ' vm_disk ' ]
vm_nic = module . params [ ' vm_nic ' ]
vm_hardware = module . params [ ' vm_hardware ' ]
vm_extra_config = module . params [ ' vm_extra_config ' ]
vm_hw_version = module . params [ ' vm_hw_version ' ]
esxi = module . params [ ' esxi ' ]
resource_pool = module . params [ ' resource_pool ' ]
cluster = module . params [ ' cluster ' ]
template_src = module . params [ ' template_src ' ]
from_template = module . params [ ' from_template ' ]
snapshot_to_clone = module . params [ ' snapshot_to_clone ' ]
power_on_after_clone = module . params [ ' power_on_after_clone ' ]
validate_certs = module . params [ ' validate_certs ' ]
# CONNECT TO THE SERVER
viserver = VIServer ( )
if validate_certs and not hasattr ( ssl , ' SSLContext ' ) and not vcenter_hostname . startswith ( ' http:// ' ) :
module . fail_json ( msg = ' pysphere does not support verifying certificates with python < 2.7.9. Either update python or set validate_certs=False on the task ' )
try :
viserver . connect ( vcenter_hostname , username , password )
except ssl . SSLError as sslerr :
if ' [SSL: CERTIFICATE_VERIFY_FAILED] ' in sslerr . strerror :
if not validate_certs :
default_context = ssl . _create_default_https_context
ssl . _create_default_https_context = ssl . _create_unverified_context
viserver . connect ( vcenter_hostname , username , password )
else :
module . fail_json ( msg = ' Unable to validate the certificate of the vcenter host %s ' % vcenter_hostname )
else :
raise
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 )
except Exception :
pass
if vm :
# 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 ' :
module . exit_json ( changed = False )
# Fail on reconfig without params
elif state == ' reconfigured ' :
reconfigure_vm (
vsphere_client = viserver ,
vm = vm ,
module = module ,
esxi = esxi ,
resource_pool = resource_pool ,
cluster_name = cluster ,
guest = guest ,
vm_extra_config = vm_extra_config ,
vm_hardware = vm_hardware ,
vm_disk = vm_disk ,
vm_nic = vm_nic ,
state = state ,
force = force
)
elif state == ' absent ' :
delete_vm (
vsphere_client = viserver ,
module = module ,
guest = guest ,
vm = vm ,
force = force )
# VM doesn't exist
else :
# Fail for fact gather task
if vmware_guest_facts :
module . fail_json (
msg = " No such VM %s . Fact gathering requires an existing vm "
% guest )
elif from_template :
deploy_template (
vsphere_client = viserver ,
esxi = esxi ,
resource_pool = resource_pool ,
guest = guest ,
template_src = template_src ,
module = module ,
cluster_name = cluster ,
snapshot_to_clone = snapshot_to_clone ,
power_on_after_clone = power_on_after_clone ,
vm_extra_config = vm_extra_config
)
if state in [ ' restarted ' , ' reconfigured ' ] :
module . fail_json (
msg = " No such VM %s . States [ "
" 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 in [ ' present ' , ' powered_off ' , ' powered_on ' ] :
# Check the guest_config
config_check ( " vm_disk " , vm_disk , proto_vm_disk , module )
config_check ( " vm_nic " , vm_nic , proto_vm_nic , module )
config_check ( " vm_hardware " , vm_hardware , proto_vm_hardware , module )
config_check ( " esxi " , esxi , proto_esxi , module )
create_vm (
vsphere_client = viserver ,
module = module ,
esxi = esxi ,
resource_pool = resource_pool ,
cluster_name = cluster ,
guest = guest ,
vm_extra_config = vm_extra_config ,
vm_hardware = vm_hardware ,
vm_disk = vm_disk ,
vm_nic = vm_nic ,
vm_hw_version = vm_hw_version ,
state = state
)
viserver . disconnect ( )
module . exit_json (
changed = False ,
vcenter = vcenter_hostname )
# this is magic, see lib/ansible/module_common.py
from ansible . module_utils . basic import *
if __name__ == ' __main__ ' :
main ( )