rvc 1.7.0 → 1.8.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.
data/Rakefile CHANGED
@@ -5,8 +5,8 @@ begin
5
5
  gem.summary = "vSphere console UI"
6
6
  #gem.description = ""
7
7
  gem.email = "rlane@vmware.com"
8
- #gem.homepage = ""
9
- gem.authors = ["Rich Lane"]
8
+ gem.homepage = "https://github.com/vmware/rvc"
9
+ gem.authors = ["Rich Lane", "Christian Dickmann"]
10
10
  gem.add_dependency 'rbvmomi', '>= 1.6.0'
11
11
  gem.add_dependency 'trollop', '>= 1.16.2'
12
12
  gem.add_dependency 'backports', '>= 1.18.2'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.7.0
1
+ 1.8.0
@@ -164,7 +164,7 @@ class Completion
164
164
  base = absolute ? @shell.fs.root : @shell.fs.cur
165
165
  cur = @shell.fs.traverse(base, arcs).first or return []
166
166
  arcs.unshift '' if absolute
167
- children = @cache[cur, :children] rescue []
167
+ children = @cache[cur, :rvc_children] rescue []
168
168
  children.
169
169
  select { |k,v| k.gsub(' ', '\\ ') =~ /^#{Regexp.escape(last)}/ }.
170
170
  map { |k,v| (arcs+[k])*'/' }.
@@ -39,4 +39,15 @@ class RbVmomi::VIM::ClusterComputeResource
39
39
  def rvc_host_children
40
40
  RVC::Util.collect_children self, :host
41
41
  end
42
+
43
+ def display_info
44
+ super
45
+ pc = _connection.serviceContent.propertyCollector
46
+ cfg, = collect 'configurationEx'
47
+ drs = cfg.drsConfig
48
+ ha = cfg.dasConfig
49
+ puts "DRS: #{drs.enabled ? drs.defaultVmBehavior : 'disabled'}"
50
+ puts "HA: #{ha.enabled ? 'enabled' : 'disabled'}"
51
+ puts "VM Swap Placement: #{cfg.vmSwapPlacement}"
52
+ end
42
53
  end
@@ -21,15 +21,19 @@
21
21
  class RbVmomi::VIM::ComputeResource
22
22
  # TODO expand, optimize
23
23
  def display_info
24
+ pc = _connection.serviceContent.propertyCollector
25
+ name, host = collect 'name', 'host'
24
26
  stats = self.stats
25
27
  pct_cpu_used = 100.0*stats[:usedCPU]/stats[:totalCPU]
26
28
  pct_mem_used = 100.0*stats[:usedMem]/stats[:totalMem]
27
29
  puts "name: #{name}"
28
30
  puts "cpu: #{stats[:totalCPU]/1e3} GHz (#{pct_cpu_used.to_i}% used)"
29
31
  puts "memory: #{stats[:totalMem]/1e3} GB (#{pct_mem_used.to_i}% used)"
32
+
33
+ host_names = pc.collectMultiple host, 'name'
30
34
  puts "hosts:"
31
35
  host.each do |h|
32
- puts " #{h.name}"
36
+ puts " #{host_names[h]['name']}"
33
37
  end
34
38
  end
35
39
 
@@ -36,7 +36,7 @@ class RbVmomi::VIM::Datacenter
36
36
 
37
37
  # For compatibility with previous RVC versions
38
38
  def traverse_one arc
39
- children = self.children
39
+ children = self.rvc_children
40
40
  return children[arc] if children.member? arc
41
41
  if arc == 'vm' then return vmFolder
42
42
  elsif arc == 'datastore' then return datastoreFolder
@@ -41,6 +41,21 @@ class RbVmomi::VIM::HostSystem
41
41
  property 'runtime.powerState'
42
42
  end
43
43
 
44
+ field 'state.maintenancemode' do
45
+ summary "Host maintenance mode."
46
+ property 'runtime.inMaintenanceMode'
47
+ end
48
+
49
+ field 'build' do
50
+ summary "ESX build number."
51
+ property 'summary.config.product.build'
52
+ end
53
+
54
+ field 'productname' do
55
+ summary "ESX product name."
56
+ property 'summary.config.product.fullName'
57
+ end
58
+
44
59
  field 'uptime' do
45
60
  summary "Host's uptime in days"
46
61
  properties %w(runtime.bootTime)
@@ -155,6 +155,7 @@ class RbVmomi::VIM::VirtualMachine
155
155
  puts "hostname: #{guest.hostName} (#{guest.ipAddress})" if guest.hostName and guest.ipAddress
156
156
  puts "VC UUID: #{config.instanceUuid}" if config.instanceUuid and !config.instanceUuid.empty?
157
157
  puts "power: #{runtime.powerState}"
158
+ puts "connectionState: #{runtime.connectionState}"
158
159
  if runtime.question
159
160
  puts "question: #{runtime.question.text.lines.to_a.join("> ")}"
160
161
  puts "choices: #{runtime.question.choice.choiceInfo.map(&:label) * ', '}"
@@ -190,11 +191,15 @@ class RbVmomi::VIM::VirtualMachine
190
191
  end
191
192
 
192
193
  def self.ls_properties
193
- %w(name runtime.powerState)
194
+ %w(name runtime.powerState runtime.connectionState)
194
195
  end
195
196
 
196
197
  def ls_text r
197
- ": #{r['runtime.powerState']}"
198
+ out = ": #{r['runtime.powerState']}"
199
+ if r['runtime.connectionState'] != 'connected'
200
+ out = "#{out}, #{r['runtime.connectionState']}"
201
+ end
202
+ out
198
203
  end
199
204
 
200
205
  def children
@@ -78,10 +78,10 @@ class FS
78
78
  end
79
79
  when REGEX_PATTERN
80
80
  regex = Regexp.new($')
81
- cur.children.select { |k,v| k =~ regex }.map { |k,v| v.rvc_link(cur, k); v }
81
+ cur.rvc_children.select { |k,v| k =~ regex }.map { |k,v| v.rvc_link(cur, k); v }
82
82
  when GLOB_PATTERN
83
83
  regex = glob_to_regex arc
84
- cur.children.select { |k,v| k =~ regex }.map { |k,v| v.rvc_link(cur, k); v }
84
+ cur.rvc_children.select { |k,v| k =~ regex }.map { |k,v| v.rvc_link(cur, k); v }
85
85
  else
86
86
  # XXX check for ambiguous child
87
87
  if first and arc =~ /^\d+$/ and objs = @marks[arc]
@@ -65,12 +65,20 @@ module InventoryObject
65
65
  end
66
66
 
67
67
  def traverse_one arc
68
- children[arc]
68
+ rvc_children[arc]
69
69
  end
70
70
 
71
71
  def children
72
72
  {}
73
73
  end
74
+
75
+ def rvc_children
76
+ out = self.children
77
+ methods.grep(/rvc_list_children_/).each do |method|
78
+ out.merge!(self.send(method))
79
+ end
80
+ out
81
+ end
74
82
 
75
83
  def rvc_path
76
84
  [].tap do |a|
@@ -146,7 +146,7 @@ def ls obj
146
146
  return obj.rvc_ls
147
147
  end
148
148
 
149
- children = obj.children
149
+ children = obj.rvc_children
150
150
  name_map = children.invert
151
151
  children, fake_children = children.partition { |k,v| v.is_a? VIM::ManagedEntity }
152
152
  i = 0
@@ -265,6 +265,9 @@ def info obj
265
265
  puts "path: #{obj.rvc_path_str}"
266
266
  if obj.respond_to? :display_info
267
267
  obj.display_info
268
+ obj.methods.grep(/rvc_display_info_/).sort.each do |method|
269
+ obj.send(method)
270
+ end
268
271
  else
269
272
  puts "class: #{obj.class.name}"
270
273
  end
@@ -75,17 +75,61 @@ end
75
75
 
76
76
  opts :configure_ha do
77
77
  summary "Configure HA on a cluster"
78
- arg :cluster, nil, :lookup => VIM::ClusterComputeResource
78
+ arg :cluster, nil, :lookup => VIM::ClusterComputeResource, :multi => true
79
79
  opt :disabled, "Disable HA", :default => false
80
80
  end
81
81
 
82
- def configure_ha cluster, opts
82
+ def configure_ha clusters, opts
83
83
  spec = VIM::ClusterConfigSpecEx(
84
84
  :dasConfig => {
85
85
  :enabled => !opts[:disabled],
86
86
  }
87
87
  )
88
- one_progress(cluster.ReconfigureComputeResource_Task :spec => spec, :modify => true)
88
+ tasks = clusters.map do |cluster|
89
+ cluster.ReconfigureComputeResource_Task(:spec => spec, :modify => true)
90
+ end
91
+ progress(tasks)
92
+ childtasks = tasks.map{|t| t.child_tasks}.flatten.compact
93
+ if childtasks && childtasks.length > 0
94
+ progress(childtasks)
95
+ end
96
+
97
+ end
98
+
99
+ opts :configure_drs do
100
+ summary "Configure DRS on a cluster"
101
+ opt :disabled, "Disable HA", :default => false
102
+ opt :mode, "DRS mode (manual, partiallyAutomated, fullyAutomated)", :type => :string
103
+ arg :cluster, "Path to a Cluster", :lookup => VIM::ClusterComputeResource, :multi => true
104
+ end
105
+
106
+ def configure_drs clusters, opts
107
+ clusterSpec = VIM::ClusterConfigSpecEx(
108
+ :drsConfig => {
109
+ :defaultVmBehavior => opts[:mode] ? opts[:mode].to_sym : nil,
110
+ :enabled => !opts[:disabled]
111
+ }
112
+ )
113
+ tasks = clusters.map do |cluster|
114
+ cluster.ReconfigureComputeResource_Task(:modify => true, :spec => clusterSpec)
115
+ end
116
+ progress(tasks)
117
+ end
118
+
119
+ opts :configure_swap do
120
+ summary "Configure VM Swap Placement on a cluster"
121
+ opt :mode, "Swap mode (hostLocal, vmDirectory)", :type => :string
122
+ arg :cluster, "Path to a Cluster", :lookup => VIM::ClusterComputeResource, :multi => true
123
+ end
124
+
125
+ def configure_swap clusters, opts
126
+ clusterSpec = VIM::ClusterConfigSpecEx(
127
+ :vmSwapPlacement => opts[:mode]
128
+ )
129
+ tasks = clusters.map do |cluster|
130
+ cluster.ReconfigureComputeResource_Task(:modify => true, :spec => clusterSpec)
131
+ end
132
+ progress(tasks)
89
133
  end
90
134
 
91
135
  opts :recommendations do
@@ -109,30 +153,41 @@ def recommendations cluster
109
153
  name_map = pc.collectMultiple(targets, 'name')
110
154
 
111
155
  # Compose the output (tries to avoid making any API calls)
112
- out = Terminal::Table.new(['Key', 'Reason', 'Target', 'Actions']) do
113
- recommendation.each do |r|
114
- target_name = r.target ? name_map[r.target]['name'] : ""
115
- actions = r.action.map do |a|
116
- action = "#{a.class.wsdl_name}: #{name_map[a.target]['name']}"
117
- dst = nil
118
- if a.is_a?(RbVmomi::VIM::ClusterMigrationAction)
119
- dst = a.drsMigration.destination
120
- end
121
- if a.is_a?(RbVmomi::VIM::StorageMigrationAction)
122
- dst = a.destination
123
- end
124
- if dst
125
- if !name_map[dst]
126
- name_map[dst] = {'name' => dst.name}
127
- end
128
- action += " (to #{name_map[dst]['name']})"
156
+ t = Terminal::Table.new()
157
+ t << ['Key', 'Reason', 'Target', 'Actions']
158
+ recommendation.each do |r|
159
+ target_name = r.target ? name_map[r.target]['name'] : ""
160
+ actions = r.action.map do |a|
161
+ action = "#{a.class.wsdl_name}: #{name_map[a.target]['name']}"
162
+ dst = nil
163
+ if a.is_a?(RbVmomi::VIM::ClusterMigrationAction)
164
+ dst = a.drsMigration.destination
165
+ end
166
+ if a.is_a?(RbVmomi::VIM::StorageMigrationAction)
167
+ dst = a.destination
168
+ end
169
+ if dst
170
+ if !name_map[dst]
171
+ name_map[dst] = {'name' => dst.name}
129
172
  end
130
- action
173
+ action += " (to #{name_map[dst]['name']})"
131
174
  end
132
- add_row [r.key, r.reasonText, target_name, actions.join("\n")]
175
+ action
133
176
  end
177
+ t << [r.key, r.reasonText, target_name, actions.join("\n")]
178
+ end
179
+ puts t
180
+ end
181
+
182
+ def _filtered_cluster_recommendations cluster, key, type
183
+ recommendation = cluster.recommendation
184
+ if key && key.length > 0
185
+ recommendation.select! { |x| key.member?(x.key) }
134
186
  end
135
- puts out
187
+ if type && type.length > 0
188
+ recommendation.select! { |x| (type & x.action.map { |y| y.class.wsdl_name }).length > 0 }
189
+ end
190
+ recommendation
136
191
  end
137
192
 
138
193
  opts :apply_recommendations do
@@ -144,13 +199,9 @@ end
144
199
 
145
200
  def apply_recommendations cluster, opts
146
201
  pc = cluster._connection.serviceContent.propertyCollector
147
- recommendation = cluster.recommendation
148
- if opts[:key] && opts[:key].length > 0
149
- recommendation.select! { |x| opts[:key].member?(x.key) }
150
- end
151
- if opts[:type] && opts[:type].length > 0
152
- recommendation.select! { |x| (opts[:type] & x.action.map { |y| y.class.wsdl_name }).length > 0 }
153
- end
202
+ recommendation = _filtered_cluster_recommendations(
203
+ cluster, opts[:key], opts[:type]
204
+ )
154
205
  all_tasks = []
155
206
 
156
207
  # We do recommendations in chunks, because VC can't process more than a
@@ -160,16 +211,26 @@ def apply_recommendations cluster, opts
160
211
  # exceed the screensize with queued tasks
161
212
  while recommendation.length > 0
162
213
  recommendation.pop(20).each do |r|
163
- targets = r.action.map { |y| y.target }
164
- recent_tasks = pc.collectMultiple(targets, 'recentTask')
165
- prev_tasks = targets.map { |x| recent_tasks[x]['recentTask'] }
166
- cluster.ApplyRecommendation(:key => r.key)
167
- recent_tasks = pc.collectMultiple(targets, 'recentTask')
168
- tasks = targets.map { |x| recent_tasks[x]['recentTask'] }
169
- all_tasks += (tasks.flatten - prev_tasks.flatten)
214
+ begin
215
+ targets = r.action.map { |y| y.target }
216
+ recent_tasks = pc.collectMultiple(targets, 'recentTask')
217
+ prev_tasks = targets.map { |x| recent_tasks[x]['recentTask'] }
218
+ cluster.ApplyRecommendation(:key => r.key)
219
+ recent_tasks = pc.collectMultiple(targets, 'recentTask')
220
+ tasks = targets.map { |x| recent_tasks[x]['recentTask'] }
221
+ all_tasks += (tasks.flatten - prev_tasks.flatten)
222
+ rescue VIM::InvalidArgument
223
+ end
224
+ end
225
+
226
+ if all_tasks.length > 0
227
+ progress all_tasks
228
+ all_tasks = []
170
229
  end
171
230
 
172
- progress all_tasks
231
+ recommendation = _filtered_cluster_recommendations(
232
+ cluster, opts[:key], opts[:type]
233
+ )
173
234
  end
174
235
  end
175
236
 
@@ -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 :switch do
22
24
  summary "Switch to another connection"
23
25
  arg :name, "Connection name"
@@ -103,6 +103,41 @@ def add_net vm, network, opts
103
103
  end
104
104
 
105
105
 
106
+ opts :reconfig_net do
107
+ summary "Attach a network adapter to a different network"
108
+ arg :device, nil, :lookup => VIM::VirtualDevice, :multi => true
109
+ opt :network, "Network to attach to", :lookup => VIM::Network, :required => true
110
+ end
111
+
112
+ def reconfig_net devs, opts
113
+ network = opts[:network]
114
+ case network
115
+ when VIM::DistributedVirtualPortgroup
116
+ switch, pg_key = network.collect 'config.distributedVirtualSwitch', 'key'
117
+ port = VIM.DistributedVirtualSwitchPortConnection(
118
+ :switchUuid => switch.uuid,
119
+ :portgroupKey => pg_key)
120
+ summary = network.name
121
+ backing = VIM.VirtualEthernetCardDistributedVirtualPortBackingInfo(:port => port)
122
+ when VIM::Network
123
+ summary = network.name
124
+ backing = VIM.VirtualEthernetCardNetworkBackingInfo(:deviceName => network.name)
125
+ else fail
126
+ end
127
+
128
+ vm_devs = devs.group_by(&:rvc_vm)
129
+ tasks = vm_devs.map do |vm,my_devs|
130
+ device_changes = my_devs.map do |dev|
131
+ dev = dev.dup
132
+ dev.backing = backing
133
+ { :operation => :edit, :device => dev }
134
+ end
135
+ spec = { :deviceChange => device_changes }
136
+ vm.ReconfigVM_Task(:spec => spec)
137
+ end
138
+ progress(tasks)
139
+ end
140
+
106
141
  opts :add_disk do
107
142
  summary "Add a hard drive to a virtual machine"
108
143
  arg :vm, nil, :lookup => VIM::VirtualMachine
@@ -110,6 +145,7 @@ opts :add_disk do
110
145
  opt :size, 'Size', :default => '10G'
111
146
  opt :controller, 'Virtual controller', :type => :string, :lookup => VIM::VirtualController
112
147
  opt :file_op, 'File operation (create|reuse|replace)', :default => 'create'
148
+ opt :thick, "Use thick provisioning", :type => :boolean
113
149
  end
114
150
 
115
151
  def add_disk vm, path, opts
@@ -130,7 +166,7 @@ def add_disk vm, path, opts
130
166
  :backing => VIM.VirtualDiskFlatVer2BackingInfo(
131
167
  :fileName => filename,
132
168
  :diskMode => :persistent,
133
- :thinProvisioned => true
169
+ :thinProvisioned => !(opts[:thick] == true),
134
170
  ),
135
171
  :capacityInKB => MetricNumber.parse(opts[:size]).to_i/1000,
136
172
  :controllerKey => controller.key,
@@ -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
  DEFAULT_SERVER_PLACEHOLDER = '0.0.0.0'
22
24
 
23
25
  def wait_for_multiple_tasks tasks, timeout
@@ -48,6 +50,80 @@ def wait_for_multiple_tasks tasks, timeout
48
50
  results
49
51
  end
50
52
 
53
+ # http://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library
54
+ def ssh_exec!(ssh, command)
55
+ stdout_data = ""
56
+ stderr_data = ""
57
+ exit_code = nil
58
+ exit_signal = nil
59
+ ssh.open_channel do |channel|
60
+ channel.exec(command) do |ch, success|
61
+ unless success
62
+ abort "FAILED: couldn't execute command (ssh.channel.exec)"
63
+ end
64
+ channel.on_data do |ch,data|
65
+ stdout_data+=data
66
+ end
67
+
68
+ channel.on_extended_data do |ch,type,data|
69
+ stderr_data+=data
70
+ end
71
+
72
+ channel.on_request("exit-status") do |ch,data|
73
+ exit_code = data.read_long
74
+ end
75
+
76
+ end
77
+ end
78
+ ssh.loop
79
+ [stdout_data, stderr_data, exit_code]
80
+ end
81
+
82
+ opts :restart_services do
83
+ summary "Restart all services in hosts"
84
+ arg :cluster, nil, :lookup => VIM::ComputeResource, :multi => true
85
+ opt :host, "Host name (multi ok)", type: :string, short: 'n', :multi => true
86
+ opt :password, "Host password (multi ok)", type: :string, short: 'p', :multi => true
87
+ end
88
+
89
+ def restart_services clusters, opts
90
+ hosts = opts[:host]
91
+ pwds = opts[:password]
92
+ puts "Need to specify password(s) for fixing" if pwds == []
93
+
94
+ hosts.each do |host|
95
+ finished = false
96
+ pwds.each do |pwd|
97
+ break if finished
98
+ puts "\nTrying restart #{host} with pwd #{pwd}"
99
+ begin
100
+ Net::SSH.start(host, "root", :password => pwd, :paranoid => false) do |ssh|
101
+ # HZ 1258412 discusses the commands to fix a node with hostd problems
102
+ cmd = "/sbin/chkconfig usbarbitrator off"
103
+ puts "Running #{cmd}"
104
+ out = ssh_exec!(ssh,cmd)
105
+ if out[2] != 0
106
+ puts "Failed to execute #{cmd} on host #{host}"
107
+ puts out[1]
108
+ end
109
+
110
+ cmd = "/sbin/services.sh restart > /tmp/restart_services.log 2>&1"
111
+ puts "Running #{cmd}"
112
+ out = ssh_exec!(ssh,cmd)
113
+ if out[2] != 0
114
+ puts "Failed to restart all services on host #{host}"
115
+ puts out[1]
116
+ else
117
+ puts "Host #{host} restarted all services"
118
+ finished = true
119
+ end
120
+ end
121
+ rescue Net::SSH::AuthenticationFailed
122
+ puts "Failed to authenticate on host #{host}"
123
+ end
124
+ end
125
+ end
126
+ end
51
127
 
52
128
  opts :vm_create do
53
129
  summary "Check that VMs can be created on all hosts in a cluster"
@@ -55,6 +131,8 @@ opts :vm_create do
55
131
  opt :datastore, "Datastore to put (temporary) VMs on", :lookup => VIM::Datastore
56
132
  opt :vm_folder, "VM Folder to place (temporary) VMs in", :lookup => VIM::Folder
57
133
  opt :timeout, "Time to wait for VM creation to finish", :type => :int, :default => 3 * 60
134
+ opt :fix, "Fix the failing ESX hosts", :type => :boolean , :default => false
135
+ opt :password, "Passwords for fixing hosts", :type => :string, short: 'p', :multi => true
58
136
  end
59
137
 
60
138
  def vm_create clusters, opts
@@ -64,15 +142,34 @@ def vm_create clusters, opts
64
142
  err "vm_folder is a required parameter" unless vm_folder
65
143
 
66
144
  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']}"
145
+ errors = []
146
+ failed_hosts = []
147
+ begin
148
+ result = _vm_create clusters, datastore, vm_folder, opts
149
+ errors = result.select{|h, x| x['status'] != 'green'}
150
+ errors.each do |host, info|
151
+ puts "Failed to create VM on host #{host} (in cluster #{info['cluster']}): #{info['error']}"
152
+ err_msgs = ["Timed out", "InvalidState", "InvalidHostState", "InvalidHostConnectionState", "HostCommunication"]
153
+ err_msgs.each do |msg|
154
+ if info['error'].include? msg
155
+ failed_hosts << host
156
+ break
157
+ end
158
+ end
159
+ end
160
+ rescue Exception => e
161
+ puts "An error occurred:\n"
162
+ puts "e.message:", e.message
163
+ puts "e.backtrace:", e.backtrace.join("\n")
164
+ errors = [e]
72
165
  end
73
166
  if errors.length == 0
74
167
  puts "Success"
75
168
  end
169
+ if opts[:fix] && failed_hosts != []
170
+ opts[:host] = failed_hosts
171
+ restart_services(clusters, opts)
172
+ end
76
173
  end
77
174
 
78
175
  def _vm_create clusters, datastore, vm_folder, opts = {}
@@ -99,6 +196,7 @@ def _vm_create clusters, datastore, vm_folder, opts = {}
99
196
  :files => { :vmPathName => datastore_path },
100
197
  :numCPUs => 1,
101
198
  :memoryMB => 16,
199
+ :annotation => YAML.dump({'lease' => Time.now + 2 * opts[:timeout] + 60}),
102
200
  :deviceChange => [
103
201
  {
104
202
  :operation => :add,
@@ -118,11 +216,15 @@ def _vm_create clusters, datastore, vm_folder, opts = {}
118
216
  }
119
217
  ],
120
218
  }
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
219
+ begin
220
+ task = vm_folder.CreateVM_Task(:config => config,
221
+ :pool => rp,
222
+ :host => host)
223
+ tasks_map[task] = host
224
+ hosts_infos[host][:create_task] = task
225
+ rescue
226
+ puts "Failed to create task for host #{host.name}"
227
+ end
126
228
  end
127
229
  end
128
230
 
@@ -148,6 +250,7 @@ def _vm_create clusters, datastore, vm_folder, opts = {}
148
250
 
149
251
  result = host_info[:create_result]
150
252
  result = host_info[:destroy_result] if result.is_a?(VIM::VirtualMachine)
253
+ error_detail = nil
151
254
  if result == nil
152
255
  error_str = nil
153
256
  status = 'green'
@@ -157,12 +260,17 @@ def _vm_create clusters, datastore, vm_folder, opts = {}
157
260
  else
158
261
  error_str = "#{result.fault.class.wsdl_name}: #{result.localizedMessage}"
159
262
  status = 'red'
263
+ begin
264
+ error_detail = result.fault.faultMessage
265
+ rescue
266
+ end
160
267
  end
161
268
 
162
269
  out[host_props['name']] = {
163
270
  'cluster' => cluster_props['name'],
164
271
  'status' => status,
165
- 'error' => error_str
272
+ 'error' => error_str,
273
+ 'error_detail' => error_detail,
166
274
  }
167
275
  end
168
276
  out