rbvmomi 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -22,7 +22,7 @@ A simple example of turning on a VM:
22
22
  vm = dc.find_vm("myvm") or fail "VM not found"
23
23
  vm.PowerOnVM_Task.wait_for_completion
24
24
 
25
- This code uses several RbVmomi extensions to the VI API for concision. The
25
+ This code uses several RbVmomi extensions to the vSphere API for concision. The
26
26
  expanded snippet below uses only standard API calls and should be familiar to
27
27
  users of the Java SDK:
28
28
 
@@ -75,4 +75,4 @@ write something generally useful please send it to me and I'll add it in.
75
75
  == Development
76
76
 
77
77
  Fork the project on Github and send me a merge request, or send a patch to
78
- rlane@vmware.com.
78
+ rlane@vmware.com. RbVmomi developers hang out in #rbvmomi on Freenode.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.0
1
+ 1.5.0
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tempfile'
3
+
4
+ if ENV['RBVMOMI_COVERAGE'] == '1'
5
+ require 'simplecov'
6
+ SimpleCov.start
7
+ end
8
+
9
+ require 'rbvmomi'
10
+ require 'rbvmomi/deserialization'
11
+ require 'benchmark'
12
+ require 'libxml'
13
+
14
+ NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
15
+
16
+ VIM = RbVmomi::VIM
17
+ $conn = VIM.new(:ns => 'urn:vim25', :rev => '4.0')
18
+ raw = File.read(ARGV[0])
19
+
20
+ def diff a, b
21
+ a_io = Tempfile.new 'rbvmomi-diff-a'
22
+ b_io = Tempfile.new 'rbvmomi-diff-b'
23
+ PP.pp a, a_io
24
+ PP.pp b, b_io
25
+ a_io.close
26
+ b_io.close
27
+ system("diff -u #{a_io.path} #{b_io.path}")
28
+ a_io.unlink
29
+ b_io.unlink
30
+ end
31
+
32
+ begin
33
+ deserializer = RbVmomi::OldDeserializer.new($conn)
34
+ end_time = Time.now + 1
35
+ n = 0
36
+ while n == 0 or end_time > Time.now
37
+ deserializer.deserialize Nokogiri::XML(raw).root
38
+ n += 1
39
+ end
40
+ N = n*10
41
+ end
42
+
43
+ puts "iterations: #{N}"
44
+
45
+ parsed_nokogiri = Nokogiri::XML(raw)
46
+ parsed_libxml = LibXML::XML::Parser.string(raw).parse
47
+
48
+ if true
49
+ nokogiri_xml = parsed_nokogiri.root
50
+ libxml_xml = parsed_libxml.root
51
+
52
+ old_nokogiri_result = RbVmomi::OldDeserializer.new($conn).deserialize nokogiri_xml
53
+ new_nokogiri_result = RbVmomi::NewDeserializer.new($conn).deserialize nokogiri_xml
54
+ new_libxml_result = RbVmomi::NewDeserializer.new($conn).deserialize libxml_xml
55
+
56
+ if new_nokogiri_result != old_nokogiri_result
57
+ puts "new_nokogiri_result doesnt match old_nokogiri_result"
58
+ puts "old_nokogiri_result:"
59
+ pp old_nokogiri_result
60
+ puts "new_nokogiri_result:"
61
+ pp new_nokogiri_result
62
+ puts "diff:"
63
+ diff old_nokogiri_result, new_nokogiri_result
64
+ exit 1
65
+ end
66
+
67
+ if new_libxml_result != old_nokogiri_result
68
+ puts "new_libxml_result doesnt match old_nokogiri_result"
69
+ puts "old_nokogiri_result:"
70
+ pp old_nokogiri_result
71
+ puts "new_libxml_result:"
72
+ pp new_libxml_result
73
+ puts "diff:"
74
+ diff old_nokogiri_result, new_libxml_result
75
+ exit 1
76
+ end
77
+
78
+ puts "all results match"
79
+ end
80
+
81
+ Benchmark.bmbm do|b|
82
+ GC.start
83
+ b.report("nokogiri parsing") do
84
+ N.times { Nokogiri::XML(raw) }
85
+ end
86
+
87
+ GC.start
88
+ b.report("libxml parsing") do
89
+ N.times do
90
+ LibXML::XML::Parser.string(raw).parse
91
+ end
92
+ end
93
+
94
+ GC.start
95
+ b.report("old deserialization (nokogiri)") do
96
+ deserializer = RbVmomi::OldDeserializer.new($conn)
97
+ N.times do
98
+ deserializer.deserialize Nokogiri::XML(raw).root
99
+ end
100
+ end
101
+
102
+ GC.start
103
+ b.report("new deserialization (nokogiri)") do
104
+ deserializer = RbVmomi::NewDeserializer.new($conn)
105
+ N.times do
106
+ deserializer.deserialize Nokogiri::XML(raw).root
107
+ end
108
+ end
109
+
110
+ GC.start
111
+ b.report("new deserialization (libxml)") do
112
+ deserializer = RbVmomi::NewDeserializer.new($conn)
113
+ N.times do
114
+ deserializer.deserialize LibXML::XML::Parser.string(raw).parse.root
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # Find collisions between VMODL property names and Ruby methods
3
+ require 'rbvmomi'
4
+ VIM = RbVmomi::VIM
5
+
6
+ conn = VIM.new(:ns => 'urn:vim25', :rev => '4.0')
7
+
8
+ VIM.loader.typenames.each do |name|
9
+ klass = VIM.loader.get name
10
+ next unless klass.respond_to? :kind and [:managed, :data].member? klass.kind
11
+ methods = klass.kind == :managed ?
12
+ RbVmomi::BasicTypes::ObjectWithMethods.instance_methods :
13
+ RbVmomi::BasicTypes::ObjectWithProperties.instance_methods
14
+ klass.props_desc.each do |x|
15
+ name = x['name']
16
+ puts "collision: #{klass}##{name}" if methods.member? name.to_sym
17
+ end
18
+ end
@@ -7,6 +7,11 @@ internal_vmodl_filename = ARGV[1] or abort "internal vmodl filename required"
7
7
  output_vmodl_filename = ARGV[2] or abort "output vmodl filename required"
