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.
@@ -0,0 +1,235 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ require 'time'
3
+
4
+ module RbVmomi
5
+
6
+ class NewDeserializer
7
+ NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
8
+
9
+ DEMANGLED_ARRAY_TYPES = {
10
+ 'AnyType' => 'xsd:anyType',
11
+ 'DateTime' => 'xsd:dateTime',
12
+ }
13
+ %w(Boolean String Byte Short Int Long Float Double).each do |x|
14
+ DEMANGLED_ARRAY_TYPES[x] = "xsd:#{x.downcase}"
15
+ end
16
+
17
+ BUILTIN_TYPE_ACTIONS = {
18
+ 'xsd:string' => :string,
19
+ 'xsd:boolean' => :boolean,
20
+ 'xsd:byte' => :int,
21
+ 'xsd:short' => :int,
22
+ 'xsd:int' => :int,
23
+ 'xsd:long' => :int,
24
+ 'xsd:float' => :float,
25
+ 'xsd:dateTime' => :date,
26
+ 'PropertyPath' => :string,
27
+ 'MethodName' => :string,
28
+ 'TypeName' => :string,
29
+ 'xsd:base64Binary' => :binary,
30
+ 'KeyValue' => :keyvalue,
31
+ }
32
+
33
+ BUILTIN_TYPE_ACTIONS.dup.each do |k,v|
34
+ if k =~ /^xsd:/
35
+ BUILTIN_TYPE_ACTIONS[$'] = v
36
+ end
37
+ end
38
+
39
+ def initialize conn
40
+ @conn = conn
41
+ @loader = conn.class.loader
42
+ end
43
+
44
+ def deserialize node, type=nil
45
+ type_attr = node['type']
46
+ type = type_attr if type_attr
47
+
48
+ if action = BUILTIN_TYPE_ACTIONS[type]
49
+ case action
50
+ when :string
51
+ node.content
52
+ when :boolean
53
+ node.content == '1' || node.content == 'true'
54
+ when :int
55
+ node.content.to_i
56
+ when :float
57
+ node.content.to_f
58
+ when :date
59
+ leaf_date node
60
+ when :binary
61
+ leaf_binary node
62
+ when :keyvalue
63
+ leaf_keyvalue node
64
+ else fail
65
+ end
66
+ else
67
+ if type =~ /^ArrayOf/
68
+ type = DEMANGLED_ARRAY_TYPES[$'] || $'
69
+ return node.children.select(&:element?).map { |c| deserialize c, type }
70
+ end
71
+
72
+ klass = @loader.get(type) or fail "no such type #{type}"
73
+ case klass.kind
74
+ when :data
75
+ traverse_data node, klass
76
+ when :enum
77
+ node.content
78
+ when :managed
79
+ leaf_managed node, klass
80
+ else fail
81
+ end
82
+ end
83
+ end
84
+
85
+ def traverse_data node, klass
86
+ obj = klass.new nil
87
+ props = obj.props
88
+ children = node.children.select(&:element?)
89
+ n = children.size
90
+ i = 0
91
+
92
+ klass.full_props_desc.each do |desc|
93
+ name = desc['name']
94
+ child_type = desc['wsdl_type']
95
+
96
+ # Ignore unknown fields
97
+ while child = children[i] and not klass.full_props_set.member? child.name
98
+ i += 1
99
+ end
100
+
101
+ if desc['is-array']
102
+ a = []
103
+ while ((child = children[i]) && (child.name == name))
104
+ child = children[i]
105
+ a << deserialize(child, child_type)
106
+ i += 1
107
+ end
108
+ props[name.to_sym] = a
109
+ elsif ((child = children[i]) && (child.name == name))
110
+ props[name.to_sym] = deserialize(child, child_type)
111
+ i += 1
112
+ end
113
+ end
114
+
115
+ obj
116
+ end
117
+
118
+ def leaf_managed node, klass
119
+ type_attr = node['type']
120
+ klass = @loader.get(type_attr) if type_attr
121
+ klass.new(@conn, node.content)
122
+ end
123
+
124
+ def leaf_date node
125
+ Time.parse node.content
126
+ end
127
+
128
+ def leaf_binary node
129
+ node.content.unpack('m')[0]
130
+ end
131
+
132
+ # XXX does the value need to be deserialized?
133
+ def leaf_keyvalue node
134
+ h = {}
135
+ node.children.each do |child|
136
+ next unless child.element?
137
+ h[child.name] = child.content
138
+ end
139
+ [h['key'], h['value']]
140
+ end
141
+ end
142
+
143
+ class OldDeserializer
144
+ NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
145
+
146
+ def initialize conn
147
+ @conn = conn
148
+ end
149
+
150
+ def deserialize xml, typename=nil
151
+ if IS_JRUBY
152
+ type_attr = xml.attribute_nodes.find { |a| a.name == 'type' &&
153
+ a.namespace &&
154
+ a.namespace.prefix == 'xsi' }
155
+ else
156
+ type_attr = xml.attribute_with_ns('type', NS_XSI)
157
+ end
158
+ typename = (type_attr || typename).to_s
159
+
160
+ if typename =~ /^ArrayOf/
161
+ typename = demangle_array_type $'
162
+ return xml.children.select(&:element?).map { |c| deserialize c, typename }
163
+ end
164
+
165
+ t = @conn.type typename
166
+ if t <= BasicTypes::DataObject
167
+ props_desc = t.full_props_desc
168
+ h = {}
169
+ props_desc.select { |d| d['is-array'] }.each { |d| h[d['name'].to_sym] = [] }
170
+ xml.children.each do |c|
171
+ next unless c.element?
172
+ field = c.name.to_sym
173
+ d = t.find_prop_desc(field.to_s) or next
174
+ o = deserialize c, d['wsdl_type']
175
+ if h[field].is_a? Array
176
+ h[field] << o
177
+ else
178
+ h[field] = o
179
+ end
180
+ end
181
+ t.new h
182
+ elsif t == BasicTypes::ManagedObjectReference
183
+ @conn.type(xml['type']).new @conn, xml.text
184
+ elsif t <= BasicTypes::ManagedObject
185
+ @conn.type(xml['type'] || t.wsdl_name).new @conn, xml.text
186
+ elsif t <= BasicTypes::Enum
187
+ xml.text
188
+ elsif t <= BasicTypes::KeyValue
189
+ h = {}
190
+ xml.children.each do |c|
191
+ next unless c.element?
192
+ h[c.name] = c.text
193
+ end
194
+ [h['key'], h['value']]
195
+ elsif t <= String
196
+ xml.text
197
+ elsif t <= Symbol
198
+ xml.text.to_sym
199
+ elsif t <= Integer
200
+ xml.text.to_i
201
+ elsif t <= Float
202
+ xml.text.to_f
203
+ elsif t <= Time
204
+ Time.parse xml.text
205
+ elsif t == BasicTypes::Boolean
206
+ xml.text == 'true' || xml.text == '1'
207
+ elsif t == BasicTypes::Binary
208
+ xml.text.unpack('m')[0]
209
+ elsif t == BasicTypes::AnyType
210
+ fail "attempted to deserialize an AnyType"
211
+ else fail "unexpected type #{t.inspect} (#{t.ancestors * '/'})"
212
+ end
213
+ rescue
214
+ $stderr.puts "#{$!.class} while deserializing #{xml.name} (#{typename}):"
215
+ $stderr.puts xml.to_s
216
+ raise
217
+ end
218
+
219
+ def demangle_array_type x
220
+ case x
221
+ when 'AnyType' then 'anyType'
222
+ when 'DateTime' then 'dateTime'
223
+ when 'Boolean', 'String', 'Byte', 'Short', 'Int', 'Long', 'Float', 'Double' then x.downcase
224
+ else x
225
+ end
226
+ end
227
+ end
228
+
229
+ if ENV['RBVMOMI_NEW_DESERIALIZER'] == '1'
230
+ Deserializer = NewDeserializer
231
+ else
232
+ Deserializer = OldDeserializer
233
+ end
234
+
235
+ end
@@ -20,6 +20,10 @@ class RbVmomi::TrivialSoap
20
20
  restart_http
21
21
  end
22
22
 
23
+ def host
24
+ @opts[:host]
25
+ end
26
+
23
27
  def close
24
28
  @http.finish rescue IOError
25
29
  end
@@ -65,10 +69,9 @@ class RbVmomi::TrivialSoap
65
69
  xml
66
70
  end
67
71
 
68
- def request action, &b
72
+ def request action, body
69
73
  headers = { 'content-type' => 'text/xml; charset=utf-8', 'SOAPAction' => action }
70
74
  headers['cookie'] = @cookie if @cookie
71
- body = soap_envelope(&b).target!
72
75
 
73
76
  if @debug
74
77
  $stderr.puts "Request:"
@@ -101,6 +104,6 @@ class RbVmomi::TrivialSoap
101
104
  $stderr.puts
102
105
  end
103
106
 
104
- nk.xpath('//soapenv:Body/*').select(&:element?).first
107
+ [nk.xpath('//soapenv:Body/*').select(&:element?).first, response.body.size]
105
108
  end
106
109
  end
@@ -5,40 +5,41 @@ require 'monitor'
5
5
  module RbVmomi
6
6
 
7
7
  class TypeLoader
8
- def initialize target, fn
9
- @target = target
8
+ def initialize fn, extension_dirs, namespace
9
+ @extension_dirs = extension_dirs
10
+ @namespace = namespace
10
11
  @lock = Monitor.new
11
12
  @db = {}
12
13
  @id2wsdl = {}
14
+ @loaded = {}
13
15
  add_types Hash[BasicTypes::BUILTIN.map { |k| [k,nil] }]
14
16
  vmodl_database = File.open(fn, 'r') { |io| Marshal.load io }
15
17
  vmodl_database.reject! { |k,v| k =~ /^_/ }
16
18
  add_types vmodl_database
19
+ preload
17
20
  end
18
21
 
19
- def init
20
- @target.constants.select { |x| has_type? x.to_s }.each { |x| load_type x.to_s }
21
- BasicTypes::BUILTIN.each do |x|
22
- @target.const_set x, BasicTypes.const_get(x)
23
- load_extension x
24
- end
25
- Object.constants.map(&:to_s).select { |x| has_type? x }.each do |x|
26
- load_type x
27
- end
22
+ def preload
23
+ names = (@namespace.constants + Object.constants).map(&:to_s).uniq.
24
+ select { |x| has? x }
25
+ names.each { |x| get(x) }
28
26
  end
29
27
 
30
- def has_type? name
28
+ def has? name
31
29
  fail unless name.is_a? String
32
- @db.member? name
30
+ @db.member?(name) or BasicTypes::BUILTIN.member?(name)
33
31
  end
34
32
 
35
- def load_type name
33
+ def get name
36
34
  fail unless name.is_a? String
35
+ return @loaded[name] if @loaded.member? name
37
36
  @lock.synchronize do
38
- @target.const_set name, make_type(name)
37
+ return @loaded[name] if @loaded.member? name
38
+ klass = make_type(name)
39
+ @namespace.const_set name, klass
39
40
  load_extension name
41
+ @loaded[name] = klass
40
42
  end
41
- nil
42
43
  end
43
44
 
44
45
  def add_types types
@@ -54,15 +55,14 @@ class TypeLoader
54
55
  private
55
56
 
56
57
  def load_extension name
57
- dirs = @target.extension_dirs
58
- dirs.map { |x| File.join(x, "#{name}.rb") }.
59
- select { |x| File.exists? x }.
60
- each { |x| load x }
58
+ @extension_dirs.map { |x| File.join(x, "#{name}.rb") }.
59
+ select { |x| File.exists? x }.
60
+ each { |x| load x }
61
61
  end
62
62
 
63
63
  def make_type name
64
64
  name = name.to_s
65
- fail if BasicTypes::BUILTIN.member? name
65
+ return BasicTypes.const_get(name) if BasicTypes::BUILTIN.member? name
66
66
  desc = @db[name] or fail "unknown VMODL type #{name}"
67
67
  case desc['kind']
68
68
  when 'data' then make_data_type name, desc
@@ -73,14 +73,14 @@ class TypeLoader
73
73
  end
74
74
 
75
75
  def make_data_type name, desc
76
- superclass = @target.const_get desc['wsdl_base']
76
+ superclass = get desc['wsdl_base']
77
77
  Class.new(superclass).tap do |klass|
78
78
  klass.init name, desc['props']
79
79
  end
80
80
  end
81
81
 
82
82
  def make_managed_type name, desc
83
- superclass = @target.const_get desc['wsdl_base']
83
+ superclass = get desc['wsdl_base']
84
84
  Class.new(superclass).tap do |klass|
85
85
  klass.init name, desc['props'], desc['methods']
86
86
  end
@@ -41,25 +41,10 @@ class VIM < Connection
41
41
  end
42
42
 
43
43
  def close
44
- VIM::SessionManager(self, 'SessionManager').Logout
44
+ VIM::SessionManager(self, 'SessionManager').Logout rescue RbVmomi::Fault
45
+ self.cookie = nil
45
46
  super
46
47
  end
47
-
48
- def cookie= cookie
49
- super
50
- ObjectSpace.undefine_finalizer self
51
- ObjectSpace.define_finalizer(self, self.class.finalizer(cookie,@opts))
52
- end
53
-
54
- def self.finalizer cookie, opts
55
- proc do |object_id|
56
- new(opts).tap do |vim|
57
- vim.instance_variable_set :@cookie, cookie
58
- vim.close
59
- end
60
- nil
61
- end
62
- end
63
48
 
64
49
  def rev= x
65
50
  super
@@ -29,7 +29,7 @@ class RbVmomi::VIM::ComputeResource
29
29
  }]
