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

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