occi 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/.gitignore +9 -0
  2. data/.project +32 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +8 -0
  5. data/.yardopts +1 -0
  6. data/AUTHORS +8 -0
  7. data/Gemfile +35 -14
  8. data/Gemfile.lock +89 -32
  9. data/LICENSE +13 -4
  10. data/README.md +70 -21
  11. data/Rakefile +25 -21
  12. data/lib/OpenNebula/Acl.rb +256 -0
  13. data/lib/OpenNebula/AclPool.rb +53 -0
  14. data/lib/OpenNebula/Group.rb +147 -0
  15. data/lib/OpenNebula/GroupPool.rb +54 -0
  16. data/lib/OpenNebula/Host.rb +143 -0
  17. data/lib/OpenNebula/HostPool.rb +55 -0
  18. data/lib/OpenNebula/Image.rb +256 -0
  19. data/lib/OpenNebula/ImagePool.rb +74 -0
  20. data/lib/OpenNebula/OpenNebula.rb +137 -0
  21. data/lib/OpenNebula/Pool.rb +285 -0
  22. data/lib/OpenNebula/Template.rb +173 -0
  23. data/lib/OpenNebula/TemplatePool.rb +74 -0
  24. data/lib/OpenNebula/User.rb +157 -0
  25. data/lib/OpenNebula/UserPool.rb +53 -0
  26. data/lib/OpenNebula/VirtualMachine.rb +319 -0
  27. data/lib/OpenNebula/VirtualMachinePool.rb +120 -0
  28. data/lib/OpenNebula/VirtualNetwork.rb +229 -0
  29. data/lib/OpenNebula/VirtualNetworkPool.rb +74 -0
  30. data/lib/OpenNebula/XMLUtils.rb +337 -0
  31. data/lib/occi/antlr/.gitignore +18 -0
  32. data/lib/occi/antlr/OCCI.g +164 -0
  33. data/lib/occi/antlr/OCCI.tokens +75 -0
  34. data/lib/occi/antlr/OCCILexer.rb +1532 -0
  35. data/lib/occi/antlr/OCCIParser.rb +2472 -0
  36. data/lib/occi/antlr/README.md +2 -0
  37. data/lib/occi/backend/dummy.rb +232 -0
  38. data/lib/occi/backend/ec2/Compute.rb +310 -0
  39. data/lib/occi/backend/ec2/compute.rb +310 -0
  40. data/lib/occi/backend/ec2/ec2.rb +215 -0
  41. data/lib/occi/backend/manager.rb +131 -0
  42. data/lib/occi/backend/opennebula/compute.rb +360 -0
  43. data/lib/occi/backend/opennebula/network.rb +143 -0
  44. data/lib/occi/backend/opennebula/opennebula.rb +188 -0
  45. data/lib/occi/backend/opennebula/storage.rb +175 -0
  46. data/lib/occi/configuration.rb +118 -0
  47. data/lib/occi/core/action.rb +29 -0
  48. data/lib/occi/core/attribute_properties.rb +54 -0
  49. data/lib/occi/core/attributes.rb +40 -0
  50. data/lib/occi/core/category.rb +62 -0
  51. data/lib/occi/core/collection.rb +27 -0
  52. data/lib/occi/core/entity.rb +135 -0
  53. data/lib/occi/core/kind.rb +55 -0
  54. data/lib/occi/core/link.rb +88 -0
  55. data/lib/occi/core/mixin.rb +43 -0
  56. data/lib/occi/core/resource.rb +73 -0
  57. data/lib/occi/exceptions.rb +59 -0
  58. data/lib/occi/extensions/monitoring/cpu.rb +51 -0
  59. data/lib/occi/extensions/monitoring/memory.rb +49 -0
  60. data/lib/occi/extensions/monitoring/metric.rb +54 -0
  61. data/lib/occi/extensions/monitoring/netrx.rb +49 -0
  62. data/lib/occi/extensions/monitoring/nettx.rb +55 -0
  63. data/lib/occi/extensions/one/VNC.rb +58 -0
  64. data/lib/occi/extensions/one/vnc.rb +58 -0
  65. data/lib/occi/log.rb +47 -0
  66. data/lib/occi/parse.rb +164 -0
  67. data/lib/occi/registry.rb +87 -0
  68. data/lib/occi/server.rb +594 -0
  69. data/lib/occi/version.rb +3 -0
  70. data/occi.gemspec +18 -96
  71. data/spec/occi/antlr/parser_spec.rb +82 -0
  72. data/spec/spec_helper.rb +6 -0
  73. metadata +100 -208
  74. data/.autotest +0 -21
  75. data/.rvmrc +0 -1
  76. data/VERSION +0 -1
  77. data/lib/occi.rb +0 -1
  78. data/lib/occi/client.rb +0 -36
  79. data/lib/occi/compute.rb +0 -4
  80. data/lib/occi/network.rb +0 -4
  81. data/lib/occi/resource.rb +0 -59
  82. data/lib/occi/storage.rb +0 -7
  83. data/test/fixtures/cassettes/compute_all.yml +0 -28
  84. data/test/fixtures/cassettes/compute_create.yml +0 -45
  85. data/test/fixtures/cassettes/compute_destroy.yml +0 -22
  86. data/test/fixtures/cassettes/compute_find.yml +0 -28
  87. data/test/fixtures/cassettes/compute_update.yml +0 -35
  88. data/test/fixtures/cassettes/network_all.yml +0 -28
  89. data/test/fixtures/cassettes/network_create.yml +0 -37
  90. data/test/fixtures/cassettes/network_destroy.yml +0 -22
  91. data/test/fixtures/cassettes/network_find.yml +0 -28
  92. data/test/fixtures/cassettes/storage_all.yml +0 -28
  93. data/test/fixtures/cassettes/storage_create.yml +0 -50
  94. data/test/fixtures/cassettes/storage_destroy.yml +0 -22
  95. data/test/fixtures/cassettes/storage_find.yml +0 -28
  96. data/test/lib/occi/compute_test.rb +0 -91
  97. data/test/lib/occi/network_test.rb +0 -57
  98. data/test/lib/occi/storage_test.rb +0 -67
  99. data/test/test_helper.rb +0 -48
