Class: Fog::Compute::Vsphere::Real

Inherits:
Object
  • Object
show all
Includes:
Shared
Defined in:
lib/fog/vsphere/compute.rb,
lib/fog/vsphere/requests/compute/vm_clone.rb,
lib/fog/vsphere/requests/compute/vm_reboot.rb,
lib/fog/vsphere/requests/compute/create_vm.rb,
lib/fog/vsphere/requests/compute/vm_migrate.rb,
lib/fog/vsphere/requests/compute/get_folder.rb,
lib/fog/vsphere/requests/compute/vm_destroy.rb,
lib/fog/vsphere/requests/compute/vm_execute.rb,
lib/fog/vsphere/requests/compute/get_network.rb,
lib/fog/vsphere/requests/compute/vm_power_on.rb,
lib/fog/vsphere/requests/compute/get_cluster.rb,
lib/fog/vsphere/requests/compute/get_template.rb,
lib/fog/vsphere/requests/compute/list_folders.rb,
lib/fog/vsphere/requests/compute/vm_power_off.rb,
lib/fog/vsphere/requests/compute/current_time.rb,
lib/fog/vsphere/requests/compute/list_networks.rb,
lib/fog/vsphere/requests/compute/list_clusters.rb,
lib/fog/vsphere/requests/compute/create_folder.rb,
lib/fog/vsphere/requests/compute/get_datastore.rb,
lib/fog/vsphere/requests/compute/vm_config_vnc.rb,
lib/fog/vsphere/requests/compute/list_templates.rb,
lib/fog/vsphere/requests/compute/get_datacenter.rb,
lib/fog/vsphere/requests/compute/get_server_type.rb,
lib/fog/vsphere/requests/compute/list_datastores.rb,
lib/fog/vsphere/requests/compute/list_vm_volumes.rb,
lib/fog/vsphere/requests/compute/vm_reconfig_cpus.rb,
lib/fog/vsphere/requests/compute/list_datacenters.rb,
lib/fog/vsphere/requests/compute/modify_vm_volume.rb,
lib/fog/vsphere/requests/compute/get_resource_pool.rb,
lib/fog/vsphere/requests/compute/list_server_types.rb,
lib/fog/vsphere/requests/compute/list_customfields.rb,
lib/fog/vsphere/requests/compute/list_vm_interfaces.rb,
lib/fog/vsphere/requests/compute/set_vm_customvalue.rb,
lib/fog/vsphere/requests/compute/get_interface_type.rb,
lib/fog/vsphere/requests/compute/vm_reconfig_memory.rb,
lib/fog/vsphere/requests/compute/list_resource_pools.rb,
lib/fog/vsphere/requests/compute/get_virtual_machine.rb,
lib/fog/vsphere/requests/compute/modify_vm_interface.rb,
lib/fog/vsphere/requests/compute/list_interface_types.rb,
lib/fog/vsphere/requests/compute/vm_reconfig_hardware.rb,
lib/fog/vsphere/requests/compute/get_compute_resource.rb,
lib/fog/vsphere/requests/compute/list_vm_customvalues.rb,
lib/fog/vsphere/requests/compute/list_virtual_machines.rb,
lib/fog/vsphere/requests/compute/list_compute_resources.rb,
lib/fog/vsphere/requests/compute/cloudinit_to_customspec.rb,
lib/fog/vsphere/requests/compute/get_vm_first_scsi_controller.rb

Instance Attribute Summary

Attributes included from Shared

#vsphere_is_vcenter, #vsphere_rev, #vsphere_server, #vsphere_username

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Real

Returns a new instance of Real


379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/fog/vsphere/compute.rb', line 379

def initialize(options={})
  require 'rbvmomi'
  @vsphere_username = options[:vsphere_username]
  @vsphere_password = options[:vsphere_password]
  @vsphere_server   = options[:vsphere_server]
  @vsphere_port     = options[:vsphere_port] || 443
  @vsphere_path     = options[:vsphere_path] || '/sdk'
  @vsphere_ns       = options[:vsphere_ns] || 'urn:vim25'
  @vsphere_rev      = options[:vsphere_rev] || '4.0'
  @vsphere_ssl      = options[:vsphere_ssl] || true
  @vsphere_debug    = options[:vsphere_debug] || false
  @vsphere_expected_pubkey_hash = options[:vsphere_expected_pubkey_hash]
  @vsphere_must_reauthenticate = false
  @vsphere_is_vcenter = nil
  @connection = nil
  connect
  negotiate_revision(options[:vsphere_rev])
  authenticate
end

Instance Method Details

#add_vm_interface(vmid, options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
# File 'lib/fog/vsphere/requests/compute/modify_vm_interface.rb', line 5

def add_vm_interface(vmid, options = {})
  raise ArgumentError, "instance id is a required parameter" unless vmid

  interface = get_interface_from_options(vmid, options.merge(:server_id => vmid))
  vm_reconfig_hardware('instance_uuid' => vmid, 'hardware_spec' => {'deviceChange'=>[create_interface(interface, 0, :add, options)]})
end

#add_vm_volume(volume) ⇒ Object


5
6
7
# File 'lib/fog/vsphere/requests/compute/modify_vm_volume.rb', line 5

def add_vm_volume(volume)
  vm_reconfig_hardware('instance_uuid' => volume.server_id, 'hardware_spec' => {'deviceChange'=>[create_disk(volume, volume.unit_number, :add)]})
end

#cloudinit_to_customspec(user_data) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/fog/vsphere/requests/compute/cloudinit_to_customspec.rb', line 5

def cloudinit_to_customspec(user_data)
  raise ArgumentError, "user_data can't be nil" if user_data.nil?
  custom_spec = { 'customization_spec' => Hash.new }
  user_data = YAML.load(user_data)
  # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Specification.html
  # encryptionKey expects an array
  # globalIPSettings expects a hash, REQUIRED
  # identity expects an hash, REQUIRED
  # nicSettingMap expects an array
  # options expects an hash

  custom_spec['encryptionKey']    = user_data['encryptionKey'] if user_data.key?('encryptionKey')
  custom_spec['globalIPSettings'] = user_data['globalIPSettings'] if user_data.key?('globalIPSettings')
  custom_spec['identity']         = user_data['identity'] if user_data.key?('identity')
  custom_spec['nicSettingMap']    = user_data['nicSettingMap'] if user_data.key?('nicSettingMap')
  custom_spec['options']          = user_data['options'] if user_data.key?('options')
  
  # for backwards compatability
  # hostname expects a string, REQUIRED
  # netmask expects a string
  # dns expects an array
  # gateway expects an array
  # domain expects a string, REQUIRED
  # domainsuffixlist expects an array, REQUIRED
  # timezone expects a string, for example Europe/Copenhagen, REQUIRED
  custom_spec['hostname']                    =  user_data['hostname'] if user_data.key?('hostname')
  custom_spec['ipsettings']                  =  { 'ip' => user_data['ip'] } if user_data.key?('ip')
  custom_spec['ipsettings']['subnetMask']    =  user_data['netmask'] if user_data.key?('netmask')
  custom_spec['ipsettings']['dnsServerList'] =  user_data['dns'] if user_data.key?('dns')
  custom_spec['ipsettings']['gateway']       =  user_data['gateway'] if user_data.key?('gateway')
  custom_spec['domain']                      =  user_data['domain'] if user_data.key?('domain')
  custom_spec['dnsSuffixList']               =  user_data['domainsuffixlist'] if user_data.key?('domainsuffixlist')
  custom_spec['time_zone']                   =  user_data['timezone'] if user_data.key?('timezone')
  custom_spec  
end

#create_folder(datacenter, path, name) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/fog/vsphere/requests/compute/create_folder.rb', line 5