30
30
  )
31
31
 
32
- result = @soap.propertyCollector.RetrieveProperties(:specSet => [filterSpec])
32
+ result = _connection.propertyCollector.RetrieveProperties(:specSet => [filterSpec])
33
33
 
34
34
  stats = {
35
35
  :totalCPU => 0,
@@ -8,8 +8,8 @@ class RbVmomi::VIM::Datastore
8
8
  # @param path [String] Path on the datastore.
9
9
  def exists? path
10
10
  req = Net::HTTP::Head.new mkuripath(path)
11
- req.initialize_http_header 'cookie' => @soap.cookie
12
- resp = @soap.http.request req
11
+ req.initialize_http_header 'cookie' => _connection.cookie
12
+ resp = _connection.http.request req
13
13
  case resp
14
14
  when Net::HTTPSuccess
15
15
  true
@@ -25,10 +25,10 @@ class RbVmomi::VIM::Datastore
25
25
  # @param local_path [String] Destination path on the local machine.
26
26
  # @return [void]
27
27
  def download remote_path, local_path
28
- url = "http#{@soap.http.use_ssl? ? 's' : ''}://#{@soap.http.address}:#{@soap.http.port}#{mkuripath(remote_path)}"
28
+ url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}"
29
29
  pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f',
30
30
  "-o", local_path,
