rvc 1.6.0 → 1.7.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.
Files changed (66) hide show
  1. data/Rakefile +1 -1
  2. data/VERSION +1 -1
  3. data/bin/rvc +46 -70
  4. data/devel/test-dependencies.sh +4 -0
  5. data/lib/rvc.rb +5 -6
  6. data/lib/rvc/command.rb +65 -0
  7. data/lib/rvc/command_slate.rb +112 -0
  8. data/lib/rvc/completion.rb +89 -58
  9. data/lib/rvc/connection.rb +48 -0
  10. data/lib/rvc/extensions/DistributedVirtualPortgroup.rb +1 -1
  11. data/lib/rvc/extensions/DistributedVirtualSwitch.rb +3 -3
  12. data/lib/rvc/extensions/HostSystem.rb +90 -0
  13. data/lib/rvc/extensions/VirtualMachine.rb +37 -7
  14. data/lib/rvc/field.rb +59 -12
  15. data/lib/rvc/fs.rb +34 -4
  16. data/lib/rvc/inventory.rb +5 -1
  17. data/lib/rvc/modules/alarm.rb +2 -0
  18. data/lib/rvc/modules/basic.rb +66 -61
  19. data/lib/rvc/modules/cluster.rb +117 -22
  20. data/lib/rvc/modules/connection.rb +40 -0
  21. data/lib/rvc/modules/core.rb +4 -16
  22. data/lib/rvc/modules/datacenter.rb +2 -0
  23. data/lib/rvc/modules/datastore.rb +11 -78
  24. data/lib/rvc/modules/device.rb +40 -5
  25. data/lib/rvc/modules/diagnostics.rb +169 -0
  26. data/lib/rvc/modules/esxcli.rb +9 -5
  27. data/lib/rvc/modules/find.rb +5 -3
  28. data/lib/rvc/modules/host.rb +46 -3
  29. data/lib/rvc/modules/issue.rb +2 -0
  30. data/lib/rvc/modules/mark.rb +5 -3
  31. data/lib/rvc/modules/perf.rb +99 -33
  32. data/lib/rvc/modules/permissions.rb +2 -0
  33. data/lib/rvc/modules/resource_pool.rb +2 -0
  34. data/lib/rvc/modules/role.rb +3 -1
  35. data/lib/rvc/modules/snapshot.rb +12 -4
  36. data/lib/rvc/modules/statsinterval.rb +13 -11
  37. data/lib/rvc/modules/vds.rb +67 -10
  38. data/lib/rvc/modules/vim.rb +19 -53
  39. data/lib/rvc/modules/vm.rb +27 -6
  40. data/lib/rvc/modules/vm_guest.rb +490 -0
  41. data/lib/rvc/modules/vmrc.rb +60 -32
  42. data/lib/rvc/modules/vnc.rb +2 -0
  43. data/lib/rvc/namespace.rb +114 -0
  44. data/lib/rvc/option_parser.rb +12 -15
  45. data/lib/rvc/readline-ffi.rb +4 -1
  46. data/lib/rvc/ruby_evaluator.rb +84 -0
  47. data/lib/rvc/shell.rb +68 -83
  48. data/lib/rvc/uri_parser.rb +59 -0
  49. data/lib/rvc/util.rb +134 -29
  50. data/lib/rvc/{extensions/PerfCounterInfo.rb → version.rb} +2 -4
  51. data/lib/rvc/{memory_session.rb → vim.rb} +10 -32
  52. data/test/modules/foo.rb +9 -0
  53. data/test/modules/foo/bar.rb +9 -0
  54. data/test/test_completion.rb +17 -0
  55. data/test/test_fs.rb +9 -11
  56. data/test/test_help.rb +46 -0
  57. data/test/test_helper.rb +12 -0
  58. data/test/test_metric.rb +1 -2
  59. data/test/test_modules.rb +38 -0
  60. data/test/test_parse_path.rb +1 -2
  61. data/test/test_shell.rb +138 -0
  62. data/test/test_uri.rb +34 -0
  63. metadata +115 -81
  64. data/lib/rvc/extensions/PerformanceManager.rb +0 -83
  65. data/lib/rvc/filesystem_session.rb +0 -101
  66. data/lib/rvc/modules.rb +0 -138