8
8
 
9
9
  TYPES = %w(
10
+ DVSKeyedOpaqueData
11
+ DVSOpaqueDataConfigSpec
12
+ DVPortgroupSelection
13
+ DVPortSelection
14
+ DVSSelection
10
15
  DynamicTypeEnumTypeInfo
11
16
  DynamicTypeMgrAllTypeInfo
12
17
  DynamicTypeMgrAnnotation
@@ -27,9 +32,11 @@ ReflectManagedMethodExecuter
27
32
  ReflectManagedMethodExecuterSoapArgument
28
33
  ReflectManagedMethodExecuterSoapFault
29
34
  ReflectManagedMethodExecuterSoapResult
35
+ SelectionSet
30
36
  )
31
37
 
32
38
  METHODS = %w(
39
+ DistributedVirtualSwitchManager.UpdateDvsOpaqueData_Task
33
40
  HostSystem.RetrieveDynamicTypeManager
34
41
  HostSystem.RetrieveManagedMethodExecuter
35
42
  )
@@ -1,10 +1,11 @@
1
1
  # Copyright (c) 2010 VMware, Inc. All Rights Reserved.
2
2
  require 'pp'
3
+ require 'set'
3
4
 
4
5
  module RbVmomi
5
6
  module BasicTypes
6
7
 
7
- BUILTIN = %w(ManagedObject DataObject TypeName PropertyPath ManagedObjectReference MethodName MethodFault LocalizedMethodFault KeyValue)
8
+ BUILTIN = Set.new %w(ManagedObject DataObject TypeName PropertyPath ManagedObjectReference MethodName MethodFault LocalizedMethodFault KeyValue)
8
9
 
9
10
  class Base
10
11
  class << self
@@ -36,6 +37,10 @@ class ObjectWithProperties < Base
36
37
  end
37
38
  end
38
39
 
40
+ def full_props_set
41
+ @full_props_set ||= Set.new(full_props_desc.map { |x| x['name'] })
42
+ end
43
+
39
44
  def full_props_desc
40
45
  @full_props_desc ||= (self == ObjectWithProperties ? [] : superclass.full_props_desc) + props_desc
41
46
  end
@@ -83,11 +88,19 @@ end
83
88
  class DataObject < ObjectWithProperties
84
89
  attr_reader :props
85
90
 
91
+ def self.kind; :data end
92
+
86
93
  def initialize props={}
94
+ # Deserialization fast path
95
+ if props == nil
96
+ @props = {}
97
+ return
98
+ end
99
+
87
100
  @props = Hash[props.map { |k,v| [k.to_sym, v] }]
88
- self.class.full_props_desc.each do |desc|
101
+ #self.class.full_props_desc.each do |desc|
89
102
  #fail "missing required property #{desc['name'].inspect} of #{self.class.wsdl_name}" if @props[desc['name'].to_sym].nil? and not desc['is-optional']
90
- end
103
+ #end
91
104
  @props.each do |k,v|
92
105
  fail "unexpected property name #{k}" unless self.class.find_prop_desc(k)
93
106
  end
@@ -148,6 +161,8 @@ class DataObject < ObjectWithProperties
148
161
  end
149
162
 
150
163
  class ManagedObject < ObjectWithMethods
164
+ def self.kind; :managed end
165
+
151
166
  def initialize connection, ref
152
167
  super()
153
168
  @connection = connection
@@ -222,6 +237,8 @@ class Enum < Base
222
237
  end
223
238
  end
224
239
 
240
+ def self.kind; :enum end
241
+
225
242
  attr_reader :value
226
243
 
227
244
  def initialize value
@@ -1,9 +1,11 @@
1
1
  # Copyright (c) 2010 VMware, Inc. All Rights Reserved.
2
2
  require 'time'
3
+ require 'date'
3
4
  require 'rbvmomi/trivial_soap'
4
5
  require 'rbvmomi/basic_types'
5
6
  require 'rbvmomi/fault'
6
7
  require 'rbvmomi/type_loader'
8
+ require 'rbvmomi/deserialization'
7
9
 
8
10
  module RbVmomi
9
11
 
@@ -15,12 +17,24 @@ class Connection < TrivialSoap
15
17
  NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
16
18
 
17
19
  attr_accessor :rev
18
-
20
+ attr_reader :profile
21
+ attr_reader :profile_summary
22
+ attr_accessor :profiling
23
+ attr_reader :deserializer
24
+
19
25
  def initialize opts
20
26
  @ns = opts[:ns] or fail "no namespace specified"
21
27
  @rev = opts[:rev] or fail "no revision specified"
28
+ @deserializer = Deserializer.new self
29
+ reset_profiling
30
+ @profiling = false
22
31
  super opts
23
32
  end
33
+
34
+ def reset_profiling
35
+ @profile = {}
36
+ @profile_summary = {:network_latency => 0, :request_emit => 0, :response_parse => 0, :num_calls => 0}
37
+ end
24
38
 
25
39
  def emit_request xml, method, descs, this, params
26
40
  xml.tag! method, :xmlns => @ns do
@@ -41,7 +55,7 @@ class Connection < TrivialSoap
41
55
  def parse_response resp, desc
42
56
  if resp.at('faultcode')
43
57
  detail = resp.at('detail')
44
- fault = detail && xml2obj(detail.children.first, 'MethodFault')
58
+ fault = detail && @deserializer.deserialize(detail.children.first, 'MethodFault')
45
59
  msg = resp.at('faultstring').text
46
60
  if fault
47
61
  raise RbVmomi::Fault.new(msg, fault)
@@ -51,7 +65,7 @@ class Connection < TrivialSoap
51
65
  else
52
66
  if desc
53
67
  type = desc['is-task'] ? 'Task' : desc['wsdl_type']
54
- returnvals = resp.children.select(&:element?).map { |c| xml2obj c, type }
68
+ returnvals = resp.children.select(&:element?).map { |c| @deserializer.deserialize c, type }
55
69
  (desc['is-array'] && !desc['is-task']) ? returnvals : returnvals.first
56
70
  else
57
71
  nil
@@ -64,89 +78,38 @@ class Connection < TrivialSoap
64
78
  fail "parameters must be passed as a hash" unless params.is_a? Hash
65
79
  fail unless desc.is_a? Hash
66
80
 
67
- resp = request "#{@ns}/#{@rev}" do |xml|
81
+ t1 = Time.now
82
+ body = soap_envelope do |xml|
68
83
  emit_request xml, method, desc['params'], this, params
84
+ end.target!
85
+
86
+ t2 = Time.now
87
+ resp, resp_size = request "#{@ns}/#{@rev}", body
88
+
89
+ t3 = Time.now
90
+ out = parse_response resp, desc['result']
91
+
92
+ if @profiling
93
+ t4 = Time.now
94
+ @profile[method] ||= []
95
+ profile_info = {
96
+ :network_latency => (t3 - t2),
97
+ :request_emit => t2 - t1,
98
+ :response_parse => t4 - t3,
99
+ :params => params,
100
+ :obj => this,
101
+ :backtrace => caller,
102
+ :request_size => body.length,
103
+ :response_size => resp_size,
104
+ }
105
+ @profile[method] << profile_info
106
+ @profile_summary[:network_latency] += profile_info[:network_latency]
107
+ @profile_summary[:response_parse] += profile_info[:response_parse]
108
+ @profile_summary[:request_emit] += profile_info[:request_emit]
109
+ @profile_summary[:num_calls] += 1
69
110
  end
70
-
71
- parse_response resp, desc['result']
72
- end
73
-
74
- def demangle_array_type x
75
- case x
76
- when 'AnyType' then 'anyType'
77
- when 'DateTime' then 'dateTime'
78
- when 'Boolean', 'String', 'Byte', 'Short', 'Int', 'Long', 'Float', 'Double' then x.downcase
79
- else x
80
- end
81
- end
82
-
83
- def xml2obj xml, typename
84
- if IS_JRUBY
85
- type_attr = xml.attribute_nodes.find { |a| a.name == 'type' &&
86
- a.namespace &&
87
- a.namespace.prefix == 'xsi' }
88
- else
89
- type_attr = xml.attribute_with_ns('type', NS_XSI)
90
- end
91
- typename = (type_attr || typename).to_s
92
-
93
- if typename =~ /^ArrayOf/
94
- typename = demangle_array_type $'
95
- return xml.children.select(&:element?).map { |c| xml2obj c, typename }
96
- end
97
-
98
- t = type typename
99
- if t <= BasicTypes::DataObject
100
- props_desc = t.full_props_desc
101
- h = {}
102
- props_desc.select { |d| d['is-array'] }.each { |d| h[d['name'].to_sym] = [] }
103
- xml.children.each do |c|
104
- next unless c.element?
105
- field = c.name.to_sym
106
- d = t.find_prop_desc(field.to_s) or next
107
- o = xml2obj c, d['wsdl_type']
108
- if h[field].is_a? Array
109
- h[field] << o
110
- else
111
- h[field] = o
112
- end
113
- end
114
- t.new h
115
- elsif t == BasicTypes::ManagedObjectReference
116
- type(xml['type']).new self, xml.text
117
- elsif t <= BasicTypes::ManagedObject
118
- type(xml['type'] || t.wsdl_name).new self, xml.text
119
- elsif t <= BasicTypes::Enum
120
- xml.text
121
- elsif t <= BasicTypes::KeyValue
122
- h = {}
123
- xml.children.each do |c|
124
- next unless c.element?
125
- h[c.name] = c.text
126
- end
127
- [h['key'], h['value']]
128
- elsif t <= String
129
- xml.text
130
- elsif t <= Symbol
131
- xml.text.to_sym
132
- elsif t <= Integer
133
- xml.text.to_i
134
- elsif t <= Float
135
- xml.text.to_f
136
- elsif t <= Time
137
- Time.parse xml.text
138
- elsif t == BasicTypes::Boolean
139
- xml.text == 'true' || xml.text == '1'
140
- elsif t == BasicTypes::Binary
141
- xml.text.unpack('m')[0]
142
- elsif t == BasicTypes::AnyType
143
- fail "attempted to deserialize an AnyType"
144
- else fail "unexpected type #{t.inspect}"
145
- end
146
- rescue
147
- $stderr.puts "#{$!.class} while deserializing #{xml.name} (#{typename}):"
148
- $stderr.puts xml.to_s
149
- raise
111
+
112
+ out
150
113
  end
151
114
 
152
115
  # hic sunt dracones
@@ -207,7 +170,10 @@ class Connection < TrivialSoap
207
170
  xml.tag! name, o.to_s, attrs
208
171
  when DateTime
209
172
  attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType
210
- xml.tag! name, o.to_s, attrs
173
+ xml.tag! name, o.strftime('%FT%T%:z'), attrs
174
+ when Time
175
+ attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType
176
+ xml.tag! name, o.iso8601, attrs
211
177
  else fail "unexpected object class #{o.class}"
212
178
  end
213
179
  xml
@@ -230,7 +196,7 @@ class Connection < TrivialSoap
230
196
  when :base64Binary then BasicTypes::Binary
231
197
  when :KeyValue then BasicTypes::KeyValue
232
198
  else
233
- if @loader.has_type? name
199
+ if @loader.has? name
234
200
  const_get(name)
235
201
  else
236
202
  fail "no such type #{name.inspect}"
@@ -252,25 +218,25 @@ protected
252
218
 
253
219
  def self.const_missing sym
254
220
  name = sym.to_s
255
- if @loader and @loader.has_type? name
256
- @loader.load_type name
257
- const_get sym
221
+ if @loader and @loader.has? name
222
+ @loader.get(name)
258
223
  else
259
224
  super
260
225
  end
261
226
  end
262
227
 
263
228
  def self.method_missing sym, *a
264
- if @loader and @loader.has_type? sym.to_s
265
- const_get(sym).new(*a)
229
+ name = sym.to_s
230
+ if @loader and @loader.has? name
231
+ @loader.get(name).new(*a)
266
232
  else
267
233
  super
268
234
  end
269
235
  end
270
236
 
271
237
  def self.load_vmodl fn
272
- @loader = RbVmomi::TypeLoader.new self, fn
273
- @loader.init
238
+ @loader = RbVmomi::TypeLoader.new fn, extension_dirs, self
239
+ nil
274
240
  end
275
241
  end
276
242