31
- "-b", @soap.cookie,
31
+ "-b", _connection.cookie,
32
32
  url,
33
33
  :out => '/dev/null'
34
34
  Process.waitpid(pid, 0)
@@ -40,10 +40,10 @@ class RbVmomi::VIM::Datastore
40
40
  # @param local_path [String] Source path on the local machine.
41
41
  # @return [void]
42
42
  def upload remote_path, local_path
43
- url = "http#{@soap.http.use_ssl? ? 's' : ''}://#{@soap.http.address}:#{@soap.http.port}#{mkuripath(remote_path)}"
43
+ url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}"
44
44
  pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f',
45
45
  "-T", local_path,
46
- "-b", @soap.cookie,
46
+ "-b", _connection.cookie,
47
47
  url,
48
48
  :out => '/dev/null'
49
49
  Process.waitpid(pid, 0)
@@ -4,7 +4,7 @@ class RbVmomi::VIM::Folder
4
4
  # @param type [Class] Return nil unless the found entity <tt>is_a? type</tt>.
5
5
  # @return [VIM::ManagedEntity]
6
6
  def find name, type=Object
7
- x = @soap.searchIndex.FindChild(:entity => self, :name => name)
7
+ x = _connection.searchIndex.FindChild(:entity => self, :name => name)
8
8
  x if x.is_a? type
