occi-api 4.0.0.alpha.1 → 4.0.0.alpha.2

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.
data/lib/occi-api.rb CHANGED
@@ -8,7 +8,6 @@ module Occi::Api; end
8
8
  require 'occi/api/version'
9
9
  require 'occi/api/client/client_base'
10
10
  require 'occi/api/client/errors'
11
- require 'occi/api/client/http/authn_plugins'
12
11
  require 'occi/api/client/client_http'
13
12
  require 'occi/api/client/client_amqp'
14
13
  require 'occi/api/dsl'
@@ -196,17 +196,15 @@ module Occi
196
196
  # @param [String] resource name or resource identifier
197
197
  # @return [Occi::Core::Resource] new resource instance
198
198
  def get_resource(resource_type)
199
-
200
199
  Occi::Log.debug("Instantiating #{resource_type} ...")
201
200
 
202
- type_id = nil
203
- if @model.get_by_id resource_type
201
+ type_id = if @model.get_by_id resource_type
204
202
  # we got a resource type identifier
205
- type_id = resource_type
203
+ resource_type
206
204
  else
207
205
  # we got a resource type name
208
206
  type_ids = @model.kinds.select { |kind| kind.term == resource_type }
209
- type_id = type_ids.first.type_identifier if type_ids.any?
207
+ type_ids.first.type_identifier if type_ids.any?
210
208
  end
211
209
 
212
210
  raise "Unknown resource type! [#{resource_type}]" unless type_id
@@ -320,97 +318,72 @@ module Occi
320
318
  # @param [Boolean] should we describe the mixin or return its link?
321
319
  # @return [String, Occi::Collection, nil] link, mixin description or nothing found
322
320
  def find_mixin(name, type = nil, describe = false)
323
-
324
321
  Occi::Log.debug("Looking for mixin #{name} + #{type} + #{describe}")
325
-
326
- # is type valid?
327
322
  raise "Unknown mixin type! [#{type}]" if type && !@mixins.has_key?(type.to_sym)
328
323
 
329
324
  # TODO: extend this code to support multiple matches and regex filters
330
325
  # should we look for links or descriptions?
331
- if describe
332
- # we are looking for descriptions
333
- find_mixin_describe name, type
334
- else
335
- # we are looking for links
336
- find_mixin_list name, type
337
- end
326
+ describe ? describe_mixin(name, type) : list_mixin(name, type)
338
327
  end
339
328
 
340
329
  # Looks up a mixin using its name and, optionally, a type as well.
341
330
  # Will return mixin's full description.
342
331
  #
343
332
  # @example
344
- # client.find_mixin "debian6"
333
+ # client.describe_mixin "debian6"
345
334
  # # => #<Occi::Collection>
346
- # client.find_mixin "debian6", "os_tpl"
335
+ # client.describe_mixin "debian6", "os_tpl"
347
336
  # # => #<Occi::Collection>
348
- # client.find_mixin "large", "resource_tpl"
337
+ # client.describe_mixin "large", "resource_tpl"
349
338
  # # => #<Occi::Collection>
350
- # client.find_mixin "debian6", "resource_tpl" # => nil
339
+ # client.describe_mixin "debian6", "resource_tpl" # => nil
351
340
  #
352
341
  # @param [String] name of the mixin
353
342
  # @param [String] type of the mixin
354
343
  # @return [Occi::Collection, nil] mixin description or nothing found
355
- def find_mixin_describe(name, type = nil)
356
- found_ary = []
357
-
358
- if type
359
- # get the first match from either os_tpls or resource_tpls
360
- case type
361
- when "os_tpl"
362
- found_ary = get_os_templates.select { |mixin| mixin.term == name }
363
- when "resource_tpl"
364
- found_ary = get_resource_templates.select { |template| template.term == name }
365
- else
366
- # TODO: should raise an Error?
367
- end
368
- else
369
- # try in os_tpls first
370
- found_ary = get_os_templates.select { |os| os.term == name }
344
+ def describe_mixin(name, type = nil)
345
+ found_ary = type ? describe_mixin_w_type(name, type) : describe_mixin_wo_type(name)
346
+ found_ary.any? ? found_ary.first : nil
347
+ end
371
348
 
