openstack 2.2.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 884339fc18898d36fca662e853c156bb029b193f
4
- data.tar.gz: f85e37571dc9182651aec8019cec200b023d3de7
3
+ metadata.gz: 019f3ab58603e2185aa4558273454869205efb90
4
+ data.tar.gz: d6a1995446cc59cf255d03354552738e81d3efeb
5
5
  SHA512:
6
- metadata.gz: c237b9cf749c80d7147c954fb2239032dd5dffed0eeca36f87874bb549147c36713c7b851673a38c2c01e2ebbd6c4791f76553075c3d1c367672261bd5bc4515
7
- data.tar.gz: a18111ee6f10c9137e983f1afa4a0ae3d32504b80704f005bd037115283ba8a64f869935e30274db322bdd7ae87361d0a74cadec143cb7f7502ab1c760983580
6
+ metadata.gz: cde951d618983f94dc1e808d60240647ef5ead95d6d345fbd9e5b25929a0ad89742f279e5d2d9e00082ec6c71bce816ba4573fb1b5b8d9f9c24d9d6d94c519e0
7
+ data.tar.gz: 2eab3097fbd3de0458141ecbd06a4d55cb949369ef46df26220c9e3e07eac1c82b7dafca2cd04a9ee9779b983c07d1cb019ad34b4be4a918e38d5bcd0debce1c
data/README.md CHANGED
@@ -1,18 +1,33 @@
1
1
  # Ruby OpenStack
2
2
 
