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.
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/rvc +46 -70
- data/devel/test-dependencies.sh +4 -0
- data/lib/rvc.rb +5 -6
- data/lib/rvc/command.rb +65 -0
- data/lib/rvc/command_slate.rb +112 -0
- data/lib/rvc/completion.rb +89 -58
- data/lib/rvc/connection.rb +48 -0
- data/lib/rvc/extensions/DistributedVirtualPortgroup.rb +1 -1
- data/lib/rvc/extensions/DistributedVirtualSwitch.rb +3 -3
- data/lib/rvc/extensions/HostSystem.rb +90 -0
- data/lib/rvc/extensions/VirtualMachine.rb +37 -7
- data/lib/rvc/field.rb +59 -12
- data/lib/rvc/fs.rb +34 -4
- data/lib/rvc/inventory.rb +5 -1
- data/lib/rvc/modules/alarm.rb +2 -0
- data/lib/rvc/modules/basic.rb +66 -61
- data/lib/rvc/modules/cluster.rb +117 -22
- data/lib/rvc/modules/connection.rb +40 -0
- data/lib/rvc/modules/core.rb +4 -16
- data/lib/rvc/modules/datacenter.rb +2 -0
- data/lib/rvc/modules/datastore.rb +11 -78
- data/lib/rvc/modules/device.rb +40 -5
- data/lib/rvc/modules/diagnostics.rb +169 -0
- data/lib/rvc/modules/esxcli.rb +9 -5
- data/lib/rvc/modules/find.rb +5 -3
- data/lib/rvc/modules/host.rb +46 -3
- data/lib/rvc/modules/issue.rb +2 -0
- data/lib/rvc/modules/mark.rb +5 -3
- data/lib/rvc/modules/perf.rb +99 -33
- data/lib/rvc/modules/permissions.rb +2 -0
- data/lib/rvc/modules/resource_pool.rb +2 -0
- data/lib/rvc/modules/role.rb +3 -1
- data/lib/rvc/modules/snapshot.rb +12 -4
- data/lib/rvc/modules/statsinterval.rb +13 -11
- data/lib/rvc/modules/vds.rb +67 -10
- data/lib/rvc/modules/vim.rb +19 -53
- data/lib/rvc/modules/vm.rb +27 -6
- data/lib/rvc/modules/vm_guest.rb +490 -0
- data/lib/rvc/modules/vmrc.rb +60 -32
- data/lib/rvc/modules/vnc.rb +2 -0
- data/lib/rvc/namespace.rb +114 -0
- data/lib/rvc/option_parser.rb +12 -15
- data/lib/rvc/readline-ffi.rb +4 -1
- data/lib/rvc/ruby_evaluator.rb +84 -0
- data/lib/rvc/shell.rb +68 -83
- data/lib/rvc/uri_parser.rb +59 -0
- data/lib/rvc/util.rb +134 -29
- data/lib/rvc/{extensions/PerfCounterInfo.rb → version.rb} +2 -4
- data/lib/rvc/{memory_session.rb → vim.rb} +10 -32
- data/test/modules/foo.rb +9 -0
- data/test/modules/foo/bar.rb +9 -0
- data/test/test_completion.rb +17 -0
- data/test/test_fs.rb +9 -11
- data/test/test_help.rb +46 -0
- data/test/test_helper.rb +12 -0
- data/test/test_metric.rb +1 -2
- data/test/test_modules.rb +38 -0
- data/test/test_parse_path.rb +1 -2
- data/test/test_shell.rb +138 -0
- data/test/test_uri.rb +34 -0
- metadata +115 -81
- data/lib/rvc/extensions/PerformanceManager.rb +0 -83
- data/lib/rvc/filesystem_session.rb +0 -101
- data/lib/rvc/modules.rb +0 -138
data/lib/rvc/modules/basic.rb
CHANGED
@@ -18,6 +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 'rvc/vim'
|
22
|
+
|
23
|
+
require 'terminal-table'
|
24
|
+
|
21
25
|
opts :type do
|
22
26
|
summary "Display information about a VMODL type"
|
23
27
|
arg :name, "VMODL type name"
|
@@ -27,69 +31,71 @@ rvc_alias :type
|
|
27
31
|
|
28
32
|
def type name
|
29
33
|
klass = RbVmomi::VIM.type(name) rescue err("#{name.inspect} is not a VMODL type.")
|
30
|
-
|
34
|
+
shell.introspect_class klass
|
31
35
|
nil
|
32
36
|
end
|
33
37
|
|
34
38
|
|
35
39
|
opts :help do
|
36
40
|
summary "Display this text"
|
37
|
-
arg :
|
41
|
+
arg :cmd, "Command or namespace to display help for", :required => false
|
38
42
|
end
|
39
43
|
|
40
44
|
rvc_alias :help
|
41
45
|
|
42
46
|
HELP_ORDER = %w(basic vm)
|
43
47
|
|
44
|
-
def help
|
45
|
-
if
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
elsif tgt = RVC::ALIASES[path]
|
52
|
-
fail unless tgt =~ /^(.+)\.(.+)$/
|
53
|
-
RVC::MODULES[$1].opts_for($2.to_sym).educate
|
54
|
-
return
|
55
|
-
elsif path =~ /^(.+)\.(.+)$/ and
|
56
|
-
mod = RVC::MODULES[$1] and
|
57
|
-
parser = mod.opts_for($2.to_sym)
|
58
|
-
parser.educate
|
59
|
-
return
|
48
|
+
def help input
|
49
|
+
if input
|
50
|
+
cmdpath, args = Shell.parse_input input
|
51
|
+
o = shell.cmds.lookup(cmdpath, Namespace) || shell.cmds.lookup(cmdpath)
|
52
|
+
RVC::Util.err "invalid command or namespace" unless o
|
53
|
+
else
|
54
|
+
o = shell.cmds
|
60
55
|
end
|
61
56
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
puts "All commands:"
|
57
|
+
case o
|
58
|
+
when Command
|
59
|
+
o.parser.educate
|
60
|
+
when Namespace
|
61
|
+
help_namespace o
|
68
62
|
end
|
69
63
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
64
|
+
# TODO apropos
|
65
|
+
puts (<<-EOS)
|
66
|
+
|
67
|
+
To see commands in a namespace: help namespace_name
|
68
|
+
To see detailed help for a command: help namespace_name.command_name
|
69
|
+
EOS
|
70
|
+
end
|
71
|
+
|
72
|
+
# TODO namespace summaries
|
73
|
+
def help_namespace ns
|
74
|
+
unless ns.namespaces.empty?
|
75
|
+
puts "Namespaces:"
|
76
|
+
ns.namespaces.sort_by do |child_name,child|
|
77
|
+
HELP_ORDER.index(child_name.to_s) || HELP_ORDER.size
|
78
|
+
end.each do |child_name,child|
|
79
|
+
puts child_name
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
80
|
-
|
81
|
-
puts (<<-EOS)
|
83
|
+
puts unless ns.namespaces.empty? or ns.commands.empty?
|
82
84
|
|
83
|
-
|
84
|
-
|
85
|
-
|
85
|
+
unless ns.commands.empty?
|
86
|
+
puts "Commands:"
|
87
|
+
ns.commands.sort_by do |cmd_name,cmd|
|
88
|
+
HELP_ORDER.index(cmd_name.to_s) || HELP_ORDER.size
|
89
|
+
end.each do |cmd_name,cmd|
|
90
|
+
help_summary cmd
|
91
|
+
end
|
86
92
|
end
|
87
93
|
end
|
88
94
|
|
89
|
-
def help_summary
|
90
|
-
aliases =
|
95
|
+
def help_summary cmd
|
96
|
+
aliases = shell.cmds.aliases.select { |k,v| shell.cmds.lookup(v) == cmd }.map(&:first)
|
91
97
|
aliases_text = aliases.empty? ? '' : " (#{aliases*', '})"
|
92
|
-
puts "#{
|
98
|
+
puts "#{cmd.name}#{aliases_text}: #{cmd.summary}"
|
93
99
|
end
|
94
100
|
|
95
101
|
|
@@ -100,8 +106,8 @@ end
|
|
100
106
|
rvc_alias :debug
|
101
107
|
|
102
108
|
def debug
|
103
|
-
debug =
|
104
|
-
|
109
|
+
debug = shell.debug = !shell.debug
|
110
|
+
shell.connections.each do |name,conn|
|
105
111
|
conn.debug = debug if conn.respond_to? :debug
|
106
112
|
end
|
107
113
|
puts "debug mode #{debug ? 'en' : 'dis'}abled"
|
@@ -116,14 +122,14 @@ end
|
|
116
122
|
rvc_alias :cd
|
117
123
|
|
118
124
|
def cd obj
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
125
|
+
shell.fs.cd(obj)
|
126
|
+
shell.fs.marks[''] = [find_ancestor(RbVmomi::VIM::Datacenter)].compact
|
127
|
+
shell.fs.marks['@'] = [find_ancestor(RbVmomi::VIM)].compact
|
128
|
+
shell.fs.delete_numeric_marks
|
123
129
|
end
|
124
130
|
|
125
131
|
def find_ancestor klass
|
126
|
-
|
132
|
+
shell.fs.cur.rvc_path.map { |k,v| v }.reverse.find { |x| x.is_a? klass }
|
127
133
|
end
|
128
134
|
|
129
135
|
|
@@ -148,7 +154,7 @@ def ls obj
|
|
148
154
|
fake_children.each do |name,child|
|
149
155
|
puts "#{i} #{name}#{child.ls_text(nil)}"
|
150
156
|
child.rvc_link obj, name
|
151
|
-
|
157
|
+
shell.cmds.mark.mark i.to_s, [child]
|
152
158
|
i += 1
|
153
159
|
end
|
154
160
|
|
@@ -179,7 +185,7 @@ def ls obj
|
|
179
185
|
colored_name = status_color name, r['overallStatus']
|
180
186
|
puts "#{i} #{colored_name}#{realname && " [#{realname}]"}#{text}"
|
181
187
|
r.obj.rvc_link obj, name
|
182
|
-
|
188
|
+
shell.cmds.mark.mark i.to_s, [r.obj]
|
183
189
|
i += 1
|
184
190
|
end
|
185
191
|
end
|
@@ -200,10 +206,10 @@ end
|
|
200
206
|
|
201
207
|
rvc_alias :show
|
202
208
|
|
203
|
-
rvc_completor :show do |
|
204
|
-
choices =
|
209
|
+
rvc_completor :show do |word, args|
|
210
|
+
choices = shell.completion.fs_candidates word
|
205
211
|
obj = lookup_single '.'
|
206
|
-
if
|
212
|
+
if args.length == 1
|
207
213
|
if obj.class == VIM::Datacenter || obj.class == VIM
|
208
214
|
choices << ['portgroups', ' ']
|
209
215
|
choices << ['vds', ' ']
|
@@ -234,7 +240,7 @@ def show arg0, arg1
|
|
234
240
|
case arg0
|
235
241
|
when 'running-config'
|
236
242
|
if obj.class < VIM::DistributedVirtualSwitch
|
237
|
-
|
243
|
+
shell.cmds.vds.show_running_config obj
|
238
244
|
else
|
239
245
|
if arg1 != '.'
|
240
246
|
err "'#{arg1}' is not a vDS!"
|
@@ -243,11 +249,11 @@ def show arg0, arg1
|
|
243
249
|
end
|
244
250
|
end
|
245
251
|
when 'portgroups'
|
246
|
-
|
252
|
+
shell.cmds.vds.show_all_portgroups [obj]
|
247
253
|
when 'vds'
|
248
|
-
|
254
|
+
shell.cmds.vds.show_all_vds [obj]
|
249
255
|
when 'interface'
|
250
|
-
|
256
|
+
shell.cmds.vds.show_all_ports [obj]
|
251
257
|
#when 'vlan'
|
252
258
|
else
|
253
259
|
path = lookup_single arg0
|
@@ -340,9 +346,8 @@ end
|
|
340
346
|
rvc_alias :disconnect
|
341
347
|
|
342
348
|
def disconnect connection
|
343
|
-
k, =
|
344
|
-
|
345
|
-
$shell.session.set_connection k, nil
|
349
|
+
k, = shell.connections.find { |k,v| v == connection }
|
350
|
+
shell.connections.delete k
|
346
351
|
end
|
347
352
|
|
348
353
|
|
@@ -423,11 +428,11 @@ shown by the "fields" command.
|
|
423
428
|
opt :reverse, "Reverse sort order"
|
424
429
|
end
|
425
430
|
|
426
|
-
rvc_completor :table do |
|
427
|
-
if
|
431
|
+
rvc_completor :table do |word, args|
|
432
|
+
if index > 0 and args[-2] == '-f'
|
428
433
|
RVC::Field::ALL_FIELD_NAMES.map { |x| [x, ' '] }
|
429
434
|
else
|
430
|
-
|
435
|
+
shell.completion.fs_candidates word
|
431
436
|
end
|
432
437
|
end
|
433
438
|
|
data/lib/rvc/modules/cluster.rb
CHANGED
@@ -18,6 +18,9 @@
|
|
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
|
+
require "terminal-table"
|
23
|
+
|
21
24
|
opts :create do
|
22
25
|
summary "Create a cluster"
|
23
26
|
arg :dest, nil, :lookup_parent => VIM::Folder
|
@@ -32,33 +35,39 @@ end
|
|
32
35
|
opts :add_host do
|
33
36
|
summary "Add a host to a cluster"
|
34
37
|
arg :cluster, nil, :lookup => VIM::ClusterComputeResource
|
35
|
-
arg :
|
38
|
+
arg :hostnames, nil, :multi => true
|
36
39
|
opt :username, "Username", :short => 'u', :default => 'root'
|
37
40
|
opt :password, "Password", :short => 'p', :default => ''
|
41
|
+
opt :insecure, "Ignore SSL thumbprint", :short => 'k'
|
42
|
+
opt :force, "Force, e.g when host is already managed by other VC"
|
38
43
|
end
|
39
44
|
|
40
|
-
def add_host cluster,
|
45
|
+
def add_host cluster, hostnames, opts
|
41
46
|
sslThumbprint = nil
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
47
|
+
hostnames.each do |hostname|
|
48
|
+
while true
|
49
|
+
spec = {
|
50
|
+
:force => opts[:force],
|
51
|
+
:hostName => hostname,
|
52
|
+
:userName => opts[:username],
|
53
|
+
:password => opts[:password],
|
54
|
+
:sslThumbprint => sslThumbprint,
|
55
|
+
}
|
56
|
+
task = cluster.AddHost_Task :spec => spec,
|
57
|
+
:asConnected => true
|
58
|
+
begin
|
59
|
+
one_progress task
|
60
|
+
break
|
61
|
+
rescue VIM::SSLVerifyFault
|
62
|
+
unless opts[:insecure]
|
63
|
+
puts "SSL thumbprint: #{$!.fault.thumbprint}"
|
64
|
+
$stdout.write "Accept this thumbprint? (y/n) "
|
65
|
+
$stdout.flush
|
66
|
+
answer = $stdin.readline.chomp
|
67
|
+
err "Aborted" unless answer == 'y' or answer == 'yes'
|
68
|
+
end
|
69
|
+
sslThumbprint = $!.fault.thumbprint
|
70
|
+
end
|
62
71
|
end
|
63
72
|
end
|
64
73
|
end
|
@@ -78,3 +87,89 @@ def configure_ha cluster, opts
|
|
78
87
|
)
|
79
88
|
one_progress(cluster.ReconfigureComputeResource_Task :spec => spec, :modify => true)
|
80
89
|
end
|
90
|
+
|
91
|
+
opts :recommendations do
|
92
|
+
summary "List recommendations"
|
93
|
+
arg :cluster, nil, :lookup => VIM::ClusterComputeResource
|
94
|
+
end
|
95
|
+
|
96
|
+
def recommendations cluster
|
97
|
+
# Collect everything we need from VC with as few calls as possible
|
98
|
+
pc = cluster._connection.serviceContent.propertyCollector
|
99
|
+
recommendation, hosts, datastores = cluster.collect 'recommendation', 'host', 'datastore'
|
100
|
+
if recommendation.length == 0
|
101
|
+
puts "None"
|
102
|
+
return
|
103
|
+
end
|
104
|
+
targets = recommendation.map { |x| x.target }
|
105
|
+
recommendation.each { |x| targets += x.action.map { |y| y.target } }
|
106
|
+
targets += hosts
|
107
|
+
targets += datastores
|
108
|
+
targets.compact!
|
109
|
+
name_map = pc.collectMultiple(targets, 'name')
|
110
|
+
|
111
|
+
# 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']})"
|
129
|
+
end
|
130
|
+
action
|
131
|
+
end
|
132
|
+
add_row [r.key, r.reasonText, target_name, actions.join("\n")]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
puts out
|
136
|
+
end
|
137
|
+
|
138
|
+
opts :apply_recommendations do
|
139
|
+
summary "Apply recommendations"
|
140
|
+
arg :cluster, nil, :lookup => VIM::ClusterComputeResource
|
141
|
+
opt :key, "Key of a recommendation to execute", :type => :string, :multi => true
|
142
|
+
opt :type, "Type of actions to perform", :type => :string, :multi => true
|
143
|
+
end
|
144
|
+
|
145
|
+
def apply_recommendations cluster, opts
|
146
|
+
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
|
154
|
+
all_tasks = []
|
155
|
+
|
156
|
+
# We do recommendations in chunks, because VC can't process more than a
|
157
|
+
# few migrations anyway and this way we get more fair queuing, less
|
158
|
+
# timeouts of long queued migrations and a better RVC user experience
|
159
|
+
# due to less queued tasks at a time. It would otherwise be easy to
|
160
|
+
# exceed the screensize with queued tasks
|
161
|
+
while recommendation.length > 0
|
162
|
+
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)
|
170
|
+
end
|
171
|
+
|
172
|
+
progress all_tasks
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
@@ -0,0 +1,40 @@
|
|
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
|
+
opts :switch do
|
22
|
+
summary "Switch to another connection"
|
23
|
+
arg :name, "Connection name"
|
24
|
+
end
|
25
|
+
|
26
|
+
def switch name
|
27
|
+
shell.switch_connection name
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
opts :list do
|
32
|
+
summary "List connections"
|
33
|
+
end
|
34
|
+
|
35
|
+
def list
|
36
|
+
shell.connections.each do |name, conn|
|
37
|
+
next if name.empty?
|
38
|
+
puts name
|
39
|
+
end
|
40
|
+
end
|
data/lib/rvc/modules/core.rb
CHANGED
@@ -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 :quit do
|
22
24
|
summary "Exit RVC"
|
23
25
|
end
|
@@ -42,22 +44,8 @@ def reload opts
|
|
42
44
|
old_verbose = $VERBOSE
|
43
45
|
$VERBOSE = nil unless opts[:verbose]
|
44
46
|
|
45
|
-
|
46
|
-
|
47
|
-
typenames = Set.new(VIM.loader.typenames.select { |x| VIM.const_defined? x })
|
48
|
-
dirs = VIM.extension_dirs
|
49
|
-
dirs.each do |path|
|
50
|
-
Dir.open(path) do |dir|
|
51
|
-
dir.each do |file|
|
52
|
-
next unless file =~ /\.rb$/
|
53
|
-
next unless typenames.member? $`
|
54
|
-
file_path = File.join(dir, file)
|
55
|
-
puts "loading #{$`} extensions from #{file_path}" if opts[:verbose]
|
56
|
-
load file_path
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
47
|
+
shell.reload_modules opts[:verbose]
|
48
|
+
RbVmomi::VIM.reload_extensions
|
61
49
|
ensure
|
62
50
|
$VERBOSE = old_verbose
|
63
51
|
end
|