occi 3.0.0 → 3.1.0.beta.1

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