@@ -18,6 +18,8 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require 'rvc/vim'
22
+
21
23
  opts :create do
22
24
  summary "Create a datacenter"
23
25
  arg :dest, nil, :lookup_parent => [VIM::Folder, VIM]
@@ -18,7 +18,10 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'terminal-table/import'
21
+ require 'rvc/vim'
22
+ VIM::Datastore
23
+
24
+ require 'terminal-table'
22
25
 
23
26
  opts :download do
24
27
  summary "Download a file from a datastore"
@@ -27,34 +30,9 @@ opts :download do
27
30
  end
28
31
 
29
32
  def download file, local_path
30
- main_http = file.datastore._connection.http
31
- http = Net::HTTP.new(main_http.address, main_http.port)
32
- http.use_ssl = true
33
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
34
- #http.set_debug_output $stderr
35
- http.start
36
- err "certificate mismatch" unless main_http.peer_cert.to_der == http.peer_cert.to_der
33
+ download_path = http_path file.datastore.send(:datacenter).name, file.datastore.name, file.path
37
34
 
38
- headers = { 'cookie' => file.datastore._connection.cookie }
39
- path = http_path file.datastore.send(:datacenter).name, file.datastore.name, file.path
40
- http.request_get(path, headers) do |res|
41
- case res
42
- when Net::HTTPOK
43
- len = res.content_length
44
- count = 0
45
- File.open(local_path, 'wb') do |io|
46
- res.read_body do |segment|
47
- count += segment.length
48
- io.write segment
49
- $stdout.write "\e[0G\e[Kdownloading #{count}/#{len} bytes (#{(count*100)/len}%)"
50
- $stdout.flush
51
- end
52
- end
53
- $stdout.puts
54
- else
55
- err "download failed: #{res.message}"
56
- end
57
- end
35
+ http_download file.datastore._connection, download_path, local_path
58
36
  end
59
37
 
60
38
 
@@ -69,54 +47,9 @@ def upload local_path, dest
69
47
  err "local file does not exist" unless File.exists? local_path
70
48
  real_datastore_path = "#{dir.path}/#{datastore_filename}"
71
49
 
72
- main_http = dir.datastore._connection.http
73
- http = Net::HTTP.new(main_http.address, main_http.port)
74
- http.use_ssl = true
75
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
76
- #http.set_debug_output $stderr
77
- http.start
78
- err "certificate mismatch" unless main_http.peer_cert.to_der == http.peer_cert.to_der
50
+ upload_path = http_path dir.datastore.send(:datacenter).name, dir.datastore.name, real_datastore_path
79
51
 
80
- File.open(local_path, 'rb') do |io|
81
- stream = ProgressStream.new(io, io.stat.size) do |s|
82
- $stdout.write "\e[0G\e[Kuploading #{s.count}/#{s.len} bytes (#{(s.count*100)/s.len}%)"
83
- $stdout.flush
84
- end
85
-
86
- headers = {
87
- 'cookie' => dir.datastore._connection.cookie,
88
- 'content-length' => io.stat.size.to_s,
89
- 'Content-Type' => 'application/octet-stream',
90
- }
91
- path = http_path dir.datastore.send(:datacenter).name, dir.datastore.name, real_datastore_path
92
- request = Net::HTTP::Put.new path, headers
93
- request.body_stream = stream
94
- res = http.request(request)
95
- $stdout.puts
96
- case res
97
- when Net::HTTPOK
98
- else
99
- err "upload failed: #{res.message}"
100
- end
101
- end
102
- end
103
-
104
- class ProgressStream
105
- attr_reader :io, :len, :count
106
-
107
- def initialize io, len, &b
108
- @io = io
109
- @len = len
110
- @count = 0
111
- @cb = b
112
- end
113
-
114
- def read n
115
- io.read(n).tap do |c|
116
- @count += c.length if c
117
- @cb[self]
118
- end
119
- end
52
+ http_upload dir.datastore._connection, local_path, upload_path
120
53
  end
