rbvmomi2 3.0.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +114 -0
  4. data/exe/rbvmomish +138 -0
  5. data/lib/rbvmomi/basic_types.rb +383 -0
  6. data/lib/rbvmomi/connection.rb +272 -0
  7. data/lib/rbvmomi/deserialization.rb +249 -0
  8. data/lib/rbvmomi/fault.rb +19 -0
  9. data/lib/rbvmomi/optimist.rb +72 -0
  10. data/lib/rbvmomi/pbm.rb +68 -0
  11. data/lib/rbvmomi/sms/SmsStorageManager.rb +10 -0
  12. data/lib/rbvmomi/sms.rb +63 -0
  13. data/lib/rbvmomi/sso.rb +313 -0
  14. data/lib/rbvmomi/trivial_soap.rb +122 -0
  15. data/lib/rbvmomi/type_loader.rb +138 -0
  16. data/lib/rbvmomi/utils/admission_control.rb +401 -0
  17. data/lib/rbvmomi/utils/deploy.rb +318 -0
  18. data/lib/rbvmomi/utils/leases.rb +145 -0
  19. data/lib/rbvmomi/utils/perfdump.rb +631 -0
  20. data/lib/rbvmomi/version.rb +6 -0
  21. data/lib/rbvmomi/vim/ComputeResource.rb +54 -0
  22. data/lib/rbvmomi/vim/Datacenter.rb +25 -0
  23. data/lib/rbvmomi/vim/Datastore.rb +72 -0
  24. data/lib/rbvmomi/vim/DynamicTypeMgrAllTypeInfo.rb +78 -0
  25. data/lib/rbvmomi/vim/DynamicTypeMgrDataTypeInfo.rb +23 -0
  26. data/lib/rbvmomi/vim/DynamicTypeMgrManagedTypeInfo.rb +54 -0
  27. data/lib/rbvmomi/vim/Folder.rb +214 -0
  28. data/lib/rbvmomi/vim/HostSystem.rb +177 -0
  29. data/lib/rbvmomi/vim/ManagedEntity.rb +60 -0
  30. data/lib/rbvmomi/vim/ManagedObject.rb +63 -0
  31. data/lib/rbvmomi/vim/ObjectContent.rb +26 -0
  32. data/lib/rbvmomi/vim/ObjectUpdate.rb +26 -0
  33. data/lib/rbvmomi/vim/OvfManager.rb +204 -0
  34. data/lib/rbvmomi/vim/PerfCounterInfo.rb +28 -0
  35. data/lib/rbvmomi/vim/PerformanceManager.rb +113 -0
  36. data/lib/rbvmomi/vim/PropertyCollector.rb +28 -0
  37. data/lib/rbvmomi/vim/ReflectManagedMethodExecuter.rb +33 -0
  38. data/lib/rbvmomi/vim/ResourcePool.rb +58 -0
  39. data/lib/rbvmomi/vim/ServiceInstance.rb +58 -0
  40. data/lib/rbvmomi/vim/Task.rb +68 -0
  41. data/lib/rbvmomi/vim/VirtualMachine.rb +75 -0
  42. data/lib/rbvmomi/vim.rb +157 -0
  43. data/lib/rbvmomi.rb +16 -0
  44. data/lib/rbvmomi2.rb +3 -0
  45. data/vmodl.db +0 -0
  46. metadata +214 -0
