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
@@ -0,0 +1,48 @@
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
+ module RVC
22
+
23
+ # This module should be mixed in with all connection classes.
24
+ module Connection
25
+ include InventoryObject
26
+
27
+ module ClassMethods
28
+ include InventoryObject::ClassMethods
29
+
30
+ def folder?
31
+ true
32
+ end
33
+ end
34
+
35
+ def self.included m
36
+ m.extend ClassMethods
37
+ end
38
+
39
+ def _connection
40
+ self
41
+ end
42
+ end
43
+
44
+ class NullConnection
45
+ include Connection
46
+ end
47
+
48
+ end
@@ -217,7 +217,7 @@ class RbVmomi::VIM::DistributedVirtualPortgroup
217
217
  puts ""
218
218
  end
219
219
  child.rvc_link self, name
220
- CMD.mark.mark i.to_s, [child]
220
+ $shell.cmds.mark.mark i.to_s, [child]
221
221
  i += 1
222
222
  end
223
223
  end
@@ -58,11 +58,11 @@ class RbVmomi::VIM::DistributedVirtualSwitch
58
58
  vlan.vlanId.each { |range| trunk_ranges << Range.new(range.start,range.end) }
59
59
  end
60
60
  end
61
- trunks = RVC::MODULES['vds'].merge_ranges(trunk_ranges).map { |r|
61
+ trunks = $shell.cmds.vds.merge_ranges(trunk_ranges).map { |r|
62
62
  if r.begin == r.end then "#{r.begin}" else "#{r.begin}-#{r.end}" end
63
63
  }.join ','
64
64
 
65
- tags = RVC::MODULES['vds'].merge_ranges(tag_ranges).map { |r|
65
+ tags = $shell.cmds.vds.merge_ranges(tag_ranges).map { |r|
66
66
  if r.begin == r.end then "#{r.begin}" else "#{r.begin}-#{r.end}" end
67
67
  }.join ','
68
68
  str = ""
@@ -141,7 +141,7 @@ class RbVmomi::VIM::DistributedVirtualSwitch
141
141
  portgroups = RVC::FakeFolder.new(self, :portgroup_children)
142
142
  portgroups.define_singleton_method :display_info, lambda {
143
143
  vds = self.rvc_parent
144
- RVC::MODULES['basic'].table vds.portgroup, {}
144
+ $shell.cmds.basic.table vds.portgroup, {}
145
145
  }
146
146
 
147
147
  {
@@ -31,7 +31,97 @@ class RbVmomi::VIM::HostSystem
31
31
  " (host): cpu #{numCpuPkgs}*#{numCpuCores}*#{"%.2f" % (cpuMhz.to_f/1000)} GHz, memory #{"%.2f" % (memorySize/10**9)} GB"
32
32
  end
33
33
 
34
+ field 'state.connection' do
35
+ summary "State of connection to VC."
36
+ property 'runtime.connectionState'
37
+ end
38
+
39
+ field 'state.power' do
40
+ summary "Host power state."
41
+ property 'runtime.powerState'
42
+ end
43
+
44
+ field 'uptime' do
45
+ summary "Host's uptime in days"
46
+ properties %w(runtime.bootTime)
47
+ block { |t| t ? MetricNumber.new(((Time.now-t) / (24 * 60 * 60)), 'd') : nil }
48
+ end
49
+
50
+ field 'num.vms' do
51
+ summary "Number of VMs on the host"
52
+ properties %w(vm)
53
+ block { |t| t ? t.length : nil }
54
+ end
55
+
56
+ field 'num.poweredonvms' do
57
+ summary "Number of VMs on the host"
58
+ properties %w(vm)
59
+ block do |vms|
60
+ if vms && vms.length > 0
61
+ conn = vms.first._connection
62
+ pc = conn.propertyCollector
63
+ vmsProps = pc.collectMultiple(vms, 'runtime.powerState')
64
+ vmsProps.select{|vm, p| p['runtime.powerState'] == 'poweredOn'}.length
65
+ end
66
+ end
67
+ end
68
+
69
+ field 'cpuusage' do
70
+ summary "Realtime CPU usage in percent"
71
+ properties %w(summary.hardware.numCpuCores summary.hardware.cpuMhz summary.quickStats)
72
+ block do |cores, mhz, stats|
73
+ if cores && mhz && stats
74
+ value = stats.overallCpuUsage.to_f * 100 / (cores * mhz)
75
+ MetricNumber.new(value, '%')
76
+ end
77
+ end
78
+ end
79
+
80
+ field 'memusage' do
81
+ summary "Realtime Mem usage in percent"
82
+ properties %w(summary.hardware.memorySize summary.quickStats)
83
+ block do |mem, stats|
84
+ if mem && stats
85
+ value = stats.overallMemoryUsage.to_f * 100 / (mem / 1024.0 / 1024.0)
86
+ MetricNumber.new(value, '%')
87
+ end
88
+ end
89
+ end
90
+
91
+ [['.realtime', 1], ['.5min', 5 * 3], ['.10min', 10 * 3]].each do |label, max_samples|
92
+ field "cpuusage#{label}" do
93
+ summary "CPU Usage in Percent"
94
+ perfmetrics %w(cpu.usage)
95
+ perfmetric_settings :max_samples => max_samples
96
+ block do |value|
97
+ if value
98
+ value = value.sum.to_f / value.length / 100
99
+ MetricNumber.new(value, '%')
100
+ else
101
+ nil
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ [['.realtime', 1], ['.5min', 5 * 3], ['.10min', 10 * 3]].each do |label, max_samples|
108
+ field "memusage#{label}" do
109
+ summary "Mem Usage in Percent"
110
+ perfmetrics %w(mem.usage)
111
+ perfmetric_settings :max_samples => max_samples
112
+ block do |value|
113
+ if value
114
+ value = value.sum.to_f / value.length / 100
115
+ MetricNumber.new(value, '%')
116
+ else
117
+ nil
118
+ end
119
+ end
120
+ end
121
+ end
122
+
34
123
  def display_info
124
+ super
35
125
  summary = self.summary
36
126
  runtime = summary.runtime
37
127
  stats = summary.quickStats
@@ -28,26 +28,34 @@ class RbVmomi::VIM::VirtualMachine
28
28
 
29
29
  field 'storagebw' do
30
30
  summary "Storage Bandwidth"
31
- perfmetrics %w(disk.read.average disk.write.average)
31
+ perfmetrics %w(virtualDisk.read virtualDisk.write)
32
32
  block do |read, write|
33
33
  if read && write
34
- io = (read.sum.to_f / read.length) + (write.sum.to_f / write.length)
35
- MetricNumber.new(io * 1024, 'B/s')
34
+ read = read.select{|x| x != -1}
35
+ write = write.select{|x| x != -1}
36
+ if read.length > 0 && write.length > 0
37
+ io = (read.sum.to_f / read.length) + (write.sum.to_f / write.length)
38
+ MetricNumber.new(io * 1024, 'B/s')
39
+ end
36
40
  else
37
41
  nil
38
42
  end
39
43
  end
40
44
  end
41
45
 
42
- [['', 5], ['.realtime', 1], ['.5min', 5 * 3], ['.10min', 10 * 3]].each do |label, max_samples|
46
+ [['', 5], ['.realtime', 3], ['.5min', 5 * 3], ['.10min', 10 * 3]].each do |label, max_samples|
43
47
  field "storageiops#{label}" do
44
48
  summary "Storage IOPS"
45
- perfmetrics %w(disk.numberReadAveraged.average disk.numberWriteAveraged.average)
49
+ perfmetrics %w(virtualDisk.numberReadAveraged virtualDisk.numberWriteAveraged)
46
50
  perfmetric_settings :max_samples => max_samples
47
51
  block do |read, write|
48
52
  if read && write
49
- io = (read.sum.to_f / read.length) + (write.sum.to_f / write.length)
50
- MetricNumber.new(io, 'IOPS')
53
+ read = read.select{|x| x != -1}
54
+ write = write.select{|x| x != -1}
55
+ if read.length > 0 && write.length > 0
56
+ io = (read.sum.to_f / read.length) + (write.sum.to_f / write.length)
57
+ MetricNumber.new(io, 'IOPS')
58
+ end
51
59
  else
52
60
  nil
53
61
  end
@@ -55,6 +63,24 @@ class RbVmomi::VIM::VirtualMachine
55
63
  end
56
64
  end
57
65
 
66
+ ['Read', 'Write'].each do |type|
67
+ [['', 5], ['.realtime', 1], ['.5min', 5 * 3], ['.10min', 10 * 3]].each do |label, max_samples|
68
+ field "storagelatency.#{type.downcase}#{label}" do
69
+ summary "Storage Latency #{type}"
70
+ perfmetrics ["virtualDisk.total#{type}Latency"]
71
+ perfmetric_settings :max_samples => max_samples
72
+ block do |latency|
73
+ if latency
74
+ io = (latency.sum.to_f / latency.length)
75
+ MetricNumber.new(io, 'ms')
76
+ else
77
+ nil
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
58
84
  field 'ip' do
59
85
  summary "The guest tools reported IP address."
60
86
  property 'guest.ipAddress'
@@ -157,6 +183,10 @@ class RbVmomi::VIM::VirtualMachine
157
183
  storage.perDatastoreUsage.map do |usage|
158
184
  puts " #{usage.datastore.name}: committed=#{usage.committed.metric}B uncommitted=#{usage.uncommitted.metric}B unshared=#{usage.unshared.metric}B"
159
185
  end
186
+
187
+ if runtime.dasVmProtection
188
+ puts "HA protected: #{runtime.dasVmProtection.dasProtected ? 'yes' : 'no'}"
189
+ end
160
190
  end
161
191
 
162
192
  def self.ls_properties
@@ -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 'set'
22
+
21
23
  module RVC
22
24
 
23
25
  module ObjectWithFields
@@ -39,24 +41,69 @@ module ObjectWithFields
39
41
  @fields[name] = RVC::Field.new(name).tap { |f| f.instance_eval &b }
40
42
  end
41
43
  end
44
+
45
+ def field_properties names
46
+ out = []
47
+ names.each do |name|
48
+ name = name.to_s
49
+ field = self.class.fields[name]
50
+ if field == nil
51
+ nil
52
+ elsif self.class < VIM::ManagedObject
53
+ out += field.properties
54
+ end
55
+ end
56
+ out.uniq
57
+ end
58
+
59
+ def perfmetrics names
60
+ out = []
61
+ names.each do |name|
62
+ name = name.to_s
63
+ field = self.class.fields[name]
64
+ if field == nil
65
+ nil
66
+ else
67
+ perfmetrics = field.perfmetrics
68
+ if perfmetrics.length > 0
69
+ perfopts = field.perfmetric_settings.dup
70
+ perfopts[:max_samples] ||= 5
71
+ out << {:metrics => perfmetrics, :opts => perfopts}
72
+ end
73
+ end
74
+ end
75
+ out.uniq
76
+ end
42
77
 
43
- def field name
78
+ def field name, props_values = {}, perf_values = {}
44
79
  name = name.to_s
45
80
  field = self.class.fields[name]
46
81
  if field == nil
47
82
  return nil
48
83
  elsif self.class < VIM::ManagedObject
49
- *props = collect *field.properties
50
- if field.perfmetrics.length > 0
51
- perfmgr = self._connection.serviceContent.perfManager
52
- perfopts = field.perfmetric_settings.dup
53
- perfopts[:max_samples] ||= 5
54
- stats = perfmgr.retrieve_stats [self], field.perfmetrics, perfopts
55
- props += field.perfmetrics.map do |x|
56
- if stats[self]
57
- stats[self][:metrics][x]
58
- else
59
- nil
84
+ properties = field.properties
85
+ if properties.all?{|x| props_values.has_key?(x)}
86
+ props = properties.map{|x| props_values[x]}
87
+ else
88
+ *props = collect *field.properties
89
+ end
90
+ perfmetrics = field.perfmetrics
91
+ if perfmetrics.length > 0
92
+ if perfmetrics.all?{|x| perf_values.has_key?(x)}
93
+ props += perfmetrics.map do |x|
94
+ perf_values[x]
95
+ end
96
+ else
97
+ perfmgr = self._connection.serviceContent.perfManager
98
+ perfopts = field.perfmetric_settings.dup
99
+ perfopts[:max_samples] ||= 5
100
+ stats = perfmgr.retrieve_stats [self], field.perfmetrics, perfopts
101
+ props += field.perfmetrics.map do |x|
102
+ if stats[self]
103
+ stats[self][:metrics][x]
104
+ else
105
+ nil
106
+ end
60
107
  end
61
108
  end
62
109
  end
@@ -21,7 +21,7 @@
21
21
  module RVC
22
22
 
23
23
  class FS
24
- attr_reader :root, :cur
24
+ attr_reader :root, :cur, :marks
25
25
 
26
26
  MARK_PATTERN = /^~(?:([\d\w]*|~|@))$/
27
27
  REGEX_PATTERN = /^%/
@@ -31,6 +31,7 @@ class FS
31
31
  fail unless root.is_a? RVC::InventoryObject
32
32
  @root = root
33
33
  @cur = root
34
+ @marks = {}
34
35
  end
35
36
 
36
37
  def display_path
@@ -39,7 +40,7 @@ class FS
39
40
 
40
41
  def cd dst
41
42
  fail unless dst.is_a? RVC::InventoryObject
42
- $shell.session.set_mark '~', [@cur]
43
+ @marks['~'] = [@cur]
43
44
  @cur = dst
44
45
  end
45
46
 
@@ -70,7 +71,7 @@ class FS
70
71
  # XXX shouldnt be nil
71
72
  [(cur.respond_to?(:parent) && cur.parent) ? cur.parent : (cur.rvc_parent || cur)]
72
73
  when MARK_PATTERN
73
- if first and objs = $shell.session.get_mark($1)
74
+ if first and objs = @marks[$1]
74
75
  objs
75
76
  else
76
77
  []
@@ -83,7 +84,7 @@ class FS
83
84
  cur.children.select { |k,v| k =~ regex }.map { |k,v| v.rvc_link(cur, k); v }
84
85
  else
85
86
  # XXX check for ambiguous child
86
- if first and arc =~ /^\d+$/ and objs = $shell.session.get_mark(arc)
87
+ if first and arc =~ /^\d+$/ and objs = @marks[arc]
87
88
  objs
88
89
  else
89
90
  if child = cur.traverse_one(arc)
@@ -96,6 +97,35 @@ class FS
96
97
  end
97
98
  end
98
99
 
100
+ def delete_numeric_marks
101
+ @marks.reject! { |k,v| k =~ /^\d+$/ }
102
+ end
103
+
104
+ # Utility methods
105
+
106
+ def lookup_single path
107
+ objs = lookup path
108
+ Util.err "Not found: #{path.inspect}" if objs.empty?
109
+ Util.err "More than one match for #{path.inspect}" if objs.size > 1
110
+ objs.first
111
+ end
112
+
113
+ def lookup! path, types
114
+ types = [types] unless types.is_a? Enumerable
115
+ lookup(path).tap do |objs|
116
+ objs.each do |obj|
117
+ Util.err "Expected #{types*'/'} but got #{obj.class} at #{path.inspect}" unless types.any? { |type| obj.is_a? type }
118
+ end
119
+ end
120
+ end
121
+
122
+ def lookup_single! path, type
123
+ objs = lookup!(path, type)
124
+ Util.err "Not found: #{path.inspect}" if objs.empty?
125
+ Util.err "More than one match for #{path.inspect}" if objs.size > 1
126
+ objs.first
127
+ end
128
+
99
129
  private
100
130
 
101
131
  def glob_to_regex str
@@ -139,8 +139,12 @@ end
139
139
  class RootNode
140
140
  include RVC::InventoryObject
141
141
 
142
+ def initialize shell
143
+ @shell = shell
144
+ end
145
+
142
146
  def children
143
- $shell.connections
147
+ @shell.connections
144
148
  end
145
149
 
146
150
  def self.folder?
@@ -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 :show do
22
24
  summary "Show alarms on the given entities"
23
25
  arg :entity, nil, :lookup => VIM::ManagedEntity, :multi => true