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
- # opt :debug, "Log SOAP messages", :short => 'd', :default => (ENV['RBVMOMI_DEBUG'] || false)
94
-
95
- vim = RbVmomi::VIM.connect conn_opts
96
- config[:vim] = vim
97
- return vim
98
- end
99
-
100
- def find_folder(folderName)
101
- dcname = get_config(:vsphere_dc)
102
- dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
103
- baseEntity = dc.vmFolder
104
- entityArray = folderName.split('/')
105
- entityArray.each do |entityArrItem|
106
- if entityArrItem != ''
107
- baseEntity = baseEntity.childEntity.grep(RbVmomi::VIM::Folder).find { |f| f.name == entityArrItem } or
108
- abort "no such folder #{folderName} while looking for #{entityArrItem}"
109
- end
110
- end
111
- baseEntity
112
- end
113
-
114
- def find_network(networkName)
115
- dcname = get_config(:vsphere_dc)
116
- dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
117
- baseEntity = dc.network
118
- baseEntity.find { |f| f.name == networkName } or abort "no such network #{networkName}"
119
- end
120
-
121
- def find_pool(poolName)
122
- dcname = get_config(:vsphere_dc)
123
- dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
124
- baseEntity = dc.hostFolder
125
- entityArray = poolName.split('/')
126
- entityArray.each do |entityArrItem|
127
- if entityArrItem != ''
128
- if baseEntity.is_a? RbVmomi::VIM::Folder
129
- baseEntity = baseEntity.childEntity.find { |f| f.name == entityArrItem } or
130
- abort "no such pool #{poolName} while looking for #{entityArrItem}"
131
- elsif baseEntity.is_a? RbVmomi::VIM::ClusterComputeResource
132
- baseEntity = baseEntity.resourcePool.resourcePool.find { |f| f.name == entityArrItem } or
133
- abort "no such pool #{poolName} while looking for #{entityArrItem}"
134
- elsif baseEntity.is_a? RbVmomi::VIM::ResourcePool
135
- baseEntity = baseEntity.resourcePool.find { |f| f.name == entityArrItem } or
136
- abort "no such pool #{poolName} while looking for #{entityArrItem}"
137
- else
138
- abort "Unexpected Object type encountered #{baseEntity.type} while finding resourcePool"
139
- end
140
- end
141
- end
142
-
143
- baseEntity = baseEntity.resourcePool if not baseEntity.is_a?(RbVmomi::VIM::ResourcePool) and baseEntity.respond_to?(:resourcePool)
144
- baseEntity
145
- end
146
-
147
- def find_datastore(dsName)
148
- dcname = get_config(:vsphere_dc)
149
- dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
150
- baseEntity = dc.datastore
151
- baseEntity.find { |f| f.info.name == dsName } or abort "no such datastore #{dsName}"
152
- end
153
-
154
-
155
- def find_all_in_folder(folder, type)
156
- if folder.instance_of?(RbVmomi::VIM::ClusterComputeResource)
157
- folder = folder.resourcePool
158
- end
159
- if folder.instance_of?(RbVmomi::VIM::ResourcePool)
160
- folder.resourcePool.grep(type)
161
- elsif folder.instance_of?(RbVmomi::VIM::Folder)
162
- folder.childEntity.grep(type)
163
- else
164
- puts "Unknown type #{folder.class}, not enumerating"
165
- nil
166
- end
167
- end
168
-
169
- def find_in_folder(folder, type, name)
170
- folder.childEntity.grep(type).find { |o| o.name == name }
171
- end
172
-
173
- def fatal_exit(msg)
174
- ui.fatal(msg)
175
- exit 1
176
- end
177
-
178
- end
179
- end
180
- end
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
- def run
34
-
35
- $stdout.sync = true
36
-
37
- vmname = @name_args[0]
38
- if vmname.nil?
39
- show_usage
40
- ui.fatal("You must specify a virtual machine name")
41
- exit 1
42
- end
43
-
44
- vim = get_vim_connection
45
-
46
- baseFolder = find_folder(get_config(:folder));
47
-
48
- vm = find_in_folder(baseFolder, RbVmomi::VIM::VirtualMachine, vmname) or
49
- abort "VM #{vmname} not found"
50
-
51
- state = vm.runtime.powerState
52
-
53
- if config[:state].nil?
54
- puts "VM #{vmname} is currently " + PowerStates[vm.runtime.powerState]
55
- else
56
-
57
- case config[:state]
58
- when 'on'
59
- if state == PsOn
60
- puts "Virtual machine #{vmname} was already powered on"
61
- else
62
- vm.PowerOnVM_Task.wait_for_completion
63
- puts "Powered on virtual machine #{vmname}"
64
- end
65
- when 'off'
66
- if state == PsOff
67
- puts "Virtual machine #{vmname} was already powered off"
68
- else
69
- vm.PowerOffVM_Task.wait_for_completion
70
- puts "Powered off virtual machine #{vmname}"
71
- end
72
- when 'suspend'
73
- if state == PowerStates['suspended']
74
- puts "Virtual machine #{vmname} was already suspended"
75
- else
76
- vm.SuspendVM_Task.wait_for_completion
77
- puts "Suspended virtual machine #{vmname}"
78
- end
79
- when 'reset'
80
- vm.ResetVM_Task.wait_for_completion
81
- puts "Reset virtual machine #{vmname}"
82
- end
83
- end
84
- end
85
- end
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
@@ -1,4 +1,4 @@
1
1
  module KnifeVsphere
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
4
4
 
@@ -0,0 +1,4 @@
1
+ module KnifeVsphere
2
+ VERSION = "0.4.0"
3
+ end
4
+
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: knife-vsphere
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.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-03-21 00:00:00 -05:00
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: []