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 +4 -0
- data/LICENSE +19 -0
- data/README.md +12 -0
- data/Rakefile +20 -0
- data/VERSION +1 -0
- data/devel/analyze-vim-declarations.rb +206 -0
- data/devel/analyze-xml.rb +46 -0
- data/lib/rbvmomi.rb +283 -0
- data/lib/rbvmomi/extensions.rb +491 -0
- data/lib/rbvmomi/profile.rb +22 -0
- data/lib/rbvmomi/trollop.rb +29 -0
- data/lib/rbvmomi/types.rb +373 -0
- data/lib/trivial_soap.rb +83 -0
- data/test/runner.rb +3 -0
- data/test/test.rb +69 -0
- data/test/test_deserialization.rb +324 -0
- data/test/test_emit_request.rb +110 -0
- data/test/test_parse_response.rb +67 -0
- data/test/test_serialization.rb +208 -0
- data/vmodl.tc +0 -0
- metadata +151 -0
data/.gitignore
ADDED
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
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'
|