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 +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
|