rbvmomi 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -2
- data/VERSION +1 -1
- data/devel/benchmark.rb +117 -0
- data/devel/collisions.rb +18 -0
- data/devel/merge-internal-vmodl.rb +7 -0
- data/lib/rbvmomi/basic_types.rb +20 -3
- data/lib/rbvmomi/connection.rb +59 -93
- data/lib/rbvmomi/deserialization.rb +235 -0
- data/lib/rbvmomi/trivial_soap.rb +6 -3
- data/lib/rbvmomi/type_loader.rb +23 -23
- data/lib/rbvmomi/vim.rb +2 -17
- data/lib/rbvmomi/vim/ComputeResource.rb +1 -1
- data/lib/rbvmomi/vim/Datastore.rb +6 -6
- data/lib/rbvmomi/vim/Folder.rb +48 -16
- data/lib/rbvmomi/vim/HostSystem.rb +13 -1
- data/lib/rbvmomi/vim/ManagedEntity.rb +36 -25
- data/lib/rbvmomi/vim/ManagedObject.rb +3 -3
- data/lib/rbvmomi/vim/PropertyCollector.rb +4 -2
- data/lib/rbvmomi/vim/ReflectManagedMethodExecuter.rb +2 -2
- data/lib/rbvmomi/vim/ResourcePool.rb +38 -1
- data/lib/rbvmomi/vim/ServiceInstance.rb +3 -3
- data/test/test_deserialization.rb +94 -9
- data/test/test_emit_request.rb +1 -3
- data/test/test_exceptions.rb +1 -3
- data/test/test_helper.rb +14 -0
- data/test/test_misc.rb +7 -3
- data/test/test_parse_response.rb +1 -3
- data/test/test_serialization.rb +17 -5
- data/vmodl.db +0 -0
- metadata +7 -2
@@ -0,0 +1,235 @@
|
|
1
|
+
# Copyright (c) 2011 VMware, Inc. All Rights Reserved.
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module RbVmomi
|
5
|
+
|
6
|
+
class NewDeserializer
|
7
|
+
NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
|
8
|
+
|
9
|
+
DEMANGLED_ARRAY_TYPES = {
|
10
|
+
'AnyType' => 'xsd:anyType',
|
11
|
+
'DateTime' => 'xsd:dateTime',
|
12
|
+
}
|
13
|
+
%w(Boolean String Byte Short Int Long Float Double).each do |x|
|
14
|
+
DEMANGLED_ARRAY_TYPES[x] = "xsd:#{x.downcase}"
|
15
|
+
end
|
16
|
+
|
17
|
+
BUILTIN_TYPE_ACTIONS = {
|
18
|
+
'xsd:string' => :string,
|
19
|
+
'xsd:boolean' => :boolean,
|
20
|
+
'xsd:byte' => :int,
|
21
|
+
'xsd:short' => :int,
|
22
|
+
'xsd:int' => :int,
|
23
|
+
'xsd:long' => :int,
|
24
|
+
'xsd:float' => :float,
|
25
|
+
'xsd:dateTime' => :date,
|
26
|
+
'PropertyPath' => :string,
|
27
|
+
'MethodName' => :string,
|
28
|
+
'TypeName' => :string,
|
29
|
+
'xsd:base64Binary' => :binary,
|
30
|
+
'KeyValue' => :keyvalue,
|
31
|
+
}
|
32
|
+
|
33
|
+
BUILTIN_TYPE_ACTIONS.dup.each do |k,v|
|
34
|
+
if k =~ /^xsd:/
|
35
|
+
BUILTIN_TYPE_ACTIONS[$'] = v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize conn
|
40
|
+
@conn = conn
|
41
|
+
@loader = conn.class.loader
|
42
|
+
end
|
43
|
+
|
44
|
+
def deserialize node, type=nil
|
45
|
+
type_attr = node['type']
|
46
|
+
type = type_attr if type_attr
|
47
|
+
|
48
|
+
if action = BUILTIN_TYPE_ACTIONS[type]
|
49
|
+
case action
|
50
|
+
when :string
|
51
|
+
node.content
|
52
|
+
when :boolean
|
53
|
+
node.content == '1' || node.content == 'true'
|
54
|
+
when :int
|
55
|
+
node.content.to_i
|
56
|
+
when :float
|
57
|
+
node.content.to_f
|
58
|
+
when :date
|
59
|
+
leaf_date node
|
60
|
+
when :binary
|
61
|
+
leaf_binary node
|
62
|
+
when :keyvalue
|
63
|
+
leaf_keyvalue node
|
64
|
+
else fail
|
65
|
+
end
|
66
|
+
else
|
67
|
+
if type =~ /^ArrayOf/
|
68
|
+
type = DEMANGLED_ARRAY_TYPES[$'] || $'
|
69
|
+
return node.children.select(&:element?).map { |c| deserialize c, type }
|
70
|
+
end
|
71
|
+
|
72
|
+
klass = @loader.get(type) or fail "no such type #{type}"
|
73
|
+
case klass.kind
|
74
|
+
when :data
|
75
|
+
traverse_data node, klass
|
76
|
+
when :enum
|
77
|
+
node.content
|
78
|
+
when :managed
|
79
|
+
leaf_managed node, klass
|
80
|
+
else fail
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def traverse_data node, klass
|
86
|
+
obj = klass.new nil
|
87
|
+
props = obj.props
|
88
|
+
children = node.children.select(&:element?)
|
89
|
+
n = children.size
|
90
|
+
i = 0
|
91
|
+
|
92
|
+
klass.full_props_desc.each do |desc|
|
93
|
+
name = desc['name']
|
94
|
+
child_type = desc['wsdl_type']
|
95
|
+
|
96
|
+
# Ignore unknown fields
|
97
|
+
while child = children[i] and not klass.full_props_set.member? child.name
|
98
|
+
i += 1
|
99
|
+
end
|
100
|
+
|
101
|
+
if desc['is-array']
|
102
|
+
a = []
|
103
|
+
while ((child = children[i]) && (child.name == name))
|
104
|
+
child = children[i]
|
105
|
+
a << deserialize(child, child_type)
|
106
|
+
i += 1
|
107
|
+
end
|
108
|
+
props[name.to_sym] = a
|
109
|
+
elsif ((child = children[i]) && (child.name == name))
|
110
|
+
props[name.to_sym] = deserialize(child, child_type)
|
111
|
+
i += 1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
obj
|
116
|
+
end
|
117
|
+
|
118
|
+
def leaf_managed node, klass
|
119
|
+
type_attr = node['type']
|
120
|
+
klass = @loader.get(type_attr) if type_attr
|
121
|
+
klass.new(@conn, node.content)
|
122
|
+
end
|
123
|
+
|
124
|
+
def leaf_date node
|
125
|
+
Time.parse node.content
|
126
|
+
end
|
127
|
+
|
128
|
+
def leaf_binary node
|
129
|
+
node.content.unpack('m')[0]
|
130
|
+
end
|
131
|
+
|
132
|
+
# XXX does the value need to be deserialized?
|
133
|
+
def leaf_keyvalue node
|
134
|
+
h = {}
|
135
|
+
node.children.each do |child|
|
136
|
+
next unless child.element?
|
137
|
+
h[child.name] = child.content
|
138
|
+
end
|
139
|
+
[h['key'], h['value']]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class OldDeserializer
|
144
|
+
NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
|
145
|
+
|
146
|
+
def initialize conn
|
147
|
+
@conn = conn
|
148
|
+
end
|
149
|
+
|
150
|
+
def deserialize xml, typename=nil
|
151
|
+
if IS_JRUBY
|
152
|
+
type_attr = xml.attribute_nodes.find { |a| a.name == 'type' &&
|
153
|
+
a.namespace &&
|
154
|
+
a.namespace.prefix == 'xsi' }
|
155
|
+
else
|
156
|
+
type_attr = xml.attribute_with_ns('type', NS_XSI)
|
157
|
+
end
|
158
|
+
typename = (type_attr || typename).to_s
|
159
|
+
|
160
|
+
if typename =~ /^ArrayOf/
|
161
|
+
typename = demangle_array_type $'
|
162
|
+
return xml.children.select(&:element?).map { |c| deserialize c, typename }
|
163
|
+
end
|
164
|
+
|
165
|
+
t = @conn.type typename
|
166
|
+
if t <= BasicTypes::DataObject
|
167
|
+
props_desc = t.full_props_desc
|
168
|
+
h = {}
|
169
|
+
props_desc.select { |d| d['is-array'] }.each { |d| h[d['name'].to_sym] = [] }
|
170
|
+
xml.children.each do |c|
|
171
|
+
next unless c.element?
|
172
|
+
field = c.name.to_sym
|
173
|
+
d = t.find_prop_desc(field.to_s) or next
|
174
|
+
o = deserialize c, d['wsdl_type']
|
175
|
+
if h[field].is_a? Array
|
176
|
+
h[field] << o
|
177
|
+
else
|
178
|
+
h[field] = o
|
179
|
+
end
|
180
|
+
end
|
181
|
+
t.new h
|
182
|
+
elsif t == BasicTypes::ManagedObjectReference
|
183
|
+
@conn.type(xml['type']).new @conn, xml.text
|
184
|
+
elsif t <= BasicTypes::ManagedObject
|
185
|
+
@conn.type(xml['type'] || t.wsdl_name).new @conn, xml.text
|
186
|
+
elsif t <= BasicTypes::Enum
|
187
|
+
xml.text
|
188
|
+
elsif t <= BasicTypes::KeyValue
|
189
|
+
h = {}
|
190
|
+
xml.children.each do |c|
|
191
|
+
next unless c.element?
|
192
|
+
h[c.name] = c.text
|
193
|
+
end
|
194
|
+
[h['key'], h['value']]
|
195
|
+
elsif t <= String
|
196
|
+
xml.text
|
197
|
+
elsif t <= Symbol
|
198
|
+
xml.text.to_sym
|
199
|
+
elsif t <= Integer
|
200
|
+
xml.text.to_i
|
201
|
+
elsif t <= Float
|
202
|
+
xml.text.to_f
|
203
|
+
elsif t <= Time
|
204
|
+
Time.parse xml.text
|
205
|
+
elsif t == BasicTypes::Boolean
|
206
|
+
xml.text == 'true' || xml.text == '1'
|
207
|
+
elsif t == BasicTypes::Binary
|
208
|
+
xml.text.unpack('m')[0]
|
209
|
+
elsif t == BasicTypes::AnyType
|
210
|
+
fail "attempted to deserialize an AnyType"
|
211
|
+
else fail "unexpected type #{t.inspect} (#{t.ancestors * '/'})"
|
212
|
+
end
|
213
|
+
rescue
|
214
|
+
$stderr.puts "#{$!.class} while deserializing #{xml.name} (#{typename}):"
|
215
|
+
$stderr.puts xml.to_s
|
216
|
+
raise
|
217
|
+
end
|
218
|
+
|
219
|
+
def demangle_array_type x
|
220
|
+
case x
|
221
|
+
when 'AnyType' then 'anyType'
|
222
|
+
when 'DateTime' then 'dateTime'
|
223
|
+
when 'Boolean', 'String', 'Byte', 'Short', 'Int', 'Long', 'Float', 'Double' then x.downcase
|
224
|
+
else x
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
if ENV['RBVMOMI_NEW_DESERIALIZER'] == '1'
|
230
|
+
Deserializer = NewDeserializer
|
231
|
+
else
|
232
|
+
Deserializer = OldDeserializer
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
data/lib/rbvmomi/trivial_soap.rb
CHANGED
@@ -20,6 +20,10 @@ class RbVmomi::TrivialSoap
|
|
20
20
|
restart_http
|
21
21
|
end
|
22
22
|
|
23
|
+
def host
|
24
|
+
@opts[:host]
|
25
|
+
end
|
26
|
+
|
23
27
|
def close
|
24
28
|
@http.finish rescue IOError
|
25
29
|
end
|
@@ -65,10 +69,9 @@ class RbVmomi::TrivialSoap
|
|
65
69
|
xml
|
66
70
|
end
|
67
71
|
|
68
|
-
def request action,
|
72
|
+
def request action, body
|
69
73
|
headers = { 'content-type' => 'text/xml; charset=utf-8', 'SOAPAction' => action }
|
70
74
|
headers['cookie'] = @cookie if @cookie
|
71
|
-
body = soap_envelope(&b).target!
|
72
75
|
|
73
76
|
if @debug
|
74
77
|
$stderr.puts "Request:"
|
@@ -101,6 +104,6 @@ class RbVmomi::TrivialSoap
|
|
101
104
|
$stderr.puts
|
102
105
|
end
|
103
106
|
|
104
|
-
nk.xpath('//soapenv:Body/*').select(&:element?).first
|
107
|
+
[nk.xpath('//soapenv:Body/*').select(&:element?).first, response.body.size]
|
105
108
|
end
|
106
109
|
end
|
data/lib/rbvmomi/type_loader.rb
CHANGED
@@ -5,40 +5,41 @@ require 'monitor'
|
|
5
5
|
module RbVmomi
|
6
6
|
|
7
7
|
class TypeLoader
|
8
|
-
def initialize
|
9
|
-
@
|
8
|
+
def initialize fn, extension_dirs, namespace
|
9
|
+
@extension_dirs = extension_dirs
|
10
|
+
@namespace = namespace
|
10
11
|
@lock = Monitor.new
|
11
12
|
@db = {}
|
12
13
|
@id2wsdl = {}
|
14
|
+
@loaded = {}
|
13
15
|
add_types Hash[BasicTypes::BUILTIN.map { |k| [k,nil] }]
|
14
16
|
vmodl_database = File.open(fn, 'r') { |io| Marshal.load io }
|
15
17
|
vmodl_database.reject! { |k,v| k =~ /^_/ }
|
16
18
|
add_types vmodl_database
|
19
|
+
preload
|
17
20
|
end
|
18
21
|
|
19
|
-
def
|
20
|
-
@
|
21
|
-
|
22
|
-
|
23
|
-
load_extension x
|
24
|
-
end
|
25
|
-
Object.constants.map(&:to_s).select { |x| has_type? x }.each do |x|
|
26
|
-
load_type x
|
27
|
-
end
|
22
|
+
def preload
|
23
|
+
names = (@namespace.constants + Object.constants).map(&:to_s).uniq.
|
24
|
+
select { |x| has? x }
|
25
|
+
names.each { |x| get(x) }
|
28
26
|
end
|
29
27
|
|
30
|
-
def
|
28
|
+
def has? name
|
31
29
|
fail unless name.is_a? String
|
32
|
-
@db.member? name
|
30
|
+
@db.member?(name) or BasicTypes::BUILTIN.member?(name)
|
33
31
|
end
|
34
32
|
|
35
|
-
def
|
33
|
+
def get name
|
36
34
|
fail unless name.is_a? String
|
35
|
+
return @loaded[name] if @loaded.member? name
|
37
36
|
@lock.synchronize do
|
38
|
-
@
|
37
|
+
return @loaded[name] if @loaded.member? name
|
38
|
+
klass = make_type(name)
|
39
|
+
@namespace.const_set name, klass
|
39
40
|
load_extension name
|
41
|
+
@loaded[name] = klass
|
40
42
|
end
|
41
|
-
nil
|
42
43
|
end
|
43
44
|
|
44
45
|
def add_types types
|
@@ -54,15 +55,14 @@ class TypeLoader
|
|
54
55
|
private
|
55
56
|
|
56
57
|
def load_extension name
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
each { |x| load x }
|
58
|
+
@extension_dirs.map { |x| File.join(x, "#{name}.rb") }.
|
59
|
+
select { |x| File.exists? x }.
|
60
|
+
each { |x| load x }
|
61
61
|
end
|
62
62
|
|
63
63
|
def make_type name
|
64
64
|
name = name.to_s
|
65
|
-
|
65
|
+
return BasicTypes.const_get(name) if BasicTypes::BUILTIN.member? name
|
66
66
|
desc = @db[name] or fail "unknown VMODL type #{name}"
|
67
67
|
case desc['kind']
|
68
68
|
when 'data' then make_data_type name, desc
|
@@ -73,14 +73,14 @@ class TypeLoader
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def make_data_type name, desc
|
76
|
-
superclass =
|
76
|
+
superclass = get desc['wsdl_base']
|
77
77
|
Class.new(superclass).tap do |klass|
|
78
78
|
klass.init name, desc['props']
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
82
|
def make_managed_type name, desc
|
83
|
-
superclass =
|
83
|
+
superclass = get desc['wsdl_base']
|
84
84
|
Class.new(superclass).tap do |klass|
|
85
85
|
klass.init name, desc['props'], desc['methods']
|
86
86
|
end
|
data/lib/rbvmomi/vim.rb
CHANGED
@@ -41,25 +41,10 @@ class VIM < Connection
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def close
|
44
|
-
VIM::SessionManager(self, 'SessionManager').Logout
|
44
|
+
VIM::SessionManager(self, 'SessionManager').Logout rescue RbVmomi::Fault
|
45
|
+
self.cookie = nil
|
45
46
|
super
|
46
47
|
end
|
47
|
-
|
48
|
-
def cookie= cookie
|
49
|
-
super
|
50
|
-
ObjectSpace.undefine_finalizer self
|
51
|
-
ObjectSpace.define_finalizer(self, self.class.finalizer(cookie,@opts))
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.finalizer cookie, opts
|
55
|
-
proc do |object_id|
|
56
|
-
new(opts).tap do |vim|
|
57
|
-
vim.instance_variable_set :@cookie, cookie
|
58
|
-
vim.close
|
59
|
-
end
|
60
|
-
nil
|
61
|
-
end
|
62
|
-
end
|
63
48
|
|
64
49
|
def rev= x
|
65
50
|
super
|
@@ -8,8 +8,8 @@ class RbVmomi::VIM::Datastore
|
|
8
8
|
# @param path [String] Path on the datastore.
|
9
9
|
def exists? path
|
10
10
|
req = Net::HTTP::Head.new mkuripath(path)
|
11
|
-
req.initialize_http_header 'cookie' =>
|
12
|
-
resp =
|
11
|
+
req.initialize_http_header 'cookie' => _connection.cookie
|
12
|
+
resp = _connection.http.request req
|
13
13
|
case resp
|
14
14
|
when Net::HTTPSuccess
|
15
15
|
true
|
@@ -25,10 +25,10 @@ class RbVmomi::VIM::Datastore
|
|
25
25
|
# @param local_path [String] Destination path on the local machine.
|
26
26
|
# @return [void]
|
27
27
|
def download remote_path, local_path
|
28
|
-
url = "http#{
|
28
|
+
url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}"
|
29
29
|
pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f',
|
30
30
|
"-o", local_path,
|
31
|
-
"-b",
|
31
|
+
"-b", _connection.cookie,
|
32
32
|
url,
|
33
33
|
:out => '/dev/null'
|
34
34
|
Process.waitpid(pid, 0)
|
@@ -40,10 +40,10 @@ class RbVmomi::VIM::Datastore
|
|
40
40
|
# @param local_path [String] Source path on the local machine.
|
41
41
|
# @return [void]
|
42
42
|
def upload remote_path, local_path
|
43
|
-
url = "http#{
|
43
|
+
url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}"
|
44
44
|
pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f',
|
45
45
|
"-T", local_path,
|
46
|
-
"-b",
|
46
|
+
"-b", _connection.cookie,
|
47
47
|
url,
|
48
48
|
:out => '/dev/null'
|
49
49
|
Process.waitpid(pid, 0)
|
data/lib/rbvmomi/vim/Folder.rb
CHANGED
@@ -4,7 +4,7 @@ class RbVmomi::VIM::Folder
|
|
4
4
|
# @param type [Class] Return nil unless the found entity <tt>is_a? type</tt>.
|
5
5
|
# @return [VIM::ManagedEntity]
|
6
6
|
def find name, type=Object
|
7
|
-
x =
|
7
|
+
x = _connection.searchIndex.FindChild(:entity => self, :name => name)
|
8
8
|
x if x.is_a? type
|
9
9
|
end
|
10
10
|
|
@@ -19,7 +19,7 @@ class RbVmomi::VIM::Folder
|
|
19
19
|
:vmSearch => type == RbVmomi::VIM::VirtualMachine
|
20
20
|
}
|
21
21
|
propSpecs[:datacenter] = dc if dc
|
22
|
-
x =
|
22
|
+
x = _connection.searchIndex.FindByDnsName(propSpecs)
|
23
23
|
x if x.is_a? type
|
24
24
|
end
|
25
25
|
|
@@ -34,7 +34,7 @@ class RbVmomi::VIM::Folder
|
|
34
34
|
:vmSearch => type == RbVmomi::VIM::VirtualMachine
|
35
35
|
}
|
36
36
|
propSpecs[:datacenter] = dc if dc
|
37
|
-
x =
|
37
|
+
x = _connection.searchIndex.FindByIp(propSpecs)
|
38
38
|
x if x.is_a? type
|
39
39
|
end
|
40
40
|
|
@@ -49,7 +49,7 @@ class RbVmomi::VIM::Folder
|
|
49
49
|
:vmSearch => type == RbVmomi::VIM::VirtualMachine
|
50
50
|
}
|
51
51
|
propSpecs[:datacenter] = dc if dc
|
52
|
-
x =
|
52
|
+
x = _connection.searchIndex.FindByUuid(propSpecs)
|
53
53
|
x if x.is_a? type
|
54
54
|
end
|
55
55
|
|
@@ -104,21 +104,18 @@ class RbVmomi::VIM::Folder
|
|
104
104
|
# included in the results. Values are an array of
|
105
105
|
# property paths (strings) or the symbol :all.
|
106
106
|
#
|
107
|
-
# @return [Hash]
|
108
|
-
|
109
|
-
|
110
|
-
#
|
111
|
-
# @todo Return ObjectContent instead of the leaf hash.
|
112
|
-
def inventory propSpecs={}
|
113
|
-
propSet = [{ :type => 'Folder', :pathSet => ['name', 'parent'] }]
|
107
|
+
# @return [Hash] Hash of ManagedObjects to properties.
|
108
|
+
def inventory_flat propSpecs={}
|
109
|
+
propSet = [{ :type => 'Folder', :pathSet => ['name', 'parent', 'childEntity'] }]
|
114
110
|
propSpecs.each do |k,v|
|
115
111
|
case k
|
116
|
-
when
|
112
|
+
when Class
|
113
|
+
fail "key must be a subclass of ManagedEntity" unless k < RbVmomi::VIM::ManagedEntity
|
117
114
|
k = k.wsdl_name
|
118
115
|
when Symbol, String
|
119
116
|
k = k.to_s
|
120
117
|
else
|
121
|
-
fail "key
|
118
|
+
fail "invalid key"
|
122
119
|
end
|
123
120
|
|
124
121
|
h = { :type => k }
|
@@ -150,11 +147,46 @@ class RbVmomi::VIM::Folder
|
|
150
147
|
:propSet => propSet
|
151
148
|
)
|
152
149
|
|
153
|
-
result =
|
150
|
+
result = _connection.propertyCollector.RetrieveProperties(:specSet => [filterSpec])
|
151
|
+
{}.tap do |h|
|
152
|
+
result.each { |r| h[r.obj] = r }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Efficiently retrieve properties from descendants of this folder.
|
157
|
+
#
|
158
|
+
# @param propSpecs [Hash] Specification of which properties to retrieve from
|
159
|
+
# which entities. Keys may be symbols, strings, or
|
160
|
+
# classes identifying ManagedEntity subtypes to be
|
161
|
+
# included in the results. Values are an array of
|
162
|
+
# property paths (strings) or the symbol :all.
|
163
|
+
#
|
164
|
+
# @return [Hash] Tree of inventory items. Each node is a hash from
|
165
|
+
# VIM::ObjectContent to children.
|
166
|
+
def inventory_tree propSpecs={}
|
167
|
+
inv = inventory_flat propSpecs
|
168
|
+
children = inv.values.group_by { |v| v['parent'] }
|
169
|
+
rec = lambda { |parent| Hash[(children[parent]||[]).map { |x| [x, rec[x.obj]] }] }
|
170
|
+
rec[self]
|
171
|
+
end
|
154
172
|
|
173
|
+
# Efficiently retrieve properties from descendants of this folder.
|
174
|
+
#
|
175
|
+
# @param propSpecs [Hash] Specification of which properties to retrieve from
|
176
|
+
# which entities. Keys may be symbols, strings, or
|
177
|
+
# classes identifying ManagedEntity subtypes to be
|
178
|
+
# included in the results. Values are an array of
|
179
|
+
# property paths (strings) or the symbol :all.
|
180
|
+
#
|
181
|
+
# @return [Hash] Tree of inventory items. Folders are hashes from child name
|
182
|
+
# to child result. Objects are hashes from property path to
|
183
|
+
# value.
|
184
|
+
#
|
185
|
+
# @deprecated
|
186
|
+
def inventory propSpecs={}
|
187
|
+
inv = inventory_flat propSpecs
|
155
188
|
tree = { self => {} }
|
156
|
-
|
157
|
-
obj = x.obj
|
189
|
+
inv.each do |obj,x|
|
158
190
|
next if obj == self
|
159
191
|
h = Hash[x.propSet.map { |y| [y.name, y.val] }]
|
160
192
|
tree[h['parent']][h['name']] = [obj, h]
|