ovirt-engine-sdk 4.0.0.alpha11 → 4.0.0.alpha12
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.
- 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
|