knife-vsphere 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []