occi 3.0.0 → 3.1.0.beta.1

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/ext/mkrf_conf.rb CHANGED
@@ -5,7 +5,8 @@ require 'rubygems/dependency_installer.rb'
5
5
  begin
6
6
  Gem::Command.build_args = ARGV
7
7
  rescue NoMethodError
8
- # do nothing
8
+ # do nothing but warn the user
9
+ warn "Gem::Command doesn't have a method named 'build_args'!"
9
10
  end
10
11
 
11
12
  if defined? RUBY_PLATFORM && RUBY_PLATFORM == "java"
@@ -14,8 +15,8 @@ if defined? RUBY_PLATFORM && RUBY_PLATFORM == "java"
14
15
  begin
15
16
  inst.install "jruby-openssl" if ((defined? JRUBY_VERSION) && (JRUBY_VERSION.split('.')[1].to_i < 7))
16
17
  rescue
17
- # installation failed
18
- exit(1)
18
+ warn "Gem::DependencyInstaller failed to install 'jruby-openssl'!"
19
+ exit
19
20
  end
20
21
  end
21
22
 
@@ -17,16 +17,16 @@ Given /^category filter : (.*)$/ do |category_filter|
17
17
  end
18
18
 
19
19
  Given /^have an initialize Client$/ do
20
- @client = Occi::Api::Client::ClientHttp.new(
21
- @endpoint, #141.5.99.69 #11.5.99.82
22
- { :type => "none" },
23
- { :out => "/dev/null",
24
- :level => Occi::Log::DEBUG },
25
- true,
26
- @accept_type#"text/plain,text/occi"
27
- )
20
+ @client = Occi::Api::Client::ClientHttp.new({
21
+ :endpoint => @endpoint, #141.5.99.69 #11.5.99.82
22
+ :auth => { :type => "none" },
23
+ :log => { :out => "/dev/null",
24
+ :level => Occi::Log::DEBUG },
25
+ :auto_connect => true,
26
+ :media_type => @accept_type#"text/plain,text/occi"
27
+ })
28
28
  end
29
29
 
30
30
  Then /^the Client should have the response code (.*)$/ do |response_code|
31
31
  @client.last_response.code.should == response_code.to_i
32
- end
32
+ end
@@ -67,28 +67,39 @@ module Occi
67
67
  # from the server.
68
68
  #
69
69
  # @example
70
- # Occi::Api::Client::ClientAmqp.new # => #<Occi::Api::Client::ClientAmqp>
70
+ # options = {
71
+ # :endpoint => "http://localhost:3300/",
72
+ # :auth => {:type => "none"},
73
+ # :log => {:out => STDERR, :level => Occi::Log::WARN, :logger => nil},
74
+ # :media_type => "text/plain"
75
+ # }
71
76
  #
72
- # @param [String] endpoint URI
73
- # @param [Hash] auth options in a hash
74
- # @param [Hash] logging options in a hash
75
- # @param [Boolean] enable autoconnect
76
- # @param [String] media type identifier
77
+ # Occi::Api::Client::ClientAmqp.new options # => #<Occi::Api::Client::ClientAmqp>
78
+ #
79
+ # @param [Hash] options, for available options and defaults see examples
77
80
  # @return [Occi::Api::Client::ClientAmqp] client instance
78
- def initialize(endpoint = "http://localhost:3000/", auth_options = { :type => "none" },
79
- log_options = { :out => STDERR, :level => Occi::Log::WARN, :logger => nil },
80
- media_type = "text/plain")
81
+ def initialize(options = {})
82
+
83
+ defaults = {
84
+ :endpoint => "http://localhost:3300/",
85
+ :auth => {:type => "none"},
86
+ :log => {:out => STDERR, :level => Occi::Log::WARN, :logger => nil},
87
+ :media_type => "text/plain"
88
+ }
89
+
90
+ options = options.marshal_dump if options.is_a? OpenStruct
91
+ options = defaults.merge options
81
92
 
82
93
  # check the validity and canonize the endpoint URI
83
- prepare_endpoint endpoint
94
+ prepare_endpoint options[:endpoint]
84
95
 
85
96
  # set Occi::Log
86
- set_logger log_options
97
+ set_logger options[:log]
87
98
 
88
99
  # pass auth options to HTTParty
89
- change_auth auth_options
100
+ change_auth options[:auth]
90
101
 
91
- @media_type = media_type
102
+ @media_type = options[:media_type]
92
103
 
93
104
  Occi::Log.debug("Media Type: #{@media_type}")
94
105
 
@@ -96,7 +107,7 @@ module Occi
96
107
 
97
108
  Thread.new { run }
98
109
 
99
- print "Waiting for connection amqp ..."
110
+ Occi::Log.debug("Waiting for connection amqp ...")
100
111
 
101
112
  #TODO find a better solution for the thread issue
102
113
  while(!@thread_error && !@connected)
@@ -1,7 +1,9 @@
1
1
  require 'rubygems'
2
2
  require 'httparty'
3
+
3
4
  require 'occi/api/client/http/net_http_fix'
4
5
  require 'occi/api/client/http/httparty_fix'
6
+ require 'occi/api/client/http/authn_utils'
5
7
 
6
8
  module Occi
7
9
  module Api
@@ -27,79 +29,93 @@ module Occi
27
29
 
28
30
  # hash mapping HTTP response codes to human-readable messages
29
31
  HTTP_CODES = {
30
- "100" => "Continue",
31
- "101" => "Switching Protocols",
32
- "200" => "OK",
33
- "201" => "Created",
34
- "202" => "Accepted",
35
- "203" => "Non-Authoritative Information",
36
- "204" => "No Content",
37
- "205" => "Reset Content",
38
- "206" => "Partial Content",
39
- "300" => "Multiple Choices",
40
- "301" => "Moved Permanently",
41
- "302" => "Found",
42
- "303" => "See Other",
43
- "304" => "Not Modified",
44
- "305" => "Use Proxy",
45
- "307" => "Temporary Redirect",
46
- "400" => "Bad Request",
47
- "401" => "Unauthorized",
48
- "402" => "Payment Required",
49
- "403" => "Forbidden",
50
- "404" => "Not Found",
51
- "405" => "Method Not Allowed",
52
- "406" => "Not Acceptable",
53
- "407" => "Proxy Authentication Required",
54
- "408" => "Request Time-out",
55
- "409" => "Conflict",
56
- "410" => "Gone",
57
- "411" => "Length Required",
58
- "412" => "Precondition Failed",
59
- "413" => "Request Entity Too Large",
60
- "414" => "Request-URI Too Large",
61
- "415" => "Unsupported Media Type",
62
- "416" => "Requested range not satisfiable",
63
- "417" => "Expectation Failed",
64
- "500" => "Internal Server Error",
65
- "501" => "Not Implemented",
66
- "502" => "Bad Gateway",
67
- "503" => "Service Unavailable",
68
- "504" => "Gateway Time-out",
69
- "505" => "HTTP Version not supported"
32
+ "100" => "Continue",
33
+ "101" => "Switching Protocols",
34
+ "200" => "OK",
35
+ "201" => "Created",
36
+ "202" => "Accepted",
37
+ "203" => "Non-Authoritative Information",
38
+ "204" => "No Content",
39
+ "205" => "Reset Content",
40
+ "206" => "Partial Content",
41
+ "300" => "Multiple Choices",
42
+ "301" => "Moved Permanently",
43
+ "302" => "Found",
44
+ "303" => "See Other",
45
+ "304" => "Not Modified",
46
+ "305" => "Use Proxy",
47
+ "307" => "Temporary Redirect",
48
+ "400" => "Bad Request",
49
+ "401" => "Unauthorized",
50
+ "402" => "Payment Required",
51
+ "403" => "Forbidden",
52
+ "404" => "Not Found",
53
+ "405" => "Method Not Allowed",
54
+ "406" => "Not Acceptable",
55
+ "407" => "Proxy Authentication Required",
56
+ "408" => "Request Time-out",
57
+ "409" => "Conflict",
58
+ "410" => "Gone",
59
+ "411" => "Length Required",
60
+ "412" => "Precondition Failed",
61
+ "413" => "Request Entity Too Large",
62
+ "414" => "Request-URI Too Large",
63
+ "415" => "Unsupported Media Type",
64
+ "416" => "Requested range not satisfiable",
65
+ "417" => "Expectation Failed",
66
+ "500" => "Internal Server Error",
67
+ "501" => "Not Implemented",
68
+ "502" => "Bad Gateway",
69
+ "503" => "Service Unavailable",
70
+ "504" => "Gateway Time-out",
71
+ "505" => "HTTP Version not supported"
70
72
  }
71
73
 
72
74
  # Initializes client data structures and retrieves OCCI model
73
75
  # from the server.
74
76
  #
75
77
  # @example
76
- # Occi::Api::Client::ClientHttp.new # => #<Occi::Api::Client::ClientHttp>
78
+ # options = {
79
+ # :endpoint => "http://localhost:3300/",
80
+ # :auth => {:type => "none"},
81
+ # :log => {:out => STDERR, :level => Occi::Log::WARN, :logger => nil},
82
+ # :auto_connect => "value", auto_connect => true,
83
+ # :media_type => nil
84
+ # }
85
+ #
86
+ # Occi::Api::Client::ClientHttp.new options # => #<Occi::Api::Client::ClientHttp>
77
87
  #
78
- # @param [String] endpoint URI
79
- # @param [Hash] auth options in a hash
80
- # @param [Hash] logging options in a hash
81
- # @param [Boolean] enable autoconnect
82
- # @param [String] media type identifier
88
+ # @param [Hash] options, for available options and defaults see examples
83
89
  # @return [Occi::Api::Client::ClientHttp] client instance
84
- def initialize(endpoint = "http://localhost:3000/", auth_options = {:type => "none"},
85
- log_options = {:out => STDERR, :level => Occi::Log::WARN, :logger => nil},
86
- auto_connect = true, media_type = nil)
90
+ def initialize(options = {})
91
+
92
+ defaults = {
93
+ :endpoint => "http://localhost:3300/",
94
+ :auth => {:type => "none"},
95
+ :log => {:out => STDERR, :level => Occi::Log::WARN, :logger => nil},
96
+ :auto_connect => true,
97
+ :media_type => nil
98
+ }
99
+
100
+ options = options.marshal_dump if options.is_a? OpenStruct
101
+ options = defaults.merge options
102
+
87
103
  # set Occi::Log
88
- set_logger log_options
104
+ set_logger options[:log]
89
105
 
90
106
  # pass auth options to HTTParty
91
- change_auth auth_options
107
+ change_auth options[:auth]
92
108
 
93
109
  # check the validity and canonize the endpoint URI
94
- prepare_endpoint endpoint
110
+ prepare_endpoint options[:endpoint]
95
111
 
96
112
  # get accepted media types from HTTParty
97
113
  set_media_type
98
114
 
99
115
  # force media_type if provided
100
- if media_type
101
- self.class.headers 'Accept' => media_type
102
- @media_type = media_type
116
+ if options[:media_type]
117
+ self.class.headers 'Accept' => options[:media_type]
118
+ @media_type = options[:media_type]
103
119
  end
104
120
 
105
121
  Occi::Log.debug("Media Type: #{@media_type}")
@@ -110,7 +126,7 @@ module Occi
110
126
  set_model
111
127
 
112
128
  # auto-connect?
113
- @connected = auto_connect
129
+ @connected = options[:auto_connect]
114
130
  end
115
131
 
116
132
  # Creates a new resource instance, resource should be specified
@@ -134,7 +150,7 @@ module Occi
134
150
  elsif @model.kinds.select { |kind| kind.term == resource_type }.any?
135
151
  # we got a resource type name
136
152
  Occi::Core::Resource.new @model.kinds.select {
137
- |kind| kind.term == resource_type
153
+ |kind| kind.term == resource_type
138
154
  }.first.type_identifier
139
155
  else
140
156
  raise "Unknown resource type! [#{resource_type}]"
@@ -263,12 +279,12 @@ module Occi
263
279
  if type
264
280
  # get the first match from either os_tpls or resource_tpls
265
281
  case
266
- when type == "os_tpl"
267
- get_os_templates.select { |mixin| mixin.term == name }.first
268
- when type == "resource_tpl"
269
- get_resource_templates.select { |template| template.term == name }.first
270
- else
271
- nil
282
+ when type == "os_tpl"
283
+ get_os_templates.select { |mixin| mixin.term == name }.first
284
+ when type == "resource_tpl"
285
+ get_resource_templates.select { |template| template.term == name }.first
286
+ else
287
+ nil
272
288
  end
273
289
  else
274
290
  # try in os_tpls first
@@ -276,7 +292,7 @@ module Occi
276
292
 
277
293
  # then try in resource_tpls
278
294
  found = get_resource_templates.select {
279
- |template| template.term == name
295
+ |template| template.term == name
280
296
  }.first unless found
281
297
 
282
298
  found
@@ -288,12 +304,12 @@ module Occi
288
304
  if type
289
305
  # return the first match with the selected type
290
306
  @mixins[type.to_sym].select {
291
- |mixin| mixin.to_s.reverse.start_with? name.reverse
307
+ |mixin| mixin.to_s.reverse.start_with? name.reverse
292
308
  }.first
293
309
  else
294
310
  # there is no type preference, return first global match
295
311
  @mixins.flatten(2).select {
296
- |mixin| mixin.to_s.reverse.start_with? name.reverse
312
+ |mixin| mixin.to_s.reverse.start_with? name.reverse
297
313
  }.first
298
314
  end
299
315
  end
@@ -342,7 +358,7 @@ module Occi
342
358
  #
343
359
  # @return [Array<String>] list of available mixin types
344
360
  def get_mixin_types
345
- @mixins.keys.map! { |k| k.to_s }
361
+ @mixins.keys.map { |k| k.to_s }
346
362
  end
347
363
 
348
364
  # Retrieves available mixin type identifiers.
@@ -384,9 +400,9 @@ module Occi
384
400
  if resource_type_identifier
385
401
  # convert type to type identifier
386
402
  resource_type_identifier = @model.kinds.select {
387
- |kind| kind.term == resource_type_identifier
403
+ |kind| kind.term == resource_type_identifier
388
404
  }.first.type_identifier if @model.kinds.select {
389
- |kind| kind.term == resource_type_identifier
405
+ |kind| kind.term == resource_type_identifier
390
406
  }.any?
391
407
 
392
408
  # check some basic pre-conditions
@@ -429,9 +445,9 @@ module Occi
429
445
 
430
446
  # convert type to type identifier
431
447
  resource_type_identifier = @model.kinds.select {
432
- |kind| kind.term == resource_type_identifier
448
+ |kind| kind.term == resource_type_identifier
433
449
  }.first.type_identifier if @model.kinds.select {
434
- |kind| kind.term == resource_type_identifier
450
+ |kind| kind.term == resource_type_identifier
435
451
  }.any?
436
452
 
437
453
  # check some basic pre-conditions
@@ -450,7 +466,7 @@ module Occi
450
466
  locations.each do |location|
451
467
  descriptions << get(sanitize_resource_link(location))
452
468
  end
453
- elsif resource_type_identifier.start_with? @endpoint
469
+ elsif resource_type_identifier.start_with?(@endpoint) || resource_type_identifier.start_with?('/')
454
470
  # we got resource link
455
471
  # make the request
456
472
  descriptions << get(sanitize_resource_link(resource_type_identifier))
@@ -568,7 +584,7 @@ module Occi
568
584
  # TODO: not tested
569
585
  if @model.kinds.select { |kind| kind.term == resource_type }.any?
570
586
  type_identifier = @model.kinds.select {
571
- |kind| kind.term == resource_type_identifier
587
+ |kind| kind.term == resource_type_identifier
572
588
  }.first.type_identifier
573
589
 
574
590
  location = @model.get_by_id(type_identifier).location
@@ -633,45 +649,41 @@ module Occi
633
649
  @auth_options = auth_options