372
- # then try in resource_tpls
349
+ #
350
+ #
351
+ #
352
+ def describe_mixin_w_type(name, type)
353
+ return unless %w( os_tpl resource_tpl ).include? type.to_s
354
+ send("get_#{type.to_s}s".to_sym).select { |mixin| mixin.term == name }
355
+ end
373
356
 
374
- found_ary = get_resource_templates.select {
375
- |template| template.term == name
376
- } unless found_ary.any?
357
+ #
358
+ #
359
+ #
360
+ def describe_mixin_wo_type(name)
361
+ %w( os_tpl resource_tpl ).each do |type|
362
+ found = send("get_#{type}s".to_sym).select { |mixin| mixin.term == name }
363
+ return found if found.any?
377
364
  end
378
365
 
379
- found_ary.any? ? found_ary.first : nil
366
+ []
380
367
  end
381
368
 
382
369
  # Looks up a mixin using its name and, optionally, a type as well.
383
370
  # Will return mixin's full location.
384
371
  #
385
372
  # @example
386
- # client.find_mixin "debian6"
373
+ # client.list_mixin "debian6"
387
374
  # # => "http://my.occi.service/occi/infrastructure/os_tpl#debian6"
388
- # client.find_mixin "debian6", "os_tpl"
375
+ # client.list_mixin "debian6", "os_tpl"
389
376
  # # => "http://my.occi.service/occi/infrastructure/os_tpl#debian6"
390
- # client.find_mixin "large", "resource_tpl"
377
+ # client.list_mixin "large", "resource_tpl"
391
378
  # # => "http://my.occi.service/occi/infrastructure/resource_tpl#large"
392
- # client.find_mixin "debian6", "resource_tpl" # => nil
379
+ # client.list_mixin "debian6", "resource_tpl" # => nil
393
380
  #
394
381
  # @param [String] name of the mixin
395
382
  # @param [String] type of the mixin
396
383
  # @return [String, nil] link or nothing found
397
- def find_mixin_list(name, type = nil)
398
- # prefix mixin name with '#' to simplify the search
399
- mxns = []
400
- name_rev = "##{name}".reverse
401
-
402
- if type
403
- # return the first match with the selected type
404
- mxns = @mixins[type.to_sym].select {
405
- |mixin| mixin.to_s.reverse.start_with? name_rev
406
- }
407
- else
408
- # there is no type preference, return first global match
409
- mxns = @mixins.flatten(2).select {
410
- |mixin| mixin.to_s.reverse.start_with? name_rev
411
- }
412
- end
413
-
384
+ def list_mixin(name, type = nil)
385
+ mxns = type ? @mixins[type.to_sym] : @mixins.flatten(2)
386
+ mxns = mxns.select { |mixin| mixin.to_s.reverse.start_with? "##{name}".reverse }
414
387
  mxns.any? ? mxns.first : nil
415
388
  end
416
389
 
@@ -433,18 +406,11 @@ module Occi
433
406
  if type
434
407
  # is type valid?
435
408
  raise "Unknown mixin type! #{type}" unless @mixins.has_key? type.to_sym
436
-
437
- # return mixin of the selected type
438
409
  @mixins[type.to_sym]
439
410
  else
440
411
  # we did not get a type, return all mixins
441
412
  mixins = []
442
-
443
- # flatten the hash and remove its keys
444
- get_mixin_types.each do |ltype|
445
- mixins.concat @mixins[ltype.to_sym]
446
- end
447
-
413
+ get_mixin_types.each { |ltype| mixins.concat @mixins[ltype.to_sym] }
448
414
  mixins
449
415
  end
450
416
  end
@@ -472,7 +438,7 @@ module Occi
472
438
  identifiers = []
473
439
 
474
440
  get_mixin_types.each do |mixin_type|
475
- identifiers << 'http://schemas.ogf.org/occi/infrastructure#' + mixin_type
441
+ identifiers << "http://schemas.ogf.org/occi/infrastructure##{mixin_type}"
476
442
  end
477
443
 
478
444
  identifiers
@@ -487,6 +453,7 @@ module Occi
487
453
  def get_os_templates
488
454
  @model.get.mixins.select { |mixin| mixin.related.select { |rel| rel.end_with? 'os_tpl' }.any? }
489
455
  end
456
+ alias_method :get_os_tpls, :get_os_templates
490
457
 
