occi-api 4.1.1 → 4.2.0.beta.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/Gemfile +9 -0
- data/README.md +37 -11
- data/Rakefile +2 -9
- data/examples/dsl_example.rb +2 -2
- data/examples/x509auth_example.rb +9 -9
- data/lib/occi-api.rb +0 -1
- data/lib/occi/api/client/client_base.rb +102 -92
- data/lib/occi/api/client/client_http.rb +6 -6
- data/lib/occi/api/client/http/authn_plugins/keystone.rb +27 -11
- data/lib/occi/api/dsl.rb +7 -3
- data/lib/occi/api/version.rb +1 -1
- data/occi-api.gemspec +2 -14
- data/{features/cassettes/Create_an_OCCI_Resource/_http_http___141_5_99_69__text_plain_201_.yml → spec/cassettes/Occi_Api_Client_ClientHttp/using_media_type_text_plain/describes_all_available_mixins.yml} +125 -147
- data/{features/cassettes/Miscellaneous_operation_on_an_OCCI_Resource/_http_http___141_5_99_69__text_plain_201_.yml → spec/cassettes/Occi_Api_Client_ClientHttp/using_media_type_text_plain/looks_up_a_mixin_type_identifier_for_os_tpl.yml} +126 -148
- data/{features/cassettes/Delete_an_OCCI_Resource/_http_http___141_5_99_69__text_plain_201_.yml → spec/cassettes/Occi_Api_Client_ClientHttp/using_media_type_text_plain/looks_up_a_mixin_type_identifier_for_resource_tpl.yml} +126 -148
- data/spec/occi/api/client/client_http_spec.rb +52 -28
- data/spec/occi/api/dsl_spec.rb +0 -6
- data/spec/spec_helper.rb +4 -1
- metadata +31 -200
- checksums.yaml +0 -7
- data/features/cassettes/Discovery_Interface/Retrieving_all_OCCI_Categories_supported_by_the_OCCI_Server/_http_http___141_5_99_69__application_json_200_.yml +0 -333
- data/features/cassettes/Discovery_Interface/Retrieving_all_OCCI_Categories_supported_by_the_OCCI_Server/_http_http___141_5_99_69__text_plain_200_.yml +0 -529
- data/features/cassettes/Discovery_Interface/Retrieving_all_OCCI_Categories_supported_by_the_OCCI_Server/_http_http___141_5_99_69__text_plain_200_action_.yml +0 -288
- data/features/cassettes/Read_an_OCCI_Resource/_http_http___141_5_99_69__text_plain_201_.yml +0 -288
- data/features/cassettes/Update_an_OCCI_Resource/_http_http___141_5_99_69__text_plain_201_.yml +0 -288
- data/features/common/step_definitions/common_steps.rb +0 -32
- data/features/occi/core/create/create.feature +0 -18
- data/features/occi/core/create/step_definitions/create_steps.rb +0 -0
- data/features/occi/core/delete/delete.feature +0 -18
- data/features/occi/core/delete/step_definitions/delete_steps.rb +0 -0
- data/features/occi/core/discovery_interface/discovery_interface.feature +0 -37
- data/features/occi/core/discovery_interface/step_definitions/discovery_interface_steps.rb +0 -19
- data/features/occi/core/miscellaneous/miscellaneous.feature +0 -18
- data/features/occi/core/miscellaneous/step_definitions/miscellaneous_steps.rb +0 -0
- data/features/occi/core/read/read.feature +0 -18
- data/features/occi/core/read/step_definitions/read_steps.rb +0 -0
- data/features/occi/core/update/step_definitions/update_steps.rb +0 -0
- data/features/occi/core/update/update.feature +0 -18
- data/features/occi/infrastructure/create/create.feature +0 -18
- data/features/occi/infrastructure/create/step_definitions/create_steps.rb +0 -0
- data/features/support/env.rb +0 -16
- data/lib/occi/api/client/client_amqp.rb +0 -766
- data/spec/occi/api/client/client_amqp_spec.rb +0 -158
File without changes
|
File without changes
|
@@ -1,18 +0,0 @@
|
|
1
|
-
Feature:
|
2
|
-
In order to update an OCCI Resource on the OCCI Server
|
3
|
-
As an OCCI Client
|
4
|
-
I want to get an success report of the update operation
|
5
|
-
|
6
|
-
@vcr_record
|
7
|
-
Scenario Outline: Update an OCCI Resource
|
8
|
-
Given endpoint : <endpoint>
|
9
|
-
And transfer_protocol : <protocol>
|
10
|
-
And accept type : <accept_type>
|
11
|
-
And have an initialize Client
|
12
|
-
When the Client makes an update request
|
13
|
-
Then the Client should have the response code <response_code>
|
14
|
-
|
15
|
-
@vcr_record
|
16
|
-
Scenarios:
|
17
|
-
| protocol | endpoint | accept_type | response_code |
|
18
|
-
| http | http://141.5.99.69/ | text/plain | 201 |
|
@@ -1,18 +0,0 @@
|
|
1
|
-
Feature:
|
2
|
-
In order to create an OCCI Resource on the OCCI Server
|
3
|
-
As an OCCI Client
|
4
|
-
I want to get an success report of the create operation and receive the URI of the new Resource by the OCCI Server
|
5
|
-
|
6
|
-
@vcr_record
|
7
|
-
Scenario Outline: Create an OCCI Resource
|
8
|
-
Given endpoint : <endpoint>
|
9
|
-
And transfer_protocol : <protocol>
|
10
|
-
And accept type : <accept_type>
|
11
|
-
And have an initialize Client
|
12
|
-
When the Client makes a create request
|
13
|
-
Then the Client should have the response code <response_code>
|
14
|
-
|
15
|
-
@vcr_record
|
16
|
-
Scenarios:
|
17
|
-
| protocol | endpoint | accept_type | response_code |
|
18
|
-
| http | http://141.5.99.69/ | text/plain | 201 |
|
File without changes
|
data/features/support/env.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
$:.unshift(File.dirname(__FILE__) + '/../../lib')
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'occi-api'
|
5
|
-
|
6
|
-
require 'vcr'
|
7
|
-
|
8
|
-
VCR.configure do |c|
|
9
|
-
c.hook_into :webmock
|
10
|
-
c.cassette_library_dir = 'features/cassettes'
|
11
|
-
end
|
12
|
-
|
13
|
-
VCR.cucumber_tags do |t|
|
14
|
-
t.tags '@vcr_ignore', :record => :none
|
15
|
-
t.tag '@vcr_record', {:use_scenario_name => true, :record => :new_episodes}
|
16
|
-
end
|
@@ -1,766 +0,0 @@
|
|
1
|
-
require "amqp"
|
2
|
-
require "json"
|
3
|
-
|
4
|
-
module Occi
|
5
|
-
module Api
|
6
|
-
module Client
|
7
|
-
|
8
|
-
class ClientAmqp
|
9
|
-
attr_reader :endpoint, :auth_options, :connected, :last_response_status
|
10
|
-
attr_accessor :media_type, :model
|
11
|
-
|
12
|
-
CONNECTION_SETTING = {
|
13
|
-
:host => 'localhost', #IP of the MessageBroker (RabbitMQ)
|
14
|
-
:port => 5672,
|
15
|
-
:vhost => '/' ,
|
16
|
-
:password => 'password'
|
17
|
-
}
|
18
|
-
|
19
|
-
# hash mapping HTTP response codes to human-readable messages
|
20
|
-
HTTP_CODES = {
|
21
|
-
"100" => "Continue",
|
22
|
-
"101" => "Switching Protocols",
|
23
|
-
"200" => "OK",
|
24
|
-
"201" => "Created",
|
25
|
-
"202" => "Accepted",
|
26
|
-
"203" => "Non-Authoritative Information",
|
27
|
-
"204" => "No Content",
|
28
|
-
"205" => "Reset Content",
|
29
|
-
"206" => "Partial Content",
|
30
|
-
"300" => "Multiple Choices",
|
31
|
-
"301" => "Moved Permanently",
|
32
|
-
"302" => "Found",
|
33
|
-
"303" => "See Other",
|
34
|
-
"304" => "Not Modified",
|
35
|
-
"305" => "Use Proxy",
|
36
|
-
"307" => "Temporary Redirect",
|
37
|
-
"400" => "Bad Request",
|
38
|
-
"401" => "Unauthorized",
|
39
|
-
"402" => "Payment Required",
|
40
|
-
"403" => "Forbidden",
|
41
|
-
"404" => "Not Found",
|
42
|
-
"405" => "Method Not Allowed",
|
43
|
-
"406" => "Not Acceptable",
|
44
|
-
"407" => "Proxy Authentication Required",
|
45
|
-
"408" => "Request Time-out",
|
46
|
-
"409" => "Conflict",
|
47
|
-
"410" => "Gone",
|
48
|
-
"411" => "Length Required",
|
49
|
-
"412" => "Precondition Failed",
|
50
|
-
"413" => "Request Entity Too Large",
|
51
|
-
"414" => "Request-URI Too Large",
|
52
|
-
"415" => "Unsupported Media Type",
|
53
|
-
"416" => "Requested range not satisfiable",
|
54
|
-
"417" => "Expectation Failed",
|
55
|
-
"500" => "Internal Server Error",
|
56
|
-
"501" => "Not Implemented",
|
57
|
-
"502" => "Bad Gateway",
|
58
|
-
"503" => "Service Unavailable",
|
59
|
-
"504" => "Gateway Time-out",
|
60
|
-
"505" => "HTTP Version not supported"
|
61
|
-
}
|
62
|
-
|
63
|
-
#TODO Should a client have some kind of timeout
|
64
|
-
|
65
|
-
# Initializes client data structures and retrieves OCCI model
|
66
|
-
# from the server.
|
67
|
-
#
|
68
|
-
# @example
|
69
|
-
# options = {
|
70
|
-
# :endpoint => "http://localhost:3300/",
|
71
|
-
# :auth => {:type => "none"},
|
72
|
-
# :log => {:out => STDERR, :level => Occi::Log::WARN, :logger => nil},
|
73
|
-
# :media_type => "text/plain"
|
74
|
-
# }
|
75
|
-
#
|
76
|
-
# Occi::Api::Client::ClientAmqp.new options # => #<Occi::Api::Client::ClientAmqp>
|
77
|
-
#
|
78
|
-
# @param [Hash] options, for available options and defaults see examples
|
79
|
-
# @return [Occi::Api::Client::ClientAmqp] client instance
|
80
|
-
def initialize(options = {})
|
81
|
-
|
82
|
-
defaults = {
|
83
|
-
:endpoint => "http://localhost:3300/",
|
84
|
-
:auth => {:type => "none"},
|
85
|
-
:log => {:out => STDERR, :level => Occi::Log::WARN, :logger => nil},
|
86
|
-
:media_type => "text/plain"
|
87
|
-
}
|
88
|
-
|
89
|
-
options = options.marshal_dump if options.is_a? OpenStruct
|
90
|
-
options = defaults.merge options
|
91
|
-
|
92
|
-
# check the validity and canonize the endpoint URI
|
93
|
-
prepare_endpoint options[:endpoint]
|
94
|
-
|
95
|
-
# set Occi::Log
|
96
|
-
set_logger options[:log]
|
97
|
-
|
98
|
-
# pass auth options to HTTParty
|
99
|
-
change_auth options[:auth]
|
100
|
-
|
101
|
-
@media_type = options[:media_type]
|
102
|
-
|
103
|
-
Occi::Log.debug("Media Type: #{@media_type}")
|
104
|
-
|
105
|
-
@connected = false
|
106
|
-
|
107
|
-
Thread.new { run }
|
108
|
-
|
109
|
-
Occi::Log.debug("Waiting for connection amqp ...")
|
110
|
-
|
111
|
-
#TODO find a better solution for the thread issue
|
112
|
-
while(!@thread_error && !@connected)
|
113
|
-
#dont use sleep - it blocks the eventmachine
|
114
|
-
end
|
115
|
-
|
116
|
-
# get model information from the endpoint
|
117
|
-
# and create Occi::Model instance
|
118
|
-
set_model unless @thread_error
|
119
|
-
end
|
120
|
-
|
121
|
-
# @describe Retrieves available resources represented by resource locations (URIs).
|
122
|
-
# If no type identifier is specified, all available resource are listed.
|
123
|
-
# Type identifier can be specified in its shortened format (e.g. "compute",
|
124
|
-
# "storage", "network").
|
125
|
-
#
|
126
|
-
# @example
|
127
|
-
# client.list
|
128
|
-
# # => [ "http://localhost:3300/compute/jh425jhj3h413-7dj29d7djd9e3-djh2jh4j4j",
|
129
|
-
# # "http://localhost:3300/network/kh425jhj3h413-7dj29d7djd9e3-djh2jh4j4j",
|
130
|
-
# # "http://localhost:3300/storage/lh425jhj3h413-7dj29d7djd9e3-djh2jh4j4j" ]
|
131
|
-
# client.list "compute"
|
132
|
-
# # => [ "http://localhost:3300/compute/jh425jhj3h413-7dj29d7djd9e3-djh2jh4j4j" ]
|
133
|
-
# client.list "http://schemas.ogf.org/occi/infrastructure#compute"
|
134
|
-
# # => [ "http://localhost:3300/compute/jh425jhj3h413-7dj29d7djd9e3-djh2jh4j4j" ]
|
135
|
-
#
|
136
|
-
# @param [String] resource type identifier or just type name
|
137
|
-
# @return [Array<String>] list of links
|
138
|
-
def list(resource_type_identifier = nil, is_wait = true, is_parse_response = true)
|
139
|
-
|
140
|
-
if resource_type_identifier
|
141
|
-
# convert type to type identifier
|
142
|
-
resource_type_identifier = @model.kinds.select {
|
143
|
-
|kind| kind.term == resource_type_identifier
|
144
|
-
}.first.type_identifier if @model.kinds.select {
|
145
|
-
|kind| kind.term == resource_type_identifier
|
146
|
-
}.any?
|
147
|
-
|
148
|
-
# check some basic pre-conditions
|
149
|
-
raise "Endpoint is not connected!" unless @connected
|
150
|
-
raise "Unkown resource type identifier! [#{resource_type_identifier}]" unless @model.get_by_id resource_type_identifier
|
151
|
-
|
152
|
-
# split the type identifier and get the most important part
|
153
|
-
uri_part = resource_type_identifier.split('#').last
|
154
|
-
|
155
|
-
# request uri-list from the server
|
156
|
-
path = uri_part + '/'
|
157
|
-
else
|
158
|
-
path = '/'
|
159
|
-
end
|
160
|
-
|
161
|
-
message_id = get(path, true)
|
162
|
-
|
163
|
-
return response_value(message_id, is_wait, is_parse_response)
|
164
|
-
end
|
165
|
-
|
166
|
-
# @describe Retrieves descriptions for available resources specified by a type
|
167
|
-
# identifier or resource location. If no type identifier or location
|
168
|
-
# is specified, all available resources in all available resource types
|
169
|
-
# will be described.
|
170
|
-
#
|
171
|
-
# @example
|
172
|
-
# client.describe
|
173
|
-
# # => [#<Occi::Collection>, #<Occi::Collection>, #<Occi::Collection>]
|
174
|
-
# client.describe "compute"
|
175
|
-
# # => [#<Occi::Collection>, #<Occi::Collection>, #<Occi::Collection>]
|
176
|
-
# client.describe "http://schemas.ogf.org/occi/infrastructure#compute"
|
177
|
-
# # => [#<Occi::Collection>, #<Occi::Collection>, #<Occi::Collection>]
|
178
|
-
# client.describe "http://localhost:3300/compute/j5hk1234jk2524-2j3j2k34jjh234-adfaf1234"
|
179
|
-
# # => [#<Occi::Collection>]
|
180
|
-
#
|
181
|
-
# @param [String] resource type identifier, type name or resource location
|
182
|
-
# @return [Array<Occi::Collection>] list of resource descriptions
|
183
|
-
def describe(resource_type_identifier=nil, is_wait = true, is_parse_response = true)
|
184
|
-
|
185
|
-
raise "Endpoint is not connected!" unless @connected
|
186
|
-
|
187
|
-
# convert type to type identifier
|
188
|
-
resource_type_identifier = @model.kinds.select {
|
189
|
-
|kind| kind.term == resource_type_identifier
|
190
|
-
}.first.type_identifier if @model.kinds.select {
|
191
|
-
|kind| kind.term == resource_type_identifier
|
192
|
-
}.any?
|
193
|
-
|
194
|
-
descriptions = []
|
195
|
-
|
196
|
-
if resource_type_identifier.nil?
|
197
|
-
descriptions << response_value(get('/'), is_wait, is_parse_response)
|
198
|
-
elsif @model.get_by_id resource_type_identifier
|
199
|
-
# we got type identifier
|
200
|
-
# get all available resources of this type
|
201
|
-
locations = list resource_type_identifier
|
202
|
-
|
203
|
-
# make the requests
|
204
|
-
locations.each do |location|
|
205
|
-
descriptions << response_value(get(sanitize_resource_link(location)), is_wait, is_parse_response)
|
206
|
-
end
|
207
|
-
elsif resource_type_identifier.start_with? @endpoint
|
208
|
-
# we got resource link
|
209
|
-
# make the request
|
210
|
-
descriptions << response_value(get(sanitize_resource_link(resource_type_identifier)), is_wait, is_parse_response)
|
211
|
-
else
|
212
|
-
raise "Unkown resource type identifier! [#{resource_type_identifier}]"
|
213
|
-
end
|
214
|
-
|
215
|
-
descriptions
|
216
|
-
end
|
217
|
-
|
218
|
-
# Creates a new resource on the server. Resource must be provided
|
219
|
-
# as an instance of Occi::Core::Entity, e.g. instantiated using
|
220
|
-
# the get_resource method.
|
221
|
-
#
|
222
|
-
# @example
|
223
|
-
# res = client.get_resource "compute"
|
224
|
-
#
|
225
|
-
# res.title = "MyComputeResource1"
|
226
|
-
# res.mixins << client.find_mixin('small', "resource_tpl")
|
227
|
-
# res.mixins << client.find_mixin('debian6', "os_tpl")
|
228
|
-
#
|
229
|
-
# client.create res # => "http://localhost:3300/compute/df7698...f987fa"
|
230
|
-
#
|
231
|
-
def create(entity, is_wait = true, is_parse_response = true)
|
232
|
-
|
233
|
-
# check some basic pre-conditions
|
234
|
-
raise "Endpoint is not connected!" unless @connected
|
235
|
-
raise "#{entity} not an entity" unless entity.kind_of? Occi::Core::Entity
|
236
|
-
|
237
|
-
# is this entity valid?
|
238
|
-
entity.model = @model
|
239
|
-
entity.check
|
240
|
-
kind = entity.kind
|
241
|
-
raise "No kind found for #{entity}" unless kind
|
242
|
-
|
243
|
-
# get location for this kind of entity
|
244
|
-
location = entity.kind.location
|
245
|
-
collection = Occi::Collection.new
|
246
|
-
|
247
|
-
# is this entity a Resource or a Link?
|
248
|
-
collection.resources << entity if entity.kind_of? Occi::Core::Resource
|
249
|
-
collection.links << entity if entity.kind_of? Occi::Core::Link
|
250
|
-
|
251
|
-
# make the request
|
252
|
-
response_value(post(location, collection), is_wait, is_parse_response)
|
253
|
-
end
|
254
|
-
|
255
|
-
# Deletes a resource or all resource of a certain resource type
|
256
|
-
# from the server.
|
257
|
-
#
|
258
|
-
# @example
|
259
|
-
# client.delete "compute" # => true
|
260
|
-
# client.delete "http://schemas.ogf.org/occi/infrastructure#compute" # => true
|
261
|
-
# client.delete "http://localhost:3300/compute/245j42594...98s9df8s9f" # => true
|
262
|
-
#
|
263
|
-
# @param [String] resource type identifier, type name or location
|
264
|
-
# @return [Boolean] status
|
265
|
-
def delete(resource_type_identifier, is_wait = true, is_parse_response = true)
|
266
|
-
# convert type to type identifier
|
267
|
-
resource_type_identifier = @model.kinds.select {
|
268
|
-
|kind| kind.term == resource_type_identifier
|
269
|
-
}.first.type_identifier if @model.kinds.select {
|
270
|
-
|kind| kind.term == resource_type_identifier
|
271
|
-
}.any?
|
272
|
-
|
273
|
-
# check some basic pre-conditions
|
274
|
-
raise "Endpoint is not connected!" unless @connected
|
275
|
-
|
276
|
-
if resource_type_identifier.nil? || resource_type_identifier == "/"
|
277
|
-
path = "/"
|
278
|
-
else
|
279
|
-
raise "Unknown resource identifier! #{resource_type_identifier}" unless resource_type_identifier.start_with? @endpoint
|
280
|
-
path = sanitize_resource_link(resource_type_identifier)
|
281
|
-
end
|
282
|
-
|
283
|
-
# make the request
|
284
|
-
response_value(del(path), is_wait, is_parse_response)
|
285
|
-
end
|
286
|
-
|
287
|
-
# Triggers given action on a specific resource.
|
288
|
-
#
|
289
|
-
# @example
|
290
|
-
# TODO: add examples
|
291
|
-
#
|
292
|
-
# @param [String] resource location
|
293
|
-
# @param [String] type of action
|
294
|
-
# @return [String] resource location
|
295
|
-
def trigger(resource_type_identifier, action, is_wait = true, is_parse_response = true)
|
296
|
-
|
297
|
-
# TODO: not tested
|
298
|
-
resource_type_identifier = @model.kinds.select {
|
299
|
-
|kind| kind.term == resource_type_identifier
|
300
|
-
}.first.type_identifier if @model.kinds.select {
|
301
|
-
|kind| kind.term == resource_type_identifier
|
302
|
-
}.any?
|
303
|
-
|
304
|
-
# check some basic pre-conditions
|
305
|
-
raise "Endpoint is not connected!" unless @connected
|
306
|
-
|
307
|
-
if resource_type_identifier.nil? || resource_type_identifier == "/"
|
308
|
-
path = "/"
|
309
|
-
else
|
310
|
-
raise "Unknown resource identifier! #{resource_type_identifier}" unless resource_type_identifier.start_with? @endpoint
|
311
|
-
path = sanitize_resource_link(resource_type_identifier)
|
312
|
-
end
|
313
|
-
|
314
|
-
# encapsulate the acion in a collection
|
315
|
-
collection = Occi::Collection.new
|
316
|
-
scheme, term = action.split('#')
|
317
|
-
collection.actions << Occi::Core::Action.new(scheme + '#', term)
|
318
|
-
|
319
|
-
#@media_type = "text/plain"
|
320
|
-
|
321
|
-
# make the request
|
322
|
-
path = path + '?action=' + term
|
323
|
-
response_value(post(path, collection))
|
324
|
-
end
|
325
|
-
|
326
|
-
def parse_message(message_id, delete_response = true)
|
327
|
-
raise "message is empty for message_id(#{ message_id })" if @response_messages.nil? || @response_messages[message_id].nil?
|
328
|
-
|
329
|
-
payload = @response_messages[message_id][:payload]
|
330
|
-
type = @response_messages[message_id][:type]
|
331
|
-
metadata = @response_messages[message_id][:metadata]
|
332
|
-
|
333
|
-
@last_response_status = metadata.headers["status_code"]
|
334
|
-
|
335
|
-
@response_messages.delete(message_id) if delete_response
|
336
|
-
|
337
|
-
raise "OCCI-Server raise error: #{ payload } server status: #{ @last_response_status }" if metadata.headers["is_error"]
|
338
|
-
|
339
|
-
begin
|
340
|
-
return method("parse_#{ type }").call(payload, metadata)
|
341
|
-
rescue Exception => e
|
342
|
-
error_msg = e.message + "\n" + e.backtrace.to_s
|
343
|
-
Occi::Log.error error_msg
|
344
|
-
raise "Can not parse #{ type } payload: #{ payload } metadata: #{ metadata.inspect }"
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
# Looks up a mixin using its name and, optionally, a type as well.
|
349
|
-
# Will return mixin's full location (a link) or a description.
|
350
|
-
#
|
351
|
-
# @example
|
352
|
-
# client.find_mixin "debian6"
|
353
|
-
# # => "http://my.occi.service/occi/infrastructure/os_tpl#debian6"
|
354
|
-
# client.find_mixin "debian6", "os_tpl"
|
355
|
-
# # => "http://my.occi.service/occi/infrastructure/os_tpl#debian6"
|
356
|
-
# client.find_mixin "large", "resource_tpl"
|
357
|
-
# # => "http://my.occi.service/occi/infrastructure/resource_tpl#large"
|
358
|
-
# client.find_mixin "debian6", "resource_tpl" # => nil
|
359
|
-
#
|
360
|
-
# @param [String] name of the mixin
|
361
|
-
# @param [String] type of the mixin
|
362
|
-
# @param [Boolean] should we describe the mixin or return its link?
|
363
|
-
# @return [String, Occi::Collection, nil] link, mixin description or nothing found
|
364
|
-
def find_mixin(name, type = nil, describe = false)
|
365
|
-
|
366
|
-
Occi::Log.debug("Looking for mixin #{name} + #{type} + #{describe}")
|
367
|
-
|
368
|
-
# is type valid?
|
369
|
-
if type
|
370
|
-
raise "Unknown mixin type! [#{type}]" unless @mixins.has_key? type.to_sym
|
371
|
-
end
|
372
|
-
|
373
|
-
# TODO: extend this code to support multiple matches and regex filters
|
374
|
-
# should we look for links or descriptions?
|
375
|
-
if describe
|
376
|
-
# we are looking for descriptions
|
377
|
-
if type
|
378
|
-
# get the first match from either os_tpls or resource_tpls
|
379
|
-
case
|
380
|
-
when type == "os_tpl"
|
381
|
-
get_os_templates.select { |mixin| mixin.term == name }.first
|
382
|
-
when type == "resource_tpl"
|
383
|
-
get_resource_templates.select { |template| template.term == name }.first
|
384
|
-
else
|
385
|
-
nil
|
386
|
-
end
|
387
|
-
else
|
388
|
-
# try in os_tpls first
|
389
|
-
found = get_os_templates.select { |os| os.term == name }.first
|
390
|
-
|
391
|
-
# then try in resource_tpls
|
392
|
-
found = get_resource_templates.select {
|
393
|
-
|template| template.term == name
|
394
|
-
}.first unless found
|
395
|
-
|
396
|
-
found
|
397
|
-
end
|
398
|
-
else
|
399
|
-
# we are looking for links
|
400
|
-
# prefix mixin name with '#' to simplify the search
|
401
|
-
name = "#" + name
|
402
|
-
if type
|
403
|
-
# return the first match with the selected type
|
404
|
-
@mixins[type.to_sym].select {
|
405
|
-
|mixin| mixin.to_s.reverse.start_with? name.reverse
|
406
|
-
}.first
|
407
|
-
else
|
408
|
-
# there is no type preference, return first global match
|
409
|
-
@mixins.flatten(2).select {
|
410
|
-
|mixin| mixin.to_s.reverse.start_with? name.reverse
|
411
|
-
}.first
|
412
|
-
end
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
# Creates a new resource instance, resource should be specified
|
417
|
-
# by its name or identifier.
|
418
|
-
#
|
419
|
-
# @example
|
420
|
-
# client.get_resource "compute" # => Occi::Core::Resource
|
421
|
-
# client.get_resource "storage" # => Occi::Core::Resource
|
422
|
-
# client.get_resource "http://schemas.ogf.org/occi/infrastructure#network"
|
423
|
-
# # => Occi::Core::Resource
|
424
|
-
#
|
425
|
-
# @param [String] resource name or resource identifier
|
426
|
-
# @return [Occi::Core::Resource] new resource instance
|
427
|
-
def get_resource(resource_type)
|
428
|
-
|
429
|
-
Occi::Log.debug("Instantiating #{resource_type} ...")
|
430
|
-
|
431
|
-
if @model.get_by_id resource_type
|
432
|
-
# we got a resource type identifier
|
433
|
-
Occi::Core::Resource.new resource_type
|
434
|
-
elsif @model.kinds.select { |kind| kind.term == resource_type }.any?
|
435
|
-
# we got a resource type name
|
436
|
-
Occi::Core::Resource.new @model.kinds.select {
|
437
|
-
|kind| kind.term == resource_type
|
438
|
-
}.first.type_identifier
|
439
|
-
else
|
440
|
-
raise "Unknown resource type! [#{resource_type}]"
|
441
|
-
end
|
442
|
-
|
443
|
-
end
|
444
|
-
|
445
|
-
# Retrieves available os_tpls from the model.
|
446
|
-
#
|
447
|
-
# @example
|
448
|
-
# get_os_templates # => #<Occi::Collection>
|
449
|
-
#
|
450
|
-
# @return [Occi::Collection] collection containing all registered OS templates
|
451
|
-
def get_os_templates
|
452
|
-
@model.get.mixins.select { |mixin| mixin.related.select { |rel| rel.end_with? 'os_tpl' }.any? }
|
453
|
-
end
|
454
|
-
|
455
|
-
# Retrieves available resource_tpls from the model.
|
456
|
-
#
|
457
|
-
# @example
|
458
|
-
# get_resource_templates # => #<Occi::Collection>
|
459
|
-
#
|
460
|
-
# @return [Occi::Collection] collection containing all registered resource templates
|
461
|
-
def get_resource_templates
|
462
|
-
@model.get.mixins.select { |mixin| mixin.related.select { |rel| rel.end_with? 'resource_tpl' }.any? }
|
463
|
-
end
|
464
|
-
|
465
|
-
# Refreshes the Occi::Model used inside the client. Useful for
|
466
|
-
# updating the model without creating a new instance or
|
467
|
-
# reconnecting. Saves a lot of time in an interactive mode.
|
468
|
-
#
|
469
|
-
# @example
|
470
|
-
# client.refresh
|
471
|
-
def refresh
|
472
|
-
# re-download the model from the server
|
473
|
-
set_model
|
474
|
-
end
|
475
|
-
|
476
|
-
# private stuff --------------------------------------------------------------------------------------------------
|
477
|
-
private
|
478
|
-
|
479
|
-
def parse_get(payload, metadata)
|
480
|
-
if metadata.content_type == "text/uri-list"
|
481
|
-
return payload.split("\n")
|
482
|
-
else
|
483
|
-
path = metadata.headers["path_info"]
|
484
|
-
kind = @model.get_by_location(('/' + path).match(/\/.*\//).to_s) if @model
|
485
|
-
kind ? entity_type = kind.entity_type : entity_type = nil
|
486
|
-
collection = Occi::Parser.parse(metadata.content_type, payload, path.include?("/-/"), entity_type)
|
487
|
-
return collection
|
488
|
-
end
|
489
|
-
end
|
490
|
-
|
491
|
-
def parse_post(payload, metadata)
|
492
|
-
return URI.parse(payload).to_s
|
493
|
-
end
|
494
|
-
|
495
|
-
def parse_delete(payload, metadata)
|
496
|
-
return true
|
497
|
-
end
|
498
|
-
|
499
|
-
def run
|
500
|
-
begin
|
501
|
-
AMQP.start(CONNECTION_SETTING) do |connection, open_ok|
|
502
|
-
@channel = AMQP::Channel.new(connection)
|
503
|
-
@exchange = @channel.default_exchange
|
504
|
-
|
505
|
-
@channel.on_error(&method(:handle_channel_exception))
|
506
|
-
|
507
|
-
@replies_queue = @channel.queue(Time.now.to_f.to_s + Kernel.rand().to_s, :exclusive => true)
|
508
|
-
@replies_queue.subscribe(&method(:handle_message))
|
509
|
-
|
510
|
-
@connected = true;
|
511
|
-
end
|
512
|
-
rescue Exception => e
|
513
|
-
@thread_error = true
|
514
|
-
Occi::Log.error "Amqp Thread get an Error: #{e.message} \n #{e.backtrace.join("\n")}"
|
515
|
-
end
|
516
|
-
end
|
517
|
-
|
518
|
-
def handle_message(metadata, payload)
|
519
|
-
|
520
|
-
correlation_id = metadata.correlation_id
|
521
|
-
|
522
|
-
unless correlation_id.size > 0
|
523
|
-
raise "Message has no correlation_id (message_id)"
|
524
|
-
end
|
525
|
-
|
526
|
-
@response_messages = Hash.new if @response_messages.nil?
|
527
|
-
raise "Double Response Message ID: (#{ correlation_id })" if @response_messages.has_key?(correlation_id)
|
528
|
-
|
529
|
-
#save responses message
|
530
|
-
@response_messages[correlation_id] = {:payload => payload, :metadata => metadata, :type => @response_waiting[correlation_id][:options][:type]}
|
531
|
-
|
532
|
-
#delete message_id from waiting stack
|
533
|
-
@response_waiting.delete(correlation_id) unless @response_waiting.nil?
|
534
|
-
end
|
535
|
-
|
536
|
-
def set_model
|
537
|
-
collection = response_value(get('/-/'))
|
538
|
-
@model = Occi::Model.new(collection)
|
539
|
-
|
540
|
-
@mixins = {
|
541
|
-
:os_tpl => [],
|
542
|
-
:resource_tpl => [],
|
543
|
-
:simulation => []
|
544
|
-
}
|
545
|
-
|
546
|
-
#
|
547
|
-
get_os_templates.each do |os_tpl|
|
548
|
-
@mixins[:os_tpl] << os_tpl.type_identifier unless os_tpl.nil? or os_tpl.type_identifier.nil?
|
549
|
-
end
|
550
|
-
|
551
|
-
#
|
552
|
-
get_resource_templates.each do |res_tpl|
|
553
|
-
@mixins[:resource_tpl] << res_tpl.type_identifier unless res_tpl.nil? or res_tpl.type_identifier.nil?
|
554
|
-
end
|
555
|
-
end
|
556
|
-
|
557
|
-
# Extracts the resource path from a resource link. It will remove the leading @endpoint
|
558
|
-
# and replace it with a slash.
|
559
|
-
#
|
560
|
-
# @example
|
561
|
-
# sanitize_resource_link "http://localhost:3300/compute/35ad4f45gsf-gsfg524s6gsfg-sfgsf4gsfg"
|
562
|
-
# # => "/compute/35ad4f45gsf-gsfg524s6gsfg-sfgsf4gsfg"
|
563
|
-
#
|
564
|
-
# @param [String] string containing the full resource link
|
565
|
-
# @return [String] extracted path, with a leading slash
|
566
|
-
def sanitize_resource_link(resource_link)
|
567
|
-
raise "Resource link #{resource_link} is not valid!" unless resource_link.start_with? @endpoint
|
568
|
-
|
569
|
-
resource_link.gsub @endpoint, '/'
|
570
|
-
end
|
571
|
-
|
572
|
-
#TODO filter
|
573
|
-
def get(path='', is_uri_list = false, filter=nil)
|
574
|
-
raise "OCCI AMQP is not connected!" if !@connected
|
575
|
-
|
576
|
-
options = {
|
577
|
-
:routing_key => @endpoint_queue,
|
578
|
-
:type => "get",
|
579
|
-
:content_type => "text/plain",
|
580
|
-
:reply_to => reply_queue_name,
|
581
|
-
:message_id => next_message_id,
|
582
|
-
:headers => {
|
583
|
-
:accept => is_uri_list ? "text/uri-list" : @media_type,
|
584
|
-
:path_info => "/" + path.gsub(/\A\//, '')
|
585
|
-
}
|
586
|
-
}
|
587
|
-
|
588
|
-
publish('', options)
|
589
|
-
|
590
|
-
return options[:message_id]
|
591
|
-
end
|
592
|
-
|
593
|
-
#@description
|
594
|
-
#@param [String] path path to the resource
|
595
|
-
#@param [OCCI::Collection] collection
|
596
|
-
def post(path, collection=nil)
|
597
|
-
path = path.reverse.chomp('/').reverse
|
598
|
-
|
599
|
-
if @media_type == 'application/occi+json'
|
600
|
-
message = collection.to_json
|
601
|
-
content_type = 'application/occi+json'
|
602
|
-
else
|
603
|
-
message = collection.to_text
|
604
|
-
content_type = 'text/plain'
|
605
|
-
end
|
606
|
-
|
607
|
-
options = {
|
608
|
-
:routing_key => @endpoint_queue,
|
609
|
-
:content_type => content_type,
|
610
|
-
:type => "post",
|
611
|
-
:reply_to => reply_queue_name, #queue for response from the rOCCI
|
612
|
-
:message_id => next_message_id, #Identifier for message so that the client can match the answer from the rOCCI
|
613
|
-
:headers => {
|
614
|
-
:accept => "text/uri-list",
|
615
|
-
:path_info => "/#{ path }",
|
616
|
-
:auth => {
|
617
|
-
:type => "basic",
|
618
|
-
:username => "user",
|
619
|
-
:password => "mypass",
|
620
|
-
},
|
621
|
-
}
|
622
|
-
}
|
623
|
-
|
624
|
-
publish(message, options)
|
625
|
-
|
626
|
-
return options[:message_id]
|
627
|
-
end
|
628
|
-
|
629
|
-
# Performs DELETE requests and returns True on success.
|
630
|
-
#
|
631
|
-
# @example
|
632
|
-
# del "/compute/65sf4g65sf4g-sf6g54sf5g-sfgsf32g3" # => true
|
633
|
-
#
|
634
|
-
# @param [String] path for the DELETE request
|
635
|
-
# @param [Occi::Collection] collection of filters (currently NOT used)
|
636
|
-
# @return [Boolean] status
|
637
|
-
def del(path, filter=nil)
|
638
|
-
# remove the leading slash
|
639
|
-
path.gsub!(/\A\//, '')
|
640
|
-
|
641
|
-
options = {
|
642
|
-
:routing_key => @endpoint_queue,
|
643
|
-
:content_type => "text/plain",
|
644
|
-
:type => "delete",
|
645
|
-
:reply_to => reply_queue_name, #queue for response from the rOCCI
|
646
|
-
:message_id => next_message_id, #Identifier for message so that the client can match the answer from the rOCCI
|
647
|
-
:headers => {
|
648
|
-
:accept => @media_type,
|
649
|
-
:path_info => "/#{ path }",
|
650
|
-
:auth => {
|
651
|
-
:type => "basic",
|
652
|
-
:username => "user",
|
653
|
-
:password => "mypass",
|
654
|
-
},
|
655
|
-
}
|
656
|
-
}
|
657
|
-
|
658
|
-
publish('', options)
|
659
|
-
|
660
|
-
return options[:message_id]
|
661
|
-
end
|
662
|
-
|
663
|
-
def response_value(message_id, is_wait = true, is_parse_response = true)
|
664
|
-
waiting_for_response(message_id) if is_wait || is_parse_response
|
665
|
-
|
666
|
-
if is_parse_response
|
667
|
-
value = parse_message(message_id)
|
668
|
-
else
|
669
|
-
value = message_id
|
670
|
-
end
|
671
|
-
|
672
|
-
return value
|
673
|
-
end
|
674
|
-
|
675
|
-
# @param [String] message_id '' == all
|
676
|
-
def waiting_for_response(message_id = '')
|
677
|
-
return if @response_waiting.nil?
|
678
|
-
|
679
|
-
if message_id.size > 0
|
680
|
-
while !@response_waiting[message_id].nil?
|
681
|
-
sleep(0.1)
|
682
|
-
end
|
683
|
-
else
|
684
|
-
while size(@response_waiting) > 0
|
685
|
-
sleep(0.1)
|
686
|
-
end
|
687
|
-
end
|
688
|
-
end
|
689
|
-
|
690
|
-
def publish(message, options = {})
|
691
|
-
raise "No Message Id found" if options[:message_id] == nil?
|
692
|
-
|
693
|
-
@exchange.publish(message, options)
|
694
|
-
|
695
|
-
@response_waiting = Hash.new if @response_waiting.nil?
|
696
|
-
@response_waiting[options[:message_id]] = {:options => options, :message => message}
|
697
|
-
end
|
698
|
-
|
699
|
-
def reply_queue_name
|
700
|
-
@replies_queue.name
|
701
|
-
end
|
702
|
-
|
703
|
-
def next_message_id
|
704
|
-
@message_id = 0 if @message_id.nil?
|
705
|
-
@message_id += 1
|
706
|
-
@message_id.to_s;
|
707
|
-
end
|
708
|
-
|
709
|
-
# Sets auth method and appropriate httparty attributes. Supported auth methods
|
710
|
-
# are: ["none"] and nil
|
711
|
-
#
|
712
|
-
# @example
|
713
|
-
# change_auth { :type => "none" }
|
714
|
-
#
|
715
|
-
# @param [Hash] authentication options
|
716
|
-
def change_auth(auth_options)
|
717
|
-
@auth_options = auth_options
|
718
|
-
|
719
|
-
case @auth_options[:type]
|
720
|
-
when "none", nil
|
721
|
-
# do nothing
|
722
|
-
else
|
723
|
-
raise ArgumentError, "Unknown AUTH method [#{@auth_options[:type]}]!"
|
724
|
-
end
|
725
|
-
end
|
726
|
-
|
727
|
-
def handle_channel_exception(channel, channel_close)
|
728
|
-
Occi::Log.error "OCCI/AMQP: Channel-level exception [ code = #{channel_close.reply_code}, message = #{channel_close.reply_text} ]"
|
729
|
-
end
|
730
|
-
|
731
|
-
# Sets the logger and log levels. This allows users to pass existing logger
|
732
|
-
# instances to the rOCCI client.
|
733
|
-
#
|
734
|
-
# @example
|
735
|
-
# set_logger { :out => STDERR, :level => Occi::Log::WARN, :logger => nil }
|
736
|
-
#
|
737
|
-
# @param [Hash] logger options
|
738
|
-
def set_logger(log_options)
|
739
|
-
if log_options[:logger].nil? or (not log_options[:logger].kind_of? Occi::Log)
|
740
|
-
logger = Occi::Log.new(log_options[:out])
|
741
|
-
logger.level = log_options[:level]
|
742
|
-
end
|
743
|
-
|
744
|
-
self.class.debug_output $stderr if log_options[:level] == Occi::Log::DEBUG
|
745
|
-
end
|
746
|
-
|
747
|
-
# Checks whether the given endpoint URI is valid and adds a trailing
|
748
|
-
# slash if necessary.
|
749
|
-
#
|
750
|
-
# @example
|
751
|
-
# prepare_endpoint "http://localhost:3300" # => "http://localhost:3300/"
|
752
|
-
#
|
753
|
-
# @param [String] endpoint URI in a non-canonical string
|
754
|
-
# @return [String] canonical endpoint URI in a string, with a trailing slash
|
755
|
-
def prepare_endpoint(endpoint)
|
756
|
-
raise 'Endpoint not a valid URI' if (endpoint =~ URI::ABS_URI).nil?
|
757
|
-
|
758
|
-
@endpoint = endpoint.chomp('/') + '/'
|
759
|
-
@endpoint_queue = "amqp.occi.#{@endpoint}"
|
760
|
-
end
|
761
|
-
|
762
|
-
end
|
763
|
-
|
764
|
-
end
|
765
|
-
end
|
766
|
-
end
|