634
650
 
635
651
  case @auth_options[:type]
636
- when "basic"
637
- # set up basic auth
638
- raise ArgumentError, "Missing required options 'username' and 'password' for basic auth!" unless @auth_options[:username] and @auth_options[:password]
639
- self.class.basic_auth @auth_options[:username], @auth_options[:password]
640
- when "digest"
641
- # set up digest auth
642
- raise ArgumentError, "Missing required options 'username' and 'password' for digest auth!" unless @auth_options[:username] and @auth_options[:password]
643
- self.class.digest_auth @auth_options[:username], @auth_options[:password]
644
- when "x509"
645
- # set up pem and optionally pem_password and ssl_ca_path
646
- raise ArgumentError, "Missing required option 'user_cert' for x509 auth!" unless @auth_options[:user_cert]
647
- raise ArgumentError, "The file specified in 'user_cert' does not exist!" unless File.exists? @auth_options[:user_cert]
648
-
649
- self.class.pem File.read(@auth_options[:user_cert]), @auth_options[:user_cert_password]
650
- self.class.ssl_ca_path @auth_options[:ca_path] unless @auth_options[:ca_path].nil?
651
- self.class.ssl_ca_file @auth_options[:ca_file] unless @auth_options[:ca_file].nil?
652
- self.class.ssl_extra_chain_cert certs_to_file_ary(@auth_options[:proxy_ca]) unless @auth_options[:proxy_ca].nil?
653
- when "keystone"
654
- # set up OpenStack Keystone token based auth
655
- raise ArgumentError, "Missing required option 'token' for OpenStack Keystone auth!" unless @auth_options[:token]
656
- self.class.headers['X-Auth-Token'] = @auth_options[:token]
657
- when "none", nil
658
- # do nothing
652
+ when "basic"
653
+ # set up basic auth
654
+ raise ArgumentError, "Missing required options 'username' and 'password' for basic auth!" unless @auth_options[:username] and @auth_options[:password]
655
+ self.class.basic_auth @auth_options[:username], @auth_options[:password]
656
+ when "digest"
657
+ # set up digest auth
658
+ raise ArgumentError, "Missing required options 'username' and 'password' for digest auth!" unless @auth_options[:username] and @auth_options[:password]
659
+ self.class.digest_auth @auth_options[:username], @auth_options[:password]
660
+ when "x509"
661
+ # set up pem and optionally pem_password and ssl_ca_path
662
+ raise ArgumentError, "Missing required option 'user_cert' for x509 auth!" unless @auth_options[:user_cert]
663
+ raise ArgumentError, "The file specified in 'user_cert' does not exist!" unless File.exists? @auth_options[:user_cert]
664
+
665
+ # handle PKCS#12 credentials before passing them
666
+ # to httparty
667
+ if /\A(.)+\.p12\z/ =~ @auth_options[:user_cert]
668
+ self.class.pem AuthnUtils.extract_pem_from_pkcs12(@auth_options[:user_cert], @auth_options[:user_cert_password]), ''
659
669
  else
660
- raise ArgumentError, "Unknown AUTH method [#{@auth_options[:type]}]!"
661
- end
662
- end
670
+ # httparty will handle ordinary PEM formatted credentials
671
+ # TODO: Issue #49, check PEM credentials in jRuby
672
+ self.class.pem File.open(@auth_options[:user_cert], 'rb').read, @auth_options[:user_cert_password]
673
+ end
663
674
 
