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