9
9
  end
10
10
 
@@ -19,7 +19,7 @@ class RbVmomi::VIM::Folder
19
19
  :vmSearch => type == RbVmomi::VIM::VirtualMachine
20
20
  }
21
21
  propSpecs[:datacenter] = dc if dc
22
- x = @soap.searchIndex.FindByDnsName(propSpecs)
22
+ x = _connection.searchIndex.FindByDnsName(propSpecs)
23
23
  x if x.is_a? type
24
24
  end
25
25
 
@@ -34,7 +34,7 @@ class RbVmomi::VIM::Folder
34
34
  :vmSearch => type == RbVmomi::VIM::VirtualMachine
35
35
  }
36
36
  propSpecs[:datacenter] = dc if dc
37
- x = @soap.searchIndex.FindByIp(propSpecs)
37
+ x = _connection.searchIndex.FindByIp(propSpecs)
38
38
  x if x.is_a? type
39
39
  end
40
40
 
@@ -49,7 +49,7 @@ class RbVmomi::VIM::Folder
49
49
  :vmSearch => type == RbVmomi::VIM::VirtualMachine
50
50
  }
51
51
  propSpecs[:datacenter] = dc if dc
52
- x = @soap.searchIndex.FindByUuid(propSpecs)
52
+ x = _connection.searchIndex.FindByUuid(propSpecs)
53
53
  x if x.is_a? type