664
- # Reads X.509 certificates from a file to an array.
665
- #
666
- # @example
667
- # certs_to_file_ary "~/.globus/usercert.pem"
668
- # # => [#<String>, #<String>, ...]
669
- #
670
- # @param [String] Path to a PEM file containing certificates
671
- # @return [Array<String>] An array of read certificates
672
- def certs_to_file_ary(ca_file)
673
- # TODO: read and separate multiple certificates
674
- [] << File.read(ca_file)
675
+ self.class.ssl_ca_path @auth_options[:ca_path] unless @auth_options[:ca_path].nil?
676
+ self.class.ssl_ca_file @auth_options[:ca_file] unless @auth_options[:ca_file].nil?
677
+ self.class.ssl_extra_chain_cert AuthnUtils.certs_to_file_ary(@auth_options[:proxy_ca]) unless @auth_options[:proxy_ca].nil?
678
+ when "keystone"
679
+ # set up OpenStack Keystone token based auth
680
+ raise ArgumentError, "Missing required option 'token' for OpenStack Keystone auth!" unless @auth_options[:token]
681
+ self.class.headers['X-Auth-Token'] = @auth_options[:token]
682
+ when "none", nil
683
+ # do nothing
684
+ else
685
+ raise ArgumentError, "Unknown AUTH method [#{@auth_options[:type]}]!"
686
+ end
675
687
  end
676
688
 
677
689
  # Performs GET request and parses the responses to collections.
@@ -686,7 +698,7 @@ module Occi
686
698
  # @return [Occi::Collection] parsed result of the request
687
699
  def get(path='', filter=nil)
688
700
  # remove the leading slash
