occi-api 4.2.0.beta.4 → 4.2.0.beta.6

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.
@@ -1,4 +1,2 @@
1
- module Occi::Api::Client::Errors; end
2
-
3
1
  # load all
4
2
  Dir[File.join(File.dirname(__FILE__), 'errors', '*.rb')].each { |file| require file.gsub('.rb', '') }
@@ -1,6 +1,3 @@
1
- require 'occi/api/client/http/authn_plugins/base'
2
- require 'occi/api/client/http/authn_plugins/dummy'
3
- require 'occi/api/client/http/authn_plugins/basic'
4
- require 'occi/api/client/http/authn_plugins/digest'
5
- require 'occi/api/client/http/authn_plugins/x509'
6
- require 'occi/api/client/http/authn_plugins/keystone'
1
+ # load all plugins
2
+ require File.join(File.dirname(__FILE__), 'authn_plugins', 'base')
3
+ Dir[File.join(File.dirname(__FILE__), 'authn_plugins', '*.rb')].each { |file| require file.gsub('.rb', '') }
@@ -0,0 +1,64 @@
1
+ module Occi::Api::Client
2
+ module Http
3
+
4
+ module CodeHelpers
5
+
6
+ # hash mapping HTTP response codes to human-readable messages
7
+ HTTP_CODES = {
8
+ "100" => "Continue",
9
+ "101" => "Switching Protocols",
10
+ "200" => "OK",
11
+ "201" => "Created",
12
+ "202" => "Accepted",
13
+ "203" => "Non-Authoritative Information",
14
+ "204" => "No Content",
15
+ "205" => "Reset Content",
16
+ "206" => "Partial Content",
17
+ "300" => "Multiple Choices",
18
+ "301" => "Moved Permanently",
19
+ "302" => "Found",
20
+ "303" => "See Other",
21
+ "304" => "Not Modified",
22
+ "305" => "Use Proxy",
23
+ "307" => "Temporary Redirect",
24
+ "400" => "Bad Request",
25
+ "401" => "Unauthorized",
26
+ "402" => "Payment Required",
27
+ "403" => "Forbidden",
28
+ "404" => "Not Found",
29
+ "405" => "Method Not Allowed",
30
+ "406" => "Not Acceptable",
31
+ "407" => "Proxy Authentication Required",
32
+ "408" => "Request Time-out",
33
+ "409" => "Conflict",
34
+ "410" => "Gone",
35
+ "411" => "Length Required",
36
+ "412" => "Precondition Failed",
37
+ "413" => "Request Entity Too Large",
38
+ "414" => "Request-URI Too Large",
39
+ "415" => "Unsupported Media Type",
40
+ "416" => "Requested range not satisfiable",
41
+ "417" => "Expectation Failed",
42
+ "500" => "Internal Server Error",
43
+ "501" => "Not Implemented",
44
+ "502" => "Bad Gateway",
45
+ "503" => "Service Unavailable",
46
+ "504" => "Gateway Time-out",
47
+ "505" => "HTTP Version not supported"
48
+ }
49
+
50
+ # Converts HTTP response codes to human-readable phrases.
51
+ #
52
+ # @example
53
+ # reason_phrase(500) # => "Internal Server Error"
54
+ #
55
+ # @param [Integer] HTTP response code
56
+ # @return [String] human-readable phrase
57
+ def reason_phrase(code)
58
+ HTTP_CODES[code.to_s]
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,99 @@
1
+ module Occi::Api::Client
2
+ module Http
3
+
4
+ module Helpers
5
+
6
+ # @see Occi::Api::Client::ClientBase
7
+ def get_logger(log_options)
8
+ logger = super(log_options)
9
+ self.class.debug_output $stderr if logger.level == Occi::Log::DEBUG
10
+
11
+ logger
12
+ end
13
+
14
+ # @see Occi::Api::Client::ClientBase
15
+ def get_auth(auth_options, fallback = false)
16
+ # select appropriate authN type
17
+ case auth_options[:type]
18
+ when "basic"
19
+ @authn_plugin = Http::AuthnPlugins::Basic.new self, auth_options
20
+ when "digest"
21
+ @authn_plugin = Http::AuthnPlugins::Digest.new self, auth_options
22
+ when "x509"
23
+ @authn_plugin = Http::AuthnPlugins::X509.new self, auth_options
24
+ when "keystone"
25
+ raise ::Occi::Api::Client::Errors::AuthnError, "This authN method is for fallback only!" unless fallback
26
+ @authn_plugin = Http::AuthnPlugins::Keystone.new self, auth_options
27
+ when "none", nil
28
+ @authn_plugin = Http::AuthnPlugins::Dummy.new self
29
+ else
30
+ raise ::Occi::Api::Client::Errors::AuthnError, "Unknown authN method [#{@auth_options[:type]}]!"
31
+ end
32
+
33
+ @authn_plugin.setup
34
+
35
+ auth_options
36
+ end
37
+
38
+ # @see Occi::Api::Client::ClientBase
39
+ def preauthenticate
40
+ begin
41
+ @authn_plugin.authenticate
42
+ rescue ::Occi::Api::Client::Errors::AuthnError => e
43
+ Occi::Log.debug e.message
44
+
45
+ if @authn_plugin.fallbacks.any?
46
+ # TODO: multiple fallbacks
47
+ @auth_options[:original_type] = @auth_options[:type]
48
+ @auth_options[:type] = @authn_plugin.fallbacks.first
49
+
50
+ @auth_options = get_auth(@auth_options, true)
51
+ @authn_plugin.authenticate
52
+ else
53
+ raise e
54
+ end
55
+ end
56
+ end
57
+
58
+ # @see Occi::Api::Client::ClientBase
59
+ def get_media_type(force_type = nil)
60
+ # force media_type if provided
61
+ unless force_type.blank?
62
+ self.class.headers 'Accept' => force_type
63
+ media_type = force_type
64
+ else
65
+ media_types = self.class.head(@endpoint.to_s).headers['accept']
66
+
67
+ Occi::Log.debug("Available media types: #{media_types.inspect}")
68
+ media_type = case media_types
69
+ when /application\/occi\+json/
70
+ 'application/occi+json'
71
+ when /application\/occi\+xml/
72
+ 'application/occi+xml'
73
+ when /text\/occi/
74
+ 'text/occi'
75
+ else
76
+ 'text/plain'
77
+ end
78
+ end
79
+
80
+ media_type
81
+ end
82
+
83
+ # Generates a human-readable response message based on the HTTP response code.
84
+ #
85
+ # @example
86
+ # response_message self.class.delete(path)
87
+ # # => 'HTTP Response status: [200] OK'
88
+ #
89
+ # @param [HTTParty::Response] HTTParty response object
90
+ # @return [String] message
91
+ def response_message(response)
92
+ @last_response = response
93
+ "HTTP Response status: [#{response.code.to_s}] #{reason_phrase(response.code)}"
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,2 @@
1
+ # load all fixes
2
+ Dir[File.join(File.dirname(__FILE__), 'monkey_patches', '*.rb')].each { |file| require file.gsub('.rb', '') }
@@ -1,3 +1,7 @@
1
+ ##############################################################################
2
+ ## HTTParty hack allowing the use of X.509 proxy certificates.
3
+ ##############################################################################
4
+
1
5
  module HTTParty