@@ -0,0 +1,63 @@
1
+ # Copyright (c) 2013-2017 VMware, Inc. All Rights Reserved.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ require_relative '../rbvmomi'
5
+ module RbVmomi
6
+
7
+ # A connection to one vSphere SMS endpoint.
8
+ # @see #serviceInstance
9
+ class SMS < Connection
10
+ # Connect to a vSphere SMS endpoint
11
+ #
12
+ # @param [VIM] Connection to main vSphere API endpoint
13
+ # @param [Hash] opts The options hash.
14
+ # @option opts [String] :host Host to connect to.
15
+ # @option opts [Numeric] :port (443) Port to connect to.
16
+ # @option opts [Boolean] :ssl (true) Whether to use SSL.
17
+ # @option opts [Boolean] :insecure (false) If true, ignore SSL certificate errors.
18
+ # @option opts [String] :path (/sms/sdk) SDK endpoint path.
19
+ # @option opts [Boolean] :debug (false) If true, print SOAP traffic to stderr.
20
+ def self.connect vim, opts = {}
21
+ fail unless opts.is_a? Hash
22
+ opts[:host] = vim.host
23
+ opts[:ssl] = true unless opts.member? :ssl or opts[:"no-ssl"]
24
+ opts[:insecure] ||= true
25
+ opts[:port] ||= (opts[:ssl] ? 443 : 80)
26
+ opts[:path] ||= '/sms/sdk'
27
+ opts[:ns] ||= 'urn:sms'
28
+ rev_given = opts[:rev] != nil
29
+ opts[:rev] = '4.0' unless rev_given
30
+ opts[:debug] = (!ENV['RBVMOMI_DEBUG'].empty? rescue false) unless opts.member? :debug
31
+
32
+ new(opts).tap do |sms|
33
+ sms.vcSessionCookie = vim.cookie.split('"')[1]
34
+ end
35
+ end
36
+
37
+ def vcSessionCookie= cookie
38
+ @vcSessionCookie = cookie
39
+ end
40
+
41
+ def rev= x
42
+ super
43
+ @serviceContent = nil
44
+ end
45
+
46
+ # Return the ServiceInstance
47
+ #
48
+ # The ServiceInstance is the root of the vSphere inventory.
49
+ def serviceInstance
50
+ @serviceInstance ||= VIM::SmsServiceInstance self, 'ServiceInstance'
51
+ end
52
+
53
+ # @private
54
+ def pretty_print pp
55
+ pp.text "SMS(#{@opts[:host]})"
56
+ end
57
+
58
+ add_extension_dir File.join(File.dirname(__FILE__), "sms")
59
+ load_vmodl(ENV['VMODL'] || File.join(File.dirname(__FILE__), "../../vmodl.db"))
60
+ end
61
+
62
+ end
63
+
@@ -0,0 +1,313 @@
1
+ require 'base64'
2
+ require 'net/https'
3
+ require 'nokogiri'
4
+ require 'openssl'
5
+ require 'securerandom'
6
+ require 'time'
7
+
8
+ module RbVmomi
9
+ # Provides access to vCenter Single Sign-On
10
+ class SSO
11
+ BST_PROFILE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'.freeze
12
+ C14N_CLASS = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
13
+ C14N_METHOD = 'http://www.w3.org/2001/10/xml-exc-c14n#'.freeze
14
+ DIGEST_METHOD = 'http://www.w3.org/2001/04/xmlenc#sha512'.freeze
15
+ ENCODING_METHOD = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'.freeze
16
+ SIGNATURE_METHOD = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'.freeze
17
+ STS_PATH = '/sts/STSService'.freeze
18
+ TOKEN_TYPE = 'urn:oasis:names:tc:SAML:2.0:assertion'.freeze
19
+ TOKEN_PROFILE = 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0'.freeze
20
+ NAMESPACES = {
21
+ :ds => 'http://www.w3.org/2000/09/xmldsig#',
22
+ :soap => 'http://schemas.xmlsoap.org/soap/envelope/',
23
+ :wsse => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
24
+ :wsse11 => 'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd',
25
+ :wst => 'http://docs.oasis-open.org/ws-sx/ws-trust/200512',
26
+ :wsu => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
27
+ }.freeze
28
+
29
+ attr_reader :assertion,
30
+ :assertion_id,
31
+ :certificate,
32
+ :host,
33
+ :user,
34
+ :password,
35
+ :path,
36
+ :port,
37
+ :private_key
38
+
39
+ # Creates an instance of an SSO object
40
+ #
41
+ # @param [Hash] opts the options to create the object with
42
+ # @option opts [String] :host the host to connect to
43
+ # @option opts [Fixnum] :port (443) the port to connect to
44
+ # @option opts [String] :path the path to call
45
+ # @option opts [String] :user the user to authenticate with
46
+ # @option opts [String] :password the password to authenticate with
47
+ # @option opts [String] :private_key the private key to use
48
+ # @option opts [String] :certificate the certificate to use
49
+ # @option opts [Boolean] :insecure (false) whether to connect insecurely
50
+ def initialize(opts = {})
51
+ @host = opts[:host]
52
+ @insecure = opts.fetch(:insecure, false)
53
+ @password = opts[:password]
54
+ @path = opts.fetch(:path, STS_PATH)
55
+ @port = opts.fetch(:port, 443)
56
+ @user = opts[:user]
57
+
58
+ load_x509(opts[:private_key], opts[:certificate])
59
+ end
60
+
61
+ def request_token
62
+ req = sso_call(hok_token_request)
63
+
64
+ unless req.is_a?(Net::HTTPSuccess)
65
+ resp = Nokogiri::XML(req.body)
66
+ resp.remove_namespaces!
67
+ raise(resp.at_xpath('//Envelope/Body/Fault/faultstring/text()'))
68
+ end
69
+
70
+ extract_assertion(req.body)
71
+ end
72
+
73
+ def sign_request(request)
74
+ raise('Need SAML2 assertion') unless @assertion
75
+ raise('No SAML2 assertion ID') unless @assertion_id
76
+
77
+ request_id = generate_id
78
+ timestamp_id = generate_id
79
+
80
+ request = request.is_a?(String) ? Nokogiri::XML(request) : request
81
+ builder = Nokogiri::XML::Builder.new do |xml|
82
+ xml[:soap].Header(Hash[NAMESPACES.map { |ns, uri| ["xmlns:#{ns}", uri] }]) do
83
+ xml[:wsse].Security do
84
+ wsu_timestamp(xml, timestamp_id)
85
+ ds_signature(xml, request_id, timestamp_id) do |x|
86
+ x[:wsse].SecurityTokenReference('wsse11:TokenType' => TOKEN_PROFILE) do
87
+ x[:wsse].KeyIdentifier(
88
+ @assertion_id,
89
+ 'ValueType' => 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID'
90
+ )
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ # To avoid Nokogiri mangling the token, we replace it as a string
98
+ # later on. Figure out a way around this.
99
+ builder.doc.at_xpath('//soap:Header/wsse:Security/wsu:Timestamp').add_previous_sibling(Nokogiri::XML::Text.new('SAML_ASSERTION_PLACEHOLDER', builder.doc))
100
+
101
+ request.at_xpath('//soap:Envelope', NAMESPACES).tap do |e|
102
+ NAMESPACES.each do |ns, uri|
103
+ e.add_namespace(ns.to_s, uri)
104
+ end
105
+ end
106
+ request.xpath('//soap:Envelope/soap:Body').each do |body|
107
+ body.add_previous_sibling(builder.doc.root)
108
+ body.add_namespace('wsu', NAMESPACES[:wsu])
109
+ body['wsu:Id'] = request_id
110
+ end
111
+
112
+ signed = sign(request)
113
+ signed.gsub!('SAML_ASSERTION_PLACEHOLDER', @assertion.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML).strip)
114
+
115
+ signed
116
+ end
117
+
118
+ # We default to Issue, since that's all we currently need.
119
+ def sso_call(body)
120
+ sso_url = URI::HTTPS.build(:host => @host, :port => @port, :path => @path)
121
+ http = Net::HTTP.new(sso_url.host, sso_url.port)
122
+ http.use_ssl = true
123
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @insecure
124
+
125
+ req = Net::HTTP::Post.new(sso_url.request_uri)
126
+ req.add_field('Accept', 'text/xml, multipart/related')
127
+ req.add_field('User-Agent', "VMware/RbVmomi #{RbVmomi::VERSION}")
128
+ req.add_field('SOAPAction', 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue')
129
+ req.content_type = 'text/xml; charset="UTF-8"'
130
+ req.body = body
131
+
132
+ http.request(req)
133
+ end
134
+
135
+ private
136
+
137
+ def hok_token_request
138
+ request_id = generate_id
139
+ security_token_id = generate_id
140
+ signature_id = generate_id
141
+ timestamp_id = generate_id
142
+
143
+ datum = Time.now.utc
144
+ created_at = datum.iso8601
145
+ token_expires_at = (datum + 1800).iso8601
146
+
147
+ builder = Nokogiri::XML::Builder.new do |xml|
148
+ xml[:soap].Envelope(Hash[NAMESPACES.map { |ns, uri| ["xmlns:#{ns}", uri] }]) do
149
+ xml[:soap].Header do
150
+ xml[:wsse].Security do
151
+ wsu_timestamp(xml, timestamp_id, datum)
152
+ wsse_username_token(xml)
153
+ wsse_binary_security_token(xml, security_token_id)
154
+ ds_signature(xml, request_id, timestamp_id, signature_id) do |x|
155
+ x[:wsse].SecurityTokenReference do
156
+ x[:wsse].Reference(
157
+ 'URI' => "##{security_token_id}",
158
+ 'ValueType' => BST_PROFILE
159
+ )
160
+ end
161
+ end
162
+ end
163
+ end
164
+ xml[:soap].Body('wsu:Id' => request_id) do
165
+ xml[:wst].RequestSecurityToken do
166
+ xml[:wst].TokenType(TOKEN_TYPE)
167
+ xml[:wst].RequestType('http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue')
168
+ xml[:wst].Lifetime do
169
+ xml[:wsu].Created(created_at)
170
+ xml[:wsu].Expires(token_expires_at)
171
+ end
172
+ xml[:wst].Renewing('Allow' => 'false', 'OK' => 'false')
173
+ xml[:wst].KeyType('http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey')
174
+ xml[:wst].SignatureAlgorithm(SIGNATURE_METHOD)
175
+ xml[:wst].Delegatable('false')
176
+ end
177
+ xml[:wst].UseKey('Sig' => signature_id)
178
+ end
179
+ end
180
+ end
181
+
182
+ sign(builder.doc)
183
+ end
184
+
185
+ def extract_assertion(sso_response)
186
+ sso_response = Nokogiri::XML(sso_response) if sso_response.is_a?(String)
187
+ namespaces = sso_response.collect_namespaces
188
+
189
+ # Doesn't matter that usually there's more than one NS with the same
190
+ # URI - either will work for XPath. We just don't want to hardcode
191
+ # xmlns:saml2.
192
+ token_ns = namespaces.find { |_, uri| uri == TOKEN_TYPE }.first.gsub(/^xmlns:/, '')
193
+
194
+ @assertion = sso_response.at_xpath("//#{token_ns}:Assertion", namespaces)
195
+ @assertion_id = @assertion.at_xpath("//#{token_ns}:Assertion/@ID", namespaces).value
196
+ end
197
+
198
+ def sign(doc)
199
+ signature_digest_references = doc.xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo/ds:Reference/@URI', doc.collect_namespaces).map { |a| a.value.sub(/^#/, '') }
200
+ signature_digest_references.each do |ref|
201
+ data = doc.at_xpath("//*[@wsu:Id='#{ref}']", doc.collect_namespaces)
202
+ digest = Base64.strict_encode64(Digest::SHA2.new(512).digest(data.canonicalize(C14N_CLASS)))
203
+ digest_tag = doc.at_xpath("/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo/ds:Reference[@URI='##{ref}']/ds:DigestValue", doc.collect_namespaces)
204
+ digest_tag.add_child(Nokogiri::XML::Text.new(digest, doc))
205
+ end
206
+
207
+ signed_info = doc.at_xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo', doc.collect_namespaces)
208
+ signature = Base64.strict_encode64(@private_key.sign(OpenSSL::Digest::SHA512.new, signed_info.canonicalize(C14N_CLASS)))
209
+ signature_value_tag = doc.at_xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignatureValue', doc.collect_namespaces)
210
+ signature_value_tag.add_child(Nokogiri::XML::Text.new(signature, doc))
211
+
212
+ doc.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML).strip
213
+ end
214
+
215
+ def load_x509(private_key, certificate)
216
+ @private_key = private_key ? private_key : OpenSSL::PKey::RSA.new(2048)
217
+ if @private_key.is_a? String
218
+ @private_key = OpenSSL::PKey::RSA.new(@private_key)
219
+ end
220
+
221
+ @certificate = certificate
222
+ if @certificate && !private_key
223
+ raise(ArgumentError, "Can't generate private key from a certificate")
224
+ end
225
+
226
+ if @certificate.is_a? String
227
+ @certificate = OpenSSL::X509::Certificate.new(@certificate)
228
+ end
229
+ # If only a private key is specified, we will generate a certificate.
230
+ unless @certificate
231
+ timestamp = Time.now.utc
232
+ @certificate = OpenSSL::X509::Certificate.new
233
+ @certificate.not_before = timestamp
234
+ @certificate.not_after = timestamp + 3600 # 3600 is 1 hour
235
+ @certificate.subject = OpenSSL::X509::Name.new([
236
+ %w[O VMware],
237
+ %w[OU RbVmomi],
238
+ %W[CN #{@user}]
239
+ ])
240
+ @certificate.issuer = @certificate.subject
241
+ @certificate.serial = rand(2**160)
242
+ @certificate.public_key = @private_key.public_key
243
+ @certificate.sign(@private_key, OpenSSL::Digest::SHA512.new)
244
+ end
245
+
246
+ true
247
+ end
248
+
249
+ def ds_signature(xml, request_id, timestamp_id, id = nil)
250
+ signature_id = {}
251
+ signature_id['Id'] = id if id
252
+ xml[:ds].Signature(signature_id) do
253
+ ds_signed_info(xml, request_id, timestamp_id)
254
+ xml[:ds].SignatureValue
255
+ xml[:ds].KeyInfo do
256
+ yield xml
257
+ end
258
+ end
259
+ end
260
+
261
+ def ds_signed_info(xml, request_id, timestamp_id)
262
+ xml[:ds].SignedInfo do
263
+ xml[:ds].CanonicalizationMethod('Algorithm' => C14N_METHOD)
264
+ xml[:ds].SignatureMethod('Algorithm' => SIGNATURE_METHOD)
265
+ xml[:ds].Reference('URI' => "##{request_id}") do
266
+ xml[:ds].Transforms do
267
+ xml[:ds].Transform('Algorithm' => C14N_METHOD)
268
+ end
269
+ xml[:ds].DigestMethod('Algorithm' => DIGEST_METHOD)
270
+ xml[:ds].DigestValue
271
+ end
272
+ xml[:ds].Reference('URI' => "##{timestamp_id}") do
273
+ xml[:ds].Transforms do
274
+ xml[:ds].Transform('Algorithm' => C14N_METHOD)
275
+ end
276
+ xml[:ds].DigestMethod('Algorithm' => DIGEST_METHOD)
277
+ xml[:ds].DigestValue
278
+ end
279
+ end
280
+ end
281
+
282
+ def wsu_timestamp(xml, id, datum = nil)
283
+ datum ||= Time.now.utc
284
+ created_at = datum.iso8601
285
+ expires_at = (datum + 600).iso8601
286
+
287
+ xml[:wsu].Timestamp('wsu:Id' => id) do
288
+ xml[:wsu].Created(created_at)
289
+ xml[:wsu].Expires(expires_at)
290
+ end
291
+ end
292
+
293
+ def wsse_username_token(xml)
294
+ xml[:wsse].UsernameToken do
295
+ xml[:wsse].Username(@user)
296
+ xml[:wsse].Password(@password)
297
+ end
298
+ end
299
+
300
+ def wsse_binary_security_token(xml, id)
301
+ xml[:wsse].BinarySecurityToken(
302
+ Base64.strict_encode64(@certificate.to_der),
303
+ 'EncodingType' => ENCODING_METHOD,
304
+ 'ValueType' => BST_PROFILE,
305
+ 'wsu:Id' => id
306
+ )
307
+ end
308
+
309
+ def generate_id
310
+ "_#{SecureRandom.uuid}"
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,122 @@
1
+ # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ require 'rubygems'
5
+ require 'builder'
6
+ require 'nokogiri'
7
+ require 'net/http'
8
+ require 'pp'
9
+
10
+ class RbVmomi::TrivialSoap
11
+ attr_accessor :debug, :cookie, :operation_id
12
+ attr_reader :http
13
+
14
+ def initialize opts
15
+ fail unless opts.is_a? Hash
16
+ @opts = opts
17
+ return unless @opts[:host] # for testcases
18
+ @debug = @opts[:debug]
19
+ @cookie = @opts[:cookie]
20
+ @sso = @opts[:sso]
21
+ @operation_id = @opts[:operation_id]
22
+ @lock = Mutex.new
23
+ @http = nil
24
+ restart_http
25
+ end
26
+
27
+ def host
28
+ @opts[:host]
29
+ end
30
+
31
+ def close
32
+ @http.finish rescue IOError
33
+ end
34
+
35
+ def restart_http
36
+ begin
37
+ @http.finish if @http
38
+ rescue Exception => ex
39
+ puts "WARNING: Ignoring exception: #{ex.message}"
40
+ puts ex.backtrace.join("\n")
41
+ end
42
+ @http = Net::HTTP.new(@opts[:host], @opts[:port], @opts[:proxyHost], @opts[:proxyPort])
43
+ if @opts[:ssl]
44
+ require 'net/https'
45
+ @http.use_ssl = true
46
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @opts[:insecure]
47
+ @http.ca_file = @opts[:ca_file] if @opts[:ca_file]
48
+ @http.cert = OpenSSL::X509::Certificate.new(@opts[:cert]) if @opts[:cert]
49
+ @http.key = OpenSSL::PKey::RSA.new(@opts[:key]) if @opts[:key]
50
+ end
51
+ @http.set_debug_output(STDERR) if $DEBUG
52
+ @http.read_timeout = @opts[:read_timeout] || 1000000
53
+ @http.open_timeout = @opts[:open_timeout] || 60
54
+ def @http.on_connect
55
+ @socket.io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
56
+ end
57
+ @http.start
58
+ end
59
+
60
+ def soap_envelope
61
+ xsd = 'http://www.w3.org/2001/XMLSchema'
62
+ env = 'http://schemas.xmlsoap.org/soap/envelope/'
63
+ xsi = 'http://www.w3.org/2001/XMLSchema-instance'
64
+ xml = Builder::XmlMarkup.new :indent => 0
65
+ xml.tag!('env:Envelope', 'xmlns:xsd' => xsd, 'xmlns:env' => env, 'xmlns:xsi' => xsi) do
66
+ if @vcSessionCookie || @operation_id
67
+ xml.tag!('env:Header') do
68
+ xml.tag!('vcSessionCookie', @vcSessionCookie) if @vcSessionCookie
69
+ xml.tag!('operationID', @operation_id) if @operation_id
70
+ end
71
+ end
72
+
73
+ xml.tag!('env:Body') do
74
+ yield xml if block_given?
75
+ end
76
+ end
77
+ xml
78
+ end
79
+
80
+ def request action, body
81
+ headers = { 'content-type' => 'text/xml; charset=utf-8', 'SOAPAction' => action }
82
+ headers['cookie'] = @cookie if @cookie
83
+
84
+ if @debug
85
+ $stderr.puts "Request:"
86
+ $stderr.puts body
87
+ $stderr.puts
88
+ end
89
+
90
+ if @cookie.nil? && @sso
91
+ @sso.request_token unless @sso.assertion_id
92
+ body = @sso.sign_request(body)
93
+ end
94
+
95
+ start_time = Time.now
96
+ response = @lock.synchronize do
97
+ begin
98
+ @http.request_post(@opts[:path], body, headers)
99
+ rescue Exception
100
+ restart_http
101
+ raise
102
+ end
103
+ end
104
+ end_time = Time.now
105
+
106
+ if response.is_a? Net::HTTPServiceUnavailable
107
+ raise "Got HTTP 503: Service unavailable"
108
+ end
109
+
110
+ self.cookie = response['set-cookie'] if response.key? 'set-cookie'
111
+
112
+ nk = Nokogiri(response.body)
113
+
114
+ if @debug
115
+ $stderr.puts "Response (in #{'%.3f' % (end_time - start_time)} s)"
116
+ $stderr.puts nk
117
+ $stderr.puts
118
+ end
119
+
120
+ [nk.xpath('//soapenv:Body/*').select(&:element?).first, response.body.size]
121
+ end
122
+ end
@@ -0,0 +1,138 @@
1
+ # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ require 'set'
5
+ require 'monitor'
6
+
7
+ module RbVmomi
8
+
9
+ class TypeLoader
10
+ def initialize fn, extension_dirs, namespace
11
+ @extension_dirs = extension_dirs
12
+ @namespace = namespace
13
+ @lock = Monitor.new
14
+ @db = {}
15
+ @id2wsdl = {}
16
+ @loaded = {}
17
+ add_types Hash[BasicTypes::BUILTIN.map { |k| [k,nil] }]
18
+ vmodl_database = File.open(fn, 'r') { |io| Marshal.load io }
19
+ vmodl_database.reject! { |k,v| k =~ /^_/ }
20
+ add_types vmodl_database
21
+ preload
22
+ end
23
+
24
+ def preload
25
+ names = (@namespace.constants + Object.constants).map(&:to_s).uniq.
26
+ select { |x| has? x }
27
+ names.each { |x| get(x) }
28
+ end
29
+
30
+ # Reload all extensions for loaded VMODL types
31
+ def reload_extensions
32
+ @extension_dirs.each do |path|
33
+ reload_extensions_dir path
34
+ end
35
+ end
36
+
37
+ # Reload all extensions for loaded VMODL types from the given directory
38
+ def reload_extensions_dir path
39
+ loaded = Set.new(typenames.select { |x| @namespace.const_defined? x })
40
+ Dir.open(path) do |dir|
41
+ dir.each do |file|
42
+ next unless file =~ /\.rb$/
43
+ next unless loaded.member? $`
44
+ file_path = File.join(dir, file)
45
+ load file_path
46
+ end
47
+ end
48
+ end
49
+
50
+ def has? name
51
+ fail unless name.is_a? String
52
+
53
+ @db.member?(name) or BasicTypes::BUILTIN.member?(name)
54
+ end
55
+
56
+ def get name
57
+ fail "name '#{name}' is #{name.class} expecting String" unless name.is_a? String
58
+
59
+ first_char = name[0].chr
60
+ if first_char.downcase == first_char
61
+ name = "%s%s" % [first_char.upcase, name[1..-1]]
62
+ end
63
+
64
+ return @loaded[name] if @loaded.member? name
65
+ @lock.synchronize do
66
+ return @loaded[name] if @loaded.member? name
67
+ klass = make_type(name)
68
+ @namespace.const_set name, klass
69
+ load_extension name
70
+ @loaded[name] = klass
71
+ end
72
+ end
73
+
74
+ def add_types types
75
+ @lock.synchronize do
76
+ @db.merge! types
77
+ @db = Hash[@db.map do |name, value|
78
+ if value
79
+ value['wsdl_name'] ||= name
80
+ end
81
+ first_char = name[0].chr
82
+ if first_char.downcase == first_char
83
+ name = "%s%s" % [first_char.upcase, name[1..-1]]
84
+ end
85
+ [name, value]
86
+ end]
87
+ end
88
+ end
89
+
90
+ def typenames
91
+ @db.keys
92
+ end
93
+
94
+ private
95
+
96
+ def load_extension name
97
+ @extension_dirs.map { |x| File.join(x, "#{name}.rb") }.
98
+ select { |x| File.exist? x }.
99
+ each { |x| load x }
100
+ end
101
+
102
+ def make_type name
103
+ name = name.to_s
104
+ return BasicTypes.const_get(name) if BasicTypes::BUILTIN.member? name
105
+ desc = @db[name] or fail "unknown VMODL type #{name}"
106
+ case desc['kind']
107
+ when 'data' then make_data_type name, desc
108
+ when 'managed' then make_managed_type name, desc
109
+ when 'enum' then make_enum_type name, desc
110
+ else fail desc.inspect
111
+ end
112
+ end
113
+
114
+ def make_data_type name, desc
115
+ superclass = get desc['wsdl_base']
116
+ Class.new(superclass).tap do |klass|
117
+ klass.init name, desc['props']
118
+ klass.wsdl_name = desc['wsdl_name']
119
+ end
120
+ end
121
+
122
+ def make_managed_type name, desc
123
+ superclass = get desc['wsdl_base']
124
+ Class.new(superclass).tap do |klass|
125
+ klass.init name, desc['props'], desc['methods']
126
+ klass.wsdl_name = desc['wsdl_name']
127
+ end
128
+ end
129
+
130
+ def make_enum_type name, desc
131
+ Class.new(BasicTypes::Enum).tap do |klass|
132
+ klass.init name, desc['values']
133
+ klass.wsdl_name = desc['wsdl_name']
134
+ end
135
+ end
136
+ end
137
+
138
+ end