def create_folder(datacenter, path, name)
  #Path cannot be nil but it can be an empty string
  raise ArgumentError, "Path cannot be nil" if path.nil?

  parent_folder = get_raw_vmfolder(path, datacenter)
  begin
    new_folder = parent_folder.CreateFolder(:name => name)
    # output is cleaned up to return the new path
    # new path will be path/name, example: "Production/Pool1"
    new_folder.path.reject { |a| a.first.class == "Folder" }.map { |a| a.first.name }.join("/").sub(/^\/?Datacenters\/#{datacenter}\/vm\/?/, '')
  rescue => e
    raise e, "failed to create folder: #{e}"
  end
end

#create_vm(attributes = { }) ⇒ Object


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/fog/vsphere/requests/compute/create_vm.rb', line 5

def create_vm attributes = { }
  # build up vm configuration

  vm_cfg        = {
    :name         => attributes[:name],
    :guestId      => attributes[:guest_id],
    :version      => attributes[:hardware_version],
    :files        => { :vmPathName => vm_path_name(attributes) },
    :numCPUs      => attributes[:cpus],
    :numCoresPerSocket => attributes[:corespersocket],
    :memoryMB     => attributes[:memory_mb],
    :deviceChange => device_change(attributes),
    :extraConfig  => extra_config(attributes),
  }
  vm_cfg[:cpuHotAddEnabled] = attributes[:cpuHotAddEnabled] if attributes.key?(:cpuHotAddEnabled)
  vm_cfg[:memoryHotAddEnabled] = attributes[:memoryHotAddEnabled] if attributes.key?(:memoryHotAddEnabled)
  vm_cfg[:firmware] = attributes[:firmware] if attributes.key?(:firmware)
  resource_pool = if attributes[:resource_pool]
                    get_raw_resource_pool(attributes[:resource_pool], attributes[:cluster], attributes[:datacenter])
                  else
                    get_raw_cluster(attributes[:cluster], attributes[:datacenter]).resourcePool
                  end
  vmFolder      = get_raw_vmfolder(attributes[:path], attributes[:datacenter])
  vm            = vmFolder.CreateVM_Task(:config => vm_cfg, :pool => resource_pool).wait_for_completion
  vm.config.instanceUuid
rescue => e
  raise e, "failed to create vm: #{e}"
end

#current_timeObject


5
6
7
8
# File 'lib/fog/vsphere/requests/compute/current_time.rb', line 5

def current_time
  current_time = @connection.serviceInstance.CurrentTime
  { 'current_time' => current_time }
end

#destroy_vm_interface(vmid, options = {}) ⇒ Object

Raises:

  • (ArgumentError)

12
13
14
15
16
17
# File 'lib/fog/vsphere/requests/compute/modify_vm_interface.rb', line 12

def destroy_vm_interface(vmid, options = {})
  raise ArgumentError, "instance id is a required parameter" unless vmid

  interface = get_interface_from_options(vmid, options.merge(:server_id => vmid))
  vm_reconfig_hardware('instance_uuid' => vmid, 'hardware_spec' => {'deviceChange'=>[create_interface(interface, interface.key, :remove)]})
end

#destroy_vm_volume(volume) ⇒ Object


9
10
11
# File 'lib/fog/vsphere/requests/compute/modify_vm_volume.rb', line 9

def destroy_vm_volume(volume)
  vm_reconfig_hardware('instance_uuid' => volume.server_id, 'hardware_spec' => {'deviceChange'=>[create_disk(volume, volume.unit_number, :remove)]})
end

#get_cluster(name, datacenter_name) ⇒ Object

Raises:

  • (Fog::Compute::Vsphere::NotFound)

5
6
7
8
9
# File 'lib/fog/vsphere/requests/compute/get_cluster.rb', line 5

def get_cluster(name, datacenter_name)
  cluster = get_raw_cluster(name, datacenter_name)
  raise(Fog::Compute::Vsphere::NotFound) unless cluster
  cluster_attributes(cluster, datacenter_name)
end

#get_compute_resource(name, datacenter_name) ⇒ Object

Raises:

  • (Fog::Compute::Vsphere::NotFound)

5
6
7
8
9
# File 'lib/fog/vsphere/requests/compute/get_compute_resource.rb', line 5

def get_compute_resource(name, datacenter_name)
  compute_resource = get_raw_compute_resource(name, datacenter_name)
  raise(Fog::Compute::Vsphere::NotFound) unless compute_resource
  compute_resource_attributes(compute_resource, datacenter_name)
end

#get_datacenter(name) ⇒ Object

Raises:

  • (Fog::Compute::Vsphere::NotFound)

5
6
7
8
9
# File 'lib/fog/vsphere/requests/compute/get_datacenter.rb', line 5

def get_datacenter name
  dc = find_raw_datacenter(name)
  raise(Fog::Compute::Vsphere::NotFound) unless dc
  {:name => dc.name, :status => dc.overallStatus, :path => raw_getpathmo(dc) }
end

#get_datastore(name, datacenter_name) ⇒ Object

Raises:

  • (Fog::Compute::Vsphere::NotFound)

5
6
7
8
9
# File 'lib/fog/vsphere/requests/compute/get_datastore.rb', line 5

def get_datastore(name, datacenter_name)
  datastore = get_raw_datastore(name, datacenter_name)
  raise(Fog::Compute::Vsphere::NotFound) unless datastore
  datastore_attributes(datastore, datacenter_name)
end

#get_folder(path, datacenter_name, type = nil) ⇒ Object


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/fog/vsphere/requests/compute/get_folder.rb', line 5

def get_folder(path, datacenter_name, type = nil)
  type ||= 'vm'

  # Cycle through all types of folders.
  case type
    when 'vm', :vm
      # if you're a vm then grab the VM.
      folder = get_raw_vmfolder(path, datacenter_name)
      raise(Fog::Compute::Vsphere::NotFound) unless folder
      folder_attributes(folder, datacenter_name)
    when 'network', :network
      raise "not implemented"
    when 'datastore', :datastore
      raise "not implemented"
    else
      raise ArgumentError, "#{type} is unknown"
  end
end

#get_interface_type(id, servertype, datacenter, filter = {}) ⇒ Object

Raises:

  • (Fog::Compute::Vsphere::NotFound)

5
6
7
8
9
10
11
# File 'lib/fog/vsphere/requests/compute/get_interface_type.rb', line 5

def get_interface_type(id, servertype, datacenter, filter={})
   interfacetype=list_interface_types(filters={:id => id,
     :datacenter => datacenter,
     :servertype => servertype.id }).first
   raise(Fog::Compute::Vsphere::NotFound) unless interfacetype
   interfacetype
end

#get_network(name, datacenter_name) ⇒ Object

Raises:

  • (Fog::Compute::Vsphere::NotFound)

5
6
7
8
9
# File 'lib/fog/vsphere/requests/compute/get_network.rb', line 5

def get_network(name, datacenter_name)
  network = get_raw_network(name, datacenter_name)
  raise(Fog::Compute::Vsphere::NotFound) unless network
  network_attributes(network, datacenter_name)
end

#get_resource_pool(name, cluster_name, datacenter_name) ⇒ Object

Raises:

  • (Fog::Compute::Vsphere::NotFound)

5
6
7
8
9
# File 'lib/fog/vsphere/requests/compute/get_resource_pool.rb', line 5

def get_resource_pool(name, cluster_name, datacenter_name)
  resource_pool = get_raw_resource_pool(name, cluster_name, datacenter_name)
  raise(Fog::Compute::Vsphere::NotFound) unless resource_pool
  resource_pool_attributes(resource_pool, cluster_name, datacenter_name)
end

#get_server_type(id, datacenter, filter = {}) ⇒ Object

Raises:

  • (Fog::Compute::Vsphere::NotFound)

5
6
7
8
9
# File 'lib/fog/vsphere/requests/compute/get_server_type.rb', line 5

def get_server_type(id, datacenter, filter={})
  server_type=get_raw_server_type(id, datacenter)
  raise(Fog::Compute::Vsphere::NotFound) unless server_type
  server_type_attributes(server_type, datacenter)
end

#get_template(id, datacenter_name = nil) ⇒ Object


5
6
7
# File 'lib/fog/vsphere/requests/compute/get_template.rb', line 5

def get_template(id, datacenter_name = nil)
  convert_vm_mob_ref_to_attr_hash(get_vm_ref(id, datacenter_name))
end

#get_virtual_machine(id, datacenter_name = nil) ⇒ Object


5
6
7
8
# File 'lib/fog/vsphere/requests/compute/get_virtual_machine.rb', line 5

def get_virtual_machine(id, datacenter_name = nil)
  # The larger the VM list the longer it will take if not searching based on UUID.
  convert_vm_mob_ref_to_attr_hash(get_vm_ref(id, datacenter_name))
end

#get_vm_first_scsi_controller(vm_id) ⇒ Object


6
7
8
# File 'lib/fog/vsphere/requests/compute/get_vm_first_scsi_controller.rb', line 6

def get_vm_first_scsi_controller(vm_id)
  Fog::Compute::Vsphere::SCSIController.new(get_vm_first_scsi_controller_raw(vm_id))
end

#get_vm_first_scsi_controller_raw(vm_id) ⇒ Object


10
11
12
13
14
15
16
17
18
# File 'lib/fog/vsphere/requests/compute/get_vm_first_scsi_controller.rb', line 10

def get_vm_first_scsi_controller_raw(vm_id)
  ctrl=get_vm_ref(vm_id).config.hardware.device.grep(RbVmomi::VIM::VirtualSCSIController).select{ | ctrl | ctrl.key == 1000 }.first
  {
    :type    => ctrl.class.to_s,
    :shared_bus  => ctrl.sharedBus.to_s,
    :unit_number => ctrl.scsiCtlrUnitNumber,
    :key => ctrl.key,
  }
end

#get_vm_interface(vm_id, options = {}) ⇒ Object

Raises:

  • (ArgumentError)

45
46
47
48
49
50
51
52
53
54
55
# File 'lib/fog/vsphere/requests/compute/list_vm_interfaces.rb', line 45

def get_vm_interface(vm_id, options={})
  raise ArgumentError, "instance id is a required parameter" unless vm_id
  if options.is_a? Fog::Compute::Vsphere::Interface
    options
  else
    raise ArgumentError, "Either key or name is a required parameter. options: #{options}" unless options.key? :key or options.key? :mac or options.key? :name
    list_vm_interfaces(vm_id).find do | nic |
      (options.key? :key and nic[:key]==options[:key].to_i) or (options.key? :mac and nic[:mac]==options[:mac]) or (options.key? :name and nic[:name]==options[:name])
    end
  end
end

#interface_type_attributes(nic, servertype, datacenter) ⇒ Object


14
15
16
17
18
19
20
21
# File 'lib/fog/vsphere/requests/compute/list_interface_types.rb', line 14

def interface_type_attributes(nic, servertype, datacenter)
  {
    :id => nic,
    :name => nic,
    :datacenter => datacenter,
    :servertype => servertype
  }
end

#list_clusters(filters = { }) ⇒ Object


5
6
7
8
9
10
11
# File 'lib/fog/vsphere/requests/compute/list_clusters.rb', line 5

def list_clusters(filters = { })
  datacenter_name = filters[:datacenter]

  raw_clusters(datacenter_name).map do |cluster|
    cluster_attributes(cluster, datacenter_name)
  end
end

#list_compute_resources(filters = { }) ⇒ Object


5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/fog/vsphere/requests/compute/list_compute_resources.rb', line 5

def list_compute_resources(filters = { })
  datacenter_name = filters[:datacenter]
  # default to show all compute_resources
  only_active = filters[:effective] || false
  compute_resources = raw_compute_resources datacenter_name
  
  compute_resources.map do |compute_resource|
    summary = compute_resource.summary
    next if only_active and summary.numEffectiveHosts == 0
    compute_resource_attributes(compute_resource, datacenter_name)
  end.compact
end

#list_customfieldsObject


5
6
7
8
9
10
11
12
13
# File 'lib/fog/vsphere/requests/compute/list_customfields.rb', line 5

def list_customfields()
  @connection.serviceContent.customFieldsManager.field.map do |customfield|
    {
      :key   => customfield.key.to_i,
      :name  => customfield.name,
      :type  => customfield.type
    }
  end
end

#list_datacenters(filters = {}) ⇒ Object


5
6
7
8
9
10
11
12
13
14
# File 'lib/fog/vsphere/requests/compute/list_datacenters.rb', line 5

def list_datacenters filters = {}
  raw_datacenters.map do |dc|
    {
      :id => managed_obj_id(dc),
      :name => dc.name,
      :path => raw_getpathmo(dc),
      :status => dc.overallStatus
    }
  end
end

#list_datastores(filters = { }) ⇒ Object


5
6
7
8
9
10
11
12
13
# File 'lib/fog/vsphere/requests/compute/list_datastores.rb', line 5

def list_datastores(filters = { })
  datacenter_name = filters[:datacenter]
  # default to show all datastores
  only_active = filters[:accessible] || false
  raw_datastores(datacenter_name).map do |datastore|
    next if only_active and !datastore.summary.accessible
    datastore_attributes(datastore, datacenter_name)
  end.compact
end

#list_folders(filters = { }) ⇒ Object

Grabs all sub folders within a given path folder.

Parameters

  • filters<~Hash>:

    • :datacenter<~String> - REQUIRED Your datacenter where you're looking for folders. Example: 'my-datacenter-name' (passed if you are using the models/collections)

      eg: vspconn.datacenters.first.vm_folders('mypath')
      
    • :path<~String> - Your path where you're looking for more folders, if return = none you will get an error. If you don't define it will look in the main datacenter folder for any folders in that datacenter.

Example Usage Testing Only:

vspconn = Fog::Compute[:vsphere]
mydc = vspconn.datacenters.first
folders = mydc.vm_folders

23
24
25
26
27
28
29
# File 'lib/fog/vsphere/requests/compute/list_folders.rb', line 23

def list_folders(filters = { })
  path            = filters[:path] || filters['path'] || ''
  datacenter_name = filters[:datacenter]
  get_raw_vmfolders(path, datacenter_name).map do |folder|
    folder_attributes(folder, datacenter_name)
  end
end

#list_interface_types(filters = {}) ⇒ Object


5
6
7
8
9
10
11
12
# File 'lib/fog/vsphere/requests/compute/list_interface_types.rb', line 5

def list_interface_types(filters={})
  datacenter_name = filters[:datacenter]
  servertype_name = filters[:servertype]
  get_raw_server_type(servertype_name, datacenter_name)[:supportedEthernetCard].map do | nictype |
    next if filters.key?(:id) and filters[:id] != nictype
    interface_type_attributes(nictype, servertype_name, datacenter_name)
  end.compact
end

#list_networks(filters = { }) ⇒ Object


5
6
7
8
9
10
11
12
13
# File 'lib/fog/vsphere/requests/compute/list_networks.rb', line 5

def list_networks(filters = { })
  datacenter_name = filters[:datacenter]
  # default to show all networks
  only_active = filters[:accessible] || false
  raw_networks(datacenter_name).map do |network|
    next if only_active and !network.summary.accessible
    network_attributes(network, datacenter_name)
  end.compact
end

#list_resource_pools(filters = { }) ⇒ Object


5
6
7
8
9
10
11
12
# File 'lib/fog/vsphere/requests/compute/list_resource_pools.rb', line 5

def list_resource_pools(filters = { })
  datacenter_name = filters[:datacenter]
  cluster_name    = filters[:cluster]
  cluster         = get_raw_cluster(cluster_name, datacenter_name)
  list_raw_resource_pools(cluster).map do |resource_pool|
    resource_pool_attributes(resource_pool, cluster_name, datacenter_name)
  end
end

#list_server_types(filters = {}) ⇒ Object


5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/fog/vsphere/requests/compute/list_server_types.rb', line 5

def list_server_types(filters={})
  datacenter_name = filters[:datacenter]
  servertypes=raw_server_types(datacenter_name)
  if servertypes
    servertypes.map do | servertype |
       server_type_attributes(servertype, datacenter_name)
    end.compact
  else
    nil
  end
  #select{ | guestdesc | guestdesc.select{ | k, v | filter.has_key?(k) and filter[k] == v }==filter }
end

#list_templates(options = { }) ⇒ Object


5
6
7
8
9
10
11
12
# File 'lib/fog/vsphere/requests/compute/list_templates.rb', line 5

def list_templates(options = { })
  options[:folder] ||= options['folder']
  if options[:folder] then
    list_all_templates_in_folder(options[:folder], options[:datacenter])
  else
    list_all_templates(options)
  end
end

#list_virtual_machines(options = { }) ⇒ Object


5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/fog/vsphere/requests/compute/list_virtual_machines.rb', line 5

def list_virtual_machines(options = { })
  # Listing all VM's can be quite slow and expensive.  Try and optimize
  # based on the available options we have.  These conditions are in
  # ascending order of time to complete for large deployments.

  options[:folder] ||= options['folder']
  if options['instance_uuid'] then
    [get_virtual_machine(options['instance_uuid'])]
  elsif options[:folder] && options[:datacenter] then
    list_all_virtual_machines_in_folder(options[:folder], options[:datacenter])
  else
    list_all_virtual_machines(options)
  end
end

#list_vm_customvalues(vm_id) ⇒ Object


5
6
7
8
9
10
11
12
# File 'lib/fog/vsphere/requests/compute/list_vm_customvalues.rb', line 5

def list_vm_customvalues(vm_id)
  get_vm_ref(vm_id).summary.customValue.map do |customvalue|
    {
      :key    => customvalue.key.to_i,
      :value  => customvalue.value,
    }
  end
end

#list_vm_interfaces(vm_id, datacenter = nil) ⇒ Object

> VirtualE1000(

addressType: “assigned”, backing: VirtualEthernetCardNetworkBackingInfo(

deviceName: "VM Network",
dynamicProperty: [],
network: Network("network-163"),
useAutoDetect: false

), connectable: VirtualDeviceConnectInfo(

allowGuestControl: true,
connected: true,
dynamicProperty: [],
startConnected: true,
status: "ok"

), controllerKey: 100, deviceInfo: Description(

dynamicProperty: [],
label: "Network adapter 1",
summary: "VM Network"

), dynamicProperty: [], key: 4000, macAddress: “00:50:56:a9:00:28”, unitNumber: 7,


31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/fog/vsphere/requests/compute/list_vm_interfaces.rb', line 31

def list_vm_interfaces(vm_id, datacenter = nil)
  get_vm_ref(vm_id, datacenter).config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).map do |nic|
    {
      :name    => nic.deviceInfo.label,
      :mac     => nic.macAddress,
      :network => nic.backing.respond_to?("network") ? nic.backing.network.name : nic.backing.port.portgroupKey,
      :status  => nic.connectable.status,
      :summary => nic.deviceInfo.summary,
      :type    => nic.class,
      :key     => nic.key,
    }
  end
end

#list_vm_volumes(vm_id) ⇒ Object

[VirtualDisk(

backing: VirtualDiskFlatVer2BackingInfo(
  contentId: "a172d19487e878e17d6b16ff2505d7eb",
  datastore: Datastore("datastore-162"),
  diskMode: "persistent",
  dynamicProperty: [],
  fileName: "[Storage1] rhel6-mfojtik/rhel6-mfojtik.vmdk",
  split: false,
  thinProvisioned: true,
  uuid: "6000C29c-a47d-4cd9-5249-c371de775f06",
  writeThrough: false
),
capacityInKB: 8388608,
controllerKey: 1000,
deviceInfo: Description(
  dynamicProperty: [],
  label: "Hard disk 1",
  summary: "8,388,608 KB"
),
dynamicProperty: [],
key: 2001,
shares: SharesInfo( dynamicProperty: [], level: "normal", shares: 1000 ),
unitNumber: 1

)]


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/fog/vsphere/requests/compute/list_vm_volumes.rb', line 30

def list_vm_volumes(vm_id)
  get_vm_ref(vm_id).disks.map do |vol|
    {
      :id => vol.backing.uuid,
      :thin => (vol.backing.thinProvisioned rescue(nil)),
      :mode => vol.backing.diskMode,
      :filename => vol.backing.fileName,
      :datastore => (vol.backing.datastore.name rescue(nil)),
      :size => vol.capacityInKB,
      :name => vol.deviceInfo.label,
      :key => vol.key,
      :unit_number => vol.unitNumber
    }
  end
end

#modify_template_nics_simple_spec(network_label, nic_type, network_adapter_device_key, datacenter) ⇒ Object

Build up the network config spec for simple case: simple case: apply just the network_label, nic_type and network_adapter_device_key


622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/fog/vsphere/requests/compute/vm_clone.rb', line 622

def modify_template_nics_simple_spec(network_label, nic_type, network_adapter_device_key, datacenter)
    config_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit')
    # Get the portgroup and handle it from there.
    network = get_raw_network(network_label, datacenter)
    if ( network.kind_of? RbVmomi::VIM::DistributedVirtualPortgroup)
        # Create the NIC backing for the distributed virtual portgroup
        nic_backing_info = RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo(
            :port => RbVmomi::VIM::DistributedVirtualSwitchPortConnection( 
                                                                          :portgroupKey => network.key,
                                                                          :switchUuid => network.config.distributedVirtualSwitch.uuid
                                                                         ) 
        )
    else
        # Otherwise it's a non distributed port group
        nic_backing_info = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(:deviceName => network_label)
    end
    connectable = RbVmomi::VIM::VirtualDeviceConnectInfo(
      :allowGuestControl => true,
      :connected => true,
      :startConnected => true)
    device = RbVmomi::VIM.public_send "#{nic_type}",
      :backing => nic_backing_info,
      :deviceInfo => RbVmomi::VIM::Description(:label => "Network adapter 1", :summary => network_label),
      :key => network_adapter_device_key,
      :connectable => connectable
    device_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
      :operation => config_spec_operation,
      :device => device)
    return device_spec
