rbvmomi 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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'