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,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
- $shell.introspect_class klass
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 :path, "Limit commands to those applicable to the given object", :required => false
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 path
45
- if mod = RVC::MODULES[path]
46
- opts = mod.instance_variable_get(:@opts)
47
- opts.each do |method_name,parser|
48
- help_summary parser, path, method_name
49
- end
50
- return
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
- obj = lookup_single(path) if path
63
-
64
- if obj
65
- puts "Relevant commands for #{obj.class}:"
66
- else
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
- MODULES.sort_by do |mod_name,mod|
71
- HELP_ORDER.index(mod_name) || HELP_ORDER.size
72
- end.each do |mod_name,mod|
73
- opts = mod.instance_variable_get(:@opts)
74
- opts.each do |method_name,parser|
75
- next unless obj.nil? or parser.applicable.any? { |x| obj.is_a? x }
76
- help_summary parser, mod_name, method_name
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
- if not obj
81
- puts (<<-EOS)
83
+ puts unless ns.namespaces.empty? or ns.commands.empty?
82
84
 
83
- To see detailed help for a command, use its --help option.
84
- To show only commands relevant to a specific object, use "help /path/to/object".
85
- EOS
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 parser, mod_name, method_name
90
- aliases = ALIASES.select { |k,v| v == "#{mod_name}.#{method_name}" }.map(&:first)
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 "#{mod_name}.#{method_name}#{aliases_text}: #{parser.summary?}" if parser.summary?
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 = $shell.debug = !$shell.debug
104
- $shell.connections.each do |name,conn|
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
- $shell.fs.cd(obj)
120
- $shell.session.set_mark '', [find_ancestor(RbVmomi::VIM::Datacenter)].compact
121
- $shell.session.set_mark '@', [find_ancestor(RbVmomi::VIM)].compact
122
- $shell.delete_numeric_marks
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
- $shell.fs.cur.rvc_path.map { |k,v| v }.reverse.find { |x| x.is_a? klass }
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
- CMD.mark.mark i.to_s, [child]
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
- CMD.mark.mark i.to_s, [r.obj]
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 |line, args, word, index|
204
- choices = RVC::Completion.fs_candidates word
209
+ rvc_completor :show do |word, args|
210
+ choices = shell.completion.fs_candidates word
205
211
  obj = lookup_single '.'
206
- if index == 0
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
- MODULES['vds'].show_running_config obj
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
- MODULES['vds'].show_all_portgroups [obj]
252
+ shell.cmds.vds.show_all_portgroups [obj]
247
253
  when 'vds'
248
- MODULES['vds'].show_all_vds [obj]
254
+ shell.cmds.vds.show_all_vds [obj]
249
255
  when 'interface'
250
- MODULES['vds'].show_all_ports [obj]
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, = $shell.connections.find { |k,v| v == connection }
344
- $shell.connections.delete k
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 |line, args, word, argnum|
427
- if argnum > 0 and args[argnum-1] == '-f'
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
- RVC::Completion.fs_candidates word
435
+ shell.completion.fs_candidates word
431
436
  end
432
437
  end
433
438
 
@@ -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 :hostname, nil
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, hostname, opts
45
+ def add_host cluster, hostnames, opts
41
46
  sslThumbprint = nil
42
- while true
43
- spec = {
44
- :force => false,
45
- :hostName => hostname,
46
- :userName => opts[:username],
47
- :password => opts[:password],
48
- :sslThumbprint => sslThumbprint,
49
- }
50
- task = cluster.AddHost_Task :spec => spec,
51
- :asConnected => true
52
- begin
53
- one_progress task
54
- break
55
- rescue VIM::SSLVerifyFault
56
- puts "SSL thumbprint: #{$!.fault.thumbprint}"
57
- $stdout.write "Accept this thumbprint? (y/n) "
58
- $stdout.flush
59
- answer = $stdin.readline.chomp
60
- err "Aborted" unless answer == 'y' or answer == 'yes'
61
- sslThumbprint = $!.fault.thumbprint
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
@@ -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
- RVC.reload_modules opts[:verbose]
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