fog-vsphere 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.travis.yml +32 -0
- data/CONTRIBUTING.md +18 -0
- data/CONTRIBUTORS.md +59 -0
- data/Gemfile +5 -0
- data/LICENSE.md +20 -0
- data/README.md +31 -0
- data/Rakefile +8 -0
- data/fog-vsphere.gemspec +32 -0
- data/gemfiles/Gemfile.1.9.2+ +8 -0
- data/gemfiles/Gemfile.1.9.2- +11 -0
- data/lib/fog/vsphere.rb +41 -0
- data/lib/fog/vsphere/compute.rb +473 -0
- data/lib/fog/vsphere/models/compute/cluster.rb +28 -0
- data/lib/fog/vsphere/models/compute/clusters.rb +22 -0
- data/lib/fog/vsphere/models/compute/customfield.rb +16 -0
- data/lib/fog/vsphere/models/compute/customfields.rb +23 -0
- data/lib/fog/vsphere/models/compute/customvalue.rb +14 -0
- data/lib/fog/vsphere/models/compute/customvalues.rb +33 -0
- data/lib/fog/vsphere/models/compute/datacenter.rb +44 -0
- data/lib/fog/vsphere/models/compute/datacenters.rb +19 -0
- data/lib/fog/vsphere/models/compute/datastore.rb +21 -0
- data/lib/fog/vsphere/models/compute/datastores.rb +21 -0
- data/lib/fog/vsphere/models/compute/folder.rb +24 -0
- data/lib/fog/vsphere/models/compute/folders.rb +23 -0
- data/lib/fog/vsphere/models/compute/interface.rb +91 -0
- data/lib/fog/vsphere/models/compute/interfaces.rb +66 -0
- data/lib/fog/vsphere/models/compute/interfacetype.rb +22 -0
- data/lib/fog/vsphere/models/compute/interfacetypes.rb +34 -0
- data/lib/fog/vsphere/models/compute/network.rb +18 -0
- data/lib/fog/vsphere/models/compute/networks.rb +22 -0
- data/lib/fog/vsphere/models/compute/process.rb +17 -0
- data/lib/fog/vsphere/models/compute/resource_pool.rb +19 -0
- data/lib/fog/vsphere/models/compute/resource_pools.rb +22 -0
- data/lib/fog/vsphere/models/compute/scsicontroller.rb +16 -0
- data/lib/fog/vsphere/models/compute/server.rb +325 -0
- data/lib/fog/vsphere/models/compute/servers.rb +36 -0
- data/lib/fog/vsphere/models/compute/servertype.rb +36 -0
- data/lib/fog/vsphere/models/compute/servertypes.rb +23 -0
- data/lib/fog/vsphere/models/compute/snapshot.rb +35 -0
- data/lib/fog/vsphere/models/compute/snapshots.rb +27 -0
- data/lib/fog/vsphere/models/compute/template.rb +11 -0
- data/lib/fog/vsphere/models/compute/templates.rb +19 -0
- data/lib/fog/vsphere/models/compute/volume.rb +99 -0
- data/lib/fog/vsphere/models/compute/volumes.rb +53 -0
- data/lib/fog/vsphere/requests/compute/cloudinit_to_customspec.rb +65 -0
- data/lib/fog/vsphere/requests/compute/create_folder.rb +22 -0
- data/lib/fog/vsphere/requests/compute/create_vm.rb +169 -0
- data/lib/fog/vsphere/requests/compute/current_time.rb +18 -0
- data/lib/fog/vsphere/requests/compute/get_cluster.rb +25 -0
- data/lib/fog/vsphere/requests/compute/get_compute_resource.rb +41 -0
- data/lib/fog/vsphere/requests/compute/get_datacenter.rb +31 -0
- data/lib/fog/vsphere/requests/compute/get_datastore.rb +30 -0
- data/lib/fog/vsphere/requests/compute/get_folder.rb +74 -0
- data/lib/fog/vsphere/requests/compute/get_interface_type.rb +15 -0
- data/lib/fog/vsphere/requests/compute/get_network.rb +59 -0
- data/lib/fog/vsphere/requests/compute/get_resource_pool.rb +26 -0
- data/lib/fog/vsphere/requests/compute/get_server_type.rb +32 -0
- data/lib/fog/vsphere/requests/compute/get_template.rb +16 -0
- data/lib/fog/vsphere/requests/compute/get_virtual_machine.rb +57 -0
- data/lib/fog/vsphere/requests/compute/get_vm_first_scsi_controller.rb +26 -0
- data/lib/fog/vsphere/requests/compute/list_child_snapshots.rb +71 -0
- data/lib/fog/vsphere/requests/compute/list_clusters.rb +72 -0
- data/lib/fog/vsphere/requests/compute/list_compute_resources.rb +92 -0
- data/lib/fog/vsphere/requests/compute/list_customfields.rb +21 -0
- data/lib/fog/vsphere/requests/compute/list_datacenters.rb +53 -0
- data/lib/fog/vsphere/requests/compute/list_datastores.rb +40 -0
- data/lib/fog/vsphere/requests/compute/list_folders.rb +44 -0
- data/lib/fog/vsphere/requests/compute/list_interface_types.rb +25 -0
- data/lib/fog/vsphere/requests/compute/list_networks.rb +38 -0
- data/lib/fog/vsphere/requests/compute/list_processes.rb +40 -0
- data/lib/fog/vsphere/requests/compute/list_resource_pools.rb +38 -0
- data/lib/fog/vsphere/requests/compute/list_server_types.rb +54 -0
- data/lib/fog/vsphere/requests/compute/list_templates.rb +48 -0
- data/lib/fog/vsphere/requests/compute/list_virtual_machines.rb +80 -0
- data/lib/fog/vsphere/requests/compute/list_vm_customvalues.rb +20 -0
- data/lib/fog/vsphere/requests/compute/list_vm_interfaces.rb +63 -0
- data/lib/fog/vsphere/requests/compute/list_vm_snapshots.rb +66 -0
- data/lib/fog/vsphere/requests/compute/list_vm_volumes.rb +52 -0
- data/lib/fog/vsphere/requests/compute/modify_vm_interface.rb +59 -0
- data/lib/fog/vsphere/requests/compute/modify_vm_volume.rb +25 -0
- data/lib/fog/vsphere/requests/compute/revert_to_snapshot.rb +30 -0
- data/lib/fog/vsphere/requests/compute/set_vm_customvalue.rb +17 -0
- data/lib/fog/vsphere/requests/compute/vm_clone.rb +727 -0
- data/lib/fog/vsphere/requests/compute/vm_config_vnc.rb +45 -0
- data/lib/fog/vsphere/requests/compute/vm_destroy.rb +23 -0
- data/lib/fog/vsphere/requests/compute/vm_execute.rb +47 -0
- data/lib/fog/vsphere/requests/compute/vm_migrate.rb +33 -0
- data/lib/fog/vsphere/requests/compute/vm_power_off.rb +39 -0
- data/lib/fog/vsphere/requests/compute/vm_power_on.rb +26 -0
- data/lib/fog/vsphere/requests/compute/vm_reboot.rb +31 -0
- data/lib/fog/vsphere/requests/compute/vm_reconfig_cpus.rb +23 -0
- data/lib/fog/vsphere/requests/compute/vm_reconfig_hardware.rb +24 -0
- data/lib/fog/vsphere/requests/compute/vm_reconfig_memory.rb +23 -0
- data/lib/fog/vsphere/requests/compute/vm_take_snapshot.rb +37 -0
- data/lib/fog/vsphere/version.rb +5 -0
- data/tests/compute_tests.rb +53 -0
- data/tests/helper.rb +8 -0
- data/tests/helpers/mock_helper.rb +9 -0
- data/tests/helpers/succeeds_helper.rb +9 -0
- data/tests/models/compute/server_tests.rb +70 -0
- data/tests/models/compute/servers_tests.rb +15 -0
- data/tests/requests/compute/current_time_tests.rb +12 -0
- data/tests/requests/compute/get_network_tests.rb +50 -0
- data/tests/requests/compute/list_child_snapshots_tests.rb +10 -0
- data/tests/requests/compute/list_clusters_tests.rb +11 -0
- data/tests/requests/compute/list_virtual_machines_tests.rb +38 -0
- data/tests/requests/compute/list_vm_snapshots_tests.rb +10 -0
- data/tests/requests/compute/revert_to_snapshot_tests.rb +15 -0
- data/tests/requests/compute/set_vm_customvalue_tests.rb +20 -0
- data/tests/requests/compute/vm_clone_tests.rb +50 -0
- data/tests/requests/compute/vm_config_vnc_tests.rb +19 -0
- data/tests/requests/compute/vm_destroy_tests.rb +17 -0
- data/tests/requests/compute/vm_migrate_tests.rb +16 -0
- data/tests/requests/compute/vm_power_off_tests.rb +26 -0
- data/tests/requests/compute/vm_power_on_tests.rb +17 -0
- data/tests/requests/compute/vm_reboot_tests.rb +26 -0
- data/tests/requests/compute/vm_reconfig_cpus_tests.rb +19 -0
- data/tests/requests/compute/vm_reconfig_hardware_tests.rb +19 -0
- data/tests/requests/compute/vm_reconfig_memory_tests.rb +19 -0
- data/tests/requests/compute/vm_take_snapshot_tests.rb +19 -0
- metadata +289 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Fog
|
|
2
|
+
module Compute
|
|
3
|
+
class Vsphere
|
|
4
|
+
class Real
|
|
5
|
+
def add_vm_interface(vmid, options = {})
|
|
6
|
+
raise ArgumentError, "instance id is a required parameter" unless vmid
|
|
7
|
+
|
|
8
|
+
interface = get_interface_from_options(vmid, options.merge(:server_id => vmid))
|
|
9
|
+
vm_reconfig_hardware('instance_uuid' => vmid, 'hardware_spec' => {'deviceChange'=>[create_interface(interface, 0, :add, options)]})
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def destroy_vm_interface(vmid, options = {})
|
|
13
|
+
raise ArgumentError, "instance id is a required parameter" unless vmid
|
|
14
|
+
|
|
15
|
+
interface = get_interface_from_options(vmid, options.merge(:server_id => vmid))
|
|
16
|
+
vm_reconfig_hardware('instance_uuid' => vmid, 'hardware_spec' => {'deviceChange'=>[create_interface(interface, interface.key, :remove)]})
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def update_vm_interface(vmid, options = {})
|
|
20
|
+
raise ArgumentError, "instance id is a required parameter" unless vmid
|
|
21
|
+
|
|
22
|
+
interface = get_interface_from_options(vmid, options.merge(:server_id => vmid))
|
|
23
|
+
vm_reconfig_hardware('instance_uuid' => vmid, 'hardware_spec' => {'deviceChange'=>[create_interface(interface, interface.key, :edit)]})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
def get_interface_from_options(vmid, options)
|
|
28
|
+
if options and options[:interface]
|
|
29
|
+
options[:interface]
|
|
30
|
+
|
|
31
|
+
elsif options[:key] and options[:key]>0
|
|
32
|
+
oldattributes = get_vm_interface(vmid, options)
|
|
33
|
+
Fog::Compute::Vsphere::Interface.new(oldattributes.merge(options))
|
|
34
|
+
|
|
35
|
+
elsif options[:type] and options[:network]
|
|
36
|
+
Fog::Compute::Vsphere::Interface.new options
|
|
37
|
+
|
|
38
|
+
else
|
|
39
|
+
raise ArgumentError, "interface is a required parameter or pass options with type and network"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Mock
|
|
45
|
+
def add_vm_interface(vmid, options = {})
|
|
46
|
+
raise ArgumentError, "instance id is a required parameter" unless vmid
|
|
47
|
+
raise ArgumentError, "interface is a required parameter" unless options and options[:interface]
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def destroy_vm_interface(vmid, options = {})
|
|
52
|
+
raise ArgumentError, "instance id is a required parameter" unless vmid
|
|
53
|
+
raise ArgumentError, "interface is a required parameter" unless options and options[:interface]
|
|
54
|
+
true
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Fog
|
|
2
|
+
module Compute
|
|
3
|
+
class Vsphere
|
|
4
|
+
class Real
|
|
5
|
+
def add_vm_volume(volume)
|
|
6
|
+
vm_reconfig_hardware('instance_uuid' => volume.server_id, 'hardware_spec' => {'deviceChange'=>[create_disk(volume, volume.unit_number, :add)]})
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def destroy_vm_volume(volume)
|
|
10
|
+
vm_reconfig_hardware('instance_uuid' => volume.server_id, 'hardware_spec' => {'deviceChange'=>[create_disk(volume, volume.unit_number, :remove)]})
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Mock
|
|
15
|
+
def add_vm_volume(volume)
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def destroy_vm_volume(volume)
|
|
20
|
+
true
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Fog
|
|
2
|
+
module Compute
|
|
3
|
+
class Vsphere
|
|
4
|
+
class Real
|
|
5
|
+
def revert_to_snapshot(snapshot)
|
|
6
|
+
unless Snapshot === snapshot
|
|
7
|
+
fail ArgumentError, 'snapshot is a required parameter'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
task = snapshot.mo_ref.RevertToSnapshot_Task
|
|
11
|
+
task.wait_for_completion
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
'state' => task.info.state
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Mock
|
|
20
|
+
def revert_to_snapshot(snapshot)
|
|
21
|
+
fail ArgumentError, 'snapshot is a required parameter' if snapshot.nil?
|
|
22
|
+
|
|
23
|
+
{
|
|
24
|
+
'state' => 'success'
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Fog
|
|
2
|
+
module Compute
|
|
3
|
+
class Vsphere
|
|
4
|
+
class Real
|
|
5
|
+
def set_vm_customvalue(vm_id, key, value)
|
|
6
|
+
vm_ref = get_vm_ref(vm_id)
|
|
7
|
+
vm_ref.setCustomValue(:key => key, :value => value)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
class Mock
|
|
11
|
+
def set_vm_customvalue(vm_id, key, value)
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
module Fog
|
|
2
|
+
module Compute
|
|
3
|
+
class Vsphere
|
|
4
|
+
module Shared
|
|
5
|
+
private
|
|
6
|
+
def vm_clone_check_options(options)
|
|
7
|
+
default_options = {
|
|
8
|
+
'force' => false,
|
|
9
|
+
'linked_clone' => false,
|
|
10
|
+
'nic_type' => 'VirtualE1000',
|
|
11
|
+
}
|
|
12
|
+
options = default_options.merge(options)
|
|
13
|
+
# Backwards compat for "path" option
|
|
14
|
+
options["template_path"] ||= options["path"]
|
|
15
|
+
options["path"] ||= options["template_path"]
|
|
16
|
+
required_options = %w{ datacenter template_path name }
|
|
17
|
+
required_options.each do |param|
|
|
18
|
+
raise ArgumentError, "#{required_options.join(', ')} are required" unless options.key? param
|
|
19
|
+
end
|
|
20
|
+
raise Fog::Compute::Vsphere::NotFound, "Datacenter #{options["datacenter"]} Doesn't Exist!" unless get_datacenter(options["datacenter"])
|
|
21
|
+
raise Fog::Compute::Vsphere::NotFound, "Template #{options["template_path"]} Doesn't Exist!" unless get_virtual_machine(options["template_path"], options["datacenter"])
|
|
22
|
+
options
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Real
|
|
27
|
+
include Shared
|
|
28
|
+
|
|
29
|
+
# Clones a VM from a template or existing machine on your vSphere
|
|
30
|
+
# Server.
|
|
31
|
+
#
|
|
32
|
+
# ==== Parameters
|
|
33
|
+
# * options<~Hash>:
|
|
34
|
+
# * 'datacenter'<~String> - *REQUIRED* Datacenter name your cloning
|
|
35
|
+
# in. Make sure this datacenter exists, should if you're using
|
|
36
|
+
# the clone function in server.rb model.
|
|
37
|
+
# * 'template_path'<~String> - *REQUIRED* The path to the machine you
|
|
38
|
+
# want to clone FROM. Relative to Datacenter (Example:
|
|
39
|
+
# "FolderNameHere/VMNameHere")
|
|
40
|
+
# * 'name'<~String> - *REQUIRED* The VMName of the Destination
|
|
41
|
+
# * 'dest_folder'<~String> - Destination Folder of where 'name' will
|
|
42
|
+
# be placed on your cluster. Relative Path to Datacenter E.G.
|
|
43
|
+
# "FolderPlaceHere/anotherSub Folder/onemore"
|
|
44
|
+
# * 'power_on'<~Boolean> - Whether to power on machine after clone.
|
|
45
|
+
# Defaults to true.
|
|
46
|
+
# * 'wait'<~Boolean> - Whether the method should wait for the virtual
|
|
47
|
+
# machine to finish cloning before returning information from
|
|
48
|
+
# vSphere. Broken right now as you cannot return a model of a serer
|
|
49
|
+
# that isn't finished cloning. Defaults to True
|
|
50
|
+
# * 'resource_pool'<~Array> - The resource pool on your datacenter
|
|
51
|
+
# cluster you want to use. Only works with clusters within same
|
|
52
|
+
# same datacenter as where you're cloning from. Datacenter grabbed
|
|
53
|
+
# from template_path option.
|
|
54
|
+
# Example: ['cluster_name_here','resource_pool_name_here']
|
|
55
|
+
# * 'datastore'<~String> - The datastore you'd like to use.
|
|
56
|
+
# (datacenterObj.datastoreFolder.find('name') in API)
|
|
57
|
+
# * 'transform'<~String> - Not documented - see http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.RelocateSpec.html
|
|
58
|
+
# * 'numCPUs'<~Integer> - the number of Virtual CPUs of the Destination VM
|
|
59
|
+
# * 'memoryMB'<~Integer> - the size of memory of the Destination VM in MB
|
|
60
|
+
# * customization_spec<~Hash>: Options are marked as required if you
|
|
61
|
+
# use this customization_spec.
|
|
62
|
+
# As defined https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Specification.html
|
|
63
|
+
# * encryptionKey <~array of bytes> Used to encrypt/decrypt password
|
|
64
|
+
# * globalIPSettings expects a hash, REQUIRED
|
|
65
|
+
# * identity expects a hash, REQUIRED - either LinuxPrep, Sysprep or SysprepText
|
|
66
|
+
# * nicSettingMap expects an array
|
|
67
|
+
# * options expects a hash
|
|
68
|
+
# * All options can be parsed using a yaml template with cloudinit_to_customspec.rb
|
|
69
|
+
#
|
|
70
|
+
# OLD Values still supported:
|
|
71
|
+
# This only support cloning and setting DHCP on the first interface
|
|
72
|
+
# * 'domain'<~String> - *REQUIRED* This is put into
|
|
73
|
+
# /etc/resolve.conf (we hope)
|
|
74
|
+
# * 'hostname'<~String> - Hostname of the Guest Os - default is
|
|
75
|
+
# options['name']
|
|
76
|
+
# * 'hw_utc_clock'<~Boolean> - *REQUIRED* Is hardware clock UTC?
|
|
77
|
+
# Default true
|
|
78
|
+
# * 'time_zone'<~String> - *REQUIRED* Only valid linux options
|
|
79
|
+
# are valid - example: 'America/Denver'
|
|
80
|
+
# * 'interfaces' <~Array> - interfaces object to apply to
|
|
81
|
+
# the template when cloning: overrides the
|
|
82
|
+
# network_label, network_adapter_device_key and nic_type attributes
|
|
83
|
+
# * 'volumes' <~Array> - volumes object to apply to
|
|
84
|
+
# the template when cloning: this allows to resize the
|
|
85
|
+
# existing disks as well as add or remove them. The
|
|
86
|
+
# resizing is applied only when the size is bigger then the
|
|
87
|
+
# in size in the template
|
|
88
|
+
def vm_clone(options = {})
|
|
89
|
+
# Option handling
|
|
90
|
+
options = vm_clone_check_options(options)
|
|
91
|
+
|
|
92
|
+
# Added for people still using options['path']
|
|
93
|
+
template_path = options['path'] || options['template_path']
|
|
94
|
+
|
|
95
|
+
# Options['template_path']<~String>
|
|
96
|
+
# Added for people still using options['path']
|
|
97
|
+
template_path = options['path'] || options['template_path']
|
|
98
|
+
# Now find the template itself using the efficient find method
|
|
99
|
+
vm_mob_ref = get_vm_ref(template_path, options['datacenter'])
|
|
100
|
+
|
|
101
|
+
# Options['dest_folder']<~String>
|
|
102
|
+
# Grab the destination folder object if it exists else use cloned mach
|
|
103
|
+
dest_folder_path = options.fetch('dest_folder','/') # default to root path ({dc_name}/vm/)
|
|
104
|
+
dest_folder = get_raw_vmfolder(dest_folder_path, options['datacenter'])
|
|
105
|
+
|
|
106
|
+
# Options['resource_pool']<~Array>
|
|
107
|
+
# Now find _a_ resource pool to use for the clone if one is not specified
|
|
108
|
+
if ( options.key?('resource_pool') && options['resource_pool'].is_a?(Array) && options['resource_pool'].length == 2 )
|
|
109
|
+
cluster_name = options['resource_pool'][0]
|
|
110
|
+
pool_name = options['resource_pool'][1]
|
|
111
|
+
resource_pool = get_raw_resource_pool(pool_name, cluster_name, options['datacenter'])
|
|
112
|
+
elsif ( vm_mob_ref.resourcePool == nil )
|
|
113
|
+
# If the template is really a template then there is no associated resource pool,
|
|
114
|
+
# so we need to find one using the template's parent host or cluster
|
|
115
|
+
esx_host = vm_mob_ref.collect!('runtime.host')['runtime.host']
|
|
116
|
+
# The parent of the ESX host itself is a ComputeResource which has a resourcePool
|
|
117
|
+
resource_pool = esx_host.parent.resourcePool
|
|
118
|
+
end
|
|
119
|
+
# If the vm given did return a valid resource pool, default to using it for the clone.
|
|
120
|
+
# Even if specific pools aren't implemented in this environment, we will still get back
|
|
121
|
+
# at least the cluster or host we can pass on to the clone task
|
|
122
|
+
# This catches if resource_pool option is set but comes back nil and if resourcePool is
|
|
123
|
+
# already set.
|
|
124
|
+
resource_pool ||= vm_mob_ref.resourcePool.nil? ? esx_host.parent.resourcePool : vm_mob_ref.resourcePool
|
|
125
|
+
|
|
126
|
+
# Options['datastore']<~String>
|
|
127
|
+
# Grab the datastore object if option is set
|
|
128
|
+
datastore_obj = get_raw_datastore(options['datastore'], options['datacenter']) if options.key?('datastore')
|
|
129
|
+
# confirm nil if nil or option is not set
|
|
130
|
+
datastore_obj ||= nil
|
|
131
|
+
virtual_machine_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec()
|
|
132
|
+
|
|
133
|
+
device_change = []
|
|
134
|
+
# fully futured interfaces api: replace the current nics
|
|
135
|
+
# with the new based on the specification
|
|
136
|
+
if (options.key?('interfaces') )
|
|
137
|
+
if options.key?('network_label')
|
|
138
|
+
raise ArgumentError, "interfaces option can't be specified together with network_label"
|
|
139
|
+
end
|
|
140
|
+
device_change.concat(modify_template_nics_specs(template_path, options['interfaces'], options['datacenter']))
|
|
141
|
+
elsif options.key?('network_label')
|
|
142
|
+
device_change << modify_template_nics_simple_spec(options['network_label'], options['nic_type'], options['network_adapter_device_key'], options['datacenter'])
|
|
143
|
+
end
|
|
144
|
+
if disks = options['volumes']
|
|
145
|
+
device_change.concat(modify_template_volumes_specs(vm_mob_ref, options['volumes']))
|
|
146
|
+
end
|
|
147
|
+
virtual_machine_config_spec.deviceChange = device_change if device_change.any?
|
|
148
|
+
# Options['numCPUs'] or Options['memoryMB']
|
|
149
|
+
# Build up the specification for Hardware, for more details see ____________
|
|
150
|
+
# https://github.com/rlane/rbvmomi/blob/master/test/test_serialization.rb
|
|
151
|
+
# http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.ConfigSpec.html
|
|
152
|
+
# FIXME: pad this out with the rest of the useful things in VirtualMachineConfigSpec
|
|
153
|
+
virtual_machine_config_spec.numCPUs = options['numCPUs'] if ( options.key?('numCPUs') )
|
|
154
|
+
virtual_machine_config_spec.memoryMB = options['memoryMB'] if ( options.key?('memoryMB') )
|
|
155
|
+
virtual_machine_config_spec.cpuHotAddEnabled = options['cpuHotAddEnabled'] if ( options.key?('cpuHotAddEnabled') )
|
|
156
|
+
virtual_machine_config_spec.memoryHotAddEnabled = options['memoryHotAddEnabled'] if ( options.key?('memoryHotAddEnabled') )
|
|
157
|
+
virtual_machine_config_spec.firmware = options['firmware'] if ( options.key?('firmware') )
|
|
158
|
+
# Options['customization_spec']
|
|
159
|
+
# OLD Options still supported
|
|
160
|
+
# * domain <~String> - *REQUIRED* - Sets the server's domain for customization
|
|
161
|
+
# * dnsSuffixList <~Array> - Optional - Sets the dns search paths in resolv - Example: ["dev.example.com", "example.com"]
|
|
162
|
+
# * time_zone <~String> - Required - Only valid linux options are valid - example: 'America/Denver'
|
|
163
|
+
# * ipsettings <~Hash> - Optional - If not set defaults to dhcp
|
|
164
|
+
# * ip <~String> - *REQUIRED* Sets the ip address of the VM - Example: 10.0.0.10
|
|
165
|
+
# * dnsServerList <~Array> - Optional - Sets the nameservers in resolv - Example: ["10.0.0.2", "10.0.0.3"]
|
|
166
|
+
# * gateway <~Array> - Optional - Sets the gateway for the interface - Example: ["10.0.0.1"]
|
|
167
|
+
# * subnetMask <~String> - *REQUIRED* - Set the netmask of the interface - Example: "255.255.255.0"
|
|
168
|
+
# For other ip settings options see http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.customization.IPSettings.html
|
|
169
|
+
#
|
|
170
|
+
# Implement complete customization spec as per https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Specification.html
|
|
171
|
+
# * encryptionKey <~Array> - Optional, encryption key used to encypt any encrypted passwords
|
|
172
|
+
# https://pubs.vmware.com/vsphere-51/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.GlobalIPSettings.html
|
|
173
|
+
# * globalIPSettings <~Hash> - REQUIRED
|
|
174
|
+
# * dnsServerList <~Array> - Optional, list of dns servers - Example: ["10.0.0.2", "10.0.0.3"]
|
|
175
|
+
# * dnsSuffixList <~Array> - Optional, List of name resolution suffixes - Example: ["dev.example.com", "example.com"]
|
|
176
|
+
# * identity <~Hash> - REQUIRED, Network identity and settings, similar to Microsoft's Sysprep tool. This is a Sysprep, LinuxPrep, or SysprepText object
|
|
177
|
+
# * Sysprep <~Hash> - Optional, representation of a Windows sysprep.inf answer file.
|
|
178
|
+
# * guiRunOnce: <~Hash> -Optional, representation of the sysprep GuiRunOnce key
|
|
179
|
+
# * commandList: <~Array> - REQUIRED, list of commands to run at first user logon, after guest customization. - Example: ["c:\sysprep\runaftersysprep.cmd", "c:\sysprep\installpuppet.ps1"]
|
|
180
|
+
# * guiUnattended: <~Hash> - REQUIRED, representation of the sysprep GuiUnattended key
|
|
181
|
+
# * autoLogin: boolean - REQUIRED, Flag to determine whether or not the machine automatically logs on as Administrator.
|
|
182
|
+
# * autoLogonCount: int - REQUIRED, specifies the number of times the machine should automatically log on as Administrator
|
|
183
|
+
# * password: <~Hash> - REQUIRED, new administrator password for the machine
|
|
184
|
+
# * plainText: boolean - REQUIRED, specify whether or not the password is in plain text, rather than encrypted
|
|
185
|
+
# * value: <~String> - REQUIRED, password string
|
|
186
|
+
# * timeZone: <~int> - REQUIRED, (see here for values https://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx)
|
|
187
|
+
# * identification: <~Hash> - REQUIRED, representation of the sysprep Identification key
|
|
188
|
+
# * domainAdmin: <~String> - Optional, domain user account used for authentication if the virtual machine is joining a domain
|
|
189
|
+
# * domainAdminPassword: <~Hash> - Optional, password for the domain user account used for authentication
|
|
190
|
+
# * plainText: boolean - REQUIRED, specify whether or not the password is in plain text, rather than encrypted
|
|
191
|
+
# * value: <~String> - REQUIRED, password string
|
|
192
|
+
# * joinDomain: <~String> - Optional, The domain that the virtual machine should join. If this value is supplied, then domainAdmin and domainAdminPassword must also be supplied
|
|
193
|
+
# * joinWorkgroup: <~String> - Optional, The workgroup that the virtual machine should join.
|
|
194
|
+
# * licenseFilePrintData: <~Hash> - Optional, representation of the sysprep LicenseFilePrintData key
|
|
195
|
+
# * autoMode: <~String> - REQUIRED, Server licensing mode. Two strings are supported: 'perSeat' or 'perServer'
|
|
196
|
+
# * autoUsers: <~Int> - Optional, This key is valid only if AutoMode = PerServer. The integer value indicates the number of client licenses
|
|
197
|
+
# * userData: <~Hash> - REQUIRED, representation of the sysprep UserData key
|
|
198
|
+
# * computerName: <~String> - REQUIRED, The computer name of the (Windows) virtual machine. Will be truncates to 15 characters
|
|
199
|
+
# * fullName: <~String> - REQUIRED, User's full name
|
|
200
|
+
# * orgName: <~String> - REQUIRED, User's organization
|
|
201
|
+
# * productId: <~String> - REQUIRED, serial number for os, ignored if using volume licensed instance
|
|
202
|
+
# * LinuxPrep: <~Hash> - Optional, contains machine-wide settings (note the uppercase P)
|
|
203
|
+
# * domain: <~String> - REQUIRED, The fully qualified domain name.
|
|
204
|
+
# * hostName: <~String> - REQUIRED, the network host name
|
|
205
|
+
# * hwClockUTC: <~Boolean> - Optional, Specifies whether the hardware clock is in UTC or local time
|
|
206
|
+
# * 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
|
|
207
|
+
# * SysprepText: <~Hash> - Optional, alternate way to specify the sysprep.inf answer file.
|
|
208
|
+
# * value: <~String> - REQUIRED, Text for the sysprep.inf answer file.
|
|
209
|
+
# * nicSettingMap: <~Array> - Optional, IP settings that are specific to a particular virtual network adapter
|
|
210
|
+
# * Each item in array:
|
|
211
|
+
# * adapter: <~Hash> - REQUIRED, IP settings for the associated virtual network adapter
|
|
212
|
+
# * dnsDomain: <~String> - Optional, DNS domain suffix for adapter
|
|
213
|
+
# * dnsServerList: <~Array> - Optional, list of dns server ip addresses - Example: ["10.0.0.2", "10.0.0.3"]
|
|
214
|
+
# * gateway: <~Array> - Optional, list of gateways - Example: ["10.0.0.2", "10.0.0.3"]
|
|
215
|
+
# * ip: <~String> - Optional, but required if static IP
|
|
216
|
+
# * ipV6Spec: <~Hash> - Optional, IPv^ settings
|
|
217
|
+
# * ipAddress: <~String> - Optional, but required if setting static IP
|
|
218
|
+
# * gateway: <~Array> - Optional, list of ipv6 gateways
|
|
219
|
+
# * netBIOS: <~String> - Optional, NetBIOS settings, if supplied must be one of: disableNetBIOS','enableNetBIOS','enableNetBIOSViaDhcp'
|
|
220
|
+
# * primaryWINS: <~String> - Optional, IP address of primary WINS server
|
|
221
|
+
# * secondaryWINS: <~String> - Optional, IP address of secondary WINS server
|
|
222
|
+
# * subnetMask: <~String> - Optional, subnet mask for adapter
|
|
223
|
+
# * macAddress: <~String> - Optional, MAC address of adapter being customized. This cannot be set by the client
|
|
224
|
+
# * options: <~Hash> Optional operations, currently only win options have any value
|
|
225
|
+
# * changeSID: <~Boolean> - REQUIRED, The customization process should modify the machine's security identifier
|
|
226
|
+
# * deleteAccounts: <~Boolean> - REQUIRED, If deleteAccounts is true, then all user accounts are removed from the system
|
|
227
|
+
# * reboot: <~String> - Optional, (defaults to reboot), Action to be taken after running sysprep, must be one of: 'noreboot', 'reboot', 'shutdown'
|
|
228
|
+
#
|
|
229
|
+
if ( options.key?('customization_spec') )
|
|
230
|
+
custom_spec = options['customization_spec']
|
|
231
|
+
|
|
232
|
+
# backwards compatablity
|
|
233
|
+
if custom_spec.key?('domain')
|
|
234
|
+
# doing this means the old options quash any new ones passed as well... might not be the best way to do it?
|
|
235
|
+
# any 'old' options overwrite the following:
|
|
236
|
+
# - custom_spec['identity']['LinuxPrep']
|
|
237
|
+
# - custom_spec['globalIPSettings['['dnsServerList']
|
|
238
|
+
# - custom_spec['globalIPSettings']['dnsSuffixList']
|
|
239
|
+
# - custom_spec['nicSettingMap'][0]['adapter']['ip']
|
|
240
|
+
# - custom_spec['nicSettingMap'][0]['adapter']['gateway']
|
|
241
|
+
# - custom_spec['nicSettingMap'][0]['adapter']['subnetMask']
|
|
242
|
+
# - custom_spec['nicSettingMap'][0]['adapter']['dnsDomain']
|
|
243
|
+
# - custom_spec['nicSettingMap'][0]['adapter']['dnsServerList']
|
|
244
|
+
#
|
|
245
|
+
# we can assume old parameters being passed
|
|
246
|
+
cust_hostname = custom_spec['hostname'] || options['name']
|
|
247
|
+
custom_spec['identity'] = Hash.new unless custom_spec.key?('identity')
|
|
248
|
+
custom_spec['identity']['LinuxPrep'] = {"domain" => custom_spec['domain'], "hostName" => cust_hostname, "timeZone" => custom_spec['time_zone']}
|
|
249
|
+
|
|
250
|
+
if custom_spec.key?('ipsettings')
|
|
251
|
+
custom_spec['globalIPSettings']=Hash.new unless custom_spec.key?('globalIPSettings')
|
|
252
|
+
custom_spec['globalIPSettings']['dnsServerList'] = custom_spec['ipsettings']['dnsServerList'] if custom_spec['ipsettings'].key?('dnsServerList')
|
|
253
|
+
custom_spec['globalIPSettings']['dnsSuffixList'] = custom_spec['dnsSuffixList'] || [custom_spec['domain']] if ( custom_spec['dnsSuffixList'] || custom_spec['domain'])
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
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'))
|
|
257
|
+
if custom_spec['ipsettings'].key?('ip')
|
|
258
|
+
raise ArgumentError, "subnetMask is required for static ip" unless custom_spec["ipsettings"].key?("subnetMask")
|
|
259
|
+
end
|
|
260
|
+
custom_spec['nicSettingMap']=Array.new unless custom_spec.key?('nicSettingMap')
|
|
261
|
+
custom_spec['nicSettingMap'][0]=Hash.new unless custom_spec['nicSettingMap'].length > 0
|
|
262
|
+
custom_spec['nicSettingMap'][0]['adapter']=Hash.new unless custom_spec['nicSettingMap'][0].key?('adapter')
|
|
263
|
+
custom_spec['nicSettingMap'][0]['adapter']['ip'] = custom_spec['ipsettings']['ip'] if custom_spec['ipsettings'].key?('ip')
|
|
264
|
+
custom_spec['nicSettingMap'][0]['adapter']['gateway'] = custom_spec['ipsettings']['gateway'] if custom_spec['ipsettings'].key?('gateway')
|
|
265
|
+
custom_spec['nicSettingMap'][0]['adapter']['subnetMask'] = custom_spec['ipsettings']['subnetMask'] if custom_spec['ipsettings'].key?('subnetMask')
|
|
266
|
+
custom_spec['nicSettingMap'][0]['adapter']['dnsDomain'] = custom_spec['ipsettings']['domain'] if custom_spec['ipsettings'].key?('domain')
|
|
267
|
+
custom_spec['nicSettingMap'][0]['adapter']['dnsServerList'] = custom_spec['ipsettings']['dnsServerList'] if custom_spec['ipsettings'].key?('dnsServerList')
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
### End of backwards compatability
|
|
271
|
+
|
|
272
|
+
## requirements check here ##
|
|
273
|
+
raise ArgumentError, "globalIPSettings are required when using Customization Spec" unless custom_spec.key?('globalIPSettings')
|
|
274
|
+
raise ArgumentError, "identity is required when using Customization Spec" unless custom_spec.key?('identity')
|
|
275
|
+
|
|
276
|
+
# encryptionKey
|
|
277
|
+
custom_encryptionKey = custom_spec['encryptionKey'] if custom_spec.key?('encryptionKey')
|
|
278
|
+
custom_encryptionKey ||= nil
|
|
279
|
+
|
|
280
|
+
# globalIPSettings
|
|
281
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.GlobalIPSettings.html
|
|
282
|
+
custom_globalIPSettings = RbVmomi::VIM::CustomizationGlobalIPSettings.new()
|
|
283
|
+
custom_globalIPSettings.dnsServerList = custom_spec['globalIPSettings']['dnsServerList'] if custom_spec['globalIPSettings'].key?("dnsServerList")
|
|
284
|
+
custom_globalIPSettings.dnsSuffixList = custom_spec['globalIPSettings']['dnsSuffixList'] if custom_spec['globalIPSettings'].key?("dnsSuffixList")
|
|
285
|
+
|
|
286
|
+
# identity
|
|
287
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.IdentitySettings.html
|
|
288
|
+
# Accepts the 3 supported CustomizationIdentitySettings Types:
|
|
289
|
+
# 1. CustomizationLinuxPrep (LinuxPrep) - note the uppercase P
|
|
290
|
+
# 2. CustomizationSysprep (Sysprep)
|
|
291
|
+
# 3. CustomizationSysprepText (SysprepText)
|
|
292
|
+
# At least one of these is required
|
|
293
|
+
#
|
|
294
|
+
identity = custom_spec['identity']
|
|
295
|
+
if identity.key?("LinuxPrep")
|
|
296
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.LinuxPrep.html
|
|
297
|
+
# Fields:
|
|
298
|
+
# * domain: string **REQUIRED**
|
|
299
|
+
# * hostName: string (CustomizationName) **REQUIRED** Will use options['name'] if not provided.
|
|
300
|
+
# * hwClockUTC: boolean
|
|
301
|
+
# * timeZone: string (https://pubs.vmware.com/vsphere-55/topic/com.vmware.wssdk.apiref.doc/timezone.html)
|
|
302
|
+
raise ArgumentError, "domain is required when using LinuxPrep identity" unless identity['LinuxPrep'].key?('domain')
|
|
303
|
+
custom_identity = RbVmomi::VIM::CustomizationLinuxPrep(:domain => identity['LinuxPrep']['domain'])
|
|
304
|
+
cust_hostname = RbVmomi::VIM::CustomizationFixedName(:name => identity['LinuxPrep']['hostName']) if identity['LinuxPrep'].key?('hostName')
|
|
305
|
+
cust_hostname ||= RbVmomi::VIM::CustomizationFixedName(:name => options['name'])
|
|
306
|
+
custom_identity.hostName = cust_hostname
|
|
307
|
+
custom_identity.hwClockUTC = identity['LinuxPrep']['hwClockUTC'] if identity['LinuxPrep'].key?('hwClockUTC')
|
|
308
|
+
custom_identity.timeZone = identity['LinuxPrep']['timeZone'] if identity['LinuxPrep'].key?('timeZone')
|
|
309
|
+
elsif identity.key?("Sysprep")
|
|
310
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Sysprep.html
|
|
311
|
+
# Fields:
|
|
312
|
+
# * guiRunOnce: CustomizationGuiRunOnce
|
|
313
|
+
# * guiUnattended: CustomizationGuiUnattended **REQUIRED**
|
|
314
|
+
# * identification: CustomizationIdentification **REQUIRED**
|
|
315
|
+
# * licenseFilePrintData: CustomizationLicenseFilePrintData
|
|
316
|
+
# * userData: CustomizationUserData **REQUIRED**
|
|
317
|
+
#
|
|
318
|
+
raise ArgumentError, "guiUnattended is required when using Sysprep identity" unless identity['Sysprep'].key?('guiUnattended')
|
|
319
|
+
raise ArgumentError, "identification is required when using Sysprep identity" unless identity['Sysprep'].key?('identification')
|
|
320
|
+
raise ArgumentError, "userData is required when using Sysprep identity" unless identity['Sysprep'].key?('userData')
|
|
321
|
+
if identity['Sysprep']['guiRunOnce']
|
|
322
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.GuiRunOnce.html
|
|
323
|
+
# Fields:
|
|
324
|
+
# * commandList: array of string **REQUIRED***
|
|
325
|
+
#
|
|
326
|
+
raise ArgumentError, "commandList is required when using Sysprep identity and guiRunOnce" unless identity['Sysprep']['guiRunOnce'].key?('commandList')
|
|
327
|
+
cust_guirunonce = RbVmomi::VIM.CustomizationGuiRunOnce( :commandList => identity['Sysprep']['guiRunOnce']['commandList'] )
|
|
328
|
+
end
|
|
329
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.GuiUnattended.html
|
|
330
|
+
# Fields:
|
|
331
|
+
# * autoLogin: boolean **REQUIRED**
|
|
332
|
+
# * autoLogonCount: int **REQUIRED**
|
|
333
|
+
# * timeZone: int (see here for values https://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx) **REQUIRED**
|
|
334
|
+
# * password: CustomizationPassword
|
|
335
|
+
raise ArgumentError, "guiUnattended->autoLogon is required when using Sysprep identity" unless identity['Sysprep']['guiUnattended'].key?('autoLogon')
|
|
336
|
+
raise ArgumentError, "guiUnattended->autoLogonCount is required when using Sysprep identity" unless identity['Sysprep']['guiUnattended'].key?('autoLogonCount')
|
|
337
|
+
raise ArgumentError, "guiUnattended->timeZone is required when using Sysprep identity" unless identity['Sysprep']['guiUnattended'].key?('timeZone')
|
|
338
|
+
custom_guiUnattended = RbVmomi::VIM.CustomizationGuiUnattended(
|
|
339
|
+
:autoLogon => identity['Sysprep']['guiUnattended']['autoLogon'],
|
|
340
|
+
:autoLogonCount => identity['Sysprep']['guiUnattended']['autoLogonCount'],
|
|
341
|
+
:timeZone => identity['Sysprep']['guiUnattended']['timeZone']
|
|
342
|
+
)
|
|
343
|
+
if identity['Sysprep']['guiUnattended']['password']
|
|
344
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Password.html
|
|
345
|
+
# Fields:
|
|
346
|
+
# * plainText: boolean **REQUIRED**
|
|
347
|
+
# * value: string **REQUIRED**
|
|
348
|
+
raise ArgumentError, "guiUnattended->password->plainText is required when using Sysprep identity and guiUnattended -> password" unless identity['Sysprep']['guiUnattended']['password'].key?('plainText')
|
|
349
|
+
raise ArgumentError, "guiUnattended->password->value is required when using Sysprep identity and guiUnattended -> password" unless identity['Sysprep']['guiUnattended']['password'].key?('value')
|
|
350
|
+
custom_guiUnattended.password = RbVmomi::VIM.CustomizationPassword(
|
|
351
|
+
:plainText => identity['Sysprep']['guiUnattended']['password']['plainText'],
|
|
352
|
+
:value => identity['Sysprep']['guiUnattended']['password']['value']
|
|
353
|
+
)
|
|
354
|
+
end
|
|
355
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Identification.html
|
|
356
|
+
# Fields:
|
|
357
|
+
# * domainAdmin: string
|
|
358
|
+
# * domainAdminPassword: CustomizationPassword
|
|
359
|
+
# * joinDomain: string *If supplied domainAdmin and domainAdminPassword must be set
|
|
360
|
+
# * joinWorkgroup: string *If supplied, joinDomain, domainAdmin and domainAdminPassword will be ignored
|
|
361
|
+
custom_identification = RbVmomi::VIM.CustomizationIdentification()
|
|
362
|
+
if identity['Sysprep']['identification'].key?('joinWorkgroup')
|
|
363
|
+
custom_identification.joinWorkgroup = identity['Sysprep']['identification']['joinWorkgroup']
|
|
364
|
+
elsif identity['Sysprep']['identification'].key?('joinDomain')
|
|
365
|
+
raise ArgumentError, "identification->domainAdmin is required when using Sysprep identity and identification -> joinDomain" unless identity['Sysprep']['identification'].key?('domainAdmin')
|
|
366
|
+
raise ArgumentError, "identification->domainAdminPassword is required when using Sysprep identity and identification -> joinDomain" unless identity['Sysprep']['identification'].key?('domainAdmin')
|
|
367
|
+
raise ArgumentError, "identification->domainAdminPassword->plainText is required when using Sysprep identity and identification -> joinDomain" unless identity['Sysprep']['identification']['domainAdminPassword'].key?('plainText')
|
|
368
|
+
raise ArgumentError, "identification->domainAdminPassword->value is required when using Sysprep identity and identification -> joinDomain" unless identity['Sysprep']['identification']['domainAdminPassword'].key?('value')
|
|
369
|
+
custom_identification.joinDomain = identity['Sysprep']['identification']['joinDomain']
|
|
370
|
+
custom_identification.domainAdmin = identity['Sysprep']['identification']['domainAdmin']
|
|
371
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Password.html
|
|
372
|
+
# Fields:
|
|
373
|
+
# * plainText: boolean **REQUIRED**
|
|
374
|
+
# * value: string **REQUIRED**
|
|
375
|
+
custom_identification.domainAdminPassword = RbVmomi::VIM.CustomizationPassword(
|
|
376
|
+
:plainText => identity['Sysprep']['identification']['domainAdminPassword']['plainText'],
|
|
377
|
+
:value => identity['Sysprep']['identification']['domainAdminPassword']['value']
|
|
378
|
+
)
|
|
379
|
+
else
|
|
380
|
+
raise ArgumentError, "No valid Indentification found, valid values are 'joinWorkgroup' and 'joinDomain'"
|
|
381
|
+
end
|
|
382
|
+
if identity['Sysprep'].key?('licenseFilePrintData')
|
|
383
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.LicenseFilePrintData.html
|
|
384
|
+
# Fields:
|
|
385
|
+
# * autoMode: string (CustomizationLicenseDataMode) ** REQUIRED **, valid strings are: 'perSeat' or 'perServer'
|
|
386
|
+
# * autoUsers: int (valid only if AutoMode = PerServer)
|
|
387
|
+
raise ArgumentError, "licenseFilePrintData->autoMode is required when using Sysprep identity and licenseFilePrintData" unless identity['Sysprep']['licenseFilePrintData'].key?('autoMode')
|
|
388
|
+
raise ArgumentError, "Unsupported autoMode, supported modes are : 'perSeat' or 'perServer'" unless ['perSeat', 'perServer'].include? identity['Sysprep']['licenseFilePrintData']['autoMode']
|
|
389
|
+
custom_licenseFilePrintData = RbVmomi::VIM.CustomizationLicenseFilePrintData(
|
|
390
|
+
:autoMode => RbVmomi::VIM.CustomizationLicenseDataMode(identity['Sysprep']['licenseFilePrintData']['autoMode'])
|
|
391
|
+
)
|
|
392
|
+
if identity['Sysprep']['licenseFilePrintData'].key?('autoUsers')
|
|
393
|
+
custom_licenseFilePrintData.autoUsers = identity['Sysprep']['licenseFilePrintData']['autoUsers'] if identity['Sysprep']['licenseFilePrintData']['autoMode'] == "PerServer"
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.UserData.html
|
|
397
|
+
# Fields:
|
|
398
|
+
# * computerName: string (CustomizationFixedName) **REQUIRED**
|
|
399
|
+
# * fullName: string **REQUIRED**
|
|
400
|
+
# * orgName: string **REQUIRED**
|
|
401
|
+
# * productID: string **REQUIRED**
|
|
402
|
+
raise ArgumentError, "userData->computerName is required when using Sysprep identity" unless identity['Sysprep']['userData'].key?('computerName')
|
|
403
|
+
raise ArgumentError, "userData->fullName is required when using Sysprep identity" unless identity['Sysprep']['userData'].key?('fullName')
|
|
404
|
+
raise ArgumentError, "userData->orgName is required when using Sysprep identity" unless identity['Sysprep']['userData'].key?('orgName')
|
|
405
|
+
raise ArgumentError, "userData->productId is required when using Sysprep identity" unless identity['Sysprep']['userData'].key?('productId')
|
|
406
|
+
custom_userData = RbVmomi::VIM.CustomizationUserData(
|
|
407
|
+
:fullName => identity['Sysprep']['userData']['fullName'],
|
|
408
|
+
:orgName => identity['Sysprep']['userData']['orgName'],
|
|
409
|
+
:productId => identity['Sysprep']['userData']['productId'],
|
|
410
|
+
:computerName => RbVmomi::VIM.CustomizationFixedName(:name => identity['Sysprep']['userData']['computerName'])
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
custom_identity = RbVmomi::VIM::CustomizationSysprep(
|
|
414
|
+
:guiUnattended => custom_guiUnattended,
|
|
415
|
+
:identification => custom_identification,
|
|
416
|
+
:userData => custom_userData
|
|
417
|
+
)
|
|
418
|
+
custom_identity.guiRunOnce = cust_guirunonce if defined?(cust_guirunonce)
|
|
419
|
+
custom_identity.licenseFilePrintData = custom_licenseFilePrintData if defined?(custom_licenseFilePrintData)
|
|
420
|
+
elsif identity.key?("SysprepText")
|
|
421
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.SysprepText.html
|
|
422
|
+
# Fields:
|
|
423
|
+
# * value: string **REQUIRED**
|
|
424
|
+
raise ArgumentError, "SysprepText -> value is required when using SysprepText identity" unless identity['SysprepText'].key?('value')
|
|
425
|
+
custom_identity = RbVmomi::VIM::CustomizationSysprepText(:value => identity['SysprepText']['value'])
|
|
426
|
+
else
|
|
427
|
+
raise ArgumentError, "At least one of the following valid identities must be supplied: LinuxPrep, Sysprep, SysprepText"
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
if custom_spec.key?("nicSettingMap")
|
|
431
|
+
# custom_spec['nicSettingMap'] is an array of adapater mappings:
|
|
432
|
+
# custom_spec['nicSettingMap'][0]['macAddress']
|
|
433
|
+
# custom_spec['nicSettingMap'][0]['adapter']['ip']
|
|
434
|
+
#https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.AdapterMapping.html
|
|
435
|
+
# Fields:
|
|
436
|
+
# * adapter: CustomizationIPSettings **REQUIRED**
|
|
437
|
+
# * macAddress: string
|
|
438
|
+
raise ArgumentError, "At least one nicSettingMap is required when using nicSettingMap" unless custom_spec['nicSettingMap'].length > 0
|
|
439
|
+
raise ArgumentError, "Adapter is required when using nicSettingMap" unless custom_spec['nicSettingMap'][0].key?('adapter')
|
|
440
|
+
|
|
441
|
+
custom_nicSettingMap = []
|
|
442
|
+
# need to go through array here for each apapter
|
|
443
|
+
custom_spec['nicSettingMap'].each do | nic |
|
|
444
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvim.vm.customization.IPSettings.html
|
|
445
|
+
# Fields:
|
|
446
|
+
# * dnsDomain: string
|
|
447
|
+
# * gateway: array of string
|
|
448
|
+
# * ip: CustomizationIpGenerator (string) **REQUIRED IF Assigning Static IP***
|
|
449
|
+
# * ipV6Spec: CustomizationIPSettingsIpV6AddressSpec
|
|
450
|
+
# * netBIOS: CustomizationNetBIOSMode (string)
|
|
451
|
+
# * primaryWINS: string
|
|
452
|
+
# * secondaryWINS: string
|
|
453
|
+
# * subnetMask: string - Required if assigning static IP
|
|
454
|
+
if nic['adapter'].key?('ip')
|
|
455
|
+
raise ArgumentError, "SubnetMask is required when assigning static IP when using nicSettingMap -> Adapter" unless nic['adapter'].key?('subnetMask')
|
|
456
|
+
custom_ip = RbVmomi::VIM.CustomizationFixedIp(:ipAddress => nic['adapter']['ip'])
|
|
457
|
+
else
|
|
458
|
+
custom_ip = RbVmomi::VIM::CustomizationDhcpIpGenerator.new()
|
|
459
|
+
end
|
|
460
|
+
custom_adapter = RbVmomi::VIM.CustomizationIPSettings(:ip => custom_ip)
|
|
461
|
+
custom_adapter.dnsDomain = nic['adapter']['dnsDomain'] if nic['adapter'].key?('dnsDomain')
|
|
462
|
+
custom_adapter.dnsServerList = nic['adapter']['dnsServerList'] if nic['adapter'].key?('dnsServerList')
|
|
463
|
+
custom_adapter.gateway = nic['adapter']['gateway'] if nic['adapter'].key?('gateway')
|
|
464
|
+
if nic['adapter'].key?('ipV6Spec')
|
|
465
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.IPSettings.IpV6AddressSpec.html
|
|
466
|
+
# Fields:
|
|
467
|
+
# * gateway: array of string
|
|
468
|
+
# * ip: CustomizationIpV6Generator[] **Required if setting static IP **
|
|
469
|
+
if nic['adapter']['ipV6Spec'].key?('ipAddress')
|
|
470
|
+
raise ArgumentError, "SubnetMask is required when assigning static IPv6 when using nicSettingMap -> Adapter -> ipV6Spec" unless nic['adapter']['ipV6Spec'].key?('subnetMask')
|
|
471
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.FixedIpV6.html
|
|
472
|
+
# * ipAddress: string **REQUIRED**
|
|
473
|
+
# * subnetMask: int **REQUIRED**
|
|
474
|
+
custom_ipv6 = RbVmomi::VIM.CustomizationFixedIpV6(
|
|
475
|
+
:ipAddress => nic['adapter']['ipV6Spec']['ipAddress'],
|
|
476
|
+
:subnetMask => nic['adapter']['ipV6Spec']['subnetMask']
|
|
477
|
+
)
|
|
478
|
+
else
|
|
479
|
+
custom_ipv6 = RbVmomi::VIM::CustomizationDhcpIpV6Generator.new()
|
|
480
|
+
end
|
|
481
|
+
custom_ipv6Spec = RbVmomi::VIM.CustomizationIPSettingsIpV6AddressSpec(:ip => custom_ipv6)
|
|
482
|
+
custom_ipv6Spec.gateway = nic['adapter']['ipV6Spec']['gateway'] if nic['adapter']['ipV6Spec'].key?('gateway')
|
|
483
|
+
custom_adapter.ipV6Spec = custom_ipv6Spec
|
|
484
|
+
end
|
|
485
|
+
if nic['adapter'].key?('netBIOS')
|
|
486
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.IPSettings.NetBIOSMode.html
|
|
487
|
+
# Fields:
|
|
488
|
+
# netBIOS: string matching: 'disableNetBIOS','enableNetBIOS' or 'enableNetBIOSViaDhcp' ** REQUIRED **
|
|
489
|
+
#
|
|
490
|
+
raise ArgumentError, "Unsupported NetBIOSMode, supported modes are : 'disableNetBIOS','enableNetBIOS' or 'enableNetBIOSViaDhcp'" unless ['disableNetBIOS','enableNetBIOS','enableNetBIOSViaDhcp'].include? nic['adapter']['netBIOS']
|
|
491
|
+
custom_adapter.netBIOS = RbVmomi::VIM.CustomizationNetBIOSMode(nic['adapter']['netBIOS'])
|
|
492
|
+
end
|
|
493
|
+
custom_adapter.primaryWINS = nic['adapter']['primaryWINS'] if nic['adapter'].key?('primaryWINS')
|
|
494
|
+
custom_adapter.secondaryWINS = nic['adapter']['secondaryWINS'] if nic['adapter'].key?('secondaryWINS')
|
|
495
|
+
custom_adapter.subnetMask = nic['adapter']['subnetMask'] if nic['adapter'].key?('subnetMask')
|
|
496
|
+
|
|
497
|
+
custom_adapter_mapping = RbVmomi::VIM::CustomizationAdapterMapping(:adapter => custom_adapter)
|
|
498
|
+
custom_adapter_mapping.macAddress = nic['macAddress'] if nic.key?('macAddress')
|
|
499
|
+
|
|
500
|
+
# build the adapters array, creates it if not already created, otherwise appends to it
|
|
501
|
+
custom_nicSettingMap << custom_adapter_mapping
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
custom_nicSettingMap = nil if custom_nicSettingMap.length < 1
|
|
505
|
+
|
|
506
|
+
if custom_spec.key?("options")
|
|
507
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Options.html
|
|
508
|
+
# this currently doesn't have any Linux options, just windows
|
|
509
|
+
# Fields:
|
|
510
|
+
# * changeSID: boolean **REQUIRED**
|
|
511
|
+
# * deleteAccounts: boolean **REQUIRED** **note deleteAccounts is deprecated as of VI API 2.5 so can be ignored
|
|
512
|
+
# * reboot: CustomizationSysprepRebootOption: (string) one of following 'noreboot', reboot' or 'shutdown' (defaults to reboot)
|
|
513
|
+
raise ArgumentError, "changeSID id required when using Windows Options" unless custom_spec['options'].key?('changeSID')
|
|
514
|
+
raise ArgumentError, "deleteAccounts id required when using Windows Options" unless custom_spec['options'].key?('deleteAccounts')
|
|
515
|
+
custom_options = RbVmomi::VIM::CustomizationWinOptions(
|
|
516
|
+
:changeSID => custom_spec['options']['changeSID'],
|
|
517
|
+
:deleteAccounts => custom_spec['options']['deleteAccounts']
|
|
518
|
+
)
|
|
519
|
+
if custom_spec['options'].key?('reboot')
|
|
520
|
+
raise ArgumentError, "Unsupported reboot option, supported options are : 'noreboot', 'reboot' or 'shutdown'" unless ['noreboot','reboot','shutdown'].include? custom_spec['options']['reboot']
|
|
521
|
+
custom_options.reboot = RBVmomi::VIM.CustomizationSysprepRebootOption(custom_spec['options']['reboot'])
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
custom_options ||=nil
|
|
525
|
+
|
|
526
|
+
# https://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.wssdk.apiref.doc/vim.vm.customization.Specification.html
|
|
527
|
+
customization_spec = RbVmomi::VIM::CustomizationSpec(
|
|
528
|
+
:globalIPSettings => custom_globalIPSettings,
|
|
529
|
+
:identity => custom_identity
|
|
530
|
+
)
|
|
531
|
+
customization_spec.encryptionKey = custom_encryptionKey if defined?(custom_encryptionKey)
|
|
532
|
+
customization_spec.nicSettingMap = custom_nicSettingMap if defined?(custom_nicSettingMap)
|
|
533
|
+
customization_spec.options = custom_options if defined?(custom_options)
|
|
534
|
+
|
|
535
|
+
end
|
|
536
|
+
customization_spec ||= nil
|
|
537
|
+
|
|
538
|
+
relocation_spec=nil
|
|
539
|
+
if ( options['linked_clone'] )
|
|
540
|
+
# cribbed heavily from the rbvmomi clone_vm.rb
|
|
541
|
+
# this chunk of code reconfigures the disk of the clone source to be read only,
|
|
542
|
+
# and then creates a delta disk on top of that, this is required by the API in order to create
|
|
543
|
+
# linked clondes
|
|
544
|
+
disks = vm_mob_ref.config.hardware.device.select do |vm_device|
|
|
545
|
+
vm_device.class == RbVmomi::VIM::VirtualDisk
|
|
546
|
+
end
|
|
547
|
+
disks.select{|vm_device| vm_device.backing.parent == nil}.each do |disk|
|
|
548
|
+
disk_spec = {
|
|
549
|
+
:deviceChange => [
|
|
550
|
+
{
|
|
551
|
+
:operation => :remove,
|
|
552
|
+
:device => disk
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
:operation => :add,
|
|
556
|
+
:fileOperation => :create,
|
|
557
|
+
:device => disk.dup.tap{|disk_backing|
|
|
558
|
+
disk_backing.backing = disk_backing.backing.dup;
|
|
559
|
+
disk_backing.backing.fileName = "[#{disk.backing.datastore.name}]";
|
|
560
|
+
disk_backing.backing.parent = disk.backing
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
]
|
|
564
|
+
}
|
|
565
|
+
vm_mob_ref.ReconfigVM_Task(:spec => disk_spec).wait_for_completion
|
|
566
|
+
end
|
|
567
|
+
# Next, create a Relocation Spec instance
|
|
568
|
+
relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:datastore => datastore_obj,
|
|
569
|
+
:pool => resource_pool,
|
|
570
|
+
:diskMoveType => :moveChildMostDiskBacking)
|
|
571
|
+
else
|
|
572
|
+
relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:datastore => datastore_obj,
|
|
573
|
+
:pool => resource_pool,
|
|
574
|
+
:transform => options['transform'] || 'sparse')
|
|
575
|
+
end
|
|
576
|
+
# And the clone specification
|
|
577
|
+
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => relocation_spec,
|
|
578
|
+
:config => virtual_machine_config_spec,
|
|
579
|
+
:customization => customization_spec,
|
|
580
|
+
:powerOn => options.key?('power_on') ? options['power_on'] : true,
|
|
581
|
+
:template => false)
|
|
582
|
+
|
|
583
|
+
# Perform the actual Clone Task
|
|
584
|
+
task = vm_mob_ref.CloneVM_Task(:folder => dest_folder,
|
|
585
|
+
:name => options['name'],
|
|
586
|
+
:spec => clone_spec)
|
|
587
|
+
# Waiting for the VM to complete allows us to get the VirtulMachine
|
|
588
|
+
# object of the new machine when it's done. It is HIGHLY recommended
|
|
589
|
+
# to set 'wait' => true if your app wants to wait. Otherwise, you're
|
|
590
|
+
# going to have to reload the server model over and over which
|
|
591
|
+
# generates a lot of time consuming API calls to vmware.
|
|
592
|
+
if options.fetch('wait', true) then
|
|
593
|
+
# REVISIT: It would be awesome to call a block passed to this
|
|
594
|
+
# request to notify the application how far along in the process we
|
|
595
|
+
# are. I'm thinking of updating a progress bar, etc...
|
|
596
|
+
new_vm = task.wait_for_completion
|
|
597
|
+
else
|
|
598
|
+
tries = 0
|
|
599
|
+
new_vm = begin
|
|
600
|
+
# Try and find the new VM (folder.find is quite efficient)
|
|
601
|
+
dest_folder.find(options['name'], RbVmomi::VIM::VirtualMachine) or raise Fog::Vsphere::Errors::NotFound
|
|
602
|
+
rescue Fog::Vsphere::Errors::NotFound
|
|
603
|
+
tries += 1
|
|
604
|
+
if tries <= 10 then
|
|
605
|
+
sleep 15
|
|
606
|
+
retry
|
|
607
|
+
end
|
|
608
|
+
nil
|
|
609
|
+
end
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
# Return hash
|
|
613
|
+
{
|
|
614
|
+
'vm_ref' => new_vm ? new_vm._ref : nil,
|
|
615
|
+
'new_vm' => new_vm ? convert_vm_mob_ref_to_attr_hash(new_vm) : nil,
|
|
616
|
+
'task_ref' => task._ref
|
|
617
|
+
}
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# Build up the network config spec for simple case:
|
|
621
|
+
# simple case: apply just the network_label, nic_type and network_adapter_device_key
|
|
622
|
+
def modify_template_nics_simple_spec(network_label, nic_type, network_adapter_device_key, datacenter)
|
|
623
|
+
config_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit')
|
|
624
|
+
# Get the portgroup and handle it from there.
|
|
625
|
+
network = get_raw_network(network_label, datacenter)
|
|
626
|
+
if ( network.kind_of? RbVmomi::VIM::DistributedVirtualPortgroup)
|
|
627
|
+
# Create the NIC backing for the distributed virtual portgroup
|
|
628
|
+
nic_backing_info = RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo(
|
|
629
|
+
:port => RbVmomi::VIM::DistributedVirtualSwitchPortConnection(
|
|
630
|
+
:portgroupKey => network.key,
|
|
631
|
+
:switchUuid => network.config.distributedVirtualSwitch.uuid
|
|
632
|
+
)
|
|
633
|
+
)
|
|
634
|
+
else
|
|
635
|
+
# Otherwise it's a non distributed port group
|
|
636
|
+
nic_backing_info = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(:deviceName => network_label)
|
|
637
|
+
end
|
|
638
|
+
connectable = RbVmomi::VIM::VirtualDeviceConnectInfo(
|
|
639
|
+
:allowGuestControl => true,
|
|
640
|
+
:connected => true,
|
|
641
|
+
:startConnected => true)
|
|
642
|
+
device = RbVmomi::VIM.public_send "#{nic_type}",
|
|
643
|
+
:backing => nic_backing_info,
|
|
644
|
+
:deviceInfo => RbVmomi::VIM::Description(:label => "Network adapter 1", :summary => network_label),
|
|
645
|
+
:key => network_adapter_device_key,
|
|
646
|
+
:connectable => connectable
|
|
647
|
+
device_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
|
|
648
|
+
:operation => config_spec_operation,
|
|
649
|
+
:device => device)
|
|
650
|
+
return device_spec
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def modify_template_nics_specs(template_path, new_nics, datacenter)
|
|
655
|
+
template_nics = list_vm_interfaces(template_path, datacenter).map do |old_attributes|
|
|
656
|
+
Fog::Compute::Vsphere::Interface.new(old_attributes)
|
|
657
|
+
end
|
|
658
|
+
specs = []
|
|
659
|
+
|
|
660
|
+
template_nics.each do |interface|
|
|
661
|
+
specs << create_interface(interface, interface.key, :remove, :datacenter => datacenter)
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
new_nics.each do |interface|
|
|
665
|
+
specs << create_interface(interface, 0, :add, :datacenter => datacenter)
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
return specs
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
def modify_template_volumes_specs(vm_mob_ref, volumes)
|
|
672
|
+
template_volumes = vm_mob_ref.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
|
|
673
|
+
modified_volumes = volumes.take(template_volumes.size)
|
|
674
|
+
new_volumes = volumes.drop(template_volumes.size)
|
|
675
|
+
|
|
676
|
+
specs = []
|
|
677
|
+
template_volumes.zip(modified_volumes).each do |template_volume, new_volume|
|
|
678
|
+
if new_volume
|
|
679
|
+
# updated the attribtues on the existing volume
|
|
680
|
+
# it's not allowed to reduce the size of the volume when cloning
|
|
681
|
+
if new_volume.size > template_volume.capacityInKB
|
|
682
|
+
template_volume.capacityInKB = new_volume.size
|
|
683
|
+
end
|
|
684
|
+
template_volume.backing.diskMode = new_volume.mode
|
|
685
|
+
template_volume.backing.thinProvisioned = new_volume.thin
|
|
686
|
+
specs << { :operation => :edit, :device => template_volume }
|
|
687
|
+
else
|
|
688
|
+
specs << { :operation => :remove,
|
|
689
|
+
:fileOperation => :destroy,
|
|
690
|
+
:device => template_volume }
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
specs.concat(new_volumes.map { |volume| create_disk(volume, volumes.index(volume)) })
|
|
694
|
+
return specs
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
class Mock
|
|
699
|
+
include Shared
|
|
700
|
+
def vm_clone(options = {})
|
|
701
|
+
# Option handling TODO Needs better method of checking
|
|
702
|
+
options = vm_clone_check_options(options)
|
|
703
|
+
notfound = lambda { raise Fog::Compute::Vsphere::NotFound, "Could not find VM template" }
|
|
704
|
+
template = list_virtual_machines.find(notfound) do |vm|
|
|
705
|
+
vm['name'] == options['template_path'].split("/")[-1]
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
# generate a random id
|
|
709
|
+
id = [8,4,4,4,12].map{|i| Fog::Mock.random_hex(i)}.join("-")
|
|
710
|
+
new_vm = template.clone.merge({
|
|
711
|
+
"name" => options['name'],
|
|
712
|
+
"id" => id,
|
|
713
|
+
"instance_uuid" => id,
|
|
714
|
+
"path" => "/Datacenters/#{options['datacenter']}/#{options['dest_folder'] ? options['dest_folder']+"/" : ""}#{options['name']}"
|
|
715
|
+
})
|
|
716
|
+
self.data[:servers][id] = new_vm
|
|
717
|
+
|
|
718
|
+
{
|
|
719
|
+
'vm_ref' => "vm-#{Fog::Mock.random_numbers(3)}",
|
|
720
|
+
'new_vm' => new_vm,
|
|
721
|
+
'task_ref' => "task-#{Fog::Mock.random_numbers(4)}",
|
|
722
|
+
}
|
|
723
|
+
end
|
|
724
|
+
end
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
end
|