end

#modify_template_nics_specs(template_path, new_nics, datacenter) ⇒ Object


654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
# File 'lib/fog/vsphere/requests/compute/vm_clone.rb', line 654

def modify_template_nics_specs(template_path, new_nics, datacenter)
  #new_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('new')
  #remove_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('remove')

  template_nics = list_vm_interfaces(template_path, datacenter).map do |old_attributes|
    Fog::Compute::Vsphere::Interface.new(old_attributes)
  end
  specs = []

  template_nics.each do |interface|
    specs << create_interface(interface, interface.key, :remove)
  end

  new_nics.each do |interface|
    specs << create_interface(interface, 0, :add)
  end

  return specs
end

#modify_template_volumes_specs(vm_mob_ref, volumes) ⇒ Object


674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
# File 'lib/fog/vsphere/requests/compute/vm_clone.rb', line 674

def modify_template_volumes_specs(vm_mob_ref, volumes)
  template_volumes = vm_mob_ref.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
  modified_volumes = volumes.take(template_volumes.size)
  new_volumes      = volumes.drop(template_volumes.size)

  specs = []
  template_volumes.zip(modified_volumes).each do |template_volume, new_volume|
    if new_volume
      # updated the attribtues on the existing volume
      # it's not allowed to reduce the size of the volume when cloning
      if new_volume.size > template_volume.capacityInKB
        template_volume.capacityInKB = new_volume.size
      end
      template_volume.backing.diskMode = new_volume.mode
      template_volume.backing.thinProvisioned = new_volume.thin
      specs << { :operation => :edit, :device  => template_volume }
    else
      specs << { :operation => :remove,
                 :fileOperation => :destroy,
                 :device  => template_volume }
    end
  end
  specs.concat(new_volumes.map { |volume| create_disk(volume, volumes.index(volume)) })
  return specs
