knife-vsphere 0.4.0 → 0.5.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.
@@ -1,180 +1,219 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
-
# Contributor:: Jesse Campbell (<hikeit@gmail.com>)
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
|
7
|
-
require 'chef/knife'
|
8
|
-
require 'rbvmomi'
|
9
|
-
|
10
|
-
# Base class for vsphere knife commands
|
11
|
-
class Chef
|
12
|
-
class Knife
|
13
|
-
class BaseVsphereCommand < Knife
|
14
|
-
|
15
|
-
deps do
|
16
|
-
require 'chef/knife/bootstrap'
|
17
|
-
Chef::Knife::Bootstrap.load_deps
|
18
|
-
require 'fog'
|
19
|
-
require 'socket'
|
20
|
-
require 'net/ssh/multi'
|
21
|
-
require 'readline'
|
22
|
-
require 'chef/json_compat'
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.get_common_options
|
26
|
-
unless defined? $default
|
27
|
-
$default = Hash.new
|
28
|
-
end
|
29
|
-
|
30
|
-
option :vsphere_user,
|
31
|
-
:short => "-u USERNAME",
|
32
|
-
:long => "--vsuser USERNAME",
|
33
|
-
:description => "The username for vsphere"
|
34
|
-
|
35
|
-
option :vsphere_pass,
|
36
|
-
:short => "-p PASSWORD",
|
37
|
-
:long => "--vspass PASSWORD",
|
38
|
-
:description => "The password for vsphere"
|
39
|
-
|
40
|
-
option :vsphere_host,
|
41
|
-
:long => "--vshost HOST",
|
42
|
-
:description => "The vsphere host"
|
43
|
-
|
44
|
-
option :vsphere_dc,
|
45
|
-
:short => "-d DATACENTER",
|
46
|
-
:long => "--vsdc DATACENTER",
|
47
|
-
:description => "The Datacenter for vsphere"
|
48
|
-
|
49
|
-
option :vsphere_path,
|
50
|
-
:long => "--vspath SOAP_PATH",
|
51
|
-
:description => "The vsphere SOAP endpoint path"
|
52
|
-
$default[:vsphere_path] = "/sdk"
|
53
|
-
|
54
|
-
option :vsphere_port,
|
55
|
-
:long => "--vsport PORT",
|
56
|
-
:description => "The VI SDK port number to use"
|
57
|
-
$default[:vsphere_port] = 443
|
58
|
-
|
59
|
-
option :vshere_nossl,
|
60
|
-
:long => "--vsnossl",
|
61
|
-
:description => "Disable SSL connectivity"
|
62
|
-
|
63
|
-
option :vsphere_insecure,
|
64
|
-
:long => "--vsinsecure",
|
65
|
-
:description => "Disable SSL certificate verification"
|
66
|
-
|
67
|
-
option :folder,
|
68
|
-
:short => "-f FOLDER",
|
69
|
-
:long => "--folder FOLDER",
|
70
|
-
:description => "The folder to get VMs from"
|
71
|
-
$default[:folder] = ''
|
72
|
-
end
|
73
|
-
|
74
|
-
def get_config(key)
|
75
|
-
key = key.to_sym
|
76
|
-
rval = config[key] || Chef::Config[:knife][key] || $default[key]
|
77
|
-
Chef::Log.debug("value for config item #{key}: #{rval}")
|
78
|
-
rval
|
79
|
-
end
|
80
|
-
|
81
|
-
def get_vim_connection
|
82
|
-
|
83
|
-
conn_opts = {
|
84
|
-
:host => get_config(:vsphere_host),
|
85
|
-
:path => get_config(:vshere_path),
|
86
|
-
:port => get_config(:vsphere_port),
|
87
|
-
:use_ssl => !get_config(:vsphere_nossl),
|
88
|
-
:user => get_config(:vsphere_user),
|
89
|
-
:password => get_config(:vsphere_pass),
|
90
|
-
:insecure => get_config(:vsphere_insecure)
|
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
|
-
|
1
|
+
#
|
2
|
+
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
+
# Contributor:: Jesse Campbell (<hikeit@gmail.com>)
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'chef/knife'
|
8
|
+
require 'rbvmomi'
|
9
|
+
|
10
|
+
# Base class for vsphere knife commands
|
11
|
+
class Chef
|
12
|
+
class Knife
|
13
|
+
class BaseVsphereCommand < Knife
|
14
|
+
|
15
|
+
deps do
|
16
|
+
require 'chef/knife/bootstrap'
|
17
|
+
Chef::Knife::Bootstrap.load_deps
|
18
|
+
require 'fog'
|
19
|
+
require 'socket'
|
20
|
+
require 'net/ssh/multi'
|
21
|
+
require 'readline'
|
22
|
+
require 'chef/json_compat'
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.get_common_options
|
26
|
+
unless defined? $default
|
27
|
+
$default = Hash.new
|
28
|
+
end
|
29
|
+
|
30
|
+
option :vsphere_user,
|
31
|
+
:short => "-u USERNAME",
|
32
|
+
:long => "--vsuser USERNAME",
|
33
|
+
:description => "The username for vsphere"
|
34
|
+
|
35
|
+
option :vsphere_pass,
|
36
|
+
:short => "-p PASSWORD",
|
37
|
+
:long => "--vspass PASSWORD",
|
38
|
+
:description => "The password for vsphere"
|
39
|
+
|
40
|
+
option :vsphere_host,
|
41
|
+
:long => "--vshost HOST",
|
42
|
+
:description => "The vsphere host"
|
43
|
+
|
44
|
+
option :vsphere_dc,
|
45
|
+
:short => "-d DATACENTER",
|
46
|
+
:long => "--vsdc DATACENTER",
|
47
|
+
:description => "The Datacenter for vsphere"
|
48
|
+
|
49
|
+
option :vsphere_path,
|
50
|
+
:long => "--vspath SOAP_PATH",
|
51
|
+
:description => "The vsphere SOAP endpoint path"
|
52
|
+
$default[:vsphere_path] = "/sdk"
|
53
|
+
|
54
|
+
option :vsphere_port,
|
55
|
+
:long => "--vsport PORT",
|
56
|
+
:description => "The VI SDK port number to use"
|
57
|
+
$default[:vsphere_port] = 443
|
58
|
+
|
59
|
+
option :vshere_nossl,
|
60
|
+
:long => "--vsnossl",
|
61
|
+
:description => "Disable SSL connectivity"
|
62
|
+
|
63
|
+
option :vsphere_insecure,
|
64
|
+
:long => "--vsinsecure",
|
65
|
+
:description => "Disable SSL certificate verification"
|
66
|
+
|
67
|
+
option :folder,
|
68
|
+
:short => "-f FOLDER",
|
69
|
+
:long => "--folder FOLDER",
|
70
|
+
:description => "The folder to get VMs from"
|
71
|
+
$default[:folder] = ''
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_config(key)
|
75
|
+
key = key.to_sym
|
76
|
+
rval = config[key] || Chef::Config[:knife][key] || $default[key]
|
77
|
+
Chef::Log.debug("value for config item #{key}: #{rval}")
|
78
|
+
rval
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_vim_connection
|
82
|
+
|
83
|
+
conn_opts = {
|
84
|
+
:host => get_config(:vsphere_host),
|
85
|
+
:path => get_config(:vshere_path),
|
86
|
+
:port => get_config(:vsphere_port),
|
87
|
+
:use_ssl => !get_config(:vsphere_nossl),
|
88
|
+
:user => get_config(:vsphere_user),
|
89
|
+
:password => get_config(:vsphere_pass),
|
90
|
+
:insecure => get_config(:vsphere_insecure)
|
91
|
+
}
|
92
|
+
|
93
|
+
# Grab the password from the command line
|
94
|
+
# if tt is not in the config file
|
95
|
+
if not conn_opts[:password]
|
96
|
+
conn_opts[:password] = get_password
|
97
|
+
end
|
98
|
+
|
99
|
+
# opt :debug, "Log SOAP messages", :short => 'd', :default => (ENV['RBVMOMI_DEBUG'] || false)
|
100
|
+
|
101
|
+
vim = RbVmomi::VIM.connect conn_opts
|
102
|
+
config[:vim] = vim
|
103
|
+
return vim
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_password
|
107
|
+
@password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
|
108
|
+
end
|
109
|
+
|
110
|
+
def find_folder(folderName)
|
111
|
+
dcname = get_config(:vsphere_dc)
|
112
|
+
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
113
|
+
baseEntity = dc.vmFolder
|
114
|
+
entityArray = folderName.split('/')
|
115
|
+
entityArray.each do |entityArrItem|
|
116
|
+
if entityArrItem != ''
|
117
|
+
baseEntity = baseEntity.childEntity.grep(RbVmomi::VIM::Folder).find { |f| f.name == entityArrItem } or
|
118
|
+
abort "no such folder #{folderName} while looking for #{entityArrItem}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
baseEntity
|
122
|
+
end
|
123
|
+
|
124
|
+
def find_network(networkName)
|
125
|
+
dcname = get_config(:vsphere_dc)
|
126
|
+
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
127
|
+
baseEntity = dc.network
|
128
|
+
baseEntity.find { |f| f.name == networkName } or abort "no such network #{networkName}"
|
129
|
+
end
|
130
|
+
|
131
|
+
def find_pool(poolName)
|
132
|
+
dcname = get_config(:vsphere_dc)
|
133
|
+
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
134
|
+
baseEntity = dc.hostFolder
|
135
|
+
entityArray = poolName.split('/')
|
136
|
+
entityArray.each do |entityArrItem|
|
137
|
+
if entityArrItem != ''
|
138
|
+
if baseEntity.is_a? RbVmomi::VIM::Folder
|
139
|
+
baseEntity = baseEntity.childEntity.find { |f| f.name == entityArrItem } or
|
140
|
+
abort "no such pool #{poolName} while looking for #{entityArrItem}"
|
141
|
+
elsif baseEntity.is_a? RbVmomi::VIM::ClusterComputeResource
|
142
|
+
baseEntity = baseEntity.resourcePool.resourcePool.find { |f| f.name == entityArrItem } or
|
143
|
+
abort "no such pool #{poolName} while looking for #{entityArrItem}"
|
144
|
+
elsif baseEntity.is_a? RbVmomi::VIM::ResourcePool
|
145
|
+
baseEntity = baseEntity.resourcePool.find { |f| f.name == entityArrItem } or
|
146
|
+
abort "no such pool #{poolName} while looking for #{entityArrItem}"
|
147
|
+
else
|
148
|
+
abort "Unexpected Object type encountered #{baseEntity.type} while finding resourcePool"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
baseEntity = baseEntity.resourcePool if not baseEntity.is_a?(RbVmomi::VIM::ResourcePool) and baseEntity.respond_to?(:resourcePool)
|
154
|
+
baseEntity
|
155
|
+
end
|
156
|
+
|
157
|
+
def find_datastore(dsName)
|
158
|
+
dcname = get_config(:vsphere_dc)
|
159
|
+
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
160
|
+
baseEntity = dc.datastore
|
161
|
+
baseEntity.find { |f| f.info.name == dsName } or abort "no such datastore #{dsName}"
|
162
|
+
end
|
163
|
+
|
164
|
+
def find_device(vm,deviceName)
|
165
|
+
vm.config.hardware.device.each do |device|
|
166
|
+
return device if device.deviceInfo.label == deviceName
|
167
|
+
end
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
171
|
+
def find_all_in_folder(folder, type)
|
172
|
+
if folder.instance_of?(RbVmomi::VIM::ClusterComputeResource)
|
173
|
+
folder = folder.resourcePool
|
174
|
+
end
|
175
|
+
if folder.instance_of?(RbVmomi::VIM::ResourcePool)
|
176
|
+
folder.resourcePool.grep(type)
|
177
|
+
elsif folder.instance_of?(RbVmomi::VIM::Folder)
|
178
|
+
folder.childEntity.grep(type)
|
179
|
+
else
|
180
|
+
puts "Unknown type #{folder.class}, not enumerating"
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def find_in_folder(folder, type, name)
|
186
|
+
folder.childEntity.grep(type).find { |o| o.name == name }
|
187
|
+
end
|
188
|
+
|
189
|
+
def fatal_exit(msg)
|
190
|
+
ui.fatal(msg)
|
191
|
+
exit 1
|
192
|
+
end
|
193
|
+
|
194
|
+
def tcp_test_port(hostname,port)
|
195
|
+
tcp_socket = TCPSocket.new(hostname, port)
|
196
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
197
|
+
if readable
|
198
|
+
Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}") if port == 22
|
199
|
+
true
|
200
|
+
else
|
201
|
+
false
|
202
|
+
end
|
203
|
+
rescue Errno::ETIMEDOUT
|
204
|
+
false
|
205
|
+
rescue Errno::EPERM
|
206
|
+
false
|
207
|
+
rescue Errno::ECONNREFUSED
|
208
|
+
sleep 2
|
209
|
+
false
|
210
|
+
rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
|
211
|
+
sleep 2
|
212
|
+
false
|
213
|
+
ensure
|
214
|
+
tcp_socket && tcp_socket.close
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -43,6 +43,14 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
43
43
|
:long => "--cspec CUST_SPEC",
|
44
44
|
:description => "The name of any customization specification to apply"
|
45
45
|
|
46
|
+
option :customization_plugin,
|
47
|
+
:long => "--cplugin CUST_PLUGIN_PATH",
|
48
|
+
:description => "Path to plugin that implements KnifeVspherePlugin.customize_clone_spec and/or KnifeVspherePlugin.reconfig_vm"
|
49
|
+
|
50
|
+
option :customization_plugin_data,
|
51
|
+
:long => "--cplugin-data CUST_PLUGIN_DATA",
|
52
|
+
:description => "String of data to pass to the plugin. Use any format you wish."
|
53
|
+
|
46
54
|
option :customization_vlan,
|
47
55
|
:long => "--cvlan CUST_VLAN",
|
48
56
|
:description => "VLAN name for network adapter to join"
|
@@ -209,6 +217,11 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
209
217
|
task.wait_for_completion
|
210
218
|
puts "Finished creating virtual machine #{vmname}"
|
211
219
|
|
220
|
+
if customization_plugin && customization_plugin.respond_to?(:reconfig_vm)
|
221
|
+
target_vm = find_in_folder(dest_folder, RbVmomi::VIM::VirtualMachine, vmname) or abort "VM could not be found in #{dest_folder}"
|
222
|
+
customization_plugin.reconfig_vm(target_vm)
|
223
|
+
end
|
224
|
+
|
212
225
|
if get_config(:power) || get_config(:bootstrap)
|
213
226
|
vm = find_in_folder(dest_folder, RbVmomi::VIM::VirtualMachine, vmname) or
|
214
227
|
fatal_exit("VM #{vmname} not found")
|
@@ -328,10 +341,43 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
328
341
|
cust_spec.identity = ident
|
329
342
|
end
|
330
343
|
|
344
|
+
if customization_plugin && customization_plugin.respond_to?(:customize_clone_spec)
|
345
|
+
clone_spec = customization_plugin.customize_clone_spec(src_config, clone_spec)
|
346
|
+
end
|
347
|
+
|
331
348
|
clone_spec.customization = cust_spec
|
332
349
|
clone_spec
|
333
350
|
end
|
334
351
|
|
352
|
+
# Loads the customization plugin if one was specified
|
353
|
+
# @return [KnifeVspherePlugin] the loaded and initialized plugin or nil
|
354
|
+
def customization_plugin
|
355
|
+
if @customization_plugin.nil?
|
356
|
+
if cplugin_path = get_config(:customization_plugin)
|
357
|
+
if File.exists? cplugin_path
|
358
|
+
require cplugin_path
|
359
|
+
else
|
360
|
+
abort "Customization plugin could not be found at #{cplugin_path}"
|
361
|
+
end
|
362
|
+
|
363
|
+
if Object.const_defined? 'KnifeVspherePlugin'
|
364
|
+
@customization_plugin = Object.const_get('KnifeVspherePlugin').new
|
365
|
+
if cplugin_data = get_config(:customization_plugin_data)
|
366
|
+
if @customization_plugin.respond_to?(:data=)
|
367
|
+
@customization_plugin.data = cplugin_data
|
368
|
+
else
|
369
|
+
abort "Customization plugin has no :data= accessor to receive the --cplugin-data argument. Define both or neither."
|
370
|
+
end
|
371
|
+
end
|
372
|
+
else
|
373
|
+
abort "KnifeVspherePlugin class is not defined in #{cplugin_path}"
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
@customization_plugin
|
379
|
+
end
|
380
|
+
|
335
381
|
# Retrieves a CustomizationSpecItem that matches the supplied name
|
336
382
|
# @param vim [Connection] VI Connection to use
|
337
383
|
# @param name [String] name of customization
|
@@ -1,85 +1,96 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
-
# License:: Apache License, Version 2.0
|
4
|
-
#
|
5
|
-
|
6
|
-
require 'chef/knife'
|
7
|
-
require 'chef/knife/BaseVsphereCommand'
|
8
|
-
require 'rbvmomi'
|
9
|
-
require 'netaddr'
|
10
|
-
|
11
|
-
PsOn = 'poweredOn'
|
12
|
-
PsOff = 'poweredOff'
|
13
|
-
PsSuspended = 'suspended'
|
14
|
-
|
15
|
-
PowerStates = {
|
16
|
-
PsOn => 'powered on',
|
17
|
-
PsOff => 'powered off',
|
18
|
-
PsSuspended => 'suspended'
|
19
|
-
}
|
20
|
-
|
21
|
-
# Manage power state of a virtual machine
|
22
|
-
class Chef::Knife::VsphereVmState < Chef::Knife::BaseVsphereCommand
|
23
|
-
|
24
|
-
banner "knife vsphere vm state VMNAME (options)"
|
25
|
-
|
26
|
-
get_common_options
|
27
|
-
|
28
|
-
option :state,
|
29
|
-
:short => "-s STATE",
|
30
|
-
:long => "--state STATE",
|
31
|
-
:description => "The power state to transition the VM into; one of on|off|suspended"
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
1
|
+
#
|
2
|
+
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'chef/knife'
|
7
|
+
require 'chef/knife/BaseVsphereCommand'
|
8
|
+
require 'rbvmomi'
|
9
|
+
require 'netaddr'
|
10
|
+
|
11
|
+
PsOn = 'poweredOn'
|
12
|
+
PsOff = 'poweredOff'
|
13
|
+
PsSuspended = 'suspended'
|
14
|
+
|
15
|
+
PowerStates = {
|
16
|
+
PsOn => 'powered on',
|
17
|
+
PsOff => 'powered off',
|
18
|
+
PsSuspended => 'suspended'
|
19
|
+
}
|
20
|
+
|
21
|
+
# Manage power state of a virtual machine
|
22
|
+
class Chef::Knife::VsphereVmState < Chef::Knife::BaseVsphereCommand
|
23
|
+
|
24
|
+
banner "knife vsphere vm state VMNAME (options)"
|
25
|
+
|
26
|
+
get_common_options
|
27
|
+
|
28
|
+
option :state,
|
29
|
+
:short => "-s STATE",
|
30
|
+
:long => "--state STATE",
|
31
|
+
:description => "The power state to transition the VM into; one of on|off|suspended"
|
32
|
+
|
33
|
+
option :wait_port,
|
34
|
+
:short => "-w PORT",
|
35
|
+
:long => "--wait-port PORT",
|
36
|
+
:description => "Wait for VM to be accessible on a port"
|
37
|
+
|
38
|
+
def run
|
39
|
+
|
40
|
+
$stdout.sync = true
|
41
|
+
|
42
|
+
vmname = @name_args[0]
|
43
|
+
if vmname.nil?
|
44
|
+
show_usage
|
45
|
+
ui.fatal("You must specify a virtual machine name")
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
vim = get_vim_connection
|
50
|
+
|
51
|
+
baseFolder = find_folder(get_config(:folder));
|
52
|
+
|
53
|
+
vm = find_in_folder(baseFolder, RbVmomi::VIM::VirtualMachine, vmname) or
|
54
|
+
abort "VM #{vmname} not found"
|
55
|
+
|
56
|
+
state = vm.runtime.powerState
|
57
|
+
|
58
|
+
if config[:state].nil?
|
59
|
+
puts "VM #{vmname} is currently " + PowerStates[vm.runtime.powerState]
|
60
|
+
else
|
61
|
+
|
62
|
+
case config[:state]
|
63
|
+
when 'on'
|
64
|
+
if state == PsOn
|
65
|
+
puts "Virtual machine #{vmname} was already powered on"
|
66
|
+
else
|
67
|
+
vm.PowerOnVM_Task.wait_for_completion
|
68
|
+
puts "Powered on virtual machine #{vmname}"
|
69
|
+
end
|
70
|
+
when 'off'
|
71
|
+
if state == PsOff
|
72
|
+
puts "Virtual machine #{vmname} was already powered off"
|
73
|
+
else
|
74
|
+
vm.PowerOffVM_Task.wait_for_completion
|
75
|
+
puts "Powered off virtual machine #{vmname}"
|
76
|
+
end
|
77
|
+
when 'suspend'
|
78
|
+
if state == PowerStates['suspended']
|
79
|
+
puts "Virtual machine #{vmname} was already suspended"
|
80
|
+
else
|
81
|
+
vm.SuspendVM_Task.wait_for_completion
|
82
|
+
puts "Suspended virtual machine #{vmname}"
|
83
|
+
end
|
84
|
+
when 'reset'
|
85
|
+
vm.ResetVM_Task.wait_for_completion
|
86
|
+
puts "Reset virtual machine #{vmname}"
|
87
|
+
end
|
88
|
+
|
89
|
+
if get_config(:wait_port)
|
90
|
+
print "Waiting for port #{get_config(:wait_port)}..."
|
91
|
+
print "." until tcp_test_port(vmname,get_config(:wait_port))
|
92
|
+
puts "done"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Brian Flad (<bflad417@gmail.com>)
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
require 'chef/knife'
|
6
|
+
require 'chef/knife/BaseVsphereCommand'
|
7
|
+
|
8
|
+
# Lists all known virtual machines in the configured datacenter
|
9
|
+
class Chef::Knife::VsphereVmVmdkAdd < Chef::Knife::BaseVsphereCommand
|
10
|
+
|
11
|
+
banner "knife vsphere vm vmdk add"
|
12
|
+
|
13
|
+
get_common_options
|
14
|
+
|
15
|
+
option :vmdk_type,
|
16
|
+
:long => "--vmdk-type TYPE",
|
17
|
+
:description => "Type of VMDK"
|
18
|
+
$default[:vmdk_type] = "thin"
|
19
|
+
|
20
|
+
def run
|
21
|
+
$stdout.sync = true
|
22
|
+
|
23
|
+
vmname = @name_args[0]
|
24
|
+
if vmname.nil?
|
25
|
+
show_usage
|
26
|
+
ui.fatal("You must specify a virtual machine name")
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
|
30
|
+
size = @name_args[1]
|
31
|
+
if size.nil?
|
32
|
+
ui.fatal "You need a VMDK size!"
|
33
|
+
show_usage
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
|
37
|
+
vim = get_vim_connection
|
38
|
+
vdm = vim.serviceContent.virtualDiskManager
|
39
|
+
vm = get_vm(vmname)
|
40
|
+
|
41
|
+
vmdk_datastore = vm.datastore[0]
|
42
|
+
vmdk_fileName = "#{vmname}/#{vmname}_1.vmdk"
|
43
|
+
vmdk_name = "[#{vmdk_datastore.name}] #{vmdk_fileName}"
|
44
|
+
vmdk_size_kb = size.to_i * 1024 * 1024
|
45
|
+
vmdk_type = get_config(:vmdk_type)
|
46
|
+
vmdk_type = "preallocated" if vmdk_type == "thick"
|
47
|
+
|
48
|
+
vmdk_spec = RbVmomi::VIM::FileBackedVirtualDiskSpec(
|
49
|
+
:adapterType => "lsiLogic",
|
50
|
+
:capacityKb => vmdk_size_kb,
|
51
|
+
:diskType => vmdk_type
|
52
|
+
)
|
53
|
+
|
54
|
+
ui.info "Creating VMDK"
|
55
|
+
ui.info "#{ui.color "Capacity:", :cyan} #{size} GB"
|
56
|
+
ui.info "#{ui.color "Disk:", :cyan} #{vmdk_name}"
|
57
|
+
|
58
|
+
if get_config(:noop)
|
59
|
+
ui.info "#{ui.color "Skipping disk creation process because --noop specified.", :red}"
|
60
|
+
else
|
61
|
+
vdm.CreateVirtualDisk_Task(
|
62
|
+
:datacenter => get_datacenter,
|
63
|
+
:name => vmdk_name,
|
64
|
+
:spec => vmdk_spec
|
65
|
+
).wait_for_completion
|
66
|
+
end
|
67
|
+
|
68
|
+
ui.info "Attaching VMDK to #{vmname}"
|
69
|
+
|
70
|
+
controller = find_device(vm,"SCSI controller 0")
|
71
|
+
|
72
|
+
vmdk_backing = RbVmomi::VIM::VirtualDiskFlatVer2BackingInfo(
|
73
|
+
:datastore => vmdk_datastore,
|
74
|
+
:diskMode => "persistent",
|
75
|
+
:fileName => vmdk_name,
|
76
|
+
)
|
77
|
+
|
78
|
+
device = RbVmomi::VIM::VirtualDisk(
|
79
|
+
:backing => vmdk_backing,
|
80
|
+
:capacityInKB => vmdk_size_kb,
|
81
|
+
:controllerKey => controller.key,
|
82
|
+
:key => -1,
|
83
|
+
:unitNumber => controller.device.size + 1
|
84
|
+
)
|
85
|
+
|
86
|
+
device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
|
87
|
+
:device => device,
|
88
|
+
:operation => RbVmomi::VIM::VirtualDeviceConfigSpecOperation("add")
|
89
|
+
)
|
90
|
+
|
91
|
+
vm_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec(
|
92
|
+
:deviceChange => [device_config_spec]
|
93
|
+
)
|
94
|
+
|
95
|
+
if get_config(:noop)
|
96
|
+
ui.info "#{ui.color "Skipping disk attaching process because --noop specified.", :red}"
|
97
|
+
else
|
98
|
+
vm.ReconfigVM_Task(:spec => vm_config_spec).wait_for_completion
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: knife-vsphere
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.5.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ezra Pagel
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-04-25 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -66,7 +66,9 @@ files:
|
|
66
66
|
- lib/chef/knife/vsphere_vm_execute.rb
|
67
67
|
- lib/chef/knife/vsphere_vm_list.rb
|
68
68
|
- lib/chef/knife/vsphere_vm_state.rb
|
69
|
+
- lib/chef/knife/vsphere_vm_vmdk_add.rb
|
69
70
|
- lib/knife-vsphere/version.rb
|
71
|
+
- lib/knife-vsphere/version.rb~
|
70
72
|
has_rdoc: true
|
71
73
|
homepage: http://github.com/ezrapagel/knife-vsphere
|
72
74
|
licenses: []
|