491
458
  # Retrieves available resource_tpls from the model.
492
459
  #
@@ -497,6 +464,7 @@ module Occi
497
464
  def get_resource_templates
498
465
  @model.get.mixins.select { |mixin| mixin.related.select { |rel| rel.end_with? 'resource_tpl' }.any? }
499
466
  end
467
+ alias_method :get_resource_tpls, :get_resource_templates
500
468
 
501
469
  # Creates a link of a specified kind and binds it to the given resource.
502
470
  #
@@ -611,7 +579,7 @@ module Occi
611
579
  #
612
580
  # @param [Hash] logger options
613
581
  def set_logger(log_options)
614
- if log_options[:logger].nil? || (not log_options[:logger].kind_of? Occi::Log)
582
+ unless log_options[:logger] && log_options[:logger].kind_of?(Occi::Log)
615
583
  @logger = Occi::Log.new(log_options[:out])
616
584
  @logger.level = log_options[:level]
617
585
  end
@@ -626,7 +594,7 @@ module Occi
626
594
  # @param [String] endpoint URI in a non-canonical string
627
595
  # @return [String] canonical endpoint URI in a string, with a trailing slash
628
596
  def set_endpoint(endpoint)
629
- raise 'Endpoint not a valid URI' if (endpoint =~ URI::ABS_URI).nil?
597
+ raise 'Endpoint not a valid URI' unless (endpoint =~ URI::ABS_URI)
630
598
  @endpoint = endpoint.chomp('/') + '/'
631
599
  end
632
600
 
@@ -644,7 +612,7 @@ module Occi
644
612
 
645
613
  @mixins = {
646
614
  :os_tpl => get_os_tpl_mixins_ary,
647
- :resource_tpl => get_res_tpl_mixins_ary
615
+ :resource_tpl => get_resource_tpl_mixins_ary
648
616
  }
649
617
 
650
618
  @model
@@ -654,32 +622,30 @@ module Occi
654
622
  #
655
623
  #
656
624
  def get_os_tpl_mixins_ary
657
- os_tpls = []
658
-
659
- get_os_templates.each do |os_tpl|
660
- unless os_tpl.nil? || os_tpl.type_identifier.nil?
661
- tid = os_tpl.type_identifier.strip
662
- os_tpls << tid unless tid.empty?
663
- end
664
- end
625
+ get_mixins_ary(:os_tpl)
626
+ end
665
627
 
666
- os_tpls
628
+ #
629
+ #
630
+ #
631
+ def get_resource_tpl_mixins_ary
632
+ get_mixins_ary(:resource_tpl)
667
633
  end
668
634
 
669
635
  #
670
636
  #
671
637
  #
672
- def get_res_tpl_mixins_ary
673
- res_tpls = []
638
+ def get_mixins_ary(mixin_type)
639
+ mixins = []
640
+
641
+ send("get_#{mixin_type.to_s}s".to_sym).each do |mixin|
642
+ next if mixin.nil? || mixin.type_identifier.nil?
674
643
 
675
- get_resource_templates.each do |res_tpl|
676
- unless res_tpl.nil? || res_tpl.type_identifier.nil?
677
- tid = res_tpl.type_identifier.strip
678
- res_tpls << tid unless tid.empty?
679
- end
644
+ tid = mixin.type_identifier.strip
645
+ mixins << tid unless tid.empty?
680
646
  end
681
647
 
682
- res_tpls
648
+ mixins
683
649
  end
684
650
 
685
651
  end
@@ -2,7 +2,8 @@ require 'httparty'
2
2
 
3
3
  require 'occi/api/client/http/net_http_fix'
4
4
  require 'occi/api/client/http/httparty_fix'
5
- require 'occi/api/client/http/authn_utils'
5
+ require 'occi/api/client/authn_utils'
6
+ require 'occi/api/client/http/authn_plugins'
6
7
 
7
8
  module Occi
8
9
  module Api
@@ -323,7 +324,9 @@ module Occi
323
324
  entity_type = Occi::Core::Link if kind.related_to? Occi::Core::Link
324
325
  end
325
326
 