end

#raw_clusters(datacenter) ⇒ Object


13
14
15
16
# File 'lib/fog/vsphere/requests/compute/list_clusters.rb', line 13

def raw_clusters(datacenter)
  folder ||= find_raw_datacenter(datacenter).hostFolder
  @raw_clusters = get_raw_clusters_from_folder(folder)
end

#raw_compute_resources(datacenter_name) ⇒ Object


18
19
20
# File 'lib/fog/vsphere/requests/compute/list_compute_resources.rb', line 18

def raw_compute_resources(datacenter_name)
  find_raw_datacenter(datacenter_name).find_compute_resource('').children
end

#raw_datastores(datacenter_name) ⇒ Object


15
16
17
# File 'lib/fog/vsphere/requests/compute/list_datastores.rb', line 15

def raw_datastores(datacenter_name)
  find_raw_datacenter(datacenter_name).datastore
end

#raw_networks(datacenter_name) ⇒ Object


15
16
17
# File 'lib/fog/vsphere/requests/compute/list_networks.rb', line 15

def raw_networks(datacenter_name)
  find_raw_datacenter(datacenter_name).network
end

#raw_server_types(datacenter_name, filter = {}) ⇒ Object


18
19
20
21
22
23
24
# File 'lib/fog/vsphere/requests/compute/list_server_types.rb', line 18

def raw_server_types(datacenter_name, filter={})
  datacenter=find_raw_datacenter(datacenter_name)
  environmentBrowser=datacenter.hostFolder.childEntity.grep(RbVmomi::VIM::ComputeResource).first.environmentBrowser
  if environmentBrowser
    environmentBrowser.QueryConfigOption[:guestOSDescriptor]
  end
end

#reloadObject


399
400
401
402
403
404
405
406
# File 'lib/fog/vsphere/compute.rb', line 399

def reload
  connect
  # Check if the negotiation was ever run
  if @vsphere_is_vcenter.nil?
    negotiate
  end
  authenticate
end

#set_vm_customvalue(vm_id, key, value) ⇒ Object


5
6
7
8
# File 'lib/fog/vsphere/requests/compute/set_vm_customvalue.rb', line 5

def set_vm_customvalue(vm_id, key, value)
  vm_ref = get_vm_ref(vm_id)
  vm_ref.setCustomValue(:key => key, :value => value)
end

#update_vm_interface(vmid, options = {}) ⇒ Object

Raises:

  • (ArgumentError)

19
20
21
22
23
24
# File 'lib/fog/vsphere/requests/compute/modify_vm_interface.rb', line 19

def update_vm_interface(vmid, options = {})
  raise ArgumentError, "instance id is a required parameter" unless vmid

  interface = get_interface_from_options(vmid, options.merge(:server_id => vmid))
  vm_reconfig_hardware('instance_uuid' => vmid, 'hardware_spec' => {'deviceChange'=>[create_interface(interface, interface.key, :edit)]})
end

#vm_clone(options = {}) ⇒ Object

Clones a VM from a template or existing machine on your vSphere Server.

Parameters

  • options<~Hash>:

    • 'datacenter'<~String> - REQUIRED Datacenter name your cloning in. Make sure this datacenter exists, should if you're using the clone function in server.rb model.

    • 'template_path'<~String> - REQUIRED The path to the machine you want to clone FROM. Relative to Datacenter (Example: “FolderNameHere/VMNameHere”)

    • 'name'<~String> - REQUIRED The VMName of the Destination

    • 'dest_folder'<~String> - Destination Folder of where 'name' will be placed on your cluster. Relative Path to Datacenter E.G. “FolderPlaceHere/anotherSub Folder/onemore”

    • 'power_on'<~Boolean> - Whether to power on machine after clone. Defaults to true.

    • 'wait'<~Boolean> - Whether the method should wait for the virtual machine to finish cloning before returning information from vSphere. Broken right now as you cannot return a model of a serer that isn't finished cloning. Defaults to True

    • 'resource_pool'<~Array> - The resource pool on your datacenter cluster you want to use. Only works with clusters within same same datacenter as where you're cloning from. Datacenter grabbed from template_path option. Example: ['cluster_name_here','resource_pool_name_here']

    • 'datastore'<~String> - The datastore you'd like to use.

      (datacenterObj.datastoreFolder.find('name') in API)
      
    • 'transform'<~String> - Not documented - see www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.RelocateSpec.html

    • 'numCPUs'<~Integer> - the number of Virtual CPUs of the Destination VM

    • 'memoryMB'<~Integer> - the size of memory of the Destination VM in MB

    • customization_spec<~Hash>: Options are marked as required if you use this customization_spec. As defined pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Specification.html

      • encryptionKey <~array of bytes> Used to encrypt/decrypt password

      • globalIPSettings expects a hash, REQUIRED

      • identity expects a hash, REQUIRED - either LinuxPrep, Sysprep or SysprepText

      • nicSettingMap expects an array

      • options expects a hash

      • All options can be parsed using a yaml template with cloudinit_to_customspec.rb

      OLD Values still supported: This only support cloning and setting DHCP on the first interface

      • 'domain'<~String> - REQUIRED This is put into /etc/resolve.conf (we hope)

      • 'hostname'<~String> - Hostname of the Guest Os - default is options

      • 'hw_utc_clock'<~Boolean> - REQUIRED Is hardware clock UTC? Default true

      • 'time_zone'<~String> - REQUIRED Only valid linux options are valid - example: 'America/Denver'

      • 'interfaces' <~Array> - interfaces object to apply to

        the template when cloning: overrides the
        network_label, network_adapter_device_key and nic_type attributes
        
      • 'volumes' <~Array> - volumes object to apply to

        the template when cloning: this allows to resize the
        existing disks as well as add or remove them. The
        resizing is applied only when the size is bigger then the
        in size in the template
        

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/fog/vsphere/requests/compute/vm_clone.rb', line 88

