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
data/README.rdoc
CHANGED
@@ -22,7 +22,7 @@ A simple example of turning on a VM:
|
|
22
22
|
vm = dc.find_vm("myvm") or fail "VM not found"
|
23
23
|
vm.PowerOnVM_Task.wait_for_completion
|
24
24
|
|
25
|
-
This code uses several RbVmomi extensions to the
|
25
|
+
This code uses several RbVmomi extensions to the vSphere API for concision. The
|
26
26
|
expanded snippet below uses only standard API calls and should be familiar to
|
27
27
|
users of the Java SDK:
|
28
28
|
|
@@ -75,4 +75,4 @@ write something generally useful please send it to me and I'll add it in.
|
|
75
75
|
== Development
|
76
76
|
|
77
77
|
Fork the project on Github and send me a merge request, or send a patch to
|
78
|
-
rlane@vmware.com.
|
78
|
+
rlane@vmware.com. RbVmomi developers hang out in #rbvmomi on Freenode.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.5.0
|
data/devel/benchmark.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
if ENV['RBVMOMI_COVERAGE'] == '1'
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rbvmomi'
|
10
|
+
require 'rbvmomi/deserialization'
|
11
|
+
require 'benchmark'
|
12
|
+
require 'libxml'
|
13
|
+
|
14
|
+
NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
|
15
|
+
|
16
|
+
VIM = RbVmomi::VIM
|
17
|
+
$conn = VIM.new(:ns => 'urn:vim25', :rev => '4.0')
|
18
|
+
raw = File.read(ARGV[0])
|
19
|
+
|
20
|
+
def diff a, b
|
21
|
+
a_io = Tempfile.new 'rbvmomi-diff-a'
|
22
|
+
b_io = Tempfile.new 'rbvmomi-diff-b'
|
23
|
+
PP.pp a, a_io
|
24
|
+
PP.pp b, b_io
|
25
|
+
a_io.close
|
26
|
+
b_io.close
|
27
|
+
system("diff -u #{a_io.path} #{b_io.path}")
|
28
|
+
a_io.unlink
|
29
|
+
b_io.unlink
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
deserializer = RbVmomi::OldDeserializer.new($conn)
|
34
|
+
end_time = Time.now + 1
|
35
|
+
n = 0
|
36
|
+
while n == 0 or end_time > Time.now
|
37
|
+
deserializer.deserialize Nokogiri::XML(raw).root
|
38
|
+
n += 1
|
39
|
+
end
|
40
|
+
N = n*10
|
41
|
+
end
|
42
|
+
|
43
|
+
puts "iterations: #{N}"
|
44
|
+
|
45
|
+
parsed_nokogiri = Nokogiri::XML(raw)
|
46
|
+
parsed_libxml = LibXML::XML::Parser.string(raw).parse
|
47
|
+
|
48
|
+
if true
|
49
|
+
nokogiri_xml = parsed_nokogiri.root
|
50
|
+
libxml_xml = parsed_libxml.root
|
51
|
+
|
52
|
+
old_nokogiri_result = RbVmomi::OldDeserializer.new($conn).deserialize nokogiri_xml
|
53
|
+
new_nokogiri_result = RbVmomi::NewDeserializer.new($conn).deserialize nokogiri_xml
|
54
|
+
new_libxml_result = RbVmomi::NewDeserializer.new($conn).deserialize libxml_xml
|
55
|
+
|
56
|
+
if new_nokogiri_result != old_nokogiri_result
|
57
|
+
puts "new_nokogiri_result doesnt match old_nokogiri_result"
|
58
|
+
puts "old_nokogiri_result:"
|
59
|
+
pp old_nokogiri_result
|
60
|
+
puts "new_nokogiri_result:"
|
61
|
+
pp new_nokogiri_result
|
62
|
+
puts "diff:"
|
63
|
+
diff old_nokogiri_result, new_nokogiri_result
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
|
67
|
+
if new_libxml_result != old_nokogiri_result
|
68
|
+
puts "new_libxml_result doesnt match old_nokogiri_result"
|
69
|
+
puts "old_nokogiri_result:"
|
70
|
+
pp old_nokogiri_result
|
71
|
+
puts "new_libxml_result:"
|
72
|
+
pp new_libxml_result
|
73
|
+
puts "diff:"
|
74
|
+
diff old_nokogiri_result, new_libxml_result
|
75
|
+
exit 1
|
76
|
+
end
|
77
|
+
|
78
|
+
puts "all results match"
|
79
|
+
end
|
80
|
+
|
81
|
+
Benchmark.bmbm do|b|
|
82
|
+
GC.start
|
83
|
+
b.report("nokogiri parsing") do
|
84
|
+
N.times { Nokogiri::XML(raw) }
|
85
|
+
end
|
86
|
+
|
87
|
+
GC.start
|
88
|
+
b.report("libxml parsing") do
|
89
|
+
N.times do
|
90
|
+
LibXML::XML::Parser.string(raw).parse
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
GC.start
|
95
|
+
b.report("old deserialization (nokogiri)") do
|
96
|
+
deserializer = RbVmomi::OldDeserializer.new($conn)
|
97
|
+
N.times do
|
98
|
+
deserializer.deserialize Nokogiri::XML(raw).root
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
GC.start
|
103
|
+
b.report("new deserialization (nokogiri)") do
|
104
|
+
deserializer = RbVmomi::NewDeserializer.new($conn)
|
105
|
+
N.times do
|
106
|
+
deserializer.deserialize Nokogiri::XML(raw).root
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
GC.start
|
111
|
+
b.report("new deserialization (libxml)") do
|
112
|
+
deserializer = RbVmomi::NewDeserializer.new($conn)
|
113
|
+
N.times do
|
114
|
+
deserializer.deserialize LibXML::XML::Parser.string(raw).parse.root
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
data/devel/collisions.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Find collisions between VMODL property names and Ruby methods
|
3
|
+
require 'rbvmomi'
|
4
|
+
VIM = RbVmomi::VIM
|
5
|
+
|
6
|
+
conn = VIM.new(:ns => 'urn:vim25', :rev => '4.0')
|
7
|
+
|
8
|
+
VIM.loader.typenames.each do |name|
|
9
|
+
klass = VIM.loader.get name
|
10
|
+
next unless klass.respond_to? :kind and [:managed, :data].member? klass.kind
|
11
|
+
methods = klass.kind == :managed ?
|
12
|
+
RbVmomi::BasicTypes::ObjectWithMethods.instance_methods :
|
13
|
+
RbVmomi::BasicTypes::ObjectWithProperties.instance_methods
|
14
|
+
klass.props_desc.each do |x|
|
15
|
+
name = x['name']
|
16
|
+
puts "collision: #{klass}##{name}" if methods.member? name.to_sym
|
17
|
+
end
|
18
|
+
end
|
@@ -7,6 +7,11 @@ internal_vmodl_filename = ARGV[1] or abort "internal vmodl filename required"
|
|
7
7
|
output_vmodl_filename = ARGV[2] or abort "output vmodl filename required"
|
8
8
|
|
9
9
|
TYPES = %w(
|
10
|
+
DVSKeyedOpaqueData
|
11
|
+
DVSOpaqueDataConfigSpec
|
12
|
+
DVPortgroupSelection
|
13
|
+
DVPortSelection
|
14
|
+
DVSSelection
|
10
15
|
DynamicTypeEnumTypeInfo
|
11
16
|
DynamicTypeMgrAllTypeInfo
|
12
17
|
DynamicTypeMgrAnnotation
|
@@ -27,9 +32,11 @@ ReflectManagedMethodExecuter
|
|
27
32
|
ReflectManagedMethodExecuterSoapArgument
|
28
33
|
ReflectManagedMethodExecuterSoapFault
|
29
34
|
ReflectManagedMethodExecuterSoapResult
|
35
|
+
SelectionSet
|
30
36
|
)
|
31
37
|
|
32
38
|
METHODS = %w(
|
39
|
+
DistributedVirtualSwitchManager.UpdateDvsOpaqueData_Task
|
33
40
|
HostSystem.RetrieveDynamicTypeManager
|
34
41
|
HostSystem.RetrieveManagedMethodExecuter
|
35
42
|
)
|
data/lib/rbvmomi/basic_types.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# Copyright (c) 2010 VMware, Inc. All Rights Reserved.
|
2
2
|
require 'pp'
|
3
|
+
require 'set'
|
3
4
|
|
4
5
|
module RbVmomi
|
5
6
|
module BasicTypes
|
6
7
|
|
7
|
-
BUILTIN = %w(ManagedObject DataObject TypeName PropertyPath ManagedObjectReference MethodName MethodFault LocalizedMethodFault KeyValue)
|
8
|
+
BUILTIN = Set.new %w(ManagedObject DataObject TypeName PropertyPath ManagedObjectReference MethodName MethodFault LocalizedMethodFault KeyValue)
|
8
9
|
|
9
10
|
class Base
|
10
11
|
class << self
|
@@ -36,6 +37,10 @@ class ObjectWithProperties < Base
|
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
40
|
+
def full_props_set
|
41
|
+
@full_props_set ||= Set.new(full_props_desc.map { |x| x['name'] })
|
42
|
+
end
|
43
|
+
|
39
44
|
def full_props_desc
|
40
45
|
@full_props_desc ||= (self == ObjectWithProperties ? [] : superclass.full_props_desc) + props_desc
|
41
46
|
end
|
@@ -83,11 +88,19 @@ end
|
|
83
88
|
class DataObject < ObjectWithProperties
|
84
89
|
attr_reader :props
|
85
90
|
|
91
|
+
def self.kind; :data end
|
92
|
+
|
86
93
|
def initialize props={}
|
94
|
+
# Deserialization fast path
|
95
|
+
if props == nil
|
96
|
+
@props = {}
|
97
|
+
return
|
98
|
+
end
|
99
|
+
|
87
100
|
@props = Hash[props.map { |k,v| [k.to_sym, v] }]
|
88
|
-
self.class.full_props_desc.each do |desc|
|
101
|
+
#self.class.full_props_desc.each do |desc|
|
89
102
|
#fail "missing required property #{desc['name'].inspect} of #{self.class.wsdl_name}" if @props[desc['name'].to_sym].nil? and not desc['is-optional']
|
90
|
-
end
|
103
|
+
#end
|
91
104
|
@props.each do |k,v|
|
92
105
|
fail "unexpected property name #{k}" unless self.class.find_prop_desc(k)
|
93
106
|
end
|
@@ -148,6 +161,8 @@ class DataObject < ObjectWithProperties
|
|
148
161
|
end
|
149
162
|
|
150
163
|
class ManagedObject < ObjectWithMethods
|
164
|
+
def self.kind; :managed end
|
165
|
+
|
151
166
|
def initialize connection, ref
|
152
167
|
super()
|
153
168
|
@connection = connection
|
@@ -222,6 +237,8 @@ class Enum < Base
|
|
222
237
|
end
|
223
238
|
end
|
224
239
|
|
240
|
+
def self.kind; :enum end
|
241
|
+
|
225
242
|
attr_reader :value
|
226
243
|
|
227
244
|
def initialize value
|
data/lib/rbvmomi/connection.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# Copyright (c) 2010 VMware, Inc. All Rights Reserved.
|
2
2
|
require 'time'
|
3
|
+
require 'date'
|
3
4
|
require 'rbvmomi/trivial_soap'
|
4
5
|
require 'rbvmomi/basic_types'
|
5
6
|
require 'rbvmomi/fault'
|
6
7
|
require 'rbvmomi/type_loader'
|
8
|
+
require 'rbvmomi/deserialization'
|
7
9
|
|
8
10
|
module RbVmomi
|
9
11
|
|
@@ -15,12 +17,24 @@ class Connection < TrivialSoap
|
|
15
17
|
NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
|
16
18
|
|
17
19
|
attr_accessor :rev
|
18
|
-
|
20
|
+
attr_reader :profile
|
21
|
+
attr_reader :profile_summary
|
22
|
+
attr_accessor :profiling
|
23
|
+
attr_reader :deserializer
|
24
|
+
|
19
25
|
def initialize opts
|
20
26
|
@ns = opts[:ns] or fail "no namespace specified"
|
21
27
|
@rev = opts[:rev] or fail "no revision specified"
|
28
|
+
@deserializer = Deserializer.new self
|
29
|
+
reset_profiling
|
30
|
+
@profiling = false
|
22
31
|
super opts
|
23
32
|
end
|
33
|
+
|
34
|
+
def reset_profiling
|
35
|
+
@profile = {}
|
36
|
+
@profile_summary = {:network_latency => 0, :request_emit => 0, :response_parse => 0, :num_calls => 0}
|
37
|
+
end
|
24
38
|
|
25
39
|
def emit_request xml, method, descs, this, params
|
26
40
|
xml.tag! method, :xmlns => @ns do
|
@@ -41,7 +55,7 @@ class Connection < TrivialSoap
|
|
41
55
|
def parse_response resp, desc
|
42
56
|
if resp.at('faultcode')
|
43
57
|
detail = resp.at('detail')
|
44
|
-
fault = detail &&
|
58
|
+
fault = detail && @deserializer.deserialize(detail.children.first, 'MethodFault')
|
45
59
|
msg = resp.at('faultstring').text
|
46
60
|
if fault
|
47
61
|
raise RbVmomi::Fault.new(msg, fault)
|
@@ -51,7 +65,7 @@ class Connection < TrivialSoap
|
|
51
65
|
else
|
52
66
|
if desc
|
53
67
|
type = desc['is-task'] ? 'Task' : desc['wsdl_type']
|
54
|
-
returnvals = resp.children.select(&:element?).map { |c|
|
68
|
+
returnvals = resp.children.select(&:element?).map { |c| @deserializer.deserialize c, type }
|
55
69
|
(desc['is-array'] && !desc['is-task']) ? returnvals : returnvals.first
|
56
70
|
else
|
57
71
|
nil
|
@@ -64,89 +78,38 @@ class Connection < TrivialSoap
|
|
64
78
|
fail "parameters must be passed as a hash" unless params.is_a? Hash
|
65
79
|
fail unless desc.is_a? Hash
|
66
80
|
|
67
|
-
|
81
|
+
t1 = Time.now
|
82
|
+
body = soap_envelope do |xml|
|
68
83
|
emit_request xml, method, desc['params'], this, params
|
84
|
+
end.target!
|
85
|
+
|
86
|
+
t2 = Time.now
|
87
|
+
resp, resp_size = request "#{@ns}/#{@rev}", body
|
88
|
+
|
89
|
+
t3 = Time.now
|
90
|
+
out = parse_response resp, desc['result']
|
91
|
+
|
92
|
+
if @profiling
|
93
|
+
t4 = Time.now
|
94
|
+
@profile[method] ||= []
|
95
|
+
profile_info = {
|
96
|
+
:network_latency => (t3 - t2),
|
97
|
+
:request_emit => t2 - t1,
|
98
|
+
:response_parse => t4 - t3,
|
99
|
+
:params => params,
|
100
|
+
:obj => this,
|
101
|
+
:backtrace => caller,
|
102
|
+
:request_size => body.length,
|
103
|
+
:response_size => resp_size,
|
104
|
+
}
|
105
|
+
@profile[method] << profile_info
|
106
|
+
@profile_summary[:network_latency] += profile_info[:network_latency]
|
107
|
+
@profile_summary[:response_parse] += profile_info[:response_parse]
|
108
|
+
@profile_summary[:request_emit] += profile_info[:request_emit]
|
109
|
+
@profile_summary[:num_calls] += 1
|
69
110
|
end
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
def demangle_array_type x
|
75
|
-
case x
|
76
|
-
when 'AnyType' then 'anyType'
|
77
|
-
when 'DateTime' then 'dateTime'
|
78
|
-
when 'Boolean', 'String', 'Byte', 'Short', 'Int', 'Long', 'Float', 'Double' then x.downcase
|
79
|
-
else x
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def xml2obj xml, typename
|
84
|
-
if IS_JRUBY
|
85
|
-
type_attr = xml.attribute_nodes.find { |a| a.name == 'type' &&
|
86
|
-
a.namespace &&
|
87
|
-
a.namespace.prefix == 'xsi' }
|
88
|
-
else
|
89
|
-
type_attr = xml.attribute_with_ns('type', NS_XSI)
|
90
|
-
end
|
91
|
-
typename = (type_attr || typename).to_s
|
92
|
-
|
93
|
-
if typename =~ /^ArrayOf/
|
94
|
-
typename = demangle_array_type $'
|
95
|
-
return xml.children.select(&:element?).map { |c| xml2obj c, typename }
|
96
|
-
end
|
97
|
-
|
98
|
-
t = type typename
|
99
|
-
if t <= BasicTypes::DataObject
|
100
|
-
props_desc = t.full_props_desc
|
101
|
-
h = {}
|
102
|
-
props_desc.select { |d| d['is-array'] }.each { |d| h[d['name'].to_sym] = [] }
|
103
|
-
xml.children.each do |c|
|
104
|
-
next unless c.element?
|
105
|
-
field = c.name.to_sym
|
106
|
-
d = t.find_prop_desc(field.to_s) or next
|
107
|
-
o = xml2obj c, d['wsdl_type']
|
108
|
-
if h[field].is_a? Array
|
109
|
-
h[field] << o
|
110
|
-
else
|
111
|
-
h[field] = o
|
112
|
-
end
|
113
|
-
end
|
114
|
-
t.new h
|
115
|
-
elsif t == BasicTypes::ManagedObjectReference
|
116
|
-
type(xml['type']).new self, xml.text
|
117
|
-
elsif t <= BasicTypes::ManagedObject
|
118
|
-
type(xml['type'] || t.wsdl_name).new self, xml.text
|
119
|
-
elsif t <= BasicTypes::Enum
|
120
|
-
xml.text
|
121
|
-
elsif t <= BasicTypes::KeyValue
|
122
|
-
h = {}
|
123
|
-
xml.children.each do |c|
|
124
|
-
next unless c.element?
|
125
|
-
h[c.name] = c.text
|
126
|
-
end
|
127
|
-
[h['key'], h['value']]
|
128
|
-
elsif t <= String
|
129
|
-
xml.text
|
130
|
-
elsif t <= Symbol
|
131
|
-
xml.text.to_sym
|
132
|
-
elsif t <= Integer
|
133
|
-
xml.text.to_i
|
134
|
-
elsif t <= Float
|
135
|
-
xml.text.to_f
|
136
|
-
elsif t <= Time
|
137
|
-
Time.parse xml.text
|
138
|
-
elsif t == BasicTypes::Boolean
|
139
|
-
xml.text == 'true' || xml.text == '1'
|
140
|
-
elsif t == BasicTypes::Binary
|
141
|
-
xml.text.unpack('m')[0]
|
142
|
-
elsif t == BasicTypes::AnyType
|
143
|
-
fail "attempted to deserialize an AnyType"
|
144
|
-
else fail "unexpected type #{t.inspect}"
|
145
|
-
end
|
146
|
-
rescue
|
147
|
-
$stderr.puts "#{$!.class} while deserializing #{xml.name} (#{typename}):"
|
148
|
-
$stderr.puts xml.to_s
|
149
|
-
raise
|
111
|
+
|
112
|
+
out
|
150
113
|
end
|
151
114
|
|
152
115
|
# hic sunt dracones
|
@@ -207,7 +170,10 @@ class Connection < TrivialSoap
|
|
207
170
|
xml.tag! name, o.to_s, attrs
|
208
171
|
when DateTime
|
209
172
|
attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType
|
210
|
-
xml.tag! name, o.
|
173
|
+
xml.tag! name, o.strftime('%FT%T%:z'), attrs
|
174
|
+
when Time
|
175
|
+
attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType
|
176
|
+
xml.tag! name, o.iso8601, attrs
|
211
177
|
else fail "unexpected object class #{o.class}"
|
212
178
|
end
|
213
179
|
xml
|
@@ -230,7 +196,7 @@ class Connection < TrivialSoap
|
|
230
196
|
when :base64Binary then BasicTypes::Binary
|
231
197
|
when :KeyValue then BasicTypes::KeyValue
|
232
198
|
else
|
233
|
-
if @loader.
|
199
|
+
if @loader.has? name
|
234
200
|
const_get(name)
|
235
201
|
else
|
236
202
|
fail "no such type #{name.inspect}"
|
@@ -252,25 +218,25 @@ protected
|
|
252
218
|
|
253
219
|
def self.const_missing sym
|
254
220
|
name = sym.to_s
|
255
|
-
if @loader and @loader.
|
256
|
-
@loader.
|
257
|
-
const_get sym
|
221
|
+
if @loader and @loader.has? name
|
222
|
+
@loader.get(name)
|
258
223
|
else
|
259
224
|
super
|
260
225
|
end
|
261
226
|
end
|
262
227
|
|
263
228
|
def self.method_missing sym, *a
|
264
|
-
|
265
|
-
|
229
|
+
name = sym.to_s
|
230
|
+
if @loader and @loader.has? name
|
231
|
+
@loader.get(name).new(*a)
|
266
232
|
else
|
267
233
|
super
|
268
234
|
end
|
269
235
|
end
|
270
236
|
|
271
237
|
def self.load_vmodl fn
|
272
|
-
@loader = RbVmomi::TypeLoader.new
|
273
|
-
|
238
|
+
@loader = RbVmomi::TypeLoader.new fn, extension_dirs, self
|
239
|
+
nil
|
274
240
|
end
|
275
241
|
end
|
276
242
|
|