2
6
  class ConnectionAdapter
3
7
 
@@ -0,0 +1,173 @@
1
+ module Occi::Api::Client
2
+ module Http
3
+
4
+ module PartyWrappers
5
+
6
+ # Performs GET request and parses the responses to collections.
7
+ #
8
+ # @example
9
+ # get "/-/" # => #<Occi::Collection>
10
+ # get "/compute/" # => #<Occi::Collection>
11
+ # get "/compute/fs65g4fs6g-sf54g54gsf-aa12faddf52" # => #<Occi::Collection>
12
+ #
13
+ # @param [String] path for the GET request
14
+ # @param [Occi::Collection] collection of filters
15
+ # @return [Occi::Collection] parsed result of the request
16
+ def get(path='/', filter=nil)
17
+ # apply filters if present
18
+ response = if filter
19
+ categories = filter.categories.collect { |category| category.to_text }.join(',')
20
+ attributes = filter.entities.collect { |entity| entity.attributes.combine.collect { |k, v| k + '=' + v } }.join(',')
21
+
22
+ headers = self.class.headers.clone
23
+ headers['Content-Type'] = 'text/occi'
24
+ headers['Category'] = categories unless categories.empty?
25
+ headers['X-OCCI-Attributes'] = attributes unless attributes.empty?
26
+
27
+ self.class.get(path, :headers => headers)
28
+ else
29
+ self.class.get(path)
30
+ end
31
+
32
+ response_msg = response_message response
33
+ raise "HTTP GET failed! #{response_msg}" unless response.code.between? 200, 300
34
+
35
+ Occi::Log.debug "Response location: #{path.inspect}"
36
+ kind = @model.get_by_location(path) if @model
37
+
38
+ Occi::Log.debug "Response kind: #{kind.inspect}"
39
+
40
+ entity_type = nil
41
+ if kind && kind.related_to?(Occi::Core::Link)
42
+ entity_type = Occi::Core::Link
43
+ end
44
+
45
+ entity_type = Occi::Core::Resource unless entity_type
46
+
47
+ Occi::Log.debug "Parser call: #{response.content_type} #{path.include?('/-/')} #{entity_type} #{response.headers.inspect}"
48
+ collection = Occi::Parser.parse(response.content_type, response.body, path.include?('/-/'), entity_type, response.headers)
49
+
50
+ Occi::Log.debug "Parsed collection: empty? #{collection.empty?}"
51
+ collection
52
+ end
53
+
54
+ # Performs POST requests and returns URI locations. Resource data must be provided
55
+ # in an Occi::Collection instance.
56
+ #
57
+ # @example
58
+ # collection = Occi::Collection.new
59
+ # collection.resources << entity if entity.kind_of? Occi::Core::Resource
60
+ # collection.links << entity if entity.kind_of? Occi::Core::Link
61
+ #
62
+ # post "/compute/", collection # => "http://localhost:3300/compute/23sf4g65as-asdgsg2-sdfgsf2g"
63
+ # post "/network/", collection # => "http://localhost:3300/network/23sf4g65as-asdgsg2-sdfgsf2g"
64
+ # post "/storage/", collection # => "http://localhost:3300/storage/23sf4g65as-asdgsg2-sdfgsf2g"
65
+ #
66
+ # @param [String] path for the POST request
67
+ # @param [Occi::Collection] resource data to be POSTed
68
+ # @return [String] URI location
69
+ def post(path, collection)
70
+ raise ArgumentError, "Path is a required argument!" if path.blank?
71
+
72
+ headers = self.class.headers.clone
73
+ headers['Content-Type'] = @media_type
74
+
75
+ response = case @media_type
76
+ when 'application/occi+json'
77
+ self.class.post(path,
78
+ :body => collection.to_json,
79
+ :headers => headers)
80
+ when 'text/occi'
81
+ self.class.post(path,
82
+ :headers => collection.to_header.merge(headers))
83
+ else
84
+ self.class.post(path,
85
+ :body => collection.to_text,
86
+ :headers => headers)
87
+ end
88
+
89
+ response_msg = response_message(response)
90
+
91
+ case response.code
92
+ when 200
93
+ collection = Occi::Parser.parse(response.header["content-type"].split(";").first, response.body)
94
+
95
+ if collection.empty?
96
+ Occi::Parser.locations(response.header["content-type"].split(";").first, response.body, response.headers).first
97
+ else
98
+ collection.resources.first.location if collection.resources.first
99
+ end
100
+ when 201
101
+ # TODO: OCCI-OS hack, look for header Location instead of uri-list
102
+ # This should be probably implemented in Occi::Parser.locations
103
+ if response.header['location']
104
+ response.header['location']
105
+ else
106
+ Occi::Parser.locations(response.header["content-type"].split(";").first, response.body, response.headers).first
107
+ end
108
+ else
109
+ raise "HTTP POST failed! #{response_msg}"
110
+ end
111
+ end
112
+
113
+ # Performs PUT requests and parses responses to collections.
114
+ #
115
+ # @example
116
+ # TODO: add examples
117
+ #
118
+ # @param [String] path for the PUT request
119
+ # @param [Occi::Collection] resource data to send
120
+ # @return [Occi::Collection] parsed result of the request
121
+ def put(path, collection)
122
+ raise ArgumentError, "Path is a required argument!" if path.blank?
123
+
124
+ headers = self.class.headers.clone
125
+ headers['Content-Type'] = @media_type
126
+
127
+ response = case @media_type
128
+ when 'application/occi+json'
129
+ self.class.post(path,
130
+ :body => collection.to_json,
131
+ :headers => headers)
132
+ when 'text/occi'
133
+ self.class.post(path,
134
+ :headers => collection.to_header.merge(headers))
135
+ else
136
+ self.class.post(path,
137
+ :body => collection.to_text,
138
+ :headers => headers)
139
+ end
140
+
141
+ response_msg = response_message(response)
142
+
143
+ case response.code
144
+ when 200, 201
145
+ Occi::Parser.parse(response.header["content-type"].split(";").first, response.body)
146
+ else
147
+ raise "HTTP POST failed! #{response_msg}"
148
+ end
149
+ end
150
+
151
+ # Performs DELETE requests and returns True on success.
152
+ #
153
+ # @example
154
+ # del "/compute/65sf4g65sf4g-sf6g54sf5g-sfgsf32g3" # => true
155
+ #
156
+ # @param [String] path for the DELETE request
157
+ # @param [Occi::Collection] collection of filters (currently NOT used)
158
+ # @return [Boolean] status
159
+ def del(path, filter=nil)
160
+ raise ArgumentError, "Path is a required argument!" if path.blank?
161
+
162
+ response = self.class.delete(path)
163
+
164
+ response_msg = response_message(response)
165
+ raise "HTTP DELETE failed! #{response_msg}" unless response.code.between? 200, 300
166
+
167
+ true
168
+ end
169
+
170
+ end
171
+
172
+ end
173
+ end
data/lib/occi/api/dsl.rb CHANGED
@@ -1,207 +1,46 @@
1
- module Occi
2
- module Api
3
- module Dsl
1
+ # load all parts of the DSL
2
+ Dir[File.join(File.dirname(__FILE__), 'dsl', '*.rb')].each { |file| require file.gsub('.rb', '') }
4
3
 