def vm_clone(options = {})
  # Option handling
  options = vm_clone_check_options(options)

  # Added for people still using options['path']
  template_path = options['path'] || options['template_path']

  # Options['template_path']<~String>
  # Added for people still using options['path']
  template_path = options['path'] || options['template_path']
  # Now find the template itself using the efficient find method
  vm_mob_ref = get_vm_ref(template_path, options['datacenter'])

  # Options['dest_folder']<~String>
  # Grab the destination folder object if it exists else use cloned mach
  dest_folder_path = options.fetch('dest_folder','/') # default to root path ({dc_name}/vm/)
  dest_folder = get_raw_vmfolder(dest_folder_path, options['datacenter'])

  # Options['resource_pool']<~Array>
  # Now find _a_ resource pool to use for the clone if one is not specified
  if ( options.key?('resource_pool') && options['resource_pool'].is_a?(Array) && options['resource_pool'].length == 2 )
    cluster_name = options['resource_pool'][0]
    pool_name = options['resource_pool'][1]
    resource_pool = get_raw_resource_pool(pool_name, cluster_name, options['datacenter'])
  elsif ( vm_mob_ref.resourcePool == nil )
    # If the template is really a template then there is no associated resource pool,
    # so we need to find one using the template's parent host or cluster
    esx_host = vm_mob_ref.collect!('runtime.host')['runtime.host']
    # The parent of the ESX host itself is a ComputeResource which has a resourcePool
    resource_pool = esx_host.parent.resourcePool
  end
  # If the vm given did return a valid resource pool, default to using it for the clone.
  # Even if specific pools aren't implemented in this environment, we will still get back
  # at least the cluster or host we can pass on to the clone task
  # This catches if resource_pool option is set but comes back nil and if resourcePool is
  # already set.
  resource_pool ||= vm_mob_ref.resourcePool.nil? ? esx_host.parent.resourcePool : vm_mob_ref.resourcePool

  # Options['datastore']<~String>
  # Grab the datastore object if option is set
  datastore_obj = get_raw_datastore(options['datastore'], options['datacenter']) if options.key?('datastore')
  # confirm nil if nil or option is not set
  datastore_obj ||= nil
  virtual_machine_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec()

  device_change = []
  # fully futured interfaces api: replace the current nics
  # with the new based on the specification
  if (options.key?('interfaces') )
    if options.key?('network_label')
      raise ArgumentError, "interfaces option can't be specified together with network_label"
    end
    device_change.concat(modify_template_nics_specs(template_path, options['interfaces'], options['datacenter']))
  elsif options.key?('network_label')
    device_change << modify_template_nics_simple_spec(options['network_label'], options['nic_type'], options['network_adapter_device_key'], options['datacenter'])
  end
  if disks = options['volumes']
    device_change.concat(modify_template_volumes_specs(vm_mob_ref, options['volumes']))
  end
  virtual_machine_config_spec.deviceChange = device_change if device_change.any?
  # Options['numCPUs'] or Options['memoryMB']
  # Build up the specification for Hardware, for more details see ____________
  # https://github.com/rlane/rbvmomi/blob/master/test/test_serialization.rb
  # http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.ConfigSpec.html
  # FIXME: pad this out with the rest of the useful things in VirtualMachineConfigSpec
  virtual_machine_config_spec.numCPUs = options['numCPUs'] if  ( options.key?('numCPUs') )
  virtual_machine_config_spec.memoryMB = options['memoryMB'] if ( options.key?('memoryMB') )
  virtual_machine_config_spec.cpuHotAddEnabled = options['cpuHotAddEnabled'] if ( options.key?('cpuHotAddEnabled') )
  virtual_machine_config_spec.memoryHotAddEnabled = options['memoryHotAddEnabled'] if ( options.key?('memoryHotAddEnabled') )
  virtual_machine_config_spec.firmware = options['firmware'] if ( options.key?('firmware') )
  # Options['customization_spec']
  # OLD Options still supported
  # * domain <~String> - *REQUIRED* - Sets the server's domain for customization
  # * dnsSuffixList <~Array> - Optional - Sets the dns search paths in resolv - Example: ["dev.example.com", "example.com"]
  # * time_zone <~String> - Required - Only valid linux options are valid - example: 'America/Denver'
  # * ipsettings <~Hash> - Optional - If not set defaults to dhcp
  #  * ip <~String> - *REQUIRED* Sets the ip address of the VM - Example: 10.0.0.10
  #  * dnsServerList <~Array> - Optional - Sets the nameservers in resolv - Example: ["10.0.0.2", "10.0.0.3"]
  #  * gateway <~Array> - Optional - Sets the gateway for the interface - Example: ["10.0.0.1"]
  #  * subnetMask <~String> - *REQUIRED* - Set the netmask of the interface - Example: "255.255.255.0"
  #    For other ip settings options see http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.customization.IPSettings.html
  #
  #  Implement complete customization spec as per https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Specification.html
  #   * encryptionKey <~Array> - Optional, encryption key used to encypt any encrypted passwords
  #   https://pubs.vmware.com/vsphere-51/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.GlobalIPSettings.html
  #   * globalIPSettings <~Hash> - REQUIRED
  #   *   dnsServerList <~Array> - Optional, list of dns servers - Example: ["10.0.0.2", "10.0.0.3"]
  #   *   dnsSuffixList <~Array> - Optional, List of name resolution suffixes - Example: ["dev.example.com", "example.com"]
  #   * identity <~Hash> - REQUIRED, Network identity and settings, similar to Microsoft's Sysprep tool. This is a Sysprep, LinuxPrep, or SysprepText object
  #   *   Sysprep <~Hash> - Optional, representation of a Windows sysprep.inf answer file.
  #   *     guiRunOnce: <~Hash> -Optional, representation of the sysprep GuiRunOnce key
  #   *       commandList: <~Array> - REQUIRED, list of commands to run at first user logon, after guest customization. - Example: ["c:\sysprep\runaftersysprep.cmd", "c:\sysprep\installpuppet.ps1"]
  #   *     guiUnattended: <~Hash> - REQUIRED, representation of the sysprep GuiUnattended key
  #   *       autoLogin: boolean - REQUIRED, Flag to determine whether or not the machine automatically logs on as Administrator.
  #   *       autoLogonCount: int - REQUIRED, specifies the number of times the machine should automatically log on as Administrator
  #   *       password: <~Hash> - REQUIRED, new administrator password for the machine
  #   *         plainText: boolean - REQUIRED, specify whether or not the password is in plain text, rather than encrypted
  #   *         value: <~String> - REQUIRED, password string
  #   *       timeZone: <~int> - REQUIRED, (see here for values https://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx)    
  #   *     identification: <~Hash> - REQUIRED, representation of the sysprep Identification key
  #   *       domainAdmin: <~String> - Optional, domain user account used for authentication if the virtual machine is joining a domain
  #   *       domainAdminPassword: <~Hash> - Optional, password for the domain user account used for authentication 
  #   *         plainText: boolean - REQUIRED, specify whether or not the password is in plain text, rather than encrypted
  #   *         value: <~String> - REQUIRED, password string
  #   *       joinDomain: <~String> - Optional, The domain that the virtual machine should join. If this value is supplied, then domainAdmin and domainAdminPassword must also be supplied
  #   *       joinWorkgroup: <~String> - Optional, The workgroup that the virtual machine should join.
  #   *     licenseFilePrintData: <~Hash> - Optional, representation of the sysprep LicenseFilePrintData key
  #   *       autoMode: <~String> - REQUIRED, Server licensing mode. Two strings are supported: 'perSeat' or 'perServer'
  #   *       autoUsers: <~Int> - Optional, This key is valid only if AutoMode = PerServer. The integer value indicates the number of client licenses
  #   *     userData: <~Hash> - REQUIRED, representation of the sysprep UserData key
  #   *       computerName: <~String> - REQUIRED, The computer name of the (Windows) virtual machine. Will be truncates to 15 characters
  #   *       fullName: <~String> - REQUIRED, User's full name
  #   *       orgName: <~String> - REQUIRED, User's organization
  #   *       productId: <~String> - REQUIRED, serial number for os, ignored if using volume licensed instance
  #   *   LinuxPrep: <~Hash> - Optional, contains machine-wide settings (note the uppercase P)
  #   *     domain: <~String> - REQUIRED, The fully qualified domain name.
  #   *     hostName: <~String> - REQUIRED, the network host name
  #   *     hwClockUTC: <~Boolean> - Optional, Specifies whether the hardware clock is in UTC or local time
  #   *     timeZone: <~String> - Optional, Case sensistive timezone, valid values can be found at https://pubs.vmware.com/vsphere-51/topic/com.vmware.wssdk.apiref.doc/timezone.html
  #   *   SysprepText: <~Hash> - Optional, alternate way to specify the sysprep.inf answer file.
  #   *     value: <~String> - REQUIRED, Text for the sysprep.inf answer file. 
  #   * nicSettingMap: <~Array> - Optional, IP settings that are specific to a particular virtual network adapter
  #   *   Each item in array:
  #   *   adapter: <~Hash> - REQUIRED, IP settings for the associated virtual network adapter
  #   *     dnsDomain: <~String> - Optional, DNS domain suffix for adapter
  #   *     dnsServerList: <~Array> - Optional, list of dns server ip addresses - Example: ["10.0.0.2", "10.0.0.3"]
  #   *     gateway: <~Array> - Optional, list of gateways - Example: ["10.0.0.2", "10.0.0.3"]
  #   *     ip: <~String> - Optional, but required if static IP
  #   *     ipV6Spec: <~Hash> - Optional, IPv^ settings
  #   *       ipAddress: <~String> - Optional, but required if setting static IP
  #   *       gateway: <~Array> - Optional, list of ipv6 gateways
  #   *     netBIOS: <~String> - Optional, NetBIOS settings, if supplied must be one of: disableNetBIOS','enableNetBIOS','enableNetBIOSViaDhcp'
  #   *     primaryWINS: <~String> - Optional, IP address of primary WINS server
  #   *     secondaryWINS: <~String> - Optional, IP address of secondary WINS server
  #   *     subnetMask: <~String> - Optional, subnet mask for adapter
  #   *   macAddress: <~String> - Optional, MAC address of adapter being customized. This cannot be set by the client
  #   * options: <~Hash> Optional operations, currently only win options have any value
  #   *   changeSID: <~Boolean> - REQUIRED, The customization process should modify the machine's security identifier
  #   *   deleteAccounts: <~Boolean> - REQUIRED, If deleteAccounts is true, then all user accounts are removed from the system
  #   *   reboot: <~String> - Optional, (defaults to reboot), Action to be taken after running sysprep, must be one of: 'noreboot', 'reboot', 'shutdown'
  #
  if ( options.key?('customization_spec') )
    custom_spec = options['customization_spec']

    # backwards compatablity
    if custom_spec.key?('domain')
      # doing this means the old options quash any new ones passed as well... might not be the best way to do it?
      # any 'old' options overwrite the following:
      #   - custom_spec['identity']['LinuxPrep']
      #   - custom_spec['globalIPSettings['['dnsServerList']
      #   - custom_spec['globalIPSettings']['dnsSuffixList']
      #   - custom_spec['nicSettingMap'][0]['adapter']['ip']
      #   - custom_spec['nicSettingMap'][0]['adapter']['gateway']
      #   - custom_spec['nicSettingMap'][0]['adapter']['subnetMask']
      #   - custom_spec['nicSettingMap'][0]['adapter']['dnsDomain']
      #   - custom_spec['nicSettingMap'][0]['adapter']['dnsServerList']
      #
      # we can assume old parameters being passed
      cust_hostname = custom_spec['hostname'] || options['name']
      custom_spec['identity'] = Hash.new unless custom_spec.key?('identity')
      custom_spec['identity']['LinuxPrep'] = {"domain" => custom_spec['domain'], "hostName" => cust_hostname, "timeZone" => custom_spec['time_zone']}
    
      if custom_spec.key?('ipsettings')
        custom_spec['globalIPSettings']=Hash.new unless custom_spec.key?('globalIPSettings')
        custom_spec['globalIPSettings']['dnsServerList'] = custom_spec['ipsettings']['dnsServerList'] if custom_spec['ipsettings'].key?('dnsServerList')
        custom_spec['globalIPSettings']['dnsSuffixList'] = custom_spec['dnsSuffixList'] || [custom_spec['domain']] if ( custom_spec['dnsSuffixList'] || custom_spec['domain'])
      end

      if (custom_spec['ipsettings'].key?('ip') or custom_spec['ipsettings'].key?('gateway') or custom_spec['ipsettings'].key?('subnetMask') or custom_spec['ipsettings'].key?('domain') or custom_spec['ipsettings'].key?('dnsServerList'))
        if custom_spec['ipsettings'].key?('ip') 
          raise ArgumentError, "subnetMask is required for static ip" unless custom_spec["ipsettings"].key?("subnetMask")
        end
        custom_spec['nicSettingMap']=Array.new unless custom_spec.key?('nicSettingMap')
        custom_spec['nicSettingMap'][0]=Hash.new unless custom_spec['nicSettingMap'].length > 0
        custom_spec['nicSettingMap'][0]['adapter']=Hash.new unless custom_spec['nicSettingMap'][0].key?('adapter')
        custom_spec['nicSettingMap'][0]['adapter']['ip'] = custom_spec['ipsettings']['ip'] if custom_spec['ipsettings'].key?('ip')
        custom_spec['nicSettingMap'][0]['adapter']['gateway'] = custom_spec['ipsettings']['gateway'] if custom_spec['ipsettings'].key?('gateway')
        custom_spec['nicSettingMap'][0]['adapter']['subnetMask'] = custom_spec['ipsettings']['subnetMask'] if custom_spec['ipsettings'].key?('subnetMask')
        custom_spec['nicSettingMap'][0]['adapter']['dnsDomain'] = custom_spec['ipsettings']['domain'] if custom_spec['ipsettings'].key?('domain')
        custom_spec['nicSettingMap'][0]['adapter']['dnsServerList'] = custom_spec['ipsettings']['dnsServerList'] if custom_spec['ipsettings'].key?('dnsServerList')
      end       
    end
    ### End of backwards compatability 

    ## requirements check here ##
    raise ArgumentError, "globalIPSettings are required when using Customization Spec" unless custom_spec.key?('globalIPSettings')
    raise ArgumentError, "identity is required when using Customization Spec" unless custom_spec.key?('identity')
  
    # encryptionKey
    custom_encryptionKey = custom_spec['encryptionKey'] if custom_spec.key?('encryptionKey')
    custom_encryptionKey ||= nil
    
    # globalIPSettings
    # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.GlobalIPSettings.html
    custom_globalIPSettings = RbVmomi::VIM::CustomizationGlobalIPSettings.new()
    custom_globalIPSettings.dnsServerList = custom_spec['globalIPSettings']['dnsServerList'] if custom_spec['globalIPSettings'].key?("dnsServerList")
    custom_globalIPSettings.dnsSuffixList = custom_spec['globalIPSettings']['dnsSuffixList'] if custom_spec['globalIPSettings'].key?("dnsSuffixList")
    
    # identity
    # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.IdentitySettings.html
    # Accepts the 3 supported CustomizationIdentitySettings Types:
    # 1. CustomizationLinuxPrep (LinuxPrep) - note the uppercase P
    # 2. CustomizationSysprep (Sysprep)
    # 3. CustomizationSysprepText (SysprepText)
    # At least one of these is required
    #
    identity = custom_spec['identity']
    if identity.key?("LinuxPrep")
      # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.LinuxPrep.html
      # Fields:
      #   * domain: string **REQUIRED**
      #   * hostName: string (CustomizationName)  **REQUIRED** Will use options['name'] if not provided.
      #   * hwClockUTC: boolean
      #   * timeZone: string (https://pubs.vmware.com/vsphere-55/topic/com.vmware.wssdk.apiref.doc/timezone.html) 
      raise ArgumentError, "domain is required when using LinuxPrep identity" unless identity['LinuxPrep'].key?('domain')
      custom_identity = RbVmomi::VIM::CustomizationLinuxPrep(:domain => identity['LinuxPrep']['domain'])
      cust_hostname = RbVmomi::VIM::CustomizationFixedName(:name => identity['LinuxPrep']['hostName']) if identity['LinuxPrep'].key?('hostName')
      cust_hostname ||= RbVmomi::VIM::CustomizationFixedName(:name => options['name'])
      custom_identity.hostName = cust_hostname
      custom_identity.hwClockUTC = identity['LinuxPrep']['hwClockUTC'] if identity['LinuxPrep'].key?('hwClockUTC')
      custom_identity.timeZone = identity['LinuxPrep']['timeZone'] if identity['LinuxPrep'].key?('timeZone') 
    elsif identity.key?("Sysprep")
      # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Sysprep.html
      # Fields:
      #   * guiRunOnce: CustomizationGuiRunOnce
      #   * guiUnattended: CustomizationGuiUnattended  **REQUIRED**
      #   * identification: CustomizationIdentification  **REQUIRED**
      #   * licenseFilePrintData: CustomizationLicenseFilePrintData
      #   * userData: CustomizationUserData **REQUIRED**
      # 
      raise ArgumentError, "guiUnattended is required when using Sysprep identity" unless identity['Sysprep'].key?('guiUnattended')
      raise ArgumentError, "identification is required when using Sysprep identity" unless identity['Sysprep'].key?('identification')
      raise ArgumentError, "userData is required when using Sysprep identity" unless identity['Sysprep'].key?('userData')
      if identity['Sysprep']['guiRunOnce']
        # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.GuiRunOnce.html
        # Fields:
        #  * commandList: array of string **REQUIRED***
        #
        raise ArgumentError, "commandList is required when using Sysprep identity and guiRunOnce" unless identity['Sysprep']['guiRunOnce'].key?('commandList')
        cust_guirunonce = RbVmomi::VIM.CustomizationGuiRunOnce( :commandList => identity['Sysprep']['guiRunOnce']['commandList'] )
      end
      # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.GuiUnattended.html
      # Fields:
      #   * autoLogin: boolean **REQUIRED**
      #   * autoLogonCount: int **REQUIRED**
      #   * timeZone: int (see here for values https://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx) **REQUIRED**
      #   * password: CustomizationPassword 
      raise ArgumentError, "guiUnattended->autoLogon is required when using Sysprep identity" unless identity['Sysprep']['guiUnattended'].key?('autoLogon')
      raise ArgumentError, "guiUnattended->autoLogonCount is required when using Sysprep identity" unless identity['Sysprep']['guiUnattended'].key?('autoLogonCount')
      raise ArgumentError, "guiUnattended->timeZone is required when using Sysprep identity" unless identity['Sysprep']['guiUnattended'].key?('timeZone')
      custom_guiUnattended = RbVmomi::VIM.CustomizationGuiUnattended(
        :autoLogon => identity['Sysprep']['guiUnattended']['autoLogon'],
        :autoLogonCount => identity['Sysprep']['guiUnattended']['autoLogonCount'],
        :timeZone => identity['Sysprep']['guiUnattended']['timeZone']
      )
      if identity['Sysprep']['guiUnattended']['password']
        # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Password.html
        # Fields:
        #   * plainText: boolean  **REQUIRED**
        #   * value: string  **REQUIRED**
        raise ArgumentError, "guiUnattended->password->plainText is required when using Sysprep identity and guiUnattended -> password" unless identity['Sysprep']['guiUnattended']['password'].key?('plainText')
        raise ArgumentError, "guiUnattended->password->value is required when using Sysprep identity and guiUnattended -> password" unless identity['Sysprep']['guiUnattended']['password'].key?('value')
        custom_guiUnattended.password = RbVmomi::VIM.CustomizationPassword(
          :plainText => identity['Sysprep']['guiUnattended']['password']['plainText'],
          :value => identity['Sysprep']['guiUnattended']['password']['value']
        )
      end
      # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Identification.html
      # Fields:
      #   * domainAdmin: string
      #   * domainAdminPassword: CustomizationPassword
      #   * joinDomain: string *If supplied domainAdmin and domainAdminPassword must be set
      #   * joinWorkgroup: string *If supplied, joinDomain, domainAdmin and domainAdminPassword will be ignored
      custom_identification = RbVmomi::VIM.CustomizationIdentification()
      if identity['Sysprep']['identification'].key?('joinWorkgroup')
        custom_identification.joinWorkgroup = identity['Sysprep']['identification']['joinWorkgroup']
      elsif identity['Sysprep']['identification'].key?('joinDomain')
        raise ArgumentError, "identification->domainAdmin is required when using Sysprep identity and identification -> joinDomain" unless identity['Sysprep']['identification'].key?('domainAdmin')
        raise ArgumentError, "identification->domainAdminPassword is required when using Sysprep identity and identification -> joinDomain" unless identity['Sysprep']['identification'].key?('domainAdmin')
        raise ArgumentError, "identification->domainAdminPassword->plainText is required when using Sysprep identity and identification -> joinDomain" unless identity['Sysprep']['identification']['domainAdminPassword'].key?('plainText')
        raise ArgumentError, "identification->domainAdminPassword->value is required when using Sysprep identity and identification -> joinDomain" unless identity['Sysprep']['identification']['domainAdminPassword'].key?('value')
        custom_identification.joinDomain = identity['Sysprep']['identification']['joinDomain']
        custom_identification.domainAdmin = identity['Sysprep']['identification']['domainAdmin']
        # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Password.html
        # Fields:
        #   * plainText: boolean **REQUIRED**
        #   * value: string **REQUIRED**
        custom_identification.domainAdminPassword = RbVmomi::VIM.CustomizationPassword( 
          :plainText => identity['Sysprep']['identification']['domainAdminPassword']['plainText'],
          :value => identity['Sysprep']['identification']['domainAdminPassword']['value']
        )
      else
        raise ArgumentError, "No valid Indentification found, valid values are 'joinWorkgroup' and 'joinDomain'"
      end
      if identity['Sysprep'].key?('licenseFilePrintData')
        # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.LicenseFilePrintData.html
        # Fields:
        #   * autoMode: string (CustomizationLicenseDataMode) ** REQUIRED **, valid strings are: 'perSeat' or 'perServer'
        #   * autoUsers: int (valid only if AutoMode = PerServer)
        raise ArgumentError, "licenseFilePrintData->autoMode is required when using Sysprep identity and licenseFilePrintData" unless identity['Sysprep']['licenseFilePrintData'].key?('autoMode')
        raise ArgumentError, "Unsupported autoMode, supported modes are : 'perSeat' or 'perServer'" unless ['perSeat', 'perServer'].include? identity['Sysprep']['licenseFilePrintData']['autoMode']
        custom_licenseFilePrintData = RbVmomi::VIM.CustomizationLicenseFilePrintData(
          :autoMode => RbVmomi::VIM.CustomizationLicenseDataMode(identity['Sysprep']['licenseFilePrintData']['autoMode'])
        )
        if identity['Sysprep']['licenseFilePrintData'].key?('autoUsers')
          custom_licenseFilePrintData.autoUsers = identity['Sysprep']['licenseFilePrintData']['autoUsers'] if identity['Sysprep']['licenseFilePrintData']['autoMode'] == "PerServer"
        end
      end
      # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.UserData.html
      # Fields:
      #   * computerName: string (CustomizationFixedName)  **REQUIRED**
      #   * fullName: string **REQUIRED**
      #   * orgName: string **REQUIRED**
      #   * productID: string **REQUIRED**
      raise ArgumentError, "userData->computerName is required when using Sysprep identity" unless identity['Sysprep']['userData'].key?('computerName')
      raise ArgumentError, "userData->fullName is required when using Sysprep identity" unless identity['Sysprep']['userData'].key?('fullName')
      raise ArgumentError, "userData->orgName is required when using Sysprep identity" unless identity['Sysprep']['userData'].key?('orgName')
      raise ArgumentError, "userData->productId is required when using Sysprep identity" unless identity['Sysprep']['userData'].key?('productId')
      custom_userData = RbVmomi::VIM.CustomizationUserData(
        :fullName => identity['Sysprep']['userData']['fullName'],
        :orgName => identity['Sysprep']['userData']['orgName'],
        :productId => identity['Sysprep']['userData']['productId'],
        :computerName => RbVmomi::VIM.CustomizationFixedName(:name => identity['Sysprep']['userData']['computerName'])
      )

      custom_identity = RbVmomi::VIM::CustomizationSysprep(          
        :guiUnattended => custom_guiUnattended,
        :identification => custom_identification,
        :userData => custom_userData
      )
      custom_identity.guiRunOnce = cust_guirunonce if defined?(cust_guirunonce)
      custom_identity.licenseFilePrintData = custom_licenseFilePrintData if defined?(custom_licenseFilePrintData)
    elsif identity.key?("SysprepText")
      # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.SysprepText.html
      # Fields:
      #   * value: string **REQUIRED**
      raise ArgumentError, "SysprepText -> value is required when using SysprepText identity" unless identity['SysprepText'].key?('value')
      custom_identity = RbVmomi::VIM::CustomizationSysprepText(:value => identity['SysprepText']['value'])
    else
      raise ArgumentError, "At least one of the following valid identities must be supplied: LinuxPrep, Sysprep, SysprepText"
    end

    if custom_spec.key?("nicSettingMap")
      # custom_spec['nicSettingMap'] is an array of adapater mappings:
      # custom_spec['nicSettingMap'][0]['macAddress']
      # custom_spec['nicSettingMap'][0]['adapter']['ip']
      #https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.AdapterMapping.html
      # Fields:
      #   * adapter: CustomizationIPSettings **REQUIRED**
      #   * macAddress: string
      raise ArgumentError, "At least one nicSettingMap is required when using nicSettingMap" unless custom_spec['nicSettingMap'].length > 0
      raise ArgumentError, "Adapter is required when using nicSettingMap" unless custom_spec['nicSettingMap'][0].key?('adapter')
      
      custom_nicSettingMap = [] 
      # need to go through array here for each apapter
      custom_spec['nicSettingMap'].each do | nic |
        # https://pubs.vmware.com/vsphere-55/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvim.vm.customization.IPSettings.html
        # Fields:
        #   * dnsDomain: string
        #   * gateway: array of string
        #   * ip: CustomizationIpGenerator (string) **REQUIRED IF Assigning Static IP***
        #   * ipV6Spec: CustomizationIPSettingsIpV6AddressSpec
        #   * netBIOS: CustomizationNetBIOSMode (string)
        #   * primaryWINS: string
        #   * secondaryWINS: string
        #   * subnetMask: string - Required if assigning static IP
        if nic['adapter'].key?('ip')
          raise ArgumentError, "SubnetMask is required when assigning static IP when using nicSettingMap -> Adapter" unless nic['adapter'].key?('subnetMask')
          custom_ip = RbVmomi::VIM.CustomizationFixedIp(:ipAddress => nic['adapter']['ip'])
        else
          custom_ip = RbVmomi::VIM::CustomizationDhcpIpGenerator.new()
        end
        custom_adapter = RbVmomi::VIM.CustomizationIPSettings(:ip => custom_ip)
        custom_adapter.dnsDomain = nic['adapter']['dnsDomain'] if nic['adapter'].key?('dnsDomain')
        custom_adapter.dnsServerList = nic['adapter']['dnsServerList'] if nic['adapter'].key?('dnsServerList')
        custom_adapter.gateway = nic['adapter']['gateway'] if nic['adapter'].key?('gateway')
        if nic['adapter'].key?('ipV6Spec')
          # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.IPSettings.IpV6AddressSpec.html
          # Fields:
          #   * gateway: array of string
          #   * ip: CustomizationIpV6Generator[] **Required if setting static IP **
          if nic['adapter']['ipV6Spec'].key?('ipAddress')
            raise ArgumentError, "SubnetMask is required when assigning static IPv6 when using nicSettingMap -> Adapter -> ipV6Spec" unless nic['adapter']['ipV6Spec'].key?('subnetMask')
            # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.FixedIpV6.html
            #   * ipAddress: string **REQUIRED**
            #   * subnetMask: int **REQUIRED**
            custom_ipv6 = RbVmomi::VIM.CustomizationFixedIpV6(
              :ipAddress => nic['adapter']['ipV6Spec']['ipAddress'],
              :subnetMask => nic['adapter']['ipV6Spec']['subnetMask']
            )
          else
            custom_ipv6 = RbVmomi::VIM::CustomizationDhcpIpV6Generator.new()
          end
          custom_ipv6Spec = RbVmomi::VIM.CustomizationIPSettingsIpV6AddressSpec(:ip => custom_ipv6)
          custom_ipv6Spec.gateway = nic['adapter']['ipV6Spec']['gateway'] if nic['adapter']['ipV6Spec'].key?('gateway')
          custom_adapter.ipV6Spec = custom_ipv6Spec
        end
        if nic['adapter'].key?('netBIOS')
          # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.IPSettings.NetBIOSMode.html
          # Fields:
          #   netBIOS: string matching: 'disableNetBIOS','enableNetBIOS' or 'enableNetBIOSViaDhcp' ** REQUIRED **
          #
          raise ArgumentError, "Unsupported NetBIOSMode, supported modes are : 'disableNetBIOS','enableNetBIOS' or 'enableNetBIOSViaDhcp'" unless ['disableNetBIOS','enableNetBIOS','enableNetBIOSViaDhcp'].include? nic['adapter']['netBIOS']
          custom_adapter.netBIOS = RbVmomi::VIM.CustomizationNetBIOSMode(nic['adapter']['netBIOS'])
        end
        custom_adapter.primaryWINS = nic['adapter']['primaryWINS'] if nic['adapter'].key?('primaryWINS')
        custom_adapter.secondaryWINS = nic['adapter']['secondaryWINS'] if nic['adapter'].key?('secondaryWINS')
        custom_adapter.subnetMask = nic['adapter']['subnetMask'] if nic['adapter'].key?('subnetMask')
        
        custom_adapter_mapping = RbVmomi::VIM::CustomizationAdapterMapping(:adapter => custom_adapter)
        custom_adapter_mapping.macAddress = nic['macAddress'] if nic.key?('macAddress')
        
        # build the adapters array, creates it if not already created, otherwise appends to it
        custom_nicSettingMap << custom_adapter_mapping 
      end
    end  
    custom_nicSettingMap = nil if custom_nicSettingMap.length < 1
              
    if custom_spec.key?("options") 
      # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Options.html
      # this currently doesn't have any Linux options, just windows
      # Fields:
      #   * changeSID: boolean **REQUIRED**
      #   * deleteAccounts: boolean **REQUIRED** **note deleteAccounts is deprecated as of VI API 2.5 so can be ignored
      #   * reboot: CustomizationSysprepRebootOption: (string) one of following 'noreboot', reboot' or 'shutdown' (defaults to reboot)
      raise ArgumentError, "changeSID id required when using Windows Options" unless custom_spec['options'].key?('changeSID')
      raise ArgumentError, "deleteAccounts id required when using Windows Options" unless custom_spec['options'].key?('deleteAccounts')
      custom_options = RbVmomi::VIM::CustomizationWinOptions(
        :changeSID => custom_spec['options']['changeSID'],
        :deleteAccounts => custom_spec['options']['deleteAccounts']
      )
      if custom_spec['options'].key?('reboot')
        raise ArgumentError, "Unsupported reboot option, supported options are : 'noreboot', 'reboot' or 'shutdown'" unless ['noreboot','reboot','shutdown'].include? custom_spec['options']['reboot']
        custom_options.reboot = RBVmomi::VIM.CustomizationSysprepRebootOption(custom_spec['options']['reboot'])
      end
    end
    custom_options ||=nil  
    
    # https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Specification.html
    customization_spec = RbVmomi::VIM::CustomizationSpec(
      :globalIPSettings => custom_globalIPSettings,
      :identity         => custom_identity
    )
    customization_spec.encryptionKey = custom_encryptionKey if defined?(custom_encryptionKey)
    customization_spec.nicSettingMap = custom_nicSettingMap if defined?(custom_nicSettingMap)
    customization_spec.options = custom_options if defined?(custom_options)

  end
  customization_spec ||= nil

  relocation_spec=nil
  if ( options['linked_clone'] )
    # cribbed heavily from the rbvmomi clone_vm.rb
    # this chunk of code reconfigures the disk of the clone source to be read only,
    # and then creates a delta disk on top of that, this is required by the API in order to create
    # linked clondes
    disks = vm_mob_ref.config.hardware.device.select do |vm_device|
      vm_device.class == RbVmomi::VIM::VirtualDisk
    end
    disks.select{|vm_device| vm_device.backing.parent == nil}.each do |disk|
      disk_spec = {
        :deviceChange => [
          {
            :operation => :remove,
            :device => disk
          },
          {
            :operation => :add,
            :fileOperation => :create,
            :device => disk.dup.tap{|disk_backing|
              disk_backing.backing = disk_backing.backing.dup;
              disk_backing.backing.fileName = "[#{disk.backing.datastore.name}]";
              disk_backing.backing.parent = disk.backing
            }
          }
        ]
      }
      vm_mob_ref.ReconfigVM_Task(:spec => disk_spec).wait_for_completion
    end
    # Next, create a Relocation Spec instance
    relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:datastore => datastore_obj,
                                                              :pool => resource_pool,
                                                              :diskMoveType => :moveChildMostDiskBacking)
  else
    relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:datastore => datastore_obj,
                                                              :pool => resource_pool,
                                                              :transform => options['transform'] || 'sparse')
  end
  # And the clone specification
  clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => relocation_spec,
                                                    :config => virtual_machine_config_spec,
                                                    :customization => customization_spec,
                                                    :powerOn  => options.key?('power_on') ? options['power_on'] : true,
                                                    :template => false)

  # Perform the actual Clone Task
  task = vm_mob_ref.CloneVM_Task(:folder => dest_folder,
                                 :name => options['name'],
                                 :spec => clone_spec)
  # Waiting for the VM to complete allows us to get the VirtulMachine
  # object of the new machine when it's done.  It is HIGHLY recommended
  # to set 'wait' => true if your app wants to wait.  Otherwise, you're
  # going to have to reload the server model over and over which
  # generates a lot of time consuming API calls to vmware.
  if options.fetch('wait', true) then
    # REVISIT: It would be awesome to call a block passed to this
    # request to notify the application how far along in the process we
    # are.  I'm thinking of updating a progress bar, etc...
    new_vm = task.wait_for_completion
  else
    tries = 0
    new_vm = begin
      # Try and find the new VM (folder.find is quite efficient)
      dest_folder.find(options['name'], RbVmomi::VIM::VirtualMachine) or raise Fog::Vsphere::Errors::NotFound
    rescue Fog::Vsphere::Errors::NotFound
      tries += 1
      if tries <= 10 then
        sleep 15
        retry
      end
      nil
    end
  end

  # Return hash
  {
    'vm_ref'        => new_vm ? new_vm._ref : nil,
    'new_vm'        => new_vm ? convert_vm_mob_ref_to_attr_hash(new_vm) : nil,
    'task_ref'      => task._ref
  }