326
- Occi::Log.debug "Parser call: #{response.content_type} #{entity_type} #{path.include?('-/')}"
327
+ entity_type = Occi::Core::Resource unless entity_type
328
+
329
+ Occi::Log.debug "Parser call: #{response.content_type} #{path.include?('-/')} #{entity_type} #{response.headers.inspect}"
327
330
  collection = Occi::Parser.parse(response.content_type, response.body, path.include?('-/'), entity_type, response.headers)
328
331
 
329
332
  Occi::Log.debug "Parsed collection: empty? #{collection.empty?}"
@@ -378,7 +381,13 @@ module Occi
378
381
  collection.resources.first.location if collection.resources.first
379
382
  end
380
383
  when 201
381
- Occi::Parser.locations(response.header["content-type"].split(";").first, response.body, response.headers).first
384
+ # TODO: OCCI-OS hack, look for header Location instead of uri-list
385
+ # This should be probably implemented in Occi::Parser.locations
386
+ if response.header['location']
387
+ response.header['location']
388
+ else
389
+ Occi::Parser.locations(response.header["content-type"].split(";").first, response.body, response.header).first
390
+ end
382
391
  else
383
392
  raise "HTTP POST failed! #{response_msg}"
384
393
  end
@@ -481,6 +490,7 @@ module Occi
481
490
  Occi::Log.debug e.message
482
491
 
483
492
  if @authn_plugin.fallbacks.any?
493
+ # TODO: multiple fallbacks
484
494
  @auth_options[:original_type] = @auth_options[:type]
485
495
  @auth_options[:type] = @authn_plugin.fallbacks.first
486
496
 
@@ -16,7 +16,7 @@ module Occi::Api::Client
16
16
  def setup(options = {}); end
17
17
 
18
18
  def authenticate(options = {})
19
- response = @env_ref.class.head @env_ref.endpoint
19
+ response = @env_ref.class.head "#{@env_ref.endpoint}-/"
20
20
  raise ::Occi::Api::Client::Errors::AuthnError, "Authentication failed with code #{response.code.to_s}!" unless response.success?
21
21
  end
22
22
 
@@ -5,25 +5,50 @@ module Occi::Api::Client
5
5
  class Keystone < Base
6
6
 
7
7
  def setup(options = {})
8
- response = @env_ref.class.head @env_ref.endpoint
8
+ # get Keystone URL if possible, get unscoped token
9
+ set_keystone_base_url
10
+ set_auth_token
11
+
12
+ # use unscoped token for tenant discovery, get scoped token
13
+ tenant = get_prefered_tenant
14
+ set_auth_token(tenant)
15
+ end
16
+
17
+ def authenticate(options = {})
18
+ # OCCI-OS doesn't support HEAD method!
19
+ response = @env_ref.class.get "#{@env_ref.endpoint}-/"
20
+ raise ::Occi::Api::Client::Errors::AuthnError, "Authentication failed with code #{response.code.to_s}!" unless response.success?
21
+ end
22
+
23
+ private
24
+
25
+ def set_keystone_base_url
26
+ response = @env_ref.class.head "#{@env_ref.endpoint}-/"
9
27
  Occi::Log.debug response.inspect
10
28
 
11
29
  return if response.success?
12
30
  raise ::Occi::Api::Client::Errors::AuthnError, "Keystone AuthN failed with #{response.code.to_s}!" unless response.code == 401
13
31
 
14
32
  unless response.headers['www-authenticate'] && response.headers['www-authenticate'].start_with?('Keystone')
15
- raise ::Occi::Api::Client::Errors::AuthnError, "Target endpoint is probably not OpenStack!"
33
+ raise ::Occi::Api::Client::Errors::AuthnError, "Target endpoint is probably not OpenStack, fallback failed!"
16
34
  end
17
35
 
18
- keystone_uri = /^Keystone uri='(.+)'$/.match(response.headers['www-authenticate'])[1]
36
+ @keystone_url = /^Keystone uri='(.+)'$/.match(response.headers['www-authenticate'])[1]
37
+ raise ::Occi::Api::Client::Errors::AuthnError, "Unable to get Keystone's URL from the response!" unless @keystone_url
19
38
 
20
- raise ::Occi::Api::Client::Errors::AuthnError, "Unable to get Keystone's URL from the response!" unless keystone_uri
39
+ @keystone_url = @keystone_url.chomp('/')
40
+ end
21
41
 
