rbvmomi2 3.0.0

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