end

#vm_config_vnc(options = { }) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/fog/vsphere/requests/compute/vm_config_vnc.rb', line 5

def vm_config_vnc(options = { })
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'

  search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
  vm_mob_ref    = @connection.searchIndex.FindAllByUuid(search_filter).first
  task          = vm_mob_ref.ReconfigVM_Task(:spec => {
    :extraConfig => [
      { :key => 'RemoteDisplay.vnc.enabled',  :value => options[:enabled] ? 'true' : 'false' },
      { :key => 'RemoteDisplay.vnc.password', :value => options[:password].to_s },
      { :key => 'RemoteDisplay.vnc.port',     :value => options[:port].to_s || '5910' }
    ]
  })
  task.wait_for_completion
  { 'task_state' => task.info.state }
end

#vm_destroy(options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
# File 'lib/fog/vsphere/requests/compute/vm_destroy.rb', line 5

def vm_destroy(options = {})
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'

  vm_mob_ref = get_vm_ref(options['instance_uuid'])
  task = vm_mob_ref.Destroy_Task
  task.wait_for_completion
  { 'task_state' => task.info.state }
end

#vm_execute(options = {}) ⇒ Object

NOTE: you must be using vsphere_rev 5.0 or greater to use this functionality e.g. Fog::Compute.new(provider: “vsphere”, vsphere_rev: “5.5”, etc)

  • options<~Hash>:

    • 'instance_uuid'<~String> - REQUIRED the instance uuid you would like to operate on

    • 'command'<~String> REQUIRED the command to execute

    • 'args'<~String> arguments to pass the command

    • 'working_dir'<~String> path to the working directory

    • 'user'<~String> REQUIRED the ssh username you would like to login as

    • 'password'<~String> REQUIRED the ssh password for the user you would like to log in as