3
- Ruby OpenStack Compute, Object-Store, Block Storage and Network bindings for the OpenStack API. It supports Keystone Authentication (v1.0 and v2.0), Nova and Swift. See the [getting started](https://github.com/ruby-openstack/ruby-openstack/wiki) for documentation.
3
+ Ruby OpenStack Compute, Object-Store, Block Storage and Network bindings for the OpenStack API. It supports Keystone Authentication (v1.0, v2.0 and v3.0), Nova and Swift. See the [getting started](https://github.com/ruby-openstack/ruby-openstack/wiki) for documentation.
4
4
 
5
- [![Build Status](https://travis-ci.org/ruby-openstack/ruby-openstack.svg?branch=next)](https://travis-ci.org/ruby-openstack/ruby-openstack)
5
+ [![Build Status](https://travis-ci.org/ruby-openstack/ruby-openstack.svg?branch=master)](https://travis-ci.org/ruby-openstack/ruby-openstack)
6
+
7
+ ## Installation
8
+
9
+ Add the Ruby OpenStack as dependency to your project Gemfile:
10
+
11
+ ```
12
+ source 'https://rubygems.org'
13
+ gem 'openstack'
14
+ ```
15
+
16
+ ## Maintainer
17
+
18
+ The library is currently maintained by [Nitrado](http://nitrado.net/).
19
+ The people behind the OpenStack Team from Nitrado are Alexander Birkner and Aaron Fischer.
6
20
 
7
21
  ## Authors
8
22
 
23
+ * Alexander Birkner (alexander.birkner@marbis.net)
24
+ * Aaron Fischer (aaron.fischer@marbis.net)
9
25
  * Marios Andreou (marios@redhat.com)
10
26
  * Dan Prince (dprince@redhat.com)
11
27
  * Naveed Massjouni (naveedm9@gmail.com)
12
- * Aaron Fischer (aaron.fischer@marbis.net)
13
- * Alexander Birkner (alexander.birkner@marbis.net)
14
28
 
15
- Initial code checkin on May 23rd 2012 - code refactored from and based on the Rackspace [Cloud Servers gem](https://github.com/rackspace/ruby-openstack-compute) and [Rackspace Cloud Files gem](https://github.com/rackspace/ruby-cloudfiles). Since Nov 2015 maintained by [Marbis GmbH](http://nitrado.net/).
29
+ Initial code checkin on May 23rd 2012 - code refactored from and based on the Rackspace [Cloud Servers gem](https://github.com/rackspace/ruby-openstack-compute) and [Rackspace Cloud Files gem](https://github.com/rackspace/ruby-cloudfiles).
30
+ Since Nov 2015 the library is maintained by [Nitrado](http://nitrado.net/).
16
31
 
17
32
  ## License
18
33
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.0
1
+ 3.0.0
@@ -117,7 +117,9 @@ module Compute
117
117
  # => "NewServerSHMGpvI"
118
118
  def create_server(options)
119
119
  raise OpenStack::Exception::MissingArgument, "Server name, flavorRef, and imageRef, must be supplied" unless (options[:name] && options[:flavorRef] && options[:imageRef])
120
- options[:personality] = Personalities.get_personality(options[:personality])
120
+ if options[:personality]
121
+ options[:personality] = Personalities.get_personality(options[:personality])
122
+ end
121
123
  options[:security_groups] = (options[:security_groups] || []).inject([]){|res, c| res << {"name"=>c} ;res}
122
124
  data = JSON.generate(:server => options)
123
125
  response = @connection.csreq("POST",@connection.service_host,"#{@connection.service_path}/servers",@connection.service_port,@connection.service_scheme,{'content-type' => 'application/json'},data)
@@ -125,7 +127,7 @@ module Compute
125
127
  server_info = JSON.parse(response.body)['server']
126
128
  server = OpenStack::Compute::Server.new(self,server_info['id'])
127
129
  server.adminPass = server_info['adminPass']
128
- return server
130
+ server
129
131
  end
130
132
 
131
133
  # Returns an array of hashes listing available server images that you have access too,
@@ -508,12 +510,18 @@ module Compute
508
510
  true
509
511
  end
510
512
 
511
- def get_floating_ip_polls
513
+ def get_floating_ip_pools
512
514
  check_extension 'os-floating-ip-pools'
513
515
  response = @connection.req('GET', '/os-floating-ip-pools')
514
516
  JSON.parse(response.body)['floating_ip_pools']
515
517
  end
516
518
 
519
+ #deprecated - please do not use this typo method :)
520
+ def get_floating_ip_polls
521
+ puts "get_floating_ip_polls() is DEPRECATED: Please use get_floating_ip_pools() without typo!"
522
+ self.get_floating_ip_pools
523
+ end
524
+
517
525
  def get_floating_ips_bulk
518
526
  check_extension 'os-floating-ips-bulk'
519
527
  response = @connection.req('GET', '/os-floating-ips-bulk')
@@ -22,6 +22,14 @@ class Connection
22
22
  attr_reader :auth_path
23
23
  attr_reader :service_name
24
24
  attr_reader :service_type
25
+ attr_reader :user_domain
26
+ attr_reader :user_domain_id
27
+ attr_reader :project_id
28
+ attr_reader :project_name
29
+ attr_reader :project_domain_name
30
+ attr_reader :project_domain_id
31
+ attr_reader :domain_name
32
+ attr_reader :domain_id
25
33
  attr_reader :proxy_host
26
34
  attr_reader :proxy_port
27
35
  attr_reader :ca_cert
@@ -45,14 +53,23 @@ class Connection
45
53
  #
46
54
  # options hash:
47
55
  #
48
- # :auth_method - Type of authentication - 'password', 'key', 'rax-kskey' - defaults to 'password'
49
56
  # :username - Your OpenStack username or public key, depending on auth_method. *required*
57
+ # :auth_method - Type of authentication - 'password', 'key', 'rax-kskey', 'token' - defaults to 'password'.
58
+ # For auth v3.0 valid options are 'password', 'token', 'password_user_id'
50
59
  # :authtenant_name OR :authtenant_id - Your OpenStack tenant name or id *required*. Defaults to username.
51
60
  # passing :authtenant will default to using that parameter as tenant name.
52
61
  # :api_key - Your OpenStack API key *required* (either private key or password, depending on auth_method)
53
62
  # :auth_url - Configurable auth_url endpoint.
54
63
  # :service_name - (Optional for v2.0 auth only). The optional name of the compute service to use.
55
64
  # :service_type - (Optional for v2.0 auth only). Defaults to "compute"
65
+ # :user_domain - (Optional for v3.0 auth only). The optional name of the user domain.
66
+ # :user_domain_id - (Optional for v3.0 auth only). Defaults to "default"
67
+ # :project_id - (Optional for v3.0 auth only). For authorization scoping
68
+ # :project_name - (Optional for v3.0 auth only). For authorization scoping
69
+ # :project_domain_name - (Optional for v3.0 auth only). For authorization scoping
70
+ # :project_domain_id - (Optional for v3.0 auth only). For authorization scoping
71
+ # :domain_name - (Optional for v3.0 auth only). For authorization scoping
72
+ # :domain_id - (Optional for v3.0 auth only). For authorization scoping
56
73
  # :region - (Optional for v2.0 auth only). The specific service region to use. Defaults to first returned region.
57
74
  # :retry_auth - Whether to retry if your auth token expires (defaults to true)
58
75
  # :proxy_host - If you need to connect through a proxy, supply the hostname here
@@ -60,6 +77,7 @@ class Connection
60
77
  # :ca_cert - path to a CA chain in PEM format
61
78
  # :ssl_version - explicitly set an version (:SSLv3 etc, see OpenSSL::SSL::SSLContext::METHODS)
62
79
  # :is_debug - Only for development purpose for debug output
80
+ # :endpoint_type - Type of endpoint. Optional. 'publicURL', 'internalURL', 'adminURL'
63
81
  #
64
82
  # The options hash is used to create a new OpenStack::Connection object
65
83
  # (private constructor) and this is passed to the constructor of OpenStack::Compute::Connection
@@ -98,6 +116,14 @@ class Connection
98
116
  @auth_method = options[:auth_method] || "password"
99
117
  @service_name = options[:service_name] || nil
100
118
  @service_type = options[:service_type] || "compute"
119
+ @user_domain_id = options[:user_domain_id] || "default"
120
+ @user_domain = options[:user_domain] || nil
121
+ @project_id = options[:project_id] || nil
122
+ @project_name = options[:project_name] || nil
123
+ @project_domain_name = options[:project_domain_name] || nil
124
+ @project_domain_id = options[:project_domain_id] || nil
125
+ @domain_name = options[:domain_name] || nil
126
+ @domain_id = options[:domain_id] || nil
101
127
  @region = options[:region] || @region = nil
102
128
  @regions_list = {} # this is populated during authentication - from the returned service catalogue
103
129
  @is_debug = options[:is_debug]
@@ -144,10 +170,10 @@ class Connection
144
170
  start_http(server,path,port,scheme,hdrhash)
145
171
  response = @http[server].request(request)
146
172
  if @is_debug
147
- puts "REQUEST: PUT => #{path}"
148
- puts data if data
149
- puts "RESPONSE: #{response.body}"
150
- puts '----------------------------------------'
173
+ puts "REQUEST: PUT => #{path}"
174
+ puts data if data
175
+ puts "RESPONSE: #{response.body}"
176
+ puts '----------------------------------------'
151
177
  end
152
178
  raise OpenStack::Exception::ExpiredAuthToken if response.code == "401"
153
179
  response
@@ -185,10 +211,10 @@ class Connection
185
211
  response = @http[server].request(request)
186
212
  end
187
213
  if @is_debug
188
- puts "REQUEST: #{auth_method} => #{path}"
189
- puts data if data
190
- puts "RESPONSE: #{response.body}"
191
- puts '----------------------------------------'
214
+ puts "REQUEST: #{method.to_s} => #{path}"
215
+ puts data if data
216
+ puts "RESPONSE: #{response.body}"
217
+ puts '----------------------------------------'
192
218
  end
193
219
  raise OpenStack::Exception::ExpiredAuthToken if response.code == "401"
194
220
  response
@@ -264,64 +290,119 @@ class Connection
264
290
  end
265
291
  end
266
292
 
267
- end #end class Connection
293
+ end #end class Connection
268
294
 
269
- #============================
270
- # OpenStack::Authentication
271
- #============================
295
+ #============================
296
+ # OpenStack::Authentication
297
+ #============================
272
298
 
273
- class Authentication
299
+ class Authentication
274
300
 
275
- # Performs an authentication to the OpenStack auth server.
276
- # If it succeeds, it sets the service_host, service_path, service_port,
277
- # service_scheme, authtoken, and authok variables on the connection.
278
- # If it fails, it raises an exception.
301
+ # Performs an authentication to the OpenStack auth server.
302
+ # If it succeeds, it sets the service_host, service_path, service_port,
303
+ # service_scheme, authtoken, and authok variables on the connection.
304
+ # If it fails, it raises an exception.
279
305
 
280
- def self.init(conn)
281
- if conn.auth_path =~ /.*v2.0\/?$/
282
- AuthV20.new(conn)
283
- else
284
- AuthV10.new(conn)
306
+ def self.init(conn)
307
+ if conn.auth_path =~ /.*v3\/?$/
308
+ AuthV30.new(conn)
309
+ elsif conn.auth_path =~ /.*v2.0\/?$/
310
+ AuthV20.new(conn)
311
+ else
312
+ AuthV10.new(conn)
313
+ end
285
314
  end
286
315
  end
287
316
 
288
- end
317
+ private
318
+
319
+ class AuthV10
320
+ def initialize(connection)
321
+ tries = connection.retries
322
+ time = 3
289
323
 
290
- private
291
- class AuthV20
292
- attr_reader :uri
293
- attr_reader :version
294
- def initialize(connection)
295
-
296
- tries = connection.retries
297
- time = 3
298
-
299
- begin
300
- server = Net::HTTP::Proxy(connection.proxy_host, connection.proxy_port).new(connection.auth_host, connection.auth_port)
301
- if connection.auth_scheme == "https"
302
- server.use_ssl = true
303
- server.verify_mode = OpenSSL::SSL::VERIFY_NONE
304
-
305
- # use the ca_cert if were given one, and make sure we verify!
306
- if !connection.ca_cert.nil?
307
- server.ca_file = connection.ca_cert
308
- server.verify_mode = OpenSSL::SSL::VERIFY_PEER
324
+ hdrhash = { "X-Auth-User" => connection.authuser, "X-Auth-Key" => connection.authkey }
325
+ begin
326
+ server = Net::HTTP::Proxy(connection.proxy_host, connection.proxy_port).new(connection.auth_host, connection.auth_port)
327
+ if connection.auth_scheme == "https"
328
+ server.use_ssl = true
329
+ server.verify_mode = OpenSSL::SSL::VERIFY_NONE
330
+
331
+ # use the ca_cert if were given one, and make sure to verify!
332
+ if !connection.ca_cert.nil?
333
+ server.ca_file = connection.ca_cert
334
+ server.verify_mode = OpenSSL::SSL::VERIFY_PEER
335
+ end
336
+
337
+ # explicitly set the SSL version to use
338
+ server.ssl_version = connection.ssl_version if !connection.ssl_version.nil?
309
339
  end
340
+ server.start
341
+ rescue
342
+ puts "Can't connect to the server: #{tries} tries to reconnect" if connection.is_debug
343
+ sleep time += 1
344
+ retry unless (tries -= 1) <= 0
345
+ raise OpenStack::Exception::Connection, "Unable to connect to #{server}"
346
+ end
310
347
 
311
- # explicitly set the SSL version to use
312
- server.ssl_version = connection.ssl_version if !connection.ssl_version.nil?
348
+ response = server.get(connection.auth_path, hdrhash)
349
+
350
+ if (response.code =~ /^20./)
351
+ connection.authtoken = response["x-auth-token"]
352
+ case connection.service_type
353
+ when "compute"
354
+ uri = URI.parse(response["x-server-management-url"])
355
+ when "object-store"
356
+ uri = URI.parse(response["x-storage-url"])
357
+ end
358
+ raise OpenStack::Exception::Authentication, "Unexpected Response from #{connection.auth_host} - couldn't get service URLs: \"x-server-management-url\" is: #{response["x-server-management-url"]} and \"x-storage-url\" is: #{response["x-storage-url"]}" if (uri.host.nil? || uri.host=="")
359
+ connection.service_host = uri.host
360
+ connection.service_path = uri.path
361
+ connection.service_port = uri.port
362
+ connection.service_scheme = uri.scheme
363
+ connection.authok = true
364
+ else
365
+ connection.authok = false
366
+ raise OpenStack::Exception::Authentication, "Authentication failed with response code #{response.code}"
313
367
  end
314
- server.start
315
- rescue
316
- puts "Can't connect to the server: #{tries} tries to reconnect" if connection.is_debug
317
- sleep time += 1
318
- retry unless (tries -= 1) <= 0
319
- raise OpenStack::Exception::Connection, "Unable to connect to #{server}"
368
+ server.finish
320
369
  end
370
+ end
371
+
372
+ class AuthV20
373
+ attr_reader :uri
374
+ attr_reader :version
375
+ def initialize(connection)
376
+
377
+ tries = connection.retries
378
+ time = 3
379
+
380
+ begin
381
+ server = Net::HTTP::Proxy(connection.proxy_host, connection.proxy_port).new(connection.auth_host, connection.auth_port)
382
+ if connection.auth_scheme == "https"
383
+ server.use_ssl = true
384
+ server.verify_mode = OpenSSL::SSL::VERIFY_NONE
385
+
386
+ # use the ca_cert if were given one, and make sure we verify!
387
+ if !connection.ca_cert.nil?
388
+ server.ca_file = connection.ca_cert
389
+ server.verify_mode = OpenSSL::SSL::VERIFY_PEER
390
+ end
391
+
392
+ # explicitly set the SSL version to use
393
+ server.ssl_version = connection.ssl_version if !connection.ssl_version.nil?
394
+ end
395
+ server.start
396
+ rescue
397
+ puts "Can't connect to the server: #{tries} tries to reconnect" if connection.is_debug
398
+ sleep time += 1
399
+ retry unless (tries -= 1) <= 0
400
+ raise OpenStack::Exception::Connection, "Unable to connect to #{server}"
401
+ end
321
402
 
322
- @uri = String.new
403
+ @uri = String.new
323
404
 
324
- case connection.auth_method
405
+ case connection.auth_method
325
406
  when "password"
326
407
  auth_data = JSON.generate({ "auth" => { "passwordCredentials" => { "username" => connection.authuser, "password" => connection.authkey }, connection.authtenant[:type] => connection.authtenant[:value]}})
327
408
  when "rax-kskey"
@@ -330,248 +411,330 @@ class AuthV20
330
411
  auth_data = JSON.generate({"auth" => { "apiAccessKeyCredentials" => {"accessKey" => connection.authuser, "secretKey" => connection.authkey}, connection.authtenant[:type] => connection.authtenant[:value]}})
331
412
  else
332
413
  raise Exception::InvalidArgument, "Unrecognized auth method #{connection.auth_method}"
333
- end
414
+ end
334
415
 
335
- response = server.post(connection.auth_path.chomp("/")+"/tokens", auth_data, {'Content-Type' => 'application/json'})
336
- if (response.code =~ /^20./)
337
- resp_data=JSON.parse(response.body)
338
- connection.authtoken = resp_data['access']['token']['id']
339
- implemented_services = resp_data["access"]["serviceCatalog"].inject([]){|res, current| res << current["type"] ;res}
340
- raise OpenStack::Exception::NotImplemented.new("The requested service: \"#{connection.service_type}\" is not present " +
341
- "in the returned service catalogue.", 501, "#{resp_data["access"]["serviceCatalog"]}") unless implemented_services.include?(connection.service_type)
342
- resp_data['access']['serviceCatalog'].each do |service|
343
- service["endpoints"].each do |endpoint|
344
- connection.regions_list[endpoint["region"]] ||= []
345
- connection.regions_list[endpoint["region"]] << {:service=>service["type"], :versionId => endpoint["versionId"]}
346
- end
347
- if connection.service_name
348
- check_service_name = connection.service_name
349
- else
350
- check_service_name = service['name']
351
- end
352
- if service['type'] == connection.service_type and service['name'] == check_service_name
353
- endpoints = service["endpoints"]
354
- if connection.region
355
- endpoints.each do |ep|
356
- if ep["region"] and ep["region"].upcase == connection.region.upcase
357
- @uri = URI.parse(ep[connection.endpoint_type])
358
- end
359
- end
360
- else
361
- @uri = URI.parse(endpoints[0][connection.endpoint_type])
416
+ response = server.post(connection.auth_path.chomp("/")+"/tokens", auth_data, {'Content-Type' => 'application/json'})
417
+ if (response.code =~ /^20./)
418
+ resp_data=JSON.parse(response.body)
419
+ connection.authtoken = resp_data['access']['token']['id']
420
+ implemented_services = resp_data["access"]["serviceCatalog"].inject([]){|res, current| res << current["type"] ;res}
421
+ raise OpenStack::Exception::NotImplemented.new("The requested service: \"#{connection.service_type}\" is not present " +
422
+ "in the returned service catalogue.", 501, "#{resp_data["access"]["serviceCatalog"]}") unless implemented_services.include?(connection.service_type)
423
+ resp_data['access']['serviceCatalog'].each do |service|
424
+ service["endpoints"].each do |endpoint|
425
+ connection.regions_list[endpoint["region"]] ||= []
426
+ connection.regions_list[endpoint["region"]] << {:service=>service["type"], :versionId => endpoint["versionId"]}
362
427
  end
363
- if @uri == ""
364
- raise OpenStack::Exception::Authentication, "No API endpoint for region #{connection.region}"
428
+ if connection.service_name
429
+ check_service_name = connection.service_name
365
430
  else
366
- if @version #already got one version of endpoints
367
- current_version = get_version_from_response(service,connection.endpoint_type)
368
- if @version.to_f > current_version.to_f
369
- next
431
+ check_service_name = service['name']
432
+ end
433
+ if service['type'] == connection.service_type and service['name'] == check_service_name
434
+ endpoints = service["endpoints"]
435
+ if connection.region
436
+ endpoints.each do |ep|
437
+ if ep["region"] and ep["region"].upcase == connection.region.upcase
438
+ @uri = URI.parse(ep[connection.endpoint_type])
439
+ end
440
+ end
441
+ else
442
+ @uri = URI.parse(endpoints[0][connection.endpoint_type])
443
+ end
444
+ if @uri == ""
445
+ raise OpenStack::Exception::Authentication, "No API endpoint for region #{connection.region}"
446
+ else
447
+ if @version #already got one version of endpoints
448
+ current_version = get_version_from_response(service,connection.endpoint_type)
449
+ if @version.to_f > current_version.to_f
450
+ next
451
+ end
370
452
  end
453
+ #grab version to check next time round for multi-version deployments
454
+ @version = get_version_from_response(service,connection.endpoint_type)
455
+ connection.service_host = @uri.host
456
+ connection.service_path = @uri.path
457
+ connection.service_port = @uri.port
458
+ connection.service_scheme = @uri.scheme
459
+ connection.authok = true
371
460
  end
372
- #grab version to check next time round for multi-version deployments
373
- @version = get_version_from_response(service,connection.endpoint_type)
374
- connection.service_host = @uri.host
375
- connection.service_path = @uri.path
376
- connection.service_port = @uri.port
377
- connection.service_scheme = @uri.scheme
378
- connection.authok = true
379
461
  end
380
462
  end
463
+ else
464
+ connection.authtoken = false
465
+ raise OpenStack::Exception::Authentication, "Authentication failed with response code #{response.code}"
381
466
  end
382
- else
383
- connection.authtoken = false
384
- raise OpenStack::Exception::Authentication, "Authentication failed with response code #{response.code}"
467
+ server.finish if server.started?
385
468
  end
386
- server.finish if server.started?
387
- end
388
469
 
389
- def get_version_from_response(service,endpoint_type)
390
- service["endpoints"].first["versionId"] || parse_version_from_endpoint(service["endpoints"].first[endpoint_type])
391
- end
470
+ def get_version_from_response(service,endpoint_type)
471
+ service["endpoints"].first["versionId"] || parse_version_from_endpoint(service["endpoints"].first[endpoint_type])
472
+ end
392
473
 
393
- #IN --> https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/46871569847393
394
- #OUT --> "1.1"
395
- def parse_version_from_endpoint(endpoint)
396
- endpoint.match(/\/v(\d).(\d)/).to_s.sub("/v", "")
474
+ #IN --> https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/46871569847393
475
+ #OUT --> "1.1"
476
+ def parse_version_from_endpoint(endpoint)
477
+ endpoint.match(/\/v(\d).(\d)/).to_s.sub("/v", "")
478
+ end
397
479
  end
398
480
 
399
- end
481
+ # Implement the Identity Version 3.x API
482
+ # http://developer.openstack.org/api-ref-identity-v3.html
483
+ # This is still experimental, so don't use in production.
484
+ class AuthV30
485
+ attr_reader :uri, :version
486
+ # TODO: Parse the version for an endpoint
400
487
 
401
- class AuthV10
488
+ def initialize(connection)
489
+ tries = connection.retries
490
+ time = 3
402
491
 
403
- def initialize(connection)
492
+ begin
493
+ server = Net::HTTP::Proxy(connection.proxy_host, connection.proxy_port).new(connection.auth_host, connection.auth_port)
494
+ if connection.auth_scheme == "https"
495
+ server.use_ssl = true
496
+ server.verify_mode = OpenSSL::SSL::VERIFY_NONE
497
+
498
+ # use the ca_cert if were given one, and make sure we verify!
499
+ unless connection.ca_cert.nil?
500
+ server.ca_file = connection.ca_cert
501
+ server.verify_mode = OpenSSL::SSL::VERIFY_PEER
502
+ end
404
503
 
405
- tries = connection.retries
406
- time = 3
504
+ # explicitly set the SSL version to use
505
+ server.ssl_version = connection.ssl_version unless connection.ssl_version.nil?
506
+ end
507
+ server.start
508
+ rescue
509
+ puts "Can't connect to the server: #{tries} tries to reconnect" if connection.is_debug
510
+ sleep time += 1
511
+ retry unless (tries -= 1) <= 0
512
+ raise OpenStack::Exception::Connection, "Unable to connect to #{server}"
513
+ end
407
514
 
408
- hdrhash = { "X-Auth-User" => connection.authuser, "X-Auth-Key" => connection.authkey }
409
- begin
410
- server = Net::HTTP::Proxy(connection.proxy_host, connection.proxy_port).new(connection.auth_host, connection.auth_port)
411
- if connection.auth_scheme == "https"
412
- server.use_ssl = true
413
- server.verify_mode = OpenSSL::SSL::VERIFY_NONE
515
+ # Build Auth JSON
516
+ auth = { "auth" => { "identity" => {} } }
517
+
518
+ case connection.auth_method.to_sym
519
+ when :password
520
+ auth["auth"]["identity"]["methods"] = ["password"]
521
+ auth["auth"]["identity"]["password"] = { "user" => { "name" => connection.authuser, "password" => connection.authkey } }
522
+ auth["auth"]["identity"]["password"]["user"]["domain"] = connection.user_domain ? { "name" => connection.user_domain } : { "id" => connection.user_domain_id }
523
+ when :password_user_id
524
+ auth["auth"]["identity"]["methods"] = ["password"]
525
+ auth["auth"]["identity"]["password"] = { "user" => { "id" => connection.authuser, "password" => connection.authkey } }
526
+ when :token
527
+ auth["auth"]["identity"]["methods"] = ["token"]
528
+ auth["auth"]["identity"]["token"] = { "id" => connection.authkey }
529
+ else
530
+ raise Exception::InvalidArgument, "Unrecognized auth method #{connection.auth_method}."
531
+ end
414
532
 
415
- # use the ca_cert if were given one, and make sure to verify!
416
- if !connection.ca_cert.nil?
417
- server.ca_file = connection.ca_cert
418
- server.verify_mode = OpenSSL::SSL::VERIFY_PEER
419
- end
533
+ # handle project authentication scope
534
+ if (connection.project_id || connection.project_name) && (connection.project_domain_name || connection.project_domain_id)
535
+ auth["auth"]["scope"] = { "project" => { "domain" => {} } }
536
+ auth["auth"]["scope"]["project"]["name"] = connection.project_name if connection.project_name
537
+ auth["auth"]["scope"]["project"]["id"] = connection.project_id if connection.project_id
538
+ auth["auth"]["scope"]["project"]["domain"]["name"] = connection.project_domain_name if connection.project_domain_name
539
+ auth["auth"]["scope"]["project"]["domain"]["id"] = connection.project_domain_id if connection.project_domain_id
540
+ end
420
541
 
421
- # explicitly set the SSL version to use
422
- server.ssl_version = connection.ssl_version if !connection.ssl_version.nil?
542
+ # handle domain authentication scope
543
+ if connection.domain_name || connection.domain_id
544
+ auth["auth"]["scope"] = { "domain" => {} }
545
+ auth["auth"]["scope"]["domain"]["name"] = connection.domain_name if connection.domain_name
546
+ auth["auth"]["scope"]["domain"]["id"] = connection.domain_id if connection.domain_id
423
547
  end
424
- server.start
425
- rescue
426
- puts "Can't connect to the server: #{tries} tries to reconnect" if connection.is_debug
427
- sleep time += 1
428
- retry unless (tries -= 1) <= 0
429
- raise OpenStack::Exception::Connection, "Unable to connect to #{server}"
430
- end
431
548
 
432
- response = server.get(connection.auth_path, hdrhash)
549
+ response = server.post(connection.auth_path.chomp('/') + '/auth/tokens',
550
+ JSON.generate(auth),
551
+ {'Content-Type' => 'application/json'})
433
552
 
434
- if (response.code =~ /^20./)
435
- connection.authtoken = response["x-auth-token"]
436
- case connection.service_type
437
- when "compute"
438
- uri = URI.parse(response["x-server-management-url"])
439
- when "object-store"
440
- uri = URI.parse(response["x-storage-url"])
553
+ # debugging
554
+ if connection.is_debug
555
+ puts "REQUEST: POST => #{connection.auth_path.chomp('/') + '/auth/tokens'}"
556
+ puts "RESPONSE: #{response.body}"
557
+ puts '----------------------------------------'
441
558
  end
442
- raise OpenStack::Exception::Authentication, "Unexpected Response from #{connection.auth_host} - couldn't get service URLs: \"x-server-management-url\" is: #{response["x-server-management-url"]} and \"x-storage-url\" is: #{response["x-storage-url"]}" if (uri.host.nil? || uri.host=="")
443
- connection.service_host = uri.host
444
- connection.service_path = uri.path
445
- connection.service_port = uri.port
446
- connection.service_scheme = uri.scheme
447
- connection.authok = true
448
- else
449
- connection.authok = false
450
- raise OpenStack::Exception::Authentication, "Authentication failed with response code #{response.code}"
451
- end
452
- server.finish
453
- end
454
559
 
455
- end
560
+ if response.code =~ /^20./
561
+ connection.authtoken = response['X-Subject-Token']
456
562
 
563
+ resp_data=JSON.parse(response.body)
457
564
 
458
- #============================
459
- # OpenStack::Exception
460
- #============================
565
+ catalog = resp_data["token"]["catalog"]
566
+ unless catalog
567
+ raise OpenStack::Exception::Authentication, "No service catalog returned. Maybe your auth request is unscoped. Please check if your selected user has a default project."
568
+ end
461
569
 
462
- class Exception
570
+ # Check if the used service is available
571
+ implemented_services = resp_data["token"]["catalog"].map {|service| service['type']}
572
+ raise OpenStack::Exception::NotImplemented.new("The requested service: \"#{connection.service_type}\" is not present " +
573
+ "in the returned service catalogue.", 501, "#{resp_data["access"]["serviceCatalog"]}") unless implemented_services.include?(connection.service_type)
574
+ catalog.each do |service|
575
+ service["endpoints"].each do |endpoint|
576
+ connection.regions_list[endpoint["region"]] ||= []
577
+ connection.regions_list[endpoint["region"]] << {:service=>service["type"], :versionId => endpoint["versionId"]}
578
+ end
463
579
 
464
- class ComputeError < StandardError
580
+ # set use preset service name if set, otherwise ignore.
581
+ if connection.service_name
582
+ check_service_name = connection.service_name
583
+ else
584
+ check_service_name = service['name']
585
+ end
465
586
 
466
- attr_reader :response_body
467
- attr_reader :response_code
587
+ if service['type'] == connection.service_type and service['name'] == check_service_name
588
+ endpoints = service["endpoints"]
468
589
 
469
- def initialize(message, code, response_body)
470
- @response_code=code
471
- @response_body=response_body
472
- super(message)
473
- end
590
+ # filter endpoints by interfacetype
591
+ interface_type = connection.endpoint_type.gsub('URL','')
592
+ endpoints = endpoints.select {|ep| ep['interface'] == interface_type}
474
593
 
475
- end
594
+ # Select endpoint based on region
595
+ if connection.region
596
+ endpoints.each do |ep|
597
+ if ep["region"] and ep["region"].upcase == connection.region.upcase
598
+ @uri = URI.parse(ep['url'])
599
+ end
600
+ end
601
+ else
602
+ @uri = URI.parse(endpoints[0]['url'])
603
+ end
476
604
 
477
- class ComputeFault < ComputeError # :nodoc:
478
- end
479
- class ServiceUnavailable < ComputeError # :nodoc:
480
- end
481
- class Unauthorized < ComputeError # :nodoc:
482
- end
483
- class BadRequest < ComputeError # :nodoc:
484
- end
485
- class OverLimit < ComputeError # :nodoc:
486
- end
487
- class BadMediaType < ComputeError # :nodoc:
488
- end
489
- class BadMethod < ComputeError # :nodoc:
490
- end
491
- class ItemNotFound < ComputeError # :nodoc:
492
- end
493
- class BuildInProgress < ComputeError # :nodoc:
494
- end
495
- class ServerCapacityUnavailable < ComputeError # :nodoc:
496
- end
497
- class BackupOrResizeInProgress < ComputeError # :nodoc:
498
- end
499
- class ResizeNotAllowed < ComputeError # :nodoc:
500
- end
501
- class NotImplemented < ComputeError # :nodoc:
502
- end
503
- class Other < ComputeError # :nodoc:
504
- end
505
- class ResourceStateConflict < ComputeError # :nodoc:
506
- end
507
- class QuantumError < ComputeError # :nodoc:
605
+ if @uri == ""
606
+ raise OpenStack::Exception::Authentication, "No API endpoint for region #{connection.region}"
607
+ else
608
+ connection.service_host = @uri.host
609
+ connection.service_path = @uri.path
610
+ connection.service_port = @uri.port
611
+ connection.service_scheme = @uri.scheme
612
+ connection.authok = true
613
+ end
614
+ end
615
+ end
616
+ else
617
+ connection.authtoken = false
618
+ raise OpenStack::Exception::Authentication, "Authentication failed with response code #{response.code}"
619
+ end
620
+ server.finish if server.started?
621
+ end
508
622
  end
509
623
 
510
- # Plus some others that we define here
511
624
 
512
- class ExpiredAuthToken < StandardError # :nodoc:
513
- end
514
- class MissingArgument < StandardError # :nodoc:
515
- end
516
- class InvalidArgument < StandardError # :nodoc:
517
- end
518
- class TooManyPersonalityItems < StandardError # :nodoc:
519
- end
520
- class PersonalityFilePathTooLong < StandardError # :nodoc:
521
- end
522
- class PersonalityFileTooLarge < StandardError # :nodoc:
523
- end
524
- class Authentication < StandardError # :nodoc:
525
- end
526
- class Connection < StandardError # :nodoc:
527
- end
625
+ #============================
626
+ # OpenStack::Exception
627
+ #============================
528
628
 
529
- # In the event of a non-200 HTTP status code, this method takes the HTTP response, parses
530
- # the JSON from the body to get more information about the exception, then raises the
531
- # proper error. Note that all exceptions are scoped in the OpenStack::Compute::Exception namespace.
532
- def self.raise_exception(response)
533
- return if response.code =~ /^20.$/
534
- begin
535
- fault = nil
536
- info = nil
537
- if response.body.nil? && response.code == "404" #HEAD ops no body returned
538
- exception_class = self.const_get("ItemNotFound")
539
- raise exception_class.new("The resource could not be found", "404", "")
540
- else
541
- JSON.parse(response.body).each_pair do |key, val|
542
- fault=key
543
- info=val
544
- end
545
- exception_class = self.const_get(fault[0,1].capitalize+fault[1,fault.length])
546
- raise exception_class.new((info["message"] || info), response.code, response.body)
629
+ class Exception
630
+ class ComputeError < StandardError
631
+ attr_reader :response_body
632
+ attr_reader :response_code
633
+
634
+ def initialize(message, code, response_body)
635
+ @response_code=code
636
+ @response_body=response_body
637
+ super(message)
547
638
  end
548
- rescue JSON::ParserError => parse_error
639
+ end
640
+
641
+ class ComputeFault < ComputeError # :nodoc:
642
+ end
643
+ class ServiceUnavailable < ComputeError # :nodoc:
644
+ end
645
+ class Unauthorized < ComputeError # :nodoc:
646
+ end
647
+ class BadRequest < ComputeError # :nodoc:
648
+ end
649
+ class OverLimit < ComputeError # :nodoc:
650
+ end
651
+ class BadMediaType < ComputeError # :nodoc:
652
+ end
653
+ class BadMethod < ComputeError # :nodoc:
654
+ end
655
+ class ItemNotFound < ComputeError # :nodoc:
656
+ end
657
+ class BuildInProgress < ComputeError # :nodoc:
658
+ end
659
+ class ServerCapacityUnavailable < ComputeError # :nodoc:
660
+ end
661
+ class BackupOrResizeInProgress < ComputeError # :nodoc:
662
+ end
663
+ class ResizeNotAllowed < ComputeError # :nodoc:
664
+ end
665
+ class NotImplemented < ComputeError # :nodoc:
666
+ end
667
+ class Other < ComputeError # :nodoc:
668
+ end
669
+ class ResourceStateConflict < ComputeError # :nodoc:
670
+ end
671
+ class QuantumError < ComputeError # :nodoc:
672
+ end
673
+
674
+ # Plus some others that we define here
675
+
676
+ class ExpiredAuthToken < StandardError # :nodoc:
677
+ end
678
+ class MissingArgument < StandardError # :nodoc:
679
+ end
680
+ class InvalidArgument < StandardError # :nodoc:
681
+ end
682
+ class TooManyPersonalityItems < StandardError # :nodoc:
683
+ end
684
+ class PersonalityFilePathTooLong < StandardError # :nodoc:
685
+ end
686
+ class PersonalityFileTooLarge < StandardError # :nodoc:
687
+ end
688
+ class Authentication < StandardError # :nodoc:
689
+ end
690
+ class Connection < StandardError # :nodoc:
691
+ end
692
+
693
+ # In the event of a non-200 HTTP status code, this method takes the HTTP response, parses
694
+ # the JSON from the body to get more information about the exception, then raises the
695
+ # proper error. Note that all exceptions are scoped in the OpenStack::Compute::Exception namespace.
696
+ def self.raise_exception(response)
697
+ return if response.code =~ /^20.$/
698
+ begin
699
+ fault = nil
700
+ info = nil
701
+ if response.body.nil? && response.code == "404" #HEAD ops no body returned
702
+ exception_class = self.const_get("ItemNotFound")
703
+ raise exception_class.new("The resource could not be found", "404", "")
704
+ else
705
+ JSON.parse(response.body).each_pair do |key, val|
706
+ fault=key
707
+ info=val
708
+ end
709
+ exception_class = self.const_get(fault[0,1].capitalize+fault[1,fault.length])
710
+ raise exception_class.new((info["message"] || info), response.code, response.body)
711
+ end
712
+ rescue JSON::ParserError => parse_error
549
713
  deal_with_faulty_error(response, parse_error)
550
- rescue NameError
551
- raise OpenStack::Exception::Other.new("The server returned status #{response.code}", response.code, response.body)
714
+ rescue NameError
715
+ raise OpenStack::Exception::Other.new("The server returned status #{response.code}", response.code, response.body)
716
+ end
552
717
  end
553
- end
554
718
 
555
- private
719
+ private
556
720
 
557
- #e.g. os.delete("non-existant") ==> response.body is:
558
- # "404 Not Found\n\nThe resource could not be found.\n\n "
559
- # which doesn't parse. Deal with such cases here if possible (JSON::ParserError)
560
- def self.deal_with_faulty_error(response, parse_error)
561
- case response.code
562
- when "404"
563
- klass = self.const_get("ItemNotFound")
564
- msg = "The resource could not be found"
565
- when "409"
566
- klass = self.const_get("ResourceStateConflict")
567
- msg = "There was a conflict with the state of the resource"
568
- else
569
- klass = self.const_get("Other")
570
- msg = "Oops - not sure what happened: #{parse_error}"
571
- end
572
- raise klass.new(msg, response.code.to_s, response.body)
721
+ #e.g. os.delete("non-existant") ==> response.body is:
722
+ # "404 Not Found\n\nThe resource could not be found.\n\n "
723
+ # which doesn't parse. Deal with such cases here if possible (JSON::ParserError)
724
+ def self.deal_with_faulty_error(response, parse_error)
725
+ case response.code
726
+ when "404"
727
+ klass = self.const_get("ItemNotFound")
728
+ msg = "The resource could not be found"
729
+ when "409"
730
+ klass = self.const_get("ResourceStateConflict")
731
+ msg = "There was a conflict with the state of the resource"
732
+ else
733
+ klass = self.const_get("Other")
734
+ msg = "Oops - not sure what happened: #{parse_error}"
735
+ end
736
+ raise klass.new(msg, response.code.to_s, response.body)
737
+ end
573
738
  end
574
739
  end
575
740
 
576
- end
577
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openstack
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Prince