121
54
 
122
55
 
@@ -256,7 +189,7 @@ def find_orphans ds, opts
256
189
  if data.empty?
257
190
  puts "No orphans found"
258
191
  else
259
- puts(table do
192
+ puts(Terminal::Table.new do
260
193
  data.sort_by { |a| a[1] }.each do |x|
261
194
  dir, dirSize, numFiles = x
262
195
  self.headings = 'Directory', 'Space Used', '# Files'
@@ -287,14 +220,14 @@ def find_orphans ds, opts
287
220
  RbVmomi::VIM::Datastore::FakeDatastoreFolder.new(ds, "#{dirInfo[0]}")
288
221
  end
289
222
  opts[:mark] ||= "#{dsName}_orphans"
290
- CMD.mark.mark opts[:mark], results
223
+ shell.cmds.mark.mark opts[:mark], results
291
224
  puts "Saved results to mark '#{opts[:mark]}'"
292
225
 
293
226
  i = 0
294
227
  results.each do |r|
295
228
  display_path = r.path
296
229
  puts "#{i} #{display_path}"
297
- CMD.mark.mark i.to_s, [r]
230
+ shell.cmds.mark.mark i.to_s, [r]
298
231
  i += 1
299
232
  end
300
233
  end
@@ -1,3 +1,26 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'rvc/vim'
22
+ VIM::Datastore
23
+
1
24
  opts :connect do
2
25
  summary "Connect a virtual device"
3
26
  arg :device, nil, :lookup => VIM::VirtualDevice, :multi => true
@@ -21,13 +44,14 @@ end
21
44
  opts :remove do
22
45
  summary "Remove a virtual device"
23
46
  arg :device, nil, :lookup => VIM::VirtualDevice, :multi => true
47
+ opt :no_destroy, "Do not delete backing files"
24
48
  end
25
49
 
26
- def remove devs
50
+ def remove devs, opts
27
51
  vm_devs = devs.group_by(&:rvc_vm)
28
52
  tasks = vm_devs.map do |vm,my_devs|
29
53
  device_changes = my_devs.map do |dev|
30
- fileOp = dev.backing.is_a?(VIM::VirtualDeviceFileBackingInfo) ? 'destroy' : nil
54
+ fileOp = (dev.backing.is_a?(VIM::VirtualDeviceFileBackingInfo) && !opts[:no_destroy]) ? 'destroy' : nil
31
55
  { :operation => :remove, :fileOperation => fileOp, :device => dev }
32
56
  end
33
57
  spec = { :deviceChange => device_changes }
@@ -82,15 +106,26 @@ end
82
106
  opts :add_disk do
83
107
  summary "Add a hard drive to a virtual machine"
84
108
  arg :vm, nil, :lookup => VIM::VirtualMachine
109
+ arg :path, "Filename on the datastore", :lookup_parent => VIM::Datastore::FakeDatastoreFolder, :required => false
85
110
  opt :size, 'Size', :default => '10G'
86
111
  opt :controller, 'Virtual controller', :type => :string, :lookup => VIM::VirtualController
112
+ opt :file_op, 'File operation (create|reuse|replace)', :default => 'create'
87
113
  end
88
114
 
89
- def add_disk vm, opts
115
+ def add_disk vm, path, opts
90
116
  controller, unit_number = pick_controller vm, opts[:controller], [VIM::VirtualSCSIController, VIM::VirtualIDEController]
91
117
  id = "disk-#{controller.key}-#{unit_number}"
92
- filename = "#{File.dirname(vm.summary.config.vmPathName)}/#{id}.vmdk"
93
- _add_device vm, :create, VIM::VirtualDisk(
118
+
119
+ if path
120
+ dir, file = *path
121
+ filename = "#{dir.datastore_path}/#{file}"
122
+ else
123
+ filename = "#{File.dirname(vm.summary.config.vmPathName)}/#{id}.vmdk"
124
+ end
125
+
126
+ opts[:file_op] = nil if opts[:file_op] == 'reuse'
127
+
128
+ _add_device vm, opts[:file_op], VIM::VirtualDisk(
94
129
  :key => -1,
95
130
  :backing => VIM.VirtualDiskFlatVer2BackingInfo(
96
131
  :fileName => filename,
@@ -0,0 +1,169 @@
1
+ # Copyright (c) 2012 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ DEFAULT_SERVER_PLACEHOLDER = '0.0.0.0'
22
+
23
+ def wait_for_multiple_tasks tasks, timeout
24
+ if tasks == []
25
+ return []
26
+ end
27
+ pc = tasks.first._connection.serviceContent.propertyCollector
28
+ done = false
29
+ t1 = Time.now
30
+ while !done && (Time.now - t1) < timeout
31
+ tasks_props = pc.collectMultiple(tasks, 'info.state')
32
+ if tasks_props.reject{|t,f| ['success', 'error'].member?(f['info.state'])}.empty?
33
+ done = true
34
+ end
35
+ sleep 2
36
+ end
37
+ tasks_props = pc.collectMultiple(tasks, 'info.state', 'info.error')
38
+ results = Hash[tasks_props.map do |task, props|
39
+ result = if props['info.state'] == 'success'
40
+ task.info.result
41
+ elsif props['info.state'] == 'error'
42
+ props['info.error']
43
+ else
44
+ "Timed out"
45
+ end
46
+ [task, result]
47
+ end]
48
+ results
49
+ end
50
+
51
+
52
+ opts :vm_create do
53
+ summary "Check that VMs can be created on all hosts in a cluster"
54
+ arg :cluster, nil, :lookup => VIM::ComputeResource, :multi => true
55
+ opt :datastore, "Datastore to put (temporary) VMs on", :lookup => VIM::Datastore
56
+ opt :vm_folder, "VM Folder to place (temporary) VMs in", :lookup => VIM::Folder
57
+ opt :timeout, "Time to wait for VM creation to finish", :type => :int, :default => 3 * 60
58
+ end
59
+
60
+ def vm_create clusters, opts
61
+ datastore = opts[:datastore]
62
+ vm_folder = opts[:vm_folder]
63
+ err "datastore is a required parameter" unless datastore
64
+ err "vm_folder is a required parameter" unless vm_folder
65
+
66
+ puts "Creating one VM per host ... (timeout = #{opts[:timeout]} sec)"
67
+ result = _vm_create clusters, datastore, vm_folder, opts
68
+
69
+ errors = result.select{|h, x| x['status'] != 'green'}
70
+ errors.each do |host, info|
71
+ puts "Failed to create VM on host #{host} (in cluster #{info['cluster']}): #{info['error']}"
72
+ end
73
+ if errors.length == 0
74
+ puts "Success"
75
+ end
76
+ end
77
+
78
+ def _vm_create clusters, datastore, vm_folder, opts = {}
79
+ pc = datastore._connection.serviceContent.propertyCollector
80
+ datastore_path = "[#{datastore.name}]"
81
+ run = Time.now.to_i % 1000
82
+ tasks_map = {}
83
+ cluster_host_map = {}
84
+ clusters_props = pc.collectMultiple(clusters, 'name', 'resourcePool', 'host')
85
+ all_hosts = clusters_props.map{|c, p| p['host']}.flatten
86
+ hosts_props = pc.collectMultiple(all_hosts, 'name')
87
+
88
+ hosts_infos = Hash[all_hosts.map{|host| [host, {}]}]
89
+
90
+ clusters.each do |cluster|
91
+ cluster_props = clusters_props[cluster]
92
+ rp = cluster_props['resourcePool']
93
+ hosts = cluster_props['host']
94
+ hosts.map do |host|
95
+ cluster_host_map[host] = cluster
96
+ config = {
97
+ :name => "VM-on-#{hosts_props[host]['name']}-#{run}",
98
+ :guestId => 'otherGuest',
99
+ :files => { :vmPathName => datastore_path },
100
+ :numCPUs => 1,
101
+ :memoryMB => 16,
102
+ :deviceChange => [
103
+ {
104
+ :operation => :add,
105
+ :device => VIM.VirtualCdrom(
106
+ :key => -2,
107
+ :connectable => {
108
+ :allowGuestControl => true,
109
+ :connected => true,
110
+ :startConnected => true,
111
+ },
112
+ :backing => VIM.VirtualCdromIsoBackingInfo(
113
+ :fileName => datastore_path
114
+ ),
115
+ :controllerKey => 200,
116
+ :unitNumber => 0
117
+ )
118
+ }
119
+ ],
120
+ }
121
+ task = vm_folder.CreateVM_Task(:config => config,
122
+ :pool => rp,
123
+ :host => host)
124
+ tasks_map[task] = host
125
+ hosts_infos[host][:create_task] = task
126
+ end
127
+ end
128
+
129
+ create_tasks = tasks_map.keys
130
+ create_results = wait_for_multiple_tasks create_tasks, opts[:timeout]
131
+ create_results.each { |t, r| hosts_infos[tasks_map[t]][:create_result] = r }
132
+
133
+ vms = create_results.select{|t, x| x.is_a? VIM::VirtualMachine}
134
+ destroy_tasks = Hash[vms.map{|t, x| [x.Destroy_Task, t]}]
135
+
136
+ destroy_results = wait_for_multiple_tasks destroy_tasks.keys, opts[:timeout]
137
+ destroy_results.each do |t, r|
138
+ create_task = destroy_tasks[t]
139
+ hosts_infos[tasks_map[create_task]][:destroy_result] = r
140
+ end
141
+
142
+ out = {}
143
+ all_hosts.each do |host|
144
+ host_info = hosts_infos[host]
145
+ host_props = hosts_props[host]
146
+ cluster = cluster_host_map[host]
147
+ cluster_props = clusters_props[cluster]
148
+
149
+ result = host_info[:create_result]
150
+ result = host_info[:destroy_result] if result.is_a?(VIM::VirtualMachine)
151
+ if result == nil
152
+ error_str = nil
153
+ status = 'green'
154
+ elsif result.is_a?(String)
155
+ error_str = result
156
+ status = 'red'
157
+ else
158
+ error_str = "#{result.fault.class.wsdl_name}: #{result.localizedMessage}"
159
+ status = 'red'
160
+ end
161
+
162
+ out[host_props['name']] = {
163
+ 'cluster' => cluster_props['name'],
164
+ 'status' => status,
165
+ 'error' => error_str
166
+ }
167
+ end
168
+ out
169
+ end
@@ -18,9 +18,13 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require 'rvc/vim'
22
+
23
+ require 'rvc/ttl_cache'
24
+
21
25
  raw_opts :execute, "Execute an esxcli command"
22
26
 
23
- EsxcliCache = TTLCache.new 60
27
+ EsxcliCache = RVC::TTLCache.new 60
24
28
 
25
29
  def lookup_esxcli host, args
26
30
  cur = EsxcliCache[host, :esxcli]
@@ -40,14 +44,14 @@ def lookup_esxcli host, args
40
44
  return cur
41
45
  end
42
46
 
43
- rvc_completor :execute do |line, args, word, argnum|
44
- if argnum == 0
47
+ rvc_completor :execute do |word, args|
48
+ if args.length == 1
45
49
  # HostSystem argument
46
- RVC::Completion.fs_candidates word
50
+ shell.completion.fs_candidates word
47
51
  else
48
52
  # esxcli namespace/method/arguments
49
53
  host = lookup_single! args[0], VIM::HostSystem
50
- o = lookup_esxcli host, args[1...argnum]
54
+ o = lookup_esxcli host, args[1...-1]
51
55
 
52
56
  case o
53
57
  when VIM::EsxcliCommand
@@ -18,6 +18,8 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require 'rvc/vim'
22
+
21
23
  opts :find do
22
24
  summary "Find objects matching certain criteria"
23
25
  arg :args, "Paths or +terms", :required => false, :multi => true
@@ -37,14 +39,14 @@ def find args, opts
37
39
 
38
40
  results = find_items args[:term], args[:root], opts[:type]
39
41
 
40
- CMD.mark.mark opts[:mark], results
42
+ shell.cmds.mark.mark opts[:mark], results
41
43
 
42
44
  i = 0
43
- cwd = $shell.fs.cur.rvc_path_str
45
+ cwd = shell.fs.cur.rvc_path_str
44
46
  cwd_prefix = /^#{Regexp.escape cwd}\//
45
47
  results.each do |r|
46
48
  puts "#{i} #{r.rvc_path_str.gsub(cwd_prefix, '')}"
47
- CMD.mark.mark i.to_s, [r]
49
+ shell.cmds.mark.mark i.to_s, [r]
48
50
  i += 1
49
51
  end
50
52
  end
@@ -18,14 +18,35 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require 'rvc/vim'
22
+
21
23
  opts :reboot do
22
- summary "Reboot a host"
24
+ summary "Reboot hosts"
23
25
  arg :host, nil, :lookup => VIM::HostSystem, :multi => true
24
- opt :force, "Reboot even if in maintenance mode", :default => false
26
+ opt :force, "Reboot even if not in maintenance mode", :default => false
27
+ opt :wait, "Wait for the host to be connected again", :type => :boolean
25
28
  end
26
29
 
27
30
  def reboot hosts, opts
28
31
  tasks hosts, :RebootHost, :force => opts[:force]
32
+
33
+ if opts[:wait]
34
+ puts "Waiting for hosts to reboot ..."
35
+ # There is no proper way to wait for a host to reboot, so we
36
+ # implement a heuristic that is close enough:
37
+ # First we wait for a moment to give the host time to actually
38
+ # disconnect. Then we just wait for it to be responding again.
39
+ sleep 3 * 60
40
+
41
+ hosts.each do |host|
42
+ # We could use the property collector here to wait for an
43
+ # update instead of polling.
44
+ while !(host.runtime.connectionState == "connected" && host.runtime.powerState == "poweredOn")
45
+ sleep 10
46
+ end
47
+ puts "Host #{host.name} is back up"
48
+ end
49
+ end
29
50
  end
30
51
 
31
52
 
@@ -76,10 +97,18 @@ opts :enter_maintenance_mode do
76
97
  summary "Put hosts into maintenance mode"
77
98
  arg :host, nil, :lookup => VIM::HostSystem, :multi => true
78
99
  opt :timeout, "Timeout", :default => 0
100
+ opt :evacuate_powered_off_vms, "Evacuate powered off vms", :type => :boolean
101
+ opt :no_wait, "Don't wait for Task to complete", :type => :boolean
79
102
  end
80
103
 
81
104
  def enter_maintenance_mode hosts, opts
82
- tasks hosts, :EnterMaintenanceMode, :timeout => opts[:timeout]
105
+ if opts[:no_wait]
106
+ hosts.each do |host|
107
+ host.EnterMaintenanceMode_Task(:timeout => opts[:timeout], :evacuatePoweredOffVms => opts[:evacuate_powered_off_vms])
108
+ end
109
+ else
110
+ tasks hosts, :EnterMaintenanceMode, :timeout => opts[:timeout], :evacuatePoweredOffVms => opts[:evacuate_powered_off_vms]
111
+ end
83
112
  end
84
113
 
85
114
 
@@ -160,3 +189,17 @@ def add_nfs_datastore hosts, opts
160
189
  datastoreSystem.CreateNasDatastore :spec => spec
161
190
  end
162
191
  end
192
+
193
+
194
+ opts :rescan_storage do
195
+ summary "Rescan HBAs and VMFS"
196
+ arg :host, nil, :lookup => VIM::HostSystem, :multi => true
197
+ end
198
+
199
+ def rescan_storage hosts
200
+ hosts.each do |host|
201
+ storageSystem = host.configManager.storageSystem
202
+ storageSystem.RescanAllHba
203
+ storageSystem.RescanVmfs
204
+ end
205
+ end