rvc 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/rvc/completion.rb +1 -1
- data/lib/rvc/extensions/ClusterComputeResource.rb +11 -0
- data/lib/rvc/extensions/ComputeResource.rb +5 -1
- data/lib/rvc/extensions/Datacenter.rb +1 -1
- data/lib/rvc/extensions/HostSystem.rb +15 -0
- data/lib/rvc/extensions/VirtualMachine.rb +7 -2
- data/lib/rvc/fs.rb +2 -2
- data/lib/rvc/inventory.rb +9 -1
- data/lib/rvc/modules/basic.rb +4 -1
- data/lib/rvc/modules/cluster.rb +99 -38
- data/lib/rvc/modules/connection.rb +2 -0
- data/lib/rvc/modules/device.rb +37 -1
- data/lib/rvc/modules/diagnostics.rb +119 -11
- data/lib/rvc/modules/find.rb +1 -1
- data/lib/rvc/modules/host.rb +116 -4
- data/lib/rvc/modules/perf.rb +53 -7
- data/lib/rvc/modules/snapshot.rb +7 -1
- data/lib/rvc/modules/spbm.rb +728 -0
- data/lib/rvc/modules/syslog.rb +103 -0
- data/lib/rvc/modules/vds.rb +59 -2
- data/lib/rvc/modules/vim.rb +1 -1
- data/lib/rvc/modules/vm.rb +70 -7
- data/lib/rvc/modules/vm_guest.rb +190 -91
- data/lib/rvc/modules/vnc.rb +29 -5
- data/lib/rvc/modules/vsan.rb +4105 -0
- data/lib/rvc/util.rb +31 -11
- metadata +7 -3
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
|
-
|
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.
|
1
|
+
1.8.0
|
data/lib/rvc/completion.rb
CHANGED
@@ -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, :
|
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
|
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.
|
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
|
data/lib/rvc/fs.rb
CHANGED
@@ -78,10 +78,10 @@ class FS
|
|
78
78
|
end
|
79
79
|
when REGEX_PATTERN
|
80
80
|
regex = Regexp.new($')
|
81
|
-
cur.
|
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.
|
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]
|
data/lib/rvc/inventory.rb
CHANGED
@@ -65,12 +65,20 @@ module InventoryObject
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def traverse_one arc
|
68
|
-
|
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|
|
data/lib/rvc/modules/basic.rb
CHANGED
@@ -146,7 +146,7 @@ def ls obj
|
|
146
146
|
return obj.rvc_ls
|
147
147
|
end
|
148
148
|
|
149
|
-
children = obj.
|
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
|
data/lib/rvc/modules/cluster.rb
CHANGED
@@ -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
|
82
|
+
def configure_ha clusters, opts
|
83
83
|
spec = VIM::ClusterConfigSpecEx(
|
84
84
|
:dasConfig => {
|
85
85
|
:enabled => !opts[:disabled],
|
86
86
|
}
|
87
87
|
)
|
88
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
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
|
-
|
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 =
|
148
|
-
|
149
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
231
|
+
recommendation = _filtered_cluster_recommendations(
|
232
|
+
cluster, opts[:key], opts[:type]
|
233
|
+
)
|
173
234
|
end
|
174
235
|
end
|
175
236
|
|
data/lib/rvc/modules/device.rb
CHANGED
@@ -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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|