5
- def connect(protocol, *args)
4
+ module Occi::Api::Dsl
6
5
 
7
- case protocol
8
- when :http
9
- @client = Occi::Api::Client::ClientHttp.new(*args)
10
- else
11
- raise "Protocol #{protocol.to_s} is not supported!"
12
- end
6
+ def connect(protocol = :http, options = {})
7
+ raise ArgumentError, 'Protocol is a required argument!' unless protocol
13
8
 
14
- true
15
- end
16
-
17
- def list(*args)
18
- check
19
- @client.list(*args)
20
- end
21
-
22
- def describe(*args)
23
- check
24
- @client.describe(*args)
25
- end
26
-
27
- def create(*args)
28
- check
29
- @client.create(*args)
30
- end
31
-
32
- def delete(*args)
33
- check
34
- @client.delete(*args)
35
- end
36
-
37
- def trigger(*args)
38
- check
39
- @client.trigger(*args)
40
- end
41
-
42
- def refresh
43
- check
44
- @client.refresh
45
- end
46
-
47
- def model
48
- check
49
- @client.model
50
- end
51
-
52
- ###
53
-
54
- def kind_types
55
- check
56
- @client.get_kind_types
57
- end
58
-
59
- def kind_type_identifier(*args)
60
- check
61
- @client.get_kind_type_identifier(*args)
62
- end
63
-
64
- def kind_type_identifiers
65
- check
66
- @client.get_kind_type_identifiers
67
- end
68
-
69
- def kind_type_identifiers_related_to(*args)
70
- check
71
- @client.get_kind_type_identifiers_related_to(*args)
72
- end
73
-
74
- def category_types
75
- check
76
- @client.get_category_types
77
- end
78
-
79
- def category_type_identifier(*args)
80
- check
81
- @client.get_category_type_identifier(*args)
82
- end
83
-
84
- def category_type_identifiers
85
- check
86
- @client.get_category_type_identifiers
87
- end
88
-
89
- def resource_types
90
- check
91
- @client.get_resource_types
92
- end
93
-
94
- def resource_type_identifier(*args)
95
- check
96
- @client.get_resource_type_identifier(*args)
97
- end
98
-
99
- def resource_type_identifiers
100
- check
101
- @client.get_resource_type_identifiers
102
- end
103
-
104
- def mixin_types
105
- check
106
- @client.get_mixin_types
107
- end
108
-
109
- def mixin_type_identifier(*args)
110
- check
111
- @client.get_mixin_type_identifier(*args)
112
- end
113
-
114
- def mixin_type_identifiers
115
- check
116
- @client.get_mixin_type_identifiers
117
- end
118
-
119
- def entity_types
120
- check
121
- @client.get_entity_types
122
- end
123
-
124
- def entity_type_identifier(*args)
125
- check
126
- @client.get_entity_type_identifier(*args)
127
- end
128
-
129
- def entity_type_identifiers
130
- check
131
- @client.get_entity_type_identifiers
132
- end
133
-
134
- def link_types
135
- check
136
- @client.get_link_types
137
- end
138
-
139
- def link_type_identifier(*args)
140
- check
141
- @client.get_link_type_identifier(*args)
142
- end
143
-
144
- def link_type_identifiers
145
- check
146
- @client.get_link_type_identifiers
147
- end
148
-
149
- ###
150
-
151
- def mixins(*args)
152
- check
153
- @client.get_mixins(*args)
154
- end
155
-
156
- def os_templates
157
- check
158
- @client.get_os_templates
159
- end
160
-
161
- def resource_templates
162
- check
163
- @client.get_resource_templates
164
- end
165
-
166
- def mixin_list(*args)
167
- check
168
- @client.list_mixins(*args)
169
- end
9
+ if block_given?
10
+ options = options.marshal_dump if options.is_a?(OpenStruct)
11
+ options = Hashie::Mash.new(options)
12
+ yield(options)
13
+ end
170
14
 