689
- path.gsub!(/\A\//, '')
701
+ path = path.gsub(/\A\//, '')
690
702
 
691
703
  response = if filter
692
704
  categories = filter.categories.collect { |category| category.to_text }.join(',')
@@ -739,39 +751,39 @@ module Occi
739
751
  # @return [String] URI location
740
752
  def post(path, collection)
741
753
  # remove the leading slash
742
- path.gsub!(/\A\//, '')
754
+ path = path.gsub(/\A\//, '')
743
755
 
744
756
  headers = self.class.headers.clone
745
757
  headers['Content-Type'] = @media_type
746
758
 
747
759
  response = case @media_type
748
- when 'application/occi+json'
749
- self.class.post(@endpoint + path,
750
- :body => collection.to_json,
751
- :headers => headers)
752
- when 'text/occi'
753
- self.class.post(@endpoint + path,
754
- :headers => collection.to_header.merge(headers))
755
- else
756
- self.class.post(@endpoint + path,
757
- :body => collection.to_text,
758
- :headers => headers)
760
+ when 'application/occi+json'
761
+ self.class.post(@endpoint + path,
762
+ :body => collection.to_json,
763
+ :headers => headers)
764
+ when 'text/occi'
765
+ self.class.post(@endpoint + path,
766
+ :headers => collection.to_header.merge(headers))
767
+ else
768
+ self.class.post(@endpoint + path,
769
+ :body => collection.to_text,
770
+ :headers => headers)
759
771
  end
760
772
 
761
773
  response_msg = response_message response
762
774
 
763
775
  case response.code
764
- when 200
765
- collection = Occi::Parser.parse(response.header["content-type"].split(";").first, response)
766
- if collection.empty?
767
- Occi::Parser.locations(response.header["content-type"].split(";").first, response.body, response.header).first
768
- else
769
- collection.resources.first.location if collection.resources.first
770
- end
771
- when 201
776
+ when 200
777
+ collection = Occi::Parser.parse(response.header["content-type"].split(";").first, response)
778
+ if collection.empty?
772
779
  Occi::Parser.locations(response.header["content-type"].split(";").first, response.body, response.header).first
773
780
  else
774
- raise "HTTP POST failed! #{response_msg}"
781
+ collection.resources.first.location if collection.resources.first
782
+ end
783
+ when 201
784
+ Occi::Parser.locations(response.header["content-type"].split(";").first, response.body, response.header).first
785
+ else
786
+ raise "HTTP POST failed! #{response_msg}"
775
787
  end
776
788
  end
777
789
 
@@ -785,32 +797,32 @@ module Occi
785
797
  # @return [Occi::Collection] parsed result of the request
786
798
  def put(path, collection)
787
799
  # remove the leading slash
788
- path.gsub!(/\A\//, '')
800
+ path = path.gsub(/\A\//, '')
789
801
 
790
802
  headers = self.class.headers.clone
791
803
  headers['Content-Type'] = @media_type
792
804
 
793
805
  response = case @media_type
794
- when 'application/occi+json'
795
- self.class.post(@endpoint + path,
796
- :body => collection.to_json,
797
- :headers => headers)
798
- when 'text/occi'
799
- self.class.post(@endpoint + path,
800
- :headers => collection.to_header.merge(headers))
801
- else
802
- self.class.post(@endpoint + path,
803
- :body => collection.to_text,
804
- :headers => headers)
806
+ when 'application/occi+json'
807
+ self.class.post(@endpoint + path,
808
+ :body => collection.to_json,
809
+ :headers => headers)
810
+ when 'text/occi'
811
+ self.class.post(@endpoint + path,
812
+ :headers => collection.to_header.merge(headers))
813
+ else
814
+ self.class.post(@endpoint + path,
815
+ :body => collection.to_text,
816
+ :headers => headers)
805
817
  end
806
818
 
807
819
  response_msg = response_message response
808
820
 
809
821
  case response.code
810
- when 200, 201
811
- Occi::Parser.parse(response.header["content-type"].split(";").first, response)
812
- else
813
- raise "HTTP POST failed! #{response_msg}"
822
+ when 200, 201
823
+ Occi::Parser.parse(response.header["content-type"].split(";").first, response)
824
+ else
825
+ raise "HTTP POST failed! #{response_msg}"
814
826
  end
815
827
  end
816
828
 
@@ -824,7 +836,7 @@ module Occi
824
836
  # @return [Boolean] status
825
837
  def del(path, filter=nil)
826
838
  # remove the leading slash
827
- path.gsub!(/\A\//, '')
839
+ path = path.gsub(/\A\//, '')
828
840
 
829
841
  response = self.class.delete(@endpoint + path)
830
842
 
@@ -884,10 +896,15 @@ module Occi
884
896
  # @example
885
897
  # sanitize_resource_link "http://localhost:3300/compute/35ad4f45gsf-gsfg524s6gsfg-sfgsf4gsfg"
886
898
  # # => "/compute/35ad4f45gsf-gsfg524s6gsfg-sfgsf4gsfg"
899
+ # sanitize_resource_link "/compute/35ad4f45gsf-gsfg524s6gsfg-sfgsf4gsfg"
900
+ # # => "/compute/35ad4f45gsf-gsfg524s6gsfg-sfgsf4gsfg"
887
901
  #
888
902
  # @param [String] string containing the full resource link
889
903
  # @return [String] extracted path, with a leading slash
890
904
  def sanitize_resource_link(resource_link)
905
+ # everything starting with '/' is considered to be a resource path
906
+ return resource_link if resource_link.start_with? '/'
907
+
891
908
  raise "Resource link #{resource_link} is not valid!" unless resource_link.start_with? @endpoint
892
909
 
893
910
  resource_link.gsub @endpoint, '/'
@@ -910,7 +927,7 @@ module Occi
910
927
  if kinds.any?
911
928
  #we got an type identifier
912
929
  path = "/" + kinds.first.type_identifier.split('#').last + "/"
913
- elsif resource_type_identifier.start_with? @endpoint
930
+ elsif resource_type_identifier.start_with?(@endpoint) || resource_type_identifier.start_with?('/')
914
931
  #we got an resource link
915
932
  path = sanitize_resource_link(resource_type_identifier)
916
933
  else
@@ -932,8 +949,8 @@ module Occi
932
949
  @model = Occi::Model.new(model)
933
950
 
934
951
  @mixins = {
935
- :os_tpl => [],
936
- :resource_tpl => []
952
+ :os_tpl => [],
953
+ :resource_tpl => []
937
954
  }
938
955
 
939
956
  #
@@ -978,10 +995,10 @@ module Occi
978
995
  media_types = self.class.head(@endpoint).headers['accept']
979
996
  Occi::Log.debug("Available media types: #{media_types}")
980
997
  @media_type = case media_types
981
- when /application\/occi\+json/
982
- 'application/occi+json'
983
- else
984
- 'text/plain'
998
+ when /application\/occi\+json/
999
+ 'application/occi+json'
1000
+ else
1001
+ 'text/plain'
985
1002
  end
986
1003
  end
987
1004