42
+ def set_auth_token(tenant = nil)
22
43
  headers = @env_ref.class.headers.clone
23
44
  headers['Content-Type'] = "application/json"
24
45
  headers['Accept'] = headers['Content-Type']
25
46
 
26
- response = @env_ref.class.post(keystone_uri + "/v2.0/tokens", :body => get_keystone_req, :headers => headers)
47
+ response = @env_ref.class.post(
48
+ "#{@keystone_url}/v2.0/tokens",
49
+ :body => get_keystone_req(tenant),
50
+ :headers => headers
51
+ )
27
52
  Occi::Log.debug response.inspect
28
53
 
29
54
  if response.success?
@@ -33,9 +58,7 @@ module Occi::Api::Client
33
58
  end
34
59
  end
35
60
 
36
- private
37
-
38
- def get_keystone_req(json = true)
61
+ def get_keystone_req(tenant = nil)
39
62
  if @options[:original_type] == "x509"
40
63
  body = { "auth" => { "voms" => true } }
41
64
  elsif @options[:username] && @options[:password]
@@ -51,7 +74,27 @@ module Occi::Api::Client
51
74
  raise ::Occi::Api::Client::Errors::AuthnError, "Unable to request a token from Keystone! Chosen AuthN not supported."
52
75
  end
53
76
 
54
- json ? body.to_json : body
77
+ body['auth']['tenantName'] = tenant if tenant && !tenant.empty?
78
+ body.to_json
79
+ end
80
+
81
+ def get_prefered_tenant(match = nil)
82
+ headers = @env_ref.class.headers.clone
83
+ headers['Content-Type'] = "application/json"
84
+ headers['Accept'] = headers['Content-Type']
85
+
86
+ response = @env_ref.class.get(
87
+ "#{@keystone_url}/v2.0/tenants",
88
+ :headers => headers
89
+ )
90
+ Occi::Log.debug response.inspect
91
+
92
+ # TODO: impl match with regexp in case of multiple tenants?
93
+ raise ::Occi::Api::Client::Errors::AuthnError, "Keystone didn't return any tenants!" unless response['tenants'] && response['tenants'].first
94
+ tenant = response['tenants'].first['name'] if response.success?
95
+ raise ::Occi::Api::Client::Errors::AuthnError, "Unable to get a tenant from Keystone!" unless tenant
96
+
97
+ tenant
55
98
  end
56
99
 
57
100
  end
@@ -3,44 +3,20 @@ module HTTParty
3
3
 
4
4
  private
5
5
 
6
- def attach_ssl_certificates(http, options)
7
- if http.use_ssl?
8
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
9
-
10
- # Client certificate authentication
11
- if options[:pem]
12
- http.cert = OpenSSL::X509::Certificate.new(options[:pem])
13
- http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
14
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
15
- end
16
-
17
- # Set chain of client certificates
18
- if options[:ssl_extra_chain_cert]
19
- http.extra_chain_cert = []
20
-
21
- options[:ssl_extra_chain_cert].each do |p_ca|
22
- http.extra_chain_cert << OpenSSL::X509::Certificate.new(p_ca)
23
- end
24
- end
6
+ alias_method :old_attach_ssl_certificates, :attach_ssl_certificates
25
7
 
26
- # SSL certificate authority file and/or directory
27
- if options[:ssl_ca_file]
28
- http.ca_file = options[:ssl_ca_file]
29
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
30
- end
8
+ def attach_ssl_certificates(http, options)
9
+ old_attach_ssl_certificates(http, options)
31
10
 
32
- if options[:ssl_ca_path]
33
- http.ca_path = options[:ssl_ca_path]
34
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
35
- end
11
+ # Set chain of client certificates
12
+ if options[:ssl_extra_chain_cert]
13
+ http.extra_chain_cert = []
36
14
 
37
- # This is only Ruby 1.9+
38
- if options[:ssl_version] && http.respond_to?(:ssl_version=)
39
- http.ssl_version = options[:ssl_version]
15
+ options[:ssl_extra_chain_cert].each do |p_ca|
16
+ http.extra_chain_cert << OpenSSL::X509::Certificate.new(p_ca)
40
17
  end
41
18
  end
42
19
  end
43
-
44
20
  end
45
21
 
46
22
  module ClassMethods