171
- def resource(*args)
172
- check
173
- @client.get_resource(*args)
174
- end
15
+ case protocol.to_sym
16
+ when :http
17
+ @client = Occi::Api::Client::ClientHttp.new(options)
18
+ else
19
+ raise "Protocol #{protocol.to_s} is not supported!"
20
+ end
175
21
 
176
- def mixin(*args)
177
- check
178
- @client.get_mixin(*args)
179
- end
22
+ @client.connect unless @client.connected
180
23
 
181
- ###
24
+ true
25
+ end
182
26
 
183
- def path_for_kind_type_identifier(*args)
184
- check
185
- @client.path_for_kind_type_identifier(*args)
186
- end
27
+ # include main request methods
28
+ include Occi::Api::Dsl::MainMethods
187
29
 
188
- def path_for_instance(*args)
189
- check
190
- @client.path_for_instance(*args)
191
- end
30
+ # include methods converting between types and type identifiers
31
+ include Occi::Api::Dsl::TypeMethods
192
32
 
193
- def sanitize_instance_link(*args)
194
- check
195
- @client.sanitize_instance_link(*args)
196
- end
33
+ # include methods helping with mixins
34
+ include Occi::Api::Dsl::MixinMethods
197
35
 
198
- private
36
+ # include general helpers, normalizers etc.
37
+ include Occi::Api::Dsl::HelperMethods
199
38
 
200
- def check
201
- raise "You have to issue 'connect' first!" if @client.nil?
202
- raise "Client is disconnected!" unless @client.connected
203
- end
39
+ private
204
40
 
205
- end
41
+ def check
42
+ raise "You have to issue 'connect' first!" unless @client
43
+ raise "Client is disconnected!" unless @client.connected
206
44
  end
45
+
207
46
  end