54
54
  end
55
55
 
@@ -104,21 +104,18 @@ class RbVmomi::VIM::Folder
104
104
  # included in the results. Values are an array of
105
105
  # property paths (strings) or the symbol :all.
106
106
  #
107
- # @return [Hash] Tree of inventory items. Folders are hashes from child name
108
- # to child result. Objects are hashes from property path to
109
- # value.
110
- #
111
- # @todo Return ObjectContent instead of the leaf hash.
112
- def inventory propSpecs={}
113
- propSet = [{ :type => 'Folder', :pathSet => ['name', 'parent'] }]
107
+ # @return [Hash] Hash of ManagedObjects to properties.
108
+ def inventory_flat propSpecs={}
109
+ propSet = [{ :type => 'Folder', :pathSet => ['name', 'parent', 'childEntity'] }]
114
110
  propSpecs.each do |k,v|
115
111
  case k
116
- when RbVmomi::VIM::ManagedEntity
112
+ when Class
113
+ fail "key must be a subclass of ManagedEntity" unless k < RbVmomi::VIM::ManagedEntity
117
114
  k = k.wsdl_name
118
115
  when Symbol, String
119
116
  k = k.to_s
120
117
  else
121
- fail "key must be a ManagedEntity"
118
+ fail "invalid key"
122
119
  end
123
120
 
124
121
  h = { :type => k }
@@ -150,11 +147,46 @@ class RbVmomi::VIM::Folder
150
147
  :propSet => propSet
151
148
  )
152
149
 
153
- result = @soap.propertyCollector.RetrieveProperties(:specSet => [filterSpec])
150
+ result = _connection.propertyCollector.RetrieveProperties(:specSet => [filterSpec])
151
+ {}.tap do |h|
152
+ result.each { |r| h[r.obj] = r }
153
+ end
154
+ end
155
+
156
+ # Efficiently retrieve properties from descendants of this folder.
157
+ #
158
+ # @param propSpecs [Hash] Specification of which properties to retrieve from
159
+ # which entities. Keys may be symbols, strings, or
160
+ # classes identifying ManagedEntity subtypes to be
161
+ # included in the results. Values are an array of
162
+ # property paths (strings) or the symbol :all.
163
+ #
164
+ # @return [Hash] Tree of inventory items. Each node is a hash from
165
+ # VIM::ObjectContent to children.
166
+ def inventory_tree propSpecs={}
167
+ inv = inventory_flat propSpecs
168
+ children = inv.values.group_by { |v| v['parent'] }
169
+ rec = lambda { |parent| Hash[(children[parent]||[]).map { |x| [x, rec[x.obj]] }] }
170
+ rec[self]
171
+ end
154
172
 
173
+ # Efficiently retrieve properties from descendants of this folder.
174
+ #
175
+ # @param propSpecs [Hash] Specification of which properties to retrieve from
176
+ # which entities. Keys may be symbols, strings, or
177
+ # classes identifying ManagedEntity subtypes to be
178
+ # included in the results. Values are an array of
179
+ # property paths (strings) or the symbol :all.
180
+ #
181
+ # @return [Hash] Tree of inventory items. Folders are hashes from child name
182
+ # to child result. Objects are hashes from property path to
183
+ # value.
184
+ #
185
+ # @deprecated
186
+ def inventory propSpecs={}
187
+ inv = inventory_flat propSpecs
155
188
  tree = { self => {} }
156
- result.each do |x|
157
- obj = x.obj
189
+ inv.each do |obj,x|
158
190
  next if obj == self
159
191
  h = Hash[x.propSet.map { |y| [y.name, y.val] }]
160
192
  tree[h['parent']][h['name']] = [obj, h]