rbvmomi 1.4.0 → 1.5.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/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]
|