ovirt-engine-sdk 4.0.0.alpha11 → 4.0.0.alpha12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/ovirtsdk4c/ov_xml_reader.c +85 -54
- data/ext/ovirtsdk4c/ov_xml_writer.c +116 -63
- data/lib/ovirtsdk4.rb +1 -1
- data/lib/ovirtsdk4/http.rb +300 -41
- data/lib/ovirtsdk4/readers.rb +338 -4
- data/lib/ovirtsdk4/service.rb +5 -8
- data/lib/ovirtsdk4/services.rb +1386 -3336
- data/lib/ovirtsdk4/types.rb +3228 -1412
- data/lib/ovirtsdk4/version.rb +1 -1
- data/lib/ovirtsdk4/writers.rb +134 -2
- metadata +16 -2
data/lib/ovirtsdk4.rb
CHANGED
data/lib/ovirtsdk4/http.rb
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
#++
|
16
16
|
|
17
17
|
require 'curb'
|
18
|
+
require 'json'
|
18
19
|
require 'uri'
|
19
20
|
|
20
21
|
module OvirtSDK4
|
@@ -27,7 +28,6 @@ module OvirtSDK4
|
|
27
28
|
class Request
|
28
29
|
attr_accessor :method
|
29
30
|
attr_accessor :path
|
30
|
-
attr_accessor :matrix
|
31
31
|
attr_accessor :query
|
32
32
|
attr_accessor :headers
|
33
33
|
attr_accessor :body
|
@@ -39,7 +39,6 @@ module OvirtSDK4
|
|
39
39
|
self.method = opts[:method] || :GET
|
40
40
|
self.path = opts[:path] || ''
|
41
41
|
self.headers = opts[:headers] || {}
|
42
|
-
self.matrix = opts[:matrix] || {}
|
43
42
|
self.query = opts[:query] || {}
|
44
43
|
self.body = opts[:body]
|
45
44
|
end
|
@@ -78,6 +77,21 @@ module OvirtSDK4
|
|
78
77
|
##
|
79
78
|
# Creates a new connection to the API server.
|
80
79
|
#
|
80
|
+
# Note that all the parameters with names starting with `sso` are intended for use with external authentication
|
81
|
+
# services, using the http://oauth.net/2/[OAuth2] protocol. But the typical usage doesn't require them, as they
|
82
|
+
# are automatically calculated to use the authentication service that is part of the engine. A typical connection
|
83
|
+
# can be created specifying just the `url`, `username`, `password` and `ca_file` parameters:
|
84
|
+
#
|
85
|
+
# [source,ruby]
|
86
|
+
# ----
|
87
|
+
# connection = OvirtSDK4::Connection.new(
|
88
|
+
# :url => 'https://engine.example.com/ovirt-engine/api',
|
89
|
+
# :username => 'admin@internal',
|
90
|
+
# :password => '...',
|
91
|
+
# :ca_file => '/etc/pki/ovirt-engine/ca.pem',
|
92
|
+
# )
|
93
|
+
# ----
|
94
|
+
#
|
81
95
|
# @param opts [Hash] The options used to create the connection.
|
82
96
|
#
|
83
97
|
# @option opts [String] :url A string containing the base URL of the server, usually something like
|
@@ -87,10 +101,10 @@ module OvirtSDK4
|
|
87
101
|
#
|
88
102
|
# @option opts [String] :password The password of the user.
|
89
103
|
#
|
90
|
-
# @option opts [Boolean] :insecure (
|
104
|
+
# @option opts [Boolean] :insecure (false) A boolean flag that indicates if the server TLS certificate and host
|
91
105
|
# name should be checked.
|
92
106
|
#
|
93
|
-
# @option opts [String] :ca_file The name of a
|
107
|
+
# @option opts [String] :ca_file The name of a PEM file containing the trusted CA certificates. The certificate
|
94
108
|
# presented by the server will be verified using these CA certificates.
|
95
109
|
#
|
96
110
|
# @option opts [Boolean] :debug (false) A boolean flag indicating if debug output should be generated. If the
|
@@ -105,11 +119,49 @@ module OvirtSDK4
|
|
105
119
|
# @option opts [Boolean] :kerberos (false) A boolean flag indicating if Kerberos uthentication should be used
|
106
120
|
# instead of the default basic authentication.
|
107
121
|
#
|
108
|
-
# @option opts [
|
122
|
+
# @option opts [Integer] :timeout (0) The maximun total time to wait for the response, in seconds. A value of zero
|
109
123
|
# (the default) means wait for ever. If the timeout expires before the response is received an exception will be
|
110
124
|
# raised.
|
111
125
|
#
|
112
|
-
|
126
|
+
# @option opts [Boolean] :compress (false) A boolean flag indicating if the SDK should ask the server to send
|
127
|
+
# compressed responses. Note that this is a hint for the server, and that it may return uncompressed data even
|
128
|
+
# when this parameter is set to `true`.
|
129
|
+
#
|
130
|
+
# @option opts [String] :sso_url A string containing the base URL of the authentication service. This needs to be
|
131
|
+
# specified only when using an external authentication service. By default this URL is automatically calculated
|
132
|
+
# from the value of the `url` parameter, so that authentication will be performed using the authentication
|
133
|
+
# service that is part of the engine.
|
134
|
+
#
|
135
|
+
# @option opts [String] :sso_revoke_url A string containing the base URL of the SSO revoke service. This needs to be
|
136
|
+
# specified only when using an external authentication service. By default this URL is automatically calculated
|
137
|
+
# from the value of the `url` parameter, so that SSO token revoke will be performed using the SSO service that
|
138
|
+
# is part of the engine.
|
139
|
+
#
|
140
|
+
# @option opts [Boolean] :sso_insecure A boolean flag that indicates if the SSO server TLS certificate and
|
141
|
+
# host name should be checked. Default is value of `insecure`.
|
142
|
+
#
|
143
|
+
# @option opts [String] :sso_ca_file The name of a PEM file containing the trusted CA certificates. The
|
144
|
+
# certificate presented by the SSO server will be verified using these CA certificates. Default is value of
|
145
|
+
# `ca_file`.
|
146
|
+
#
|
147
|
+
# @option opts [Boolean] :sso_debug A boolean flag indicating if SSO debug output should be generated. If the
|
148
|
+
# values is `true` all the data sent to and received from the SSO server will be written to `$stdout`. Be aware
|
149
|
+
# that user names and passwords will also be written, so handle it with care. Default is value of `debug`.
|
150
|
+
#
|
151
|
+
# @option opts [String, IO] :sso_log The log file where the SSO debug output will be written. The value can be a
|
152
|
+
# string containing a file name or an IO object. If it is a file name then the file will be created if it doesn't
|
153
|
+
# exist, and the SSO debug output will be added to the end. The file will be closed when the connection is closed.
|
154
|
+
# If it is an IO object then the SSO debug output will be written directly, and it won't be closed. Default is
|
155
|
+
# value of `log`.
|
156
|
+
#
|
157
|
+
# @option opts [Boolean] :sso_timeout The maximun total time to wait for the SSO response, in seconds. A value
|
158
|
+
# of zero means wait for ever. If the timeout expires before the SSO response is received an exception will be
|
159
|
+
# raised. Default is value of `timeout`.
|
160
|
+
#
|
161
|
+
# @option opts [String] :sso_token_name (access_token) The token name in the JSON SSO response returned from the SSO
|
162
|
+
# server. Default value is `access_token`
|
163
|
+
#
|
164
|
+
def initialize(opts = {})
|
113
165
|
# Get the values of the parameters and assign default values:
|
114
166
|
url = opts[:url]
|
115
167
|
username = opts[:username]
|
@@ -120,6 +172,15 @@ module OvirtSDK4
|
|
120
172
|
log = opts[:log]
|
121
173
|
kerberos = opts[:kerberos] || false
|
122
174
|
timeout = opts[:timeout] || 0
|
175
|
+
compress = opts[:compress] || false
|
176
|
+
sso_url = opts[:sso_url]
|
177
|
+
sso_revoke_url = opts[:sso_revoke_url]
|
178
|
+
sso_insecure = opts[:sso_insecure] || insecure
|
179
|
+
sso_ca_file = opts[:sso_ca_file] || ca_file
|
180
|
+
sso_debug = opts[:sso_debug] || debug
|
181
|
+
sso_log = opts[:sso_log] || log
|
182
|
+
sso_timeout = opts[:sso_timeout] || timeout
|
183
|
+
sso_token_name = opts[:sso_token_name] || 'access_token'
|
123
184
|
|
124
185
|
# Check mandatory parameters:
|
125
186
|
if url.nil?
|
@@ -129,25 +190,23 @@ module OvirtSDK4
|
|
129
190
|
# Save the URL:
|
130
191
|
@url = URI(url)
|
131
192
|
|
193
|
+
# Save SSO parameters:
|
194
|
+
@sso_url = sso_url
|
195
|
+
@sso_revoke_url = sso_revoke_url
|
196
|
+
@username = username
|
197
|
+
@password = password
|
198
|
+
@kerberos = kerberos
|
199
|
+
@sso_insecure = sso_insecure
|
200
|
+
@sso_ca_file = sso_ca_file
|
201
|
+
@sso_log_file = sso_log
|
202
|
+
@sso_debug = sso_debug
|
203
|
+
@sso_timeout = sso_timeout
|
204
|
+
@log_file = log
|
205
|
+
@sso_token_name = sso_token_name
|
206
|
+
|
132
207
|
# Create the cURL handle:
|
133
208
|
@curl = Curl::Easy.new
|
134
209
|
|
135
|
-
# Configure cookies so that they are enabled but stored only in memory:
|
136
|
-
@curl.enable_cookies = true
|
137
|
-
@curl.cookiefile = '/dev/null'
|
138
|
-
@curl.cookiejar = '/dev/null'
|
139
|
-
|
140
|
-
# Configure authentication:
|
141
|
-
if kerberos
|
142
|
-
@curl.http_auth_types = :gssnegotiate
|
143
|
-
@curl.username = ''
|
144
|
-
@curl.password = ''
|
145
|
-
else
|
146
|
-
@curl.http_auth_types = :basic
|
147
|
-
@curl.username = username
|
148
|
-
@curl.password = password
|
149
|
-
end
|
150
|
-
|
151
210
|
# Configure TLS parameters:
|
152
211
|
if @url.scheme == 'https'
|
153
212
|
if insecure
|
@@ -165,6 +224,12 @@ module OvirtSDK4
|
|
165
224
|
# Configure the timeout:
|
166
225
|
@curl.timeout = timeout
|
167
226
|
|
227
|
+
# Configure compression of responses (setting the value to a zero length string means accepting all the
|
228
|
+
# compression types that libcurl supports):
|
229
|
+
if compress
|
230
|
+
@curl.encoding = ''
|
231
|
+
end
|
232
|
+
|
168
233
|
# Configure debug mode:
|
169
234
|
@close_log = false
|
170
235
|
if debug
|
@@ -241,12 +306,17 @@ module OvirtSDK4
|
|
241
306
|
#
|
242
307
|
# @api private
|
243
308
|
#
|
244
|
-
def send(request
|
309
|
+
def send(request)
|
310
|
+
|
311
|
+
# Check if we already have an SSO access token:
|
312
|
+
if @sso_token.nil?
|
313
|
+
@sso_token = get_access_token
|
314
|
+
end
|
315
|
+
|
245
316
|
# Build the URL:
|
246
317
|
@curl.url = build_url({
|
247
318
|
:path => request.path,
|
248
319
|
:query => request.query,
|
249
|
-
:matrix => request.matrix,
|
250
320
|
})
|
251
321
|
|
252
322
|
# Add headers, avoiding those that have no value:
|
@@ -256,11 +326,7 @@ module OvirtSDK4
|
|
256
326
|
@curl.headers['Version'] = '4'
|
257
327
|
@curl.headers['Content-Type'] = 'application/xml'
|
258
328
|
@curl.headers['Accept'] = 'application/xml'
|
259
|
-
|
260
|
-
# All requests except the last one should indicate that we want to use persistent authentication:
|
261
|
-
if !last
|
262
|
-
@curl.headers['Prefer'] = 'persistent-auth'
|
263
|
-
end
|
329
|
+
@curl.headers['Authorization'] = 'Bearer ' + @sso_token
|
264
330
|
|
265
331
|
# Send the request and wait for the response:
|
266
332
|
case request.method
|
@@ -283,6 +349,206 @@ module OvirtSDK4
|
|
283
349
|
return response
|
284
350
|
end
|
285
351
|
|
352
|
+
##
|
353
|
+
# Obtains the access token from SSO to be used for Bearer authentication.
|
354
|
+
#
|
355
|
+
# @return [String] The URL.
|
356
|
+
#
|
357
|
+
# @api private
|
358
|
+
#
|
359
|
+
def get_access_token
|
360
|
+
# If SSO url is not supplied build default one:
|
361
|
+
if @sso_url.nil?
|
362
|
+
@sso_url = URI(build_sso_auth_url)
|
363
|
+
else
|
364
|
+
@sso_url = URI(@sso_url)
|
365
|
+
end
|
366
|
+
|
367
|
+
sso_response = get_sso_response(@sso_url)
|
368
|
+
|
369
|
+
if sso_response.is_a?(Array)
|
370
|
+
sso_response = sso_response[0]
|
371
|
+
end
|
372
|
+
|
373
|
+
if !sso_response["error"].nil?
|
374
|
+
raise Error.new("Error during SSO authentication #{sso_response['error_code']} : #{sso_response['error']}")
|
375
|
+
end
|
376
|
+
|
377
|
+
return sso_response[@sso_token_name]
|
378
|
+
end
|
379
|
+
|
380
|
+
##
|
381
|
+
# Revoke the SSO access token.
|
382
|
+
#
|
383
|
+
# @api private
|
384
|
+
#
|
385
|
+
def revoke_access_token
|
386
|
+
# If SSO revoke url is not supplied build default one:
|
387
|
+
if @sso_revoke_url.nil?
|
388
|
+
@sso_revoke_url = URI(build_sso_revoke_url)
|
389
|
+
else
|
390
|
+
@sso_revoke_url = URI(@sso_revoke_url)
|
391
|
+
end
|
392
|
+
|
393
|
+
sso_response = get_sso_response(@sso_revoke_url)
|
394
|
+
|
395
|
+
if sso_response.is_a?(Array)
|
396
|
+
sso_response = sso_response[0]
|
397
|
+
end
|
398
|
+
|
399
|
+
if !sso_response["error"].nil?
|
400
|
+
raise Error.new("Error during SSO revoke #{sso_response['error_code']} : #{sso_response['error']}")
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
##
|
405
|
+
# Execute a get request to the SSO server and return the response.
|
406
|
+
#
|
407
|
+
# @return [Hash] The JSON response.
|
408
|
+
#
|
409
|
+
# @api private
|
410
|
+
#
|
411
|
+
def get_sso_response(sso_base_url)
|
412
|
+
# Create the cURL handle for SSO:
|
413
|
+
sso_curl = Curl::Easy.new
|
414
|
+
|
415
|
+
# Configure the timeout:
|
416
|
+
sso_curl.timeout = @sso_timeout
|
417
|
+
|
418
|
+
# Configure debug mode:
|
419
|
+
sso_close_log = false
|
420
|
+
if @sso_debug
|
421
|
+
if @sso_log_file.nil?
|
422
|
+
sso_log = STDOUT
|
423
|
+
elsif @sso_log_file == @log_file
|
424
|
+
sso_log = @log
|
425
|
+
elsif @sso_log_file.is_a?(String)
|
426
|
+
sso_log = ::File.open(@sso_log_file, 'a')
|
427
|
+
sso_close_log = true
|
428
|
+
else
|
429
|
+
sso_log = @sso_log_file
|
430
|
+
end
|
431
|
+
sso_curl.verbose = true
|
432
|
+
sso_curl.on_debug do |type, data|
|
433
|
+
case type
|
434
|
+
when Curl::CURLINFO_DATA_IN
|
435
|
+
prefix = '< '
|
436
|
+
when Curl::CURLINFO_DATA_OUT
|
437
|
+
prefix = '> '
|
438
|
+
when Curl::CURLINFO_HEADER_IN
|
439
|
+
prefix = '< '
|
440
|
+
when Curl::CURLINFO_HEADER_OUT
|
441
|
+
prefix = '> '
|
442
|
+
else
|
443
|
+
prefix = '* '
|
444
|
+
end
|
445
|
+
lines = data.gsub("\r\n", "\n").strip.split("\n")
|
446
|
+
lines.each do |line|
|
447
|
+
sso_log.puts(prefix + line)
|
448
|
+
end
|
449
|
+
sso_log.flush
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
begin
|
454
|
+
# Configure TLS parameters:
|
455
|
+
if sso_base_url.scheme == 'https'
|
456
|
+
if @sso_insecure
|
457
|
+
sso_curl.ssl_verify_peer = false
|
458
|
+
sso_curl.ssl_verify_host = false
|
459
|
+
elsif @sso_ca_file.nil?
|
460
|
+
raise ArgumentError.new("The \"sso_ca_file\" argument is mandatory when using TLS.")
|
461
|
+
elsif not ::File.file?(@sso_ca_file)
|
462
|
+
raise ArgumentError.new("The CA file \"#{@sso_ca_file}\" doesn't exist.")
|
463
|
+
else
|
464
|
+
sso_curl.cacert = @sso_ca_file
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
# The username and password parameters:
|
469
|
+
params = {}
|
470
|
+
|
471
|
+
# The base SSO URL:
|
472
|
+
sso_url = sso_base_url.to_s
|
473
|
+
|
474
|
+
# Configure authentication:
|
475
|
+
if @kerberos
|
476
|
+
sso_curl.http_auth_types = :gssnegotiate
|
477
|
+
sso_curl.username = ''
|
478
|
+
sso_curl.password = ''
|
479
|
+
else
|
480
|
+
sso_curl.http_auth_types = :basic
|
481
|
+
sso_curl.username = @username
|
482
|
+
sso_curl.password = @password
|
483
|
+
if sso_url.index('?').nil?
|
484
|
+
sso_url += '?'
|
485
|
+
end
|
486
|
+
params['username'] = @username
|
487
|
+
params['password'] = @password
|
488
|
+
sso_url = sso_url + '&' + URI.encode_www_form(params)
|
489
|
+
end
|
490
|
+
|
491
|
+
# Build the SSO access_token request url:
|
492
|
+
sso_curl.url = sso_url
|
493
|
+
|
494
|
+
# Add headers:
|
495
|
+
sso_curl.headers['User-Agent'] = "RubySDK/#{VERSION}"
|
496
|
+
sso_curl.headers['Accept'] = 'application/json'
|
497
|
+
|
498
|
+
# Request access token:
|
499
|
+
sso_curl.http_get
|
500
|
+
|
501
|
+
# Parse and return the JSON response:
|
502
|
+
return JSON.parse(sso_curl.body_str)
|
503
|
+
ensure
|
504
|
+
sso_curl.close
|
505
|
+
# Close the log file, if we did open it:
|
506
|
+
if sso_close_log
|
507
|
+
sso_log.close
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
##
|
513
|
+
# Builds a request URL to acquire the access token from SSO. The URLS are different for basic auth and Kerberos,
|
514
|
+
# @return [String] The URL.
|
515
|
+
#
|
516
|
+
# @api private
|
517
|
+
#
|
518
|
+
def build_sso_auth_url
|
519
|
+
# Get the base URL:
|
520
|
+
sso_url = @url.to_s[0..@url.to_s.rindex('/')]
|
521
|
+
|
522
|
+
# The SSO access scope:
|
523
|
+
scope = 'ovirt-app-api'
|
524
|
+
|
525
|
+
# Set the grant type and entry point to request from SSO:
|
526
|
+
if @kerberos
|
527
|
+
grant_type = 'urn:ovirt:params:oauth:grant-type:http'
|
528
|
+
entry_point = 'token-http-auth'
|
529
|
+
else
|
530
|
+
grant_type = 'password'
|
531
|
+
entry_point = 'token'
|
532
|
+
end
|
533
|
+
|
534
|
+
# Build and return the SSO URL:
|
535
|
+
return "#{sso_url}sso/oauth/#{entry_point}?grant_type=#{grant_type}&scope=#{scope}"
|
536
|
+
end
|
537
|
+
|
538
|
+
##
|
539
|
+
# Builds a request URL to revoke the SSO access token.
|
540
|
+
# @return [String] The URL.
|
541
|
+
#
|
542
|
+
# @api private
|
543
|
+
#
|
544
|
+
def build_sso_revoke_url
|
545
|
+
# Get the base URL:
|
546
|
+
sso_url = @url.to_s[0..@url.to_s.rindex('/')]
|
547
|
+
|
548
|
+
# Build and return the SSO revoke URL:
|
549
|
+
return "#{sso_url}services/sso-logout?scope=&token=#{@sso_token}"
|
550
|
+
end
|
551
|
+
|
286
552
|
##
|
287
553
|
# Tests the connectivity with the server. If connectivity works correctly it returns `true`. If there is any
|
288
554
|
# connectivity problem it will either return `false` or raise an exception if the `raise_exception` parameter is
|
@@ -347,7 +613,10 @@ module OvirtSDK4
|
|
347
613
|
request = Request.new({
|
348
614
|
:method => :HEAD,
|
349
615
|
})
|
350
|
-
send(request
|
616
|
+
send(request)
|
617
|
+
|
618
|
+
# Revoke the SSO access token:
|
619
|
+
revoke_access_token
|
351
620
|
|
352
621
|
# Close the log file, if we did open it:
|
353
622
|
if @close_log
|
@@ -359,7 +628,7 @@ module OvirtSDK4
|
|
359
628
|
end
|
360
629
|
|
361
630
|
##
|
362
|
-
# Builds a request URL from a path, and the
|
631
|
+
# Builds a request URL from a path, and the set of query parameters.
|
363
632
|
#
|
364
633
|
# @params opts [Hash] The options used to build the URL.
|
365
634
|
#
|
@@ -369,10 +638,6 @@ module OvirtSDK4
|
|
369
638
|
# keys of the hash should be strings containing the names of the parameters, and the values should be strings
|
370
639
|
# containing the values.
|
371
640
|
#
|
372
|
-
# @option opts [Hash<String, String>] :matrix ({}) A hash containing the matrix parameters to add to the URL. The
|
373
|
-
# keys of the hash should be strings containing the names of the parameters, and the values should be strings
|
374
|
-
# containing the values.
|
375
|
-
#
|
376
641
|
# @return [String] The URL.
|
377
642
|
#
|
378
643
|
# @api private
|
@@ -381,15 +646,9 @@ module OvirtSDK4
|
|
381
646
|
# Get the values of the parameters and assign default values:
|
382
647
|
path = opts[:path] || ''
|
383
648
|
query = opts[:query] || {}
|
384
|
-
matrix = opts[:matrix] || {}
|
385
649
|
|
386
650
|
# Add the path and the parameters:
|
387
651
|
url = @url.to_s + path
|
388
|
-
if not matrix.empty?
|
389
|
-
matrix.each do |key, value|
|
390
|
-
url = url + ';' + URI.encode_www_form({key => value})
|
391
|
-
end
|
392
|
-
end
|
393
652
|
if not query.empty?
|
394
653
|
url = url + '?' + URI.encode_www_form(query)
|
395
654
|
end
|