rbvmomi 1.0.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.swp
2
+ pkg/
3
+ *.gemspec
4
+ vmodl
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 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.
data/README.md ADDED
@@ -0,0 +1,12 @@
1
+ RbVmomi
2
+ =======
3
+
4
+ Introduction
5
+ ------------
6
+
7
+ RbVmomi is a Ruby interface to the VI API.
8
+
9
+ Contributing
10
+ ------------
11
+
12
+ Send patches to rlane@vmware.com.
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "rbvmomi"
5
+ gem.summary = "Ruby interface to the VI API"
6
+ #gem.description = ""
7
+ gem.email = "rlane@vmware.com"
8
+ gem.homepage = "https://github.com/rlane/rbvmomi"
9
+ gem.authors = ["Rich Lane"]
10
+ gem.add_dependency 'nokogiri', '>= 1.4.1'
11
+ gem.add_dependency 'builder'
12
+ gem.add_dependency 'trollop'
13
+ gem.add_dependency 'tokyocabinet'
14
+ gem.required_ruby_version = '>= 1.9.1'
15
+ gem.files.include 'vmodl.tc'
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: gem install jeweler"
19
+ end
20
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.1
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env ruby
2
+ require 'nokogiri'
3
+ require 'tokyocabinet'
4
+
5
+ include TokyoCabinet
6
+
7
+ # usage: analyze-vim-declarations.rb vim-declarations.xml foo-declarations.xml vmodl.db
8
+
9
+ XML_FNS = ARGV[0...-1]
10
+ abort "must specify path to vim-declarations.xml" if XML_FNS.empty?
11
+ OUT_FN = ARGV[-1] or abort "must specify path to output database"
12
+
13
+ XML_FNS.each do |x|
14
+ abort "XML file #{x} does not exist" unless File.exists? x
15
+ end
16
+
17
+ db = HDB.new
18
+ if !db.open(OUT_FN, HDB::OWRITER | HDB::OCREAT | HDB::OTRUNC | HDB::TDEFLATE)
19
+ abort "VMODL db open error: #{db.errmsg(db.ecode)}"
20
+ end
21
+
22
+ TYPES = {}
23
+ VERSIONS = []
24
+
25
+ ID2NAME = Hash.new { |h,k| fail "unknown type-id #{k.inspect}" }
26
+
27
+ ID2NAME.merge!({
28
+ 'java.lang.String' => 'xsd:string',
29
+ 'BOOLEAN' => 'xsd:boolean',
30
+ 'BYTE' => 'xsd:byte',
31
+ 'SHORT' => 'xsd:short',
32
+ 'INT' => 'xsd:int',
33
+ 'LONG' => 'xsd:long',
34
+ 'FLOAT' => 'xsd:float',
35
+ 'DOUBLE' => 'xsd:double',
36
+ 'vmodl.DateTime' => 'xsd:dateTime',
37
+ 'vmodl.Binary' => 'xsd:base64Binary',
38
+ 'vmodl.Any' => 'xsd:anyType',
39
+ 'void' => nil,
40
+ })
41
+
42
+ %w(DataObject ManagedObject MethodFault MethodName
43
+ PropertyPath RuntimeFault TypeName).each do |x|
44
+ ID2NAME['vmodl.' + x] = x
45
+ end
46
+
47
+ def handle_data_object node
48
+ if TYPES[node['name']]
49
+ puts "Type #{node['name']} already exists"
50
+ return
51
+ end
52
+
53
+ ID2NAME[node['type-id']] = node['name']
54
+ TYPES[node['name']] = {
55
+ 'kind' => 'data',
56
+ 'base-type-id' => node['base-type-id'],
57
+ 'props' => node.children.select { |x| x.name == 'property' }.map do |property|
58
+ {
59
+ 'name' => property['name'],
60
+ 'type-id-ref' => property['type-id-ref'],
61
+ 'is-optional' => property['is-optional'] ? true : false,
62
+ 'is-array' => property['is-array'] ? true : false,
63
+ 'version-id-ref' => property['version-id-ref'],
64
+ }
65
+ end
66
+ }
67
+ end
68
+
69
+ def handle_managed_object node
70
+ if TYPES[node['name']]
71
+ puts "Type #{node['name']} already exists"
72
+ return
73
+ end
74
+ ID2NAME[node['type-id']] = node['name']
75
+ TYPES[node['name']] = {
76
+ 'kind' => 'managed',
77
+ 'base-type-id' => node['base-type-id'],
78
+ 'props' => node.children.select { |x| x.name == 'property' }.map do |property|
79
+ {
80
+ 'name' => property['name'],
81
+ 'type-id-ref' => property['type-id-ref'],
82
+ 'is-optional' => property['is-optional'] ? true : false,
83
+ 'is-array' => property['is-array'] ? true : false,
84
+ 'version-id-ref' => property['version-id-ref'],
85
+ }
86
+ end,
87
+ 'methods' => Hash[
88
+ node.children.select { |x| x.name == 'method' }.map do |method|
89
+ [method['is-task'] ? "#{method['name']}_Task" : method['name'],
90
+ {
91
+ 'params' => method.children.select { |x| x.name == 'parameter' }.map do |param|
92
+ {
93
+ 'name' => param['name'],
94
+ 'type-id-ref' => param['type-id-ref'],
95
+ 'is-array' => param['is-array'] ? true : false,
96
+ 'is-optional' => param['is-optional'] ? true : false,
97
+ 'version-id-ref' => param['version-id-ref'],
98
+ }
99
+ end,
100
+ 'result' => {
101
+ 'type-id-ref' => method['type-id-ref'],
102
+ 'is-array' => method['is-array'] ? true : false,
103
+ 'is-optional' => method['is-optional'] ? true : false,
104
+ 'is-task' => method['is-task'] ? true : false,
105
+ 'version-id-ref' => method['version-id-ref'],
106
+ }
107
+ }
108
+ ]
109
+ end
110
+ ]
111
+ }
112
+ end
113
+
114
+ def handle_enum node
115
+ if TYPES[node['name']]
116
+ puts "Type #{node['name']} already exists"
117
+ return
118
+ end
119
+
120
+ ID2NAME[node['type-id']] = node['name']
121
+ TYPES[node['name']] = {
122
+ 'kind' => 'enum',
123
+ 'values' => node.children.map { |child| child['name'] },
124
+ }
125
+ end
126
+
127
+ def handle_fault node
128
+ handle_data_object node
129
+ end
130
+
131
+ def handle_version x
132
+ attrs = %w(display-name name service-namespace type-id version-id vmodl-name)
133
+ h = Hash[attrs.map { |k| [k, x[k]] }]
134
+ h['compatible'] = x.children.select(&:element?).map { |y| y.text }
135
+ VERSIONS << h
136
+ end
137
+
138
+ XML_FNS.each do |fn|
139
+ puts "parsing #{fn} ..."
140
+ xml = Nokogiri.parse(File.read(fn), nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS)
141
+ xml.root.at('enums').children.each { |x| handle_enum x }
142
+ xml.root.at('managed-objects').children.each { |x| handle_managed_object x }
143
+ xml.root.at('data-objects').children.each { |x| handle_data_object x }
144
+ xml.root.at('faults').children.each { |x| handle_fault x }
145
+ #xml.root.at('definitions').at('version-types').children.each { |x| handle_version x }
146
+ end
147
+
148
+ munge_fault = lambda { |x| true }
149
+
150
+ TYPES.each do |k,t|
151
+ case t['kind']
152
+ when 'data'
153
+ t['wsdl_base'] = t['base-type-id'] ? ID2NAME[t['base-type-id']] : 'DataObject'
154
+ t.delete 'base-type-id'
155
+ t['props'].each do |x|
156
+ x['wsdl_type'] = ID2NAME[x['type-id-ref']]
157
+ x.delete 'type-id-ref'
158
+ munge_fault[x]
159
+ end
160
+ when 'managed'
161
+ t['wsdl_base'] = t['base-type-id'] ? ID2NAME[t['base-type-id']] : 'ManagedObject'
162
+ t.delete 'base-type-id'
163
+ t['props'].each do |x|
164
+ x['wsdl_type'] = ID2NAME[x['type-id-ref']]
165
+ x.delete 'type-id-ref'
166
+ munge_fault[x]
167
+ end
168
+ t['methods'].each do |mName,x|
169
+ if y = x['result']
170
+ y['wsdl_type'] = ID2NAME[y['type-id-ref']]
171
+ y.delete 'type-id-ref'
172
+ munge_fault[y]
173
+ end
174
+ x['params'].each do |r|
175
+ r['wsdl_type'] = ID2NAME[r['type-id-ref']]
176
+ r.delete 'type-id-ref'
177
+ munge_fault[r]
178
+ end
179
+ end
180
+ when 'enum'
181
+ else fail
182
+ end
183
+ end
184
+
185
+ TYPES.each do |k,t|
186
+ db[k] = Marshal.dump t
187
+ end
188
+
189
+ db['_typenames'] = Marshal.dump TYPES.keys
190
+ db['_versions'] = Marshal.dump VERSIONS
191
+
192
+ db.close
193
+
194
+ if filename = ENV['VERSION_GRAPH']
195
+ File.open(filename, 'w') do |io|
196
+ io.puts "digraph versions\n{"
197
+ VERSIONS.each do |h|
198
+ io.puts "\"#{h['vmodl-name']}\" [label=\"#{h['vmodl-name']} (#{h['version-id']})\"]"
199
+ h['compatible'].each do |x|
200
+ x =~ /^interface / or fail x
201
+ io.puts "\"#{h['vmodl-name']}\" -> \"#{$'}\""
202
+ end
203
+ end
204
+ io.puts "}\n"
205
+ end
206
+ end
@@ -0,0 +1,46 @@
1
+ require 'nokogiri'
2
+
3
+ # removes line breaks and whitespace between xml nodes.
4
+ def prepare_xml(xml)
5
+ xml = xml.gsub(/\n+/, "")
6
+ xml = xml.gsub(/(>)\s*(<)/, '\1\2')
7
+ end
8
+
9
+ def analyze_xml x, tree
10
+ subtree = (tree[x.name] ||= { :attributes => [], :min_occur => nil, :max_occur => nil })
11
+ attrs = x.attributes.keys.sort
12
+ subtree[:attributes] << attrs unless subtree[:attributes].member? attrs
13
+
14
+ child_occurs = Hash.new 0
15
+ x.children.select(&:element?).each do |c|
16
+ child_occurs[c.name] += 1
17
+ analyze_xml c, subtree
18
+ end
19
+
20
+ subtree.select { |k,v| k.is_a? String }.each do |k,v|
21
+ v[:min_occur] = [v[:min_occur], child_occurs[k]].compact.min
22
+ v[:max_occur] = [v[:max_occur], child_occurs[k]].compact.max
23
+ end
24
+ end
25
+
26
+ def print_tree tree, indent=0
27
+ tree.select { |k,v| k.is_a? String }.sort.each do |k,v|
28
+ attrs = v[:attributes] || []
29
+ min, max = v[:min_occur], v[:max_occur]
30
+ numsym = if min == 0 and max == 0 then fail
31
+ elsif min == 0 and max == 1 then '?'
32
+ elsif min == 0 then '*'
33
+ elsif min == 1 and max == 1 then ''
34
+ else '+'
35
+ end
36
+ puts "#{' '*indent}#{k}#{numsym}: #{attrs.sort.map { |a| "[#{a * ' '}]"} * ', '} {#{min},#{max}}"
37
+ print_tree v, (indent+1)
38
+ end
39
+ end
40
+
41
+ tree = {}
42
+ ARGV.each do |fn|
43
+ nk = Nokogiri(prepare_xml(File.read fn))
44
+ analyze_xml nk.root, tree
45
+ end
46
+ print_tree tree
data/lib/rbvmomi.rb ADDED
@@ -0,0 +1,283 @@
1
+ # Copyright (c) 2010 VMware, Inc. All Rights Reserved.
2
+ require 'trivial_soap'
3
+ require 'time'
4
+
5
+ module RbVmomi
6
+
7
+ class Boolean; def self.wsdl_name; 'xsd:boolean' end end
8
+ class AnyType; def self.wsdl_name; 'xsd:anyType' end end
9
+ class Binary; def self.wsdl_name; 'xsd:base64Binary' end end
10
+
11
+ def self.type name
12
+ fail unless name and (name.is_a? String or name.is_a? Symbol)
13
+ name = $' if name.to_s =~ /^xsd:/
14
+ case name.to_sym
15
+ when :anyType then AnyType
16
+ when :boolean then Boolean
17
+ when :string then String
18
+ when :int, :long, :short, :byte then Integer
19
+ when :float, :double then Float
20
+ when :dateTime then Time
21
+ when :base64Binary then Binary
22
+ else
23
+ if VIM.has_type? name
24
+ VIM.type name
25
+ else
26
+ fail "no such type #{name.inspect}"
27
+ end
28
+ end
29
+ end
30
+
31
+ class DeserializationFailed < Exception; end
32
+
33
+ class Soap < TrivialSoap
34
+ NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
35
+
36
+ def initialize opts
37
+ @vim_debug = opts[:vim_debug]
38
+ super opts
39
+ @ns = @opts[:ns] or fail "no namespace specified"
40
+ if @opts[:rev]
41
+ @rev = @opts[:rev]
42
+ elsif @opts[:host]
43
+ @rev = '4.0'
44
+ @rev = serviceContent.about.apiVersion
45
+ else
46
+ fail "no revision specified"
47
+ end
48
+ end
49
+
50
+ def serviceInstance
51
+ VIM::ServiceInstance self, 'ServiceInstance'
52
+ end
53
+
54
+ def serviceContent
55
+ @serviceContent ||= serviceInstance.RetrieveServiceContent
56
+ end
57
+
58
+ %w(rootFolder propertyCollector searchIndex).map(&:to_sym).each do |s|
59
+ define_method(s) { serviceContent.send s }
60
+ end
61
+
62
+ alias root rootFolder
63
+
64
+ def emit_request xml, method, descs, this, params
65
+ xml.tag! method, :xmlns => @ns do
66
+ obj2xml xml, '_this', 'ManagedObject', false, this
67
+ descs.each do |d|
68
+ k = d['name'].to_sym
69
+ if params.member? k or params.member? k.to_s
70
+ v = params.member?(k) ? params[k] : params[k.to_s]
71
+ obj2xml xml, d['name'], d['wsdl_type'], d['is-array'], v
72
+ else
73
+ fail "missing required parameter #{d['name']}" unless d['is-optional']
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def parse_response resp, desc
80
+ if resp.at('faultcode')
81
+ detail = resp.at('detail')
82
+ fault = detail && xml2obj(detail.children.first, 'MethodFault')
83
+ msg = resp.at('faultstring').text
84
+ if fault
85
+ raise RbVmomi.fault msg, fault
86
+ else
87
+ fail "#{resp.at('faultcode').text}: #{msg}"
88
+ end
89
+ else
90
+ if desc
91
+ type = desc['is-task'] ? 'Task' : desc['wsdl_type']
92
+ returnvals = resp.children.select(&:element?).map { |c| xml2obj c, type }
93
+ desc['is-array'] ? returnvals : returnvals.first
94
+ else
95
+ nil
96
+ end
97
+ end
98
+ end
99
+
100
+ def call method, desc, this, params
101
+ fail "this is not a managed object" unless this.is_a? RbVmomi::VIM::ManagedObject
102
+ fail "parameters must be passed as a hash" unless params.is_a? Hash
103
+ fail unless desc.is_a? Hash
104
+
105
+ if @vim_debug
106
+ $stderr.puts "Request #{method}:"
107
+ PP.pp({ _this: this }.merge(params), $stderr)
108
+ $stderr.puts
109
+ start_time = Time.now
110
+ end
111
+
112
+ resp = request "#{@ns}/#{@rev}" do |xml|
113
+ emit_request xml, method, desc['params'], this, params
114
+ end
115
+
116
+ ret = parse_response resp, desc['result']
117
+
118
+ if @vim_debug
119
+ end_time = Time.now
120
+ $stderr.puts "Response (in #{'%.3f' % (end_time - start_time)} s)"
121
+ PP.pp ret, $stderr
122
+ $stderr.puts
123
+ end
124
+
125
+ ret
126
+ end
127
+
128
+ def demangle_array_type x
129
+ case x
130
+ when 'AnyType' then 'anyType'
131
+ when 'DateTime' then 'dateTime'
132
+ when 'Boolean', 'String', 'Byte', 'Short', 'Int', 'Long', 'Float', 'Double' then x.downcase
133
+ else x
134
+ end
135
+ end
136
+
137
+ def xml2obj xml, type
138
+ type = (xml.attribute_with_ns('type', NS_XSI) || type).to_s
139
+
140
+ if type =~ /^ArrayOf/
141
+ type = demangle_array_type $'
142
+ return xml.children.select(&:element?).map { |c| xml2obj c, type }
143
+ end
144
+
145
+ t = RbVmomi.type type
146
+ if t <= VIM::DataObject
147
+ #puts "deserializing data object #{t} from #{xml.name}"
148
+ props_desc = t.full_props_desc
149
+ h = {}
150
+ props_desc.select { |d| d['is-array'] }.each { |d| h[d['name'].to_sym] = [] }
151
+ xml.children.each do |c|
152
+ next unless c.element?
153
+ field = c.name.to_sym
154
+ #puts "field #{field.to_s}: #{t.find_prop_desc(field.to_s).inspect}"
155
+ d = t.find_prop_desc(field.to_s) or next
156
+ o = xml2obj c, d['wsdl_type']
157
+ if h[field].is_a? Array
158
+ h[field] << o
159
+ else
160
+ h[field] = o
161
+ end
162
+ end
163
+ t.new h
164
+ elsif t == VIM::ManagedObjectReference
165
+ RbVmomi.type(xml['type']).new self, xml.text
166
+ elsif t <= VIM::ManagedObject
167
+ RbVmomi.type(xml['type'] || t.wsdl_name).new self, xml.text
168
+ elsif t <= VIM::Enum
169
+ xml.text
170
+ elsif t <= String
171
+ xml.text
172
+ elsif t <= Symbol
173
+ xml.text.to_sym
174
+ elsif t <= Integer
175
+ xml.text.to_i
176
+ elsif t <= Float
177
+ xml.text.to_f
178
+ elsif t <= Time
179
+ Time.parse xml.text
180
+ elsif t == Boolean
181
+ xml.text == 'true' || xml.text == '1'
182
+ elsif t == Binary
183
+ xml.text.unpack('m')[0]
184
+ elsif t == AnyType
185
+ fail "attempted to deserialize an AnyType"
186
+ else fail "unexpected type #{t.inspect}"
187
+ end
188
+ end
189
+
190
+ def obj2xml xml, name, type, is_array, o, attrs={}
191
+ expected = RbVmomi.type(type)
192
+ fail "expected array, got #{o.class.wsdl_name}" if is_array and not o.is_a? Array
193
+ case o
194
+ when Array
195
+ fail "expected #{expected.wsdl_name}, got array" unless is_array
196
+ o.each do |e|
197
+ obj2xml xml, name, expected.wsdl_name, false, e, attrs
198
+ end
199
+ when VIM::ManagedObject
200
+ fail "expected #{expected.wsdl_name}, got #{o.class.wsdl_name} for field #{name.inspect}" if expected and not expected >= o.class
201
+ xml.tag! name, o._ref, :type => o.class.wsdl_name
202
+ when VIM::DataObject
203
+ fail "expected #{expected.wsdl_name}, got #{o.class.wsdl_name} for field #{name.inspect}" if expected and not expected >= o.class
204
+ xml.tag! name, attrs.merge("xsi:type" => o.class.wsdl_name) do
205
+ o.class.full_props_desc.each do |desc|
206
+ if o.props.member? desc['name'].to_sym
207
+ v = o.props[desc['name'].to_sym]
208
+ next if v.nil?
209
+ obj2xml xml, desc['name'], desc['wsdl_type'], desc['is-array'], v
210
+ end
211
+ end
212
+ end
213
+ when VIM::Enum
214
+ xml.tag! name, o.value.to_s, attrs
215
+ when Hash
216
+ fail "expected #{expected.wsdl_name}, got a hash" unless expected <= VIM::DataObject
217
+ obj2xml xml, name, type, false, expected.new(o), attrs
218
+ when true, false
219
+ fail "expected #{expected.wsdl_name}, got a boolean" unless expected == Boolean
220
+ attrs['xsi:type'] = 'xsd:boolean' if expected == AnyType
221
+ xml.tag! name, (o ? '1' : '0'), attrs
222
+ when Symbol, String
223
+ if expected == Binary
224
+ attrs['xsi:type'] = 'xsd:base64Binary' if expected == AnyType
225
+ xml.tag! name, [o].pack('m').chomp, attrs
226
+ else
227
+ attrs['xsi:type'] = 'xsd:string' if expected == AnyType
228
+ xml.tag! name, o.to_s, attrs
229
+ end
230
+ when Integer
231
+ attrs['xsi:type'] = 'xsd:long' if expected == AnyType
232
+ xml.tag! name, o.to_s, attrs
233
+ when Float
234
+ attrs['xsi:type'] = 'xsd:double' if expected == AnyType
235
+ xml.tag! name, o.to_s, attrs
236
+ when DateTime
237
+ attrs['xsi:type'] = 'xsd:dateTime' if expected == AnyType
238
+ xml.tag! name, o.to_s, attrs
239
+ else fail "unexpected object class #{o.class}"
240
+ end
241
+ xml
242
+ end
243
+ end
244
+
245
+ # XXX fault class hierarchy
246
+ class Fault < StandardError
247
+ attr_reader :fault
248
+
249
+ def initialize msg, fault
250
+ super "#{fault.class.wsdl_name}: #{msg}"
251
+ @fault = fault
252
+ end
253
+ end
254
+
255
+ def self.fault msg, fault
256
+ Fault.new(msg, fault)
257
+ end
258
+
259
+ # host, port, ssl, user, password, path, debug
260
+ def self.connect opts
261
+ fail unless opts.is_a? Hash
262
+ fail "host option required" unless opts[:host]
263
+ opts[:user] ||= 'root'
264
+ opts[:password] ||= ''
265
+ opts[:ssl] = true unless opts.member? :ssl
266
+ opts[:port] ||= (opts[:ssl] ? 443 : 80)
267
+ opts[:path] ||= '/sdk'
268
+ opts[:ns] ||= 'urn:vim25'
269
+ opts[:debug] = (!ENV['RBVMOMI_DEBUG'].empty? rescue false) unless opts.member? :debug
270
+ opts[:vim_debug] = (!ENV['RBVMOMI_VIM_DEBUG'].empty? rescue false) unless opts.member? :vim_debug
271
+
272
+ Soap.new(opts).tap do |vim|
273
+ vim.serviceContent.sessionManager.Login :userName => opts[:user], :password => opts[:password]
274
+ end
275
+ end
276
+
277
+ end
278
+
279
+ require 'rbvmomi/types'
280
+ vmodl_fn = ENV['VMODL'] || File.join(File.dirname(__FILE__), "../vmodl.tc")
281
+ RbVmomi::VIM.load vmodl_fn
282
+
283
+ require 'rbvmomi/extensions'