Raises:

  • (ArgumentError)

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/fog/vsphere/requests/compute/vm_execute.rb', line 14

def vm_execute(options = {})
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'
  raise ArgumentError, "command is a required parameter" unless options.key? 'command'
  raise ArgumentError, "user is a required parameter" unless options.key? 'user'
  raise ArgumentError, "password is a required parameter" unless options.key? 'password'

  search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
  vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first

  auth = RbVmomi::VIM::NamePasswordAuthentication(:interactiveSession => false,
                                                  :username => options['user'],
                                                  :password => options['password'])

  spec = RbVmomi::VIM::GuestProgramSpec(:programPath => options['command'],
                                        :arguments => options['args'],
                                        :workingDirectory => options['working_dir'])

  gom = @connection.serviceContent.guestOperationsManager
  gom.processManager.StartProgramInGuest(:vm => vm_mob_ref, :auth => auth, :spec => spec)
end

#vm_get_vnc(uuid) ⇒ Object

return a hash of VNC attributes required to view the console


22
23
24
25
26
27
28
29
30
# File 'lib/fog/vsphere/requests/compute/vm_config_vnc.rb', line 22

def vm_get_vnc uuid
  search_filter = { :uuid => uuid, 'vmSearch' => true, 'instanceUuid' => true }
  vm = @connection.searchIndex.FindAllByUuid(search_filter).first
  Hash[vm.config.extraConfig.map do |config|
    if config.key =~ /^RemoteDisplay\.vnc\.(\w+)$/
      [$1.to_sym, config.value]
    end
  end.compact]