@@ -0,0 +1,594 @@
1
+ ##############################################################################
2
+ # Copyright 2011 Service Computing group, TU Dortmund
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ##############################################################################
16
+
17
+ ##############################################################################
18
+ # Description: OCCI RESTful Web Service
19
+ # Author(s): Hayati Bice, Florian Feldhaus, Piotr Kasprzak
20
+ ##############################################################################
21
+
22
+ ##############################################################################
23
+ # Require Ruby Gems
24
+
25
+ # gems
26
+ require 'rubygems'
27
+
28
+ # sinatra
29
+ require 'sinatra'
30
+ require 'sinatra/multi_route'
31
+ require 'sinatra/cross_origin'
32
+ require 'sinatra/respond_with'
33
+
34
+ # Ruby standard library
35
+ require 'uri'
36
+ require 'fileutils'
37
+
38
+ # Server configuration
39
+ require 'occi/configuration'
40
+
41
+ # Active support notifications
42
+ require 'active_support/notifications'
43
+
44
+ # Active support for xml rendering
45
+ require 'active_support/core_ext'
46
+
47
+ ##############################################################################
48
+ # Require OCCI classes
49
+
50
+ # Exceptions
51
+ require 'occi/exceptions'
52
+
53
+ # Category registry
54
+ require 'occi/registry'
55
+
56
+ # OCCI Core classes
57
+ require 'occi/core/action'
58
+ require 'occi/core/category'
59
+ require 'occi/core/entity'
60
+ require 'occi/core/kind'
61
+ require 'occi/core/link'
62
+ require 'occi/core/mixin'
63
+ require 'occi/core/resource'
64
+
65
+ # OCCI parser
66
+ require 'occi/parse'
67
+
68
+ # Backend support
69
+ require 'occi/backend/manager'
70
+
71
+ ##############################################################################
72
+ # Sinatra methods for handling HTTP requests
73
+
74
+ module OCCI
75
+ class Server < Sinatra::Application
76
+
77
+ register Sinatra::MultiRoute
78
+ register Sinatra::CrossOrigin
79
+ register Sinatra::RespondWith
80
+
81
+ enable cross_origin
82
+
83
+ # Read configuration file
84
+ def self.config
85
+ @@config ||= OCCI::Configuration.new('etc/occi-server.conf')
86
+ end
87
+
88
+ def self.location
89
+ OCCI::Server.config[:server].chomp('/')
90
+ end
91
+
92
+ def self.port
93
+ OCCI::Server.config[:port]
94
+ end
95
+
96
+ def self.uri
97
+ self.port.nil? ? self.location : self.location + ':' + self.port
98
+ end
99
+
100
+ def initialize(config = {})
101
+ # create logger
102
+ config[:log_dest] ||= STDOUT
103
+ config[:log_level] ||= Logger::INFO
104
+ config[:log_level] = case OCCI::Server.config[:log_level]
105
+ when "debug"
106
+ Logger::DEBUG
107
+ when "info"
108
+ Logger::INFO
109
+ when "warn"
110
+ Logger::WARN
111
+ when "error"
112
+ Logger::ERROR
113
+ when "fatal"
114
+ Logger::FATAL
115
+ else
116
+ Logger::INFO
117
+ end
118
+
119
+ @logger = Logger.new(config[:log_dest])
120
+ @logger.level = config[:log_level]
121
+
122
+ # subscribe to log messages and send to logger
123
+ @log_subscriber = ActiveSupport::Notifications.subscribe("log") do |name, start, finish, id, payload|
124
+ @logger.log(payload[:level], payload[:message])
125
+ end
126
+
127
+ # Configuration of HTTP Authentication
128
+ if OCCI::Server.config['username'] != nil and OCCI::Server.config['password'] != nil
129
+ use Rack::Auth::Basic, "Restricted Area" do |username, password|
130
+ [username, password] == [OCCI::Server.config['username'], OCCI::Server.config['password']]
131
+ end
132
+ end
133
+
134
+ OCCI::Server.initialize_core_model
135
+ OCCI::Server.initialize_model(OCCI::Server.config['occi_model_path'])
136
+
137
+ # set views explicitly
138
+ set :views, File.dirname(__FILE__) + "/../../views"
139
+
140
+ super
141
+ end
142
+
143
+ # ---------------------------------------------------------------------------------------------------------------------
144
+
145
+ def self.initialize_core_model
146
+ OCCI::Log.info("### Initializing OCCI Core Model ###")
147
+ OCCI::Core::Entity.register
148
+ OCCI::Core::Resource.register
149
+ OCCI::Core::Link.register
150
+ end
151
+
152
+ def self.initialize_model(path)
153
+ OCCI::Log.info("### Initializing OCCI Model from #{path} ###")
154
+ Dir.glob(path + '/**/*.json').each do |file|
155
+ collection = Hashie::Mash.new(JSON.parse(File.read(file)))
156
+ # add location of service provider to scheme if it has a relative location
157
+ collection.kinds.collect { |kind| kind.scheme = self.location + kind.scheme if kind.scheme.start_with? '/' } if collection.kinds
158
+ collection.mixins.collect { |mixin| mixin.scheme = self.location + mixin.scheme if mixin.scheme.start_with? '/' } if collection.mixins
159
+ collection.actions.collect { |action| action.scheme = self.location + action.scheme if action.scheme.start_with? '/' } if collection.actions
160
+ # register categories
161
+ collection.kinds.each { |kind| OCCI::Registry.register(OCCI::Core::Kind.new(kind)) } if collection.kinds
162
+ collection.mixins.each { |mixin| OCCI::Registry.register(OCCI::Core::Mixin.new(mixin)) } if collection.mixins
163
+ collection.actions.each { |action| OCCI::Registry.register(OCCI::Core::Action.new(action)) } if collection.actions
164
+ end
165
+ end
166
+
167
+ # ---------------------------------------------------------------------------------------------------------------------
168
+ def initialize_backend(auth)
169
+
170
+ if auth.provided? && auth.basic? && auth.credentials
171
+ user, password = auth.credentials
172
+ else
173
+ user, password = [OCCI::Server.config['one_user'], OCCI::Server.config['one_password']]
174
+ logger.debug("No basic auth data provided: using defaults from config (user = '#{user}')")
175
+ end
176
+
177
+ @backend = case OCCI::Server.config["backend"]
178
+ when "opennebula"
179
+ require 'occi/backend/opennebula/opennebula'
180
+ OCCI::Server.initialize_model('etc/backend/opennebula')
181
+ OCCI::Backend::Manager.register_backend(OCCI::Backend::OpenNebula::OpenNebula, OCCI::Backend::OpenNebula::OpenNebula::OPERATIONS)
182
+ OCCI::Backend::OpenNebula::OpenNebula.new(user, password)
183
+ when "ec2"
184
+ require 'occi/backend/ec2/ec2'
185
+ Bundler.require(:ec2)
186
+ OCCI::Server.initialize_model('etc/backend/ec2')
187
+ OCCI::Backend::Manager.register_backend(OCCI::Backend::EC2::EC2, OCCI::Backend::EC2::EC2::OPERATIONS)
188
+ OCCI::Backend::EC2::EC2.new(user, password)
189
+ when "dummy" then
190
+ require 'occi/backend/dummy'
191
+ OCCI::Backend::Manager.register_backend(OCCI::Backend::Dummy, OCCI::Backend::Dummy::OPERATIONS)
192
+ OCCI::Backend::Dummy.new()
193
+ else
194
+ raise "Backend '" + OCCI::Server.config["backend"] + "' not found"
195
+ end
196
+
197
+
198
+ end
199
+
200
+ # Parses a Rack/Sinatra Request and extract OCCI relevant information
201
+ def parse(request)
202
+ OCCI::Log.debug('### Parsing request data to OCCI data structure ###')
203
+ body = request.body.read
204
+ header = request.env
205
+ collection = OCCI::Core::Collection.new
206
+ locations = []
207
+ # always check headers
208
+ locations = OCCI::Parse.header_locations(header)
209
+ if locations.empty?
210
+ if request.path_info.include?('/-/')
211
+ collection = OCCI::Parse.header_categories(header)
212
+ else
213
+ collection = OCCI::Parse.header_entity(header)
214
+ end
215
+ end
216
+
217
+ case request.media_type
218
+ when 'text/uri-list'
219
+ body.each_line do |line|
220
+ locations << URI.parse(line)
221
+ end
222
+ when 'text/plain', nil
223
+ locations = OCCI::Parse.text_locations(body)
224
+ if locations.empty?
225
+ if request.path_info.include?('/-/')
226
+ collection = OCCI::Parse.text_categories(body)
227
+ else
228
+ collection = OCCI::Parse.text_entity(body)
229
+ end
230
+ end
231
+ when 'application/occi+json', 'application/json'
232
+ collection = OCCI::Parse.json(body)
233
+ when 'application/occi+xml', 'application/xml'
234
+ collection = OCCI::Parse.xml(body)
235
+ else
236
+ raise OCCI::ContentTypeNotSupported
237
+ end
238
+ return locations, collection
239
+ end
240
+
241
+ # ---------------------------------------------------------------------------------------------------------------------
242
+
243
+ # GET request
244
+
245
+ # tasks to be executed before the request is handled
246
+ before do
247
+ OCCI::Log.debug('--------------------------------------------------------------------')
248
+ OCCI::Log.debug("### Client IP: #{request.ip}")
249
+ OCCI::Log.debug("### Client Accept: #{request.accept}")
250
+ OCCI::Log.debug("### Client User Agent: #{request.user_agent}")
251
+ OCCI::Log.debug("### Client Request URL: #{request.url}")
252
+ OCCI::Log.debug("### Client Request method: #{request.request_method}")
253
+ OCCI::Log.debug("### Client Request Media Type: #{request.media_type}")
254
+ OCCI::Log.debug('--------------------------------------------------------------------')
255
+
256
+ OCCI::Log.debug('### Prepare response ###')
257
+ response['Accept'] = "application/occi+json,application/json,text/plain,text/uri-list,application/xml,text/xml,application/occi+xml"
258
+ response['Server'] = "rOCCI/#{VERSION_NUMBER} OCCI/1.1"
259
+ OCCI::Log.debug('### Initialize response OCCI collection ###')
260
+ @collection = Hashie::Mash.new(:kinds => [], :mixins => [], :actions => [], :resources => [], :links => [])
261
+ @locations = Array.new
262
+ OCCI::Log.debug('### Preparing authentication handling ###')
263
+ authentication = Rack::Auth::Basic::Request.new(request.env)
264
+ OCCI::Log.debug('### Initializing backend ###')
265
+ initialize_backend(authentication)
266
+ OCCI::Log.debug('### Reset OCCI model ###')
267
+ OCCI::Registry.reset
268
+ @request_locations, @request_collection = parse(request)
269
+ OCCI::Log.debug('### Fill OCCI model with entities from backend ###')
270
+ @backend.register_existing_resources
271
+ end
272
+
273
+ after do
274
+ # OCCI::Log.debug((@collection.resources.to_a + @collection.links.to_a).collect {|entity| entity.location}.join("\n"))
275
+ OCCI::Log.debug('### Rendering response ###')
276
+ @collection.delete_if { |k, v| v.empty? } # remove empty entries
277
+ respond_to do |f|
278
+ f.txt { erb :collection, :locals => {:collection => @collection, :locations => @locations} }
279
+ f.on('*/*') { erb :collection, :locals => {:collection => @collection, :locations => @locations} }
280
+ # f.html { haml :collection, :locals => {:collection => @collection} }
281
+ f.json { @collection.to_json }
282
+ f.on('application/occi+json') { @collection.to_json }
283
+ f.xml { @collection.to_xml(:root => "collection") }
284
+ f.on('application/occi+xml') { @collection.to_xml(:root => "collection") }
285
+ f.on('text/uri-list') { @locations.join("\n") }
286
+ end
287
+ OCCI::Log.debug('### Successfully rendered ###')
288
+ end
289
+
290
+ # discovery interface
291
+ # returns all kinds, mixins and actions registered for the server
292
+ get '/-/', '/.well-known/org/ogf/occi/-/' do
293
+ OCCI::Log.info("### Listing all kinds, mixins and actions ###")
294
+ @collection = OCCI::Registry.get(@request_collection.categories)
295
+ status 200
296
+ end
297
+
298
+ # Resource retrieval
299
+ # returns entities either below a certain path or belonging to a certain kind or mixin
300
+ get '*' do
301
+ if request.path_info.end_with?('/')
302
+ if request.path_info == '/'
303
+ kinds = OCCI::Registry.get.kinds
304
+ else
305
+ kinds = [OCCI::Registry.get_by_location(request.path_info)]
306
+ end
307
+
308
+ kinds.each do |kind|
309
+ OCCI::Log.info("### Listing all entities of kind #{kind.type_identifier} ###")
310
+ @collection.resources.concat kind.entities if kind.entity_type == OCCI::Core::Resource.name
311
+ @collection.links.concat kind.entities if kind.entity_type == OCCI::Core::Link.name
312
+ @locations.concat kind.entities.collect { |entity| OCCI::Server.uri + entity.location }
313
+ end
314
+ else
315
+ kind = OCCI::Registry.get_by_location(request.path_info.rpartition('/').first + '/')
316
+ uuid = request.path_info.rpartition('/').last
317
+ error 404 if kind.nil? or uuid.nil?
318
+ OCCI::Log.info("### Listing entity with uuid #{uuid} ###")
319
+ @collection.resources = kind.entities.select { |entity| entity.id == uuid } if kind.entity_type == OCCI::Core::Resource.name
320
+ @collection.links = kind.entities.select { |entity| entity.id == uuid } if kind.entity_type == OCCI::Core::Link.name
321
+ end
322
+ status 200
323
+ end
324
+
325
+ # ---------------------------------------------------------------------------------------------------------------------
326
+ # POST request
327
+ post '/-/', '/.well-known/org/ogf/occi/-/' do
328
+ logger.info("## Creating user defined mixin ###")
329
+ raise OCCI::MixinAlreadyExistsError, "Mixin already exists!" if OCCI::Registry.get(@request_collection.mixins)
330
+ @request_collection.mixins.each do |mixin|
331
+ OCCI::CategoryRegistry.register(mixin)
332
+ # TODO: inform backend about new mixin
333
+ end
334
+ end
335
+
336
+ # Create an instance appropriate to category field and optionally link an instance to another one
337
+ post '*' do
338
+
339
+ category = OCCI::Registry.get_by_location(request.path_info.rpartition('/').first + '/')
340
+
341
+ if category.nil?
342
+ OCCI::Log.debug("### No category found for request location #{request.path_info} ###")
343
+ status 404
344
+ end
345
+
346
+ # if action
347
+ if params[:action]
348
+ if @request_collection.actions.any?
349
+ action = @request_collection.actions.first
350
+ params[:method] ||= action.attributes!.method if action
351
+ else
352
+ action = OCCI::Registry.get_by_id(category.actions.select { |action| action.split('#').last == params[:action] }.first)
353
+ end
354
+
355
+ category.entities.each do |entity|
356
+ OCCI::Backend::Manager.delegate_action(@backend, action, params, entity)
357
+ status 200
358
+ end
359
+ elsif category.kind_of?(OCCI::Core::Kind)
360
+ @request_collection.resources.each do |resource|
361
+ OCCI::Log.debug("Deploying resource with title #{resource.title} in backend #{@backend.class.name}")
362
+ OCCI::Backend::Manager.signal_resource(@backend, OCCI::Backend::RESOURCE_DEPLOY, resource)
363
+ @locations << OCCI::Server.uri + resource.location
364
+ status 201
365
+ end
366
+ elsif category.kind_of?(OCCI::Core::Mixin)
367
+ @request_collection.locations.each do |location|
368
+ OCCI::Log.debug("Attaching resource #{resource.title} to mixin #{mixin.type_identifier} in backend #{@backend.class.name}")
369
+ # TODO: let backend carry out tasks related to the added mixin
370
+ category.entities << OCCI::Rendering::HTTP::LocationRegistry.get_object(location)
371
+ status 200
372
+ end
373
+ else
374
+ status 400
375
+ end
376
+
377
+ end
378
+
379
+ # ---------------------------------------------------------------------------------------------------------------------
380
+ # PUT request
381
+
382
+ put '*' do
383
+
384
+ # Add an resource instance to a mixin
385
+ unless @occi_request.mixins.empty?
386
+ mixin = OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info)
387
+
388
+ @occi_request.locations.each do |location|
389
+ entity = OCCI::Rendering::HTTP::LocationRegistry.get_object(URI.parse(location).path)
390
+
391
+ raise "No entity found at location: #{entity_location}" if entity == nil
392
+ raise "Object referenced by uri [#{entity_location}] is not a OCCI::Core::Resource instance!" if !entity.kind_of?(OCCI::Core::Resource)
393
+
394
+ logger.debug("Associating entity [#{entity}] at location #{entity_location} with mixin #{mixin}")
395
+
396
+ entity.mixins << mixin
397
+ end
398
+ break
399
+ end
400
+
401
+ # Update resource instance(s) at the given location
402
+ unless OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info).nil?
403
+ entities = []
404
+ # Determine set of resources to be updated
405
+ if OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info).kind_of?(OCCI::Core::Resource)
406
+ entities = [OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info)]
407
+ elsif not OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info).kind_of?(OCCI::Core::Category)
408
+ entities = OCCI::Rendering::HTTP::LocationRegistry.get_resources_below_location(request.path_info, OCCI::CategoryRegistry.get_all)
409
+ elsif OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info).kind_of?(OCCI::Core::Category)
410
+ object = OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info)
411
+ @occi_request.locations.each do |loc|
412
+ entities << OCCI::Rendering::HTTP::LocationRegistry.get_object(URI.parse(loc.chomp('"').reverse.chomp('"').reverse).path)
413
+ end
414
+ end
415
+ logger.info("Full update for [#{entities.size}] entities...")
416
+
417
+ # full update of mixins
418
+ object.entities.each do |entity|
419
+ entity.mixins.delete(object)
420
+ object.entities.delete(entity)
421
+ end if object.kind_of?(OCCI::Core::Mixin)
422
+
423
+ entities.each do |entity|
424
+ logger.debug("Adding entity: #{entity.get_location} to mixin #{object.type_identifier}")
425
+ entity.mixins.push(object).uniq!
426
+ object.entities.push(entity).uniq!
427
+ end if object.kind_of?(OCCI::Core::Mixin)
428
+
429
+ # full update of attributes
430
+ entities.each do |entity|
431
+ # Refresh information from backend for entities of type resource
432
+ # TODO: full update
433
+ entity.attributes.merge!(@occi_request.attributes)
434
+ # TODO: update entity in backend
435
+ end unless @occi_request.attributes.empty?
436
+
437
+ # full update of links
438
+ # TODO: full update e.g. delete old links first
439
+ @occi_request.links.each do |link_data|
440
+ logger.debug("Extracted link data: #{link_data}")
441
+ raise "Mandatory information missing (related | target | category)!" unless link_data.related != nil && link_data.target != nil && link_data.category != nil
442
+
443
+ link_mixins = []
444
+ link_kind = nil
445
+ link_data.category.split(' ').each do |link_category|
446
+ begin
447
+ cat = OCCI::CategoryRegistry.get_by_id(link_category)
448
+ rescue OCCI::CategoryNotFoundException => e
449
+ logger.info("Category #{link_category} not found")
450
+ next
451
+ end
452
+ link_kind = cat if cat.kind_of?(OCCI::Core::Kind)
453
+ link_mixins << cat if cat.kind_of?(OCCI::Core::Mixin)
454
+ end
455
+
456
+ raise "No kind for link category #{link_data.category} found" if link_kind.nil?
457
+
458
+ target_location = link_data.target_attr
459
+ target = OCCI::Rendering::HTTP::LocationRegistry.get_object(target_location)
460
+
461
+ entities.each do |entity|
462
+
463
+ source_location = OCCI::Rendering::HTTP::LocationRegistry.get_location_of_object(entity)
464
+
465
+ link_attributes = link_data.attributes.clone
466
+ link_attributes["occi.core.target"] = target_location.chomp('"').reverse.chomp('"').reverse
467
+ link_attributes["occi.core.source"] = source_location
468
+
469
+ link = link_kind.entity_type.new(link_attributes, link_mixins)
470
+ OCCI::Rendering::HTTP::LocationRegistry.register_location(link.get_location(), link)
471
+
472
+ target.links << link
473
+ entity.links << link
474
+ end
475
+ end
476
+ break
477
+ end
478
+
479
+ response.status = OCCI::Rendering::HTTP::Response::HTTP_NOT_FOUND
480
+ # Create resource instance at the given location
481
+ raise "Creating resources with method 'put' is currently not possible!"
482
+
483
+ # This must be the last statement in this block, so that sinatra does not try to respond with random body content
484
+ # (or fail utterly while trying to do that!)
485
+ nil
486
+
487
+ end
488
+
489
+ # ---------------------------------------------------------------------------------------------------------------------
490
+ # DELETE request
491
+
492
+ delete '/-/', '/.well-known/org/ogf/occi/-/' do
493
+ # Location references query interface => delete provided mixin
494
+ raise OCCI::CategoryMissingException if @request_collection.mixins.nil?
495
+ mixins = OCCI::Registry.get(@request_collection.mixins)
496
+ raise OCCI::MixinNotFoundException if mixins.nil?
497
+ mixins.each do |mixin|
498
+ OCCI::Log.debug("### Deleting mixin #{mixin.type_identifier} ###")
499
+ mixin.entities.each do |entity|
500
+ entity.mixins.delete(mixin)
501
+ end
502
+ # TODO: Notify backend to delete mixin and unassociate entities
503
+ OCCI::Registry.unregister(mixin)
504
+ end
505
+ status 200
506
+ end
507
+
508
+ delete '*' do
509
+
510
+ # unassociate resources specified by URI in payload from mixin specified by request location
511
+ if request.path_info == '/'
512
+ categories = OCCI::Registry.get.kinds
513
+ else
514
+ categories = [OCCI::Registry.get_by_location(request.path_info.rpartition('/').first + '/')]
515
+ end
516
+
517
+ categories.each do |category|
518
+ case category
519
+ when OCCI::Core::Mixin
520
+ mixin = category
521
+ OCCI::Log.debug("### Deleting entities from mixin #{mixin.type_identifier} ###")
522
+ @request_collection.locations.each do |location|
523
+ uuid = location.to_s.rpartition('/').last
524
+ mixin.entities.delete_if { |entity| entity.id == uuid }
525
+ end
526
+ when OCCI::Core::Kind
527
+ kind = category
528
+ if request.path_info.end_with?('/')
529
+ if @request_collection.mixins.any?
530
+ @request_collection.mixins.each do |mixin|
531
+ OCCI::Log.debug("### Deleting entities from kind #{kind.type_identifier} with mixin #{mixin.type_identifier} ###")
532
+ kind.entities.each { |entity| OCCI::Backend::Manager.signal_resource(@backend, OCCI::Backend::RESOURCE_DELETE, entity) if mixin.include?(entity) }
533
+ kind.entities.delete_if? { |entity| mixin.include?(entity) }
534
+ # TODO: links
535
+ end
536
+ else
537
+ # TODO: links
538
+ OCCI::Log.debug("### Deleting entities from kind #{kind.type_identifier} ###")
539
+ kind.entities.each { |resource| OCCI::Backend::Manager.signal_resource(@backend, OCCI::Backend::RESOURCE_DELETE, resource) }
540
+ kind.entities.clear
541
+ end
542
+ else
543
+ uuid = request.path_info.rpartition('/').last
544
+ OCCI::Log.debug("### Deleting entity with id #{uuid} from kind #{kind.type_identifier} ###")
545
+ OCCI::Backend::Manager.signal_resource(@backend, OCCI::Backend::RESOURCE_DELETE, entity)
546
+ kind.entities.delete_if? { |entity| entity.id == uuid }
547
+ end
548
+ end
549
+ end
550
+
551
+ # delete entities
552
+
553
+
554
+ # # Location references a mixin => unassociate all provided resources (by X_OCCI_LOCATION) from it
555
+ # object = OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info)
556
+ # if object != nil && object.kind_of?(OCCI::Core::Mixin)
557
+ # mixin = OCCI::Rendering::HTTP::LocationRegistry.get_object(request.path_info)
558
+ # logger.info("Unassociating entities from mixin: #{mixin}")
559
+ #
560
+ # @occi_request.locations.each do |loc|
561
+ # entity = OCCI::Rendering::HTTP::LocationRegistry.get_object(URI.parse(loc.chomp('"').reverse.chomp('"').reverse).path)
562
+ # mixin.entities.delete(entity)
563
+ # entity.mixins.delete(mixin)
564
+ # end
565
+ # break
566
+ # end
567
+ #
568
+ # entities = OCCI::Rendering::HTTP::LocationRegistry.get_resources_below_location(request.path_info, @occi_request.categories)
569
+ #
570
+ # unless entities.nil?
571
+ # entities.each do |entity|
572
+ # location = entity.get_location
573
+ # OCCI::Backend::Manager.signal_resource(@backend, OCCI::Backend::RESOURCE_DELETE, entity) if entity.kind_of? OCCI::Core::Resource
574
+ # # TODO: delete links in backend!
575
+ # entity.delete
576
+ # OCCI::Rendering::HTTP::LocationRegistry.unregister(location)
577
+ # end
578
+ # break
579
+ # end
580
+ #
581
+ # response.status = OCCI::Rendering::HTTP::Response::HTTP_NOT_FOUND
582
+ # # This must be the last statement in this block, so that sinatra does not try to respond with random body content
583
+ # # (or fail utterly while trying to do that!)
584
+ # nil
585
+
586
+ end
587
+
588
+ error do
589
+ OCCI::Log.error(sinatra.error)
590
+ 'Sorry there was a nasty error - ' + env['sinatra.error'].name
591
+ end
592
+
593
+ end
594
+ end