end

#vm_migrate(options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/fog/vsphere/requests/compute/vm_migrate.rb', line 5

def vm_migrate(options = {})
  #priority is the only required option, and it has a sane default option.
  priority = options['priority'].nil? ? 'defaultPriority' : options["priority"]
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'

  # Find the VM Object
  search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
  vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first

  unless vm_mob_ref.kind_of? RbVmomi::VIM::VirtualMachine
    raise Fog::Vsphere::Errors::NotFound,
      "Could not find VirtualMachine with instance uuid #{options['instance_uuid']}"
  end
  task = vm_mob_ref.MigrateVM_Task(:pool => options['pool'], :host => options['host'], :priority => "#{priority}", :state => options['state'] )
  task.wait_for_completion
  { 'task_state' => task.info.state }
end

#vm_power_off(options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/fog/vsphere/requests/compute/vm_power_off.rb', line 5

def vm_power_off(options = {})
  options = { 'force' => false }.merge(options)
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'

  search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
  vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first

  if options['force'] then
    task = vm_mob_ref.PowerOffVM_Task
    task.wait_for_completion
    { 'task_state' => task.info.result, 'power_off_type' => 'cut_power' }
  else
    vm_mob_ref.ShutdownGuest
    {
      'task_state'     => "running",
      'power_off_type' => 'shutdown_guest',
    }
  end
end

#vm_power_on(options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
13
14
15
# File 'lib/fog/vsphere/requests/compute/vm_power_on.rb', line 5

def vm_power_on(options = {})
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'

  search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
  vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first

  task = vm_mob_ref.PowerOnVM_Task
  task.wait_for_completion
  # 'success', 'running', 'queued', 'error'
  { 'task_state' => task.info.state }
end

#vm_reboot(options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/fog/vsphere/requests/compute/vm_reboot.rb', line 5

def vm_reboot(options = {})
  options = { 'force' => false }.merge(options)
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'

  search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
  vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first

  if options['force'] then
    task = vm_mob_ref.ResetVM_Task
    task.wait_for_completion
    { 'task_state' => task.info.result, 'reboot_type' => 'reset_power' }
  else
    vm_mob_ref.RebootGuest
    { 'task_state' => "running", 'reboot_type' => 'reboot_guest' }
  end
end

#vm_reconfig_cpus(options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
# File 'lib/fog/vsphere/requests/compute/vm_reconfig_cpus.rb', line 5

def vm_reconfig_cpus(options = {})
  raise ArgumentError, "cpus is a required parameter" unless options.key? 'cpus'
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'
  hardware_spec={'numCPUs' => options['cpus'], 'numCoresPerSocket' => options['corespersocket']}
  vm_reconfig_hardware('instance_uuid' => options['instance_uuid'], 'hardware_spec' => hardware_spec )
end

#vm_reconfig_hardware(options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
11
12
# File 'lib/fog/vsphere/requests/compute/vm_reconfig_hardware.rb', line 5

def vm_reconfig_hardware(options = {})
  raise ArgumentError, "hardware_spec is a required parameter" unless options.key? 'hardware_spec'
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'
  vm_mob_ref = get_vm_ref(options['instance_uuid'])
  task = vm_mob_ref.ReconfigVM_Task(:spec => RbVmomi::VIM.VirtualMachineConfigSpec(options['hardware_spec']))
  task.wait_for_completion
   { 'task_state' => task.info.state }
end

#vm_reconfig_memory(options = {}) ⇒ Object

Raises:

  • (ArgumentError)

5
6
7
8
9
10
# File 'lib/fog/vsphere/requests/compute/vm_reconfig_memory.rb', line 5

def vm_reconfig_memory(options = {})
  raise ArgumentError, "memory is a required parameter" unless options.key? 'memory'
  raise ArgumentError, "instance_uuid is a required parameter" unless options.key? 'instance_uuid'
  hardware_spec={'memoryMB' => options['memory']}
  vm_reconfig_hardware('instance_uuid' => options['instance_uuid'], 'hardware_spec' => hardware_spec )
end