deltacloud-core 0.1.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/DISCLAIMER +8 -0
  2. data/{COPYING → LICENSE} +0 -0
  3. data/NOTICE +13 -0
  4. data/Rakefile +50 -51
  5. data/bin/deltacloudd +8 -1
  6. data/config.ru +0 -2
  7. data/config/drivers.yaml +48 -0
  8. data/deltacloud-core.gemspec +75 -0
  9. data/deltacloud.rb +3 -2
  10. data/lib/deltacloud/backend_capability.rb +15 -3
  11. data/lib/deltacloud/base_driver.rb +0 -2
  12. data/lib/deltacloud/base_driver/base_driver.rb +85 -89
  13. data/lib/deltacloud/base_driver/features.rb +61 -7
  14. data/lib/deltacloud/base_driver/mock_driver.rb +42 -43
  15. data/lib/deltacloud/core_ext.rb +18 -0
  16. data/lib/deltacloud/core_ext/integer.rb +31 -0
  17. data/lib/deltacloud/core_ext/string.rb +50 -0
  18. data/lib/deltacloud/drivers/azure/azure_driver.rb +71 -22
  19. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +641 -584
  20. data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +0 -2
  21. data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +167 -0
  22. data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +39 -1
  23. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +41 -25
  24. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob1.yml +6 -3
  25. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob2.yml +6 -3
  26. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob3.yml +4 -2
  27. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob4.yml +5 -2
  28. data/lib/deltacloud/drivers/mock/data/buckets/blobs/blob5.yml +4 -2
  29. data/lib/deltacloud/drivers/mock/data/instances/inst0.yml +1 -0
  30. data/lib/deltacloud/drivers/mock/data/instances/inst1.yml +1 -0
  31. data/lib/deltacloud/drivers/mock/data/instances/inst2.yml +1 -0
  32. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol1.yml +1 -0
  33. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol2.yml +1 -0
  34. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol3.yml +1 -0
  35. data/lib/deltacloud/drivers/mock/mock_driver.rb +138 -30
  36. data/lib/deltacloud/drivers/opennebula/cloud_client.rb +13 -15
  37. data/lib/deltacloud/drivers/opennebula/occi_client.rb +13 -15
  38. data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +13 -15
  39. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +224 -113
  40. data/lib/deltacloud/drivers/rhevm/rhevm_client.rb +332 -0
  41. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +221 -170
  42. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +0 -1
  43. data/lib/deltacloud/drivers/sbc/sbc_client.rb +247 -0
  44. data/lib/deltacloud/drivers/sbc/sbc_driver.rb +297 -0
  45. data/lib/deltacloud/drivers/terremark/terremark_driver.rb +0 -2
  46. data/lib/deltacloud/hardware_profile.rb +1 -3
  47. data/lib/deltacloud/helpers.rb +0 -2
  48. data/lib/deltacloud/helpers/application_helper.rb +86 -12
  49. data/lib/deltacloud/helpers/blob_stream.rb +19 -2
  50. data/lib/deltacloud/helpers/conversion_helper.rb +0 -2
  51. data/lib/deltacloud/helpers/hardware_profiles_helper.rb +0 -2
  52. data/lib/deltacloud/method_serializer.rb +0 -2
  53. data/lib/deltacloud/models/base_model.rb +0 -2
  54. data/lib/deltacloud/models/blob.rb +1 -2
  55. data/lib/deltacloud/models/bucket.rb +0 -2
  56. data/lib/deltacloud/models/image.rb +0 -2
  57. data/lib/deltacloud/models/instance.rb +19 -2
  58. data/lib/deltacloud/models/instance_profile.rb +4 -2
  59. data/lib/deltacloud/models/key.rb +0 -2
  60. data/lib/deltacloud/models/load_balancer.rb +0 -2
  61. data/lib/deltacloud/models/realm.rb +0 -2
  62. data/lib/deltacloud/models/storage_snapshot.rb +0 -2
  63. data/lib/deltacloud/models/storage_volume.rb +4 -2
  64. data/lib/deltacloud/runner.rb +132 -0
  65. data/lib/deltacloud/state_machine.rb +0 -2
  66. data/lib/deltacloud/validation.rb +9 -7
  67. data/lib/drivers.rb +36 -48
  68. data/lib/sinatra/accept_media_types.rb +26 -0
  69. data/lib/sinatra/lazy_auth.rb +16 -0
  70. data/lib/sinatra/rabbit.rb +112 -54
  71. data/lib/sinatra/rack_driver_select.rb +50 -16
  72. data/lib/sinatra/rack_etag.rb +79 -0
  73. data/lib/sinatra/rack_matrix_params.rb +84 -0
  74. data/lib/sinatra/rack_runtime.rb +47 -0
  75. data/lib/sinatra/static_assets.rb +16 -0
  76. data/lib/sinatra/url_for.rb +31 -4
  77. data/public/favicon.ico +0 -0
  78. data/public/images/bread-bg.png +0 -0
  79. data/public/images/error.png +0 -0
  80. data/public/images/pending.png +0 -0
  81. data/public/images/running.png +0 -0
  82. data/public/images/stopped.png +0 -0
  83. data/public/javascripts/application.js +35 -0
  84. data/public/stylesheets/compiled/application.css +59 -5
  85. data/public/stylesheets/compiled/screen.css +1 -1
  86. data/server.rb +293 -29
  87. data/support/fedora/deltacloud-core +78 -0
  88. data/support/fedora/deltacloud-core.spec +143 -0
  89. data/support/fedora/deltacloudd +78 -18
  90. data/support/fedora/rubygem-deltacloud-core.spec +76 -40
  91. data/tests/common.rb +172 -0
  92. data/tests/drivers/mock/api_test.rb +133 -0
  93. data/tests/drivers/mock/hardware_profiles_test.rb +134 -0
  94. data/tests/drivers/mock/images_test.rb +126 -0
  95. data/tests/drivers/mock/instance_states_test.rb +71 -0
  96. data/tests/drivers/mock/instances_test.rb +236 -0
  97. data/tests/drivers/mock/realms_test.rb +93 -0
  98. data/tests/drivers/mock/setup.rb +3 -0
  99. data/tests/drivers/mock/url_for_test.rb +67 -0
  100. data/tests/drivers/rackspace/api_test.rb +41 -0
  101. data/tests/drivers/rackspace/hardware_profiles_test.rb +53 -0
  102. data/tests/drivers/rackspace/images_test.rb +40 -0
  103. data/tests/drivers/rackspace/instances_test.rb +161 -0
  104. data/tests/drivers/rackspace/realms_test.rb +36 -0
  105. data/tests/drivers/rackspace/setup.rb +14 -0
  106. data/tests/drivers/rhevm/api_test.rb +39 -0
  107. data/tests/drivers/rhevm/hardware_profiles_test.rb +53 -0
  108. data/tests/drivers/rhevm/images_test.rb +42 -0
  109. data/tests/drivers/rhevm/instances_test.rb +179 -0
  110. data/tests/drivers/rhevm/realms_test.rb +35 -0
  111. data/tests/drivers/rhevm/setup.rb +14 -0
  112. data/tests/rabbit_test.rb +52 -0
  113. data/views/api/show.html.haml +2 -5
  114. data/views/blobs/new.html.haml +17 -1
  115. data/views/blobs/show.html.haml +6 -0
  116. data/views/blobs/show.xml.haml +5 -1
  117. data/views/buckets/index.html.haml +1 -12
  118. data/views/buckets/index.xml.haml +3 -5
  119. data/views/docs/operation.html.haml +23 -11
  120. data/views/drivers/index.html.haml +15 -0
  121. data/views/drivers/index.xml.haml +7 -0
  122. data/views/drivers/show.html.haml +20 -0
  123. data/views/drivers/show.xml.haml +7 -0
  124. data/views/error.html.haml +31 -0
  125. data/views/errors/auth_exception.xml.haml +2 -1
  126. data/views/errors/backend_capability_failure.xml.haml +2 -1
  127. data/views/errors/backend_error.html.haml +3 -0
  128. data/views/errors/backend_error.xml.haml +2 -2
  129. data/views/errors/validation_failure.xml.haml +3 -2
  130. data/views/images/index.html.haml +1 -6
  131. data/views/images/index.xml.haml +2 -0
  132. data/views/images/new.html.haml +14 -0
  133. data/views/images/show.xml.haml +2 -0
  134. data/views/instances/index.html.haml +8 -6
  135. data/views/instances/index.xml.haml +4 -0
  136. data/views/instances/new.html.haml +40 -11
  137. data/views/instances/run.html.haml +9 -0
  138. data/views/instances/run.xml.haml +7 -0
  139. data/views/instances/run_command.html.haml +16 -0
  140. data/views/instances/show.html.haml +14 -0
  141. data/views/instances/show.xml.haml +12 -4
  142. data/views/layout.html.haml +7 -2
  143. data/views/load_balancers/index.html.haml +1 -1
  144. data/views/load_balancers/new.html.haml +2 -2
  145. data/views/load_balancers/show.html.haml +1 -1
  146. data/views/storage_snapshots/index.html.haml +3 -0
  147. data/views/storage_snapshots/new.html.haml +9 -0
  148. data/views/storage_volumes/attach.html.haml +20 -0
  149. data/views/storage_volumes/index.html.haml +16 -1
  150. data/views/storage_volumes/index.xml.haml +1 -10
  151. data/views/storage_volumes/new.html.haml +17 -0
  152. data/views/storage_volumes/show.html.haml +8 -0
  153. data/views/storage_volumes/show.xml.haml +25 -3
  154. metadata +197 -127
  155. data/lib/deltacloud/drivers/rackspace/rackspace_client.rb +0 -130
  156. data/parse.rb +0 -7
  157. data/test.rb +0 -3
  158. data/views/api/drivers.xml.haml +0 -6
@@ -7,3 +7,4 @@
7
7
  :realm_id: us
8
8
  :instance_profile: !ruby/object:InstanceProfile
9
9
  id: m1-large
10
+ :create_image: true
@@ -1,4 +1,5 @@
1
1
  :owner_id: fedoraproject
2
+ :realm_id: us
2
3
  :created: Thu Jul 30 14:35:11 UTC 2009
3
4
  :state: AVAILABLE
4
5
  :capacity: 1
@@ -1,4 +1,5 @@
1
1
  :owner_id: mockuser
2
+ :realm_id: us
2
3
  :created: Thu Jul 30 14:35:11 UTC 2009
3
4
  :state: AVAILABLE
4
5
  :capacity: 1
@@ -1,4 +1,5 @@
1
1
  :owner_id: mockuser
2
+ :realm_id: us
2
3
  :created: Thu Jul 30 14:35:11 UTC 2009
3
4
  :state: IN-USE
4
5
  :capacity: 1
@@ -1,6 +1,4 @@
1
1
  #
2
- # Copyright (C) 2009, 2010 Red Hat, Inc.
3
- #
4
2
  # Licensed to the Apache Software Foundation (ASF) under one or more
5
3
  # contributor license agreements. See the NOTICE file distributed with
6
4
  # this work for additional information regarding copyright ownership. The
@@ -25,8 +23,15 @@ module Deltacloud
25
23
  module Mock
26
24
  class MockDriver < Deltacloud::BaseDriver
27
25
 
26
+ # If the provider is set to storage, pretend to be a storage-only
27
+ # driver
28
28
  def supported_collections
29
- DEFAULT_COLLECTIONS + [ :buckets, :keys]
29
+ endpoint = Thread.current[:provider] || ENV['API_PROVIDER']
30
+ if endpoint == 'storage'
31
+ [:buckets]
32
+ else
33
+ DEFAULT_COLLECTIONS + [:buckets, :keys]
34
+ end
30
35
  end
31
36
 
32
37
  ( REALMS = [
@@ -128,6 +133,33 @@ class MockDriver < Deltacloud::BaseDriver
128
133
  images.sort_by{|e| [e.owner_id,e.description]}
129
134
  end
130
135
 
136
+ def create_image(credentials, opts={})
137
+ check_credentials(credentials)
138
+ instance = instance(credentials, :id => opts[:instance_id])
139
+ raise BackendError::new(500, 'CreateImageNotSupported', '', '') unless instance.can_create_image?
140
+ ids = Dir[ "#{@storage_root}/images/*.yml" ].collect{|e| File.basename( e, ".yml" )}
141
+ count = 0
142
+ while true
143
+ next_id = "img#{count}"
144
+ break if not ids.include?(next_id)
145
+ count += 1
146
+ end
147
+ safely do
148
+ image = {
149
+ :name => opts[:name],
150
+ :owner_id => 'root',
151
+ :description => opts[:description],
152
+ :architecture => 'i386',
153
+ :state => 'UP'
154
+ }
155
+ File.open( "#{@storage_root}/images/#{next_id}.yml", 'w' ) do |f|
156
+ YAML.dump( image, f )
157
+ end
158
+ image[:id] = next_id
159
+ Image.new(image)
160
+ end
161
+ end
162
+
131
163
  #
132
164
  # Instances
133
165
  #
@@ -190,6 +222,7 @@ class MockDriver < Deltacloud::BaseDriver
190
222
  :private_addresses=>["#{image_id}.#{next_id}.private.com"],
191
223
  :instance_profile => InstanceProfile.new(hwp.name, opts),
192
224
  :realm_id=>realm_id,
225
+ :create_image=>true,
193
226
  :actions=>instance_actions_for( 'RUNNING' )
194
227
  }
195
228
  File.open( "#{@storage_root}/instances/#{next_id}.yml", 'w' ) {|f|
@@ -311,73 +344,145 @@ class MockDriver < Deltacloud::BaseDriver
311
344
  #--
312
345
  # Buckets
313
346
  #--
314
- def buckets(credentials, opts=nil)
347
+ def buckets(credentials, opts={})
315
348
  check_credentials(credentials)
316
349
  buckets=[]
317
- Dir[ "#{@storage_root}/buckets/*.yml" ].each do |bucket_file|
318
- bucket = YAML.load( File.read( bucket_file ) )
319
- bucket[:id] = File.basename( bucket_file, ".yml" )
320
- bucket[:name] = bucket[:id]
321
- buckets << Bucket.new( bucket )
350
+ safely do
351
+ unless (opts[:id].nil?)
352
+ bucket_file = File::join(@storage_root, 'buckets', "#{opts[:id]}.yml")
353
+ bucket = YAML.load_file(bucket_file)
354
+ bucket[:id] = opts[:id]
355
+ bucket[:name] = bucket[:id]
356
+ buckets << Bucket.new( bucket )
357
+ else
358
+ Dir[ File::join(@storage_root, 'buckets', '*.yml')].each do |bucket_file|
359
+ bucket_id = File.basename( bucket_file, ".yml" )
360
+ buckets << Bucket.new( {:id => bucket_id, :name => bucket_id } )
361
+ end
362
+ end
322
363
  end
323
364
  buckets = filter_on( buckets, :id, opts )
324
- buckets
325
365
  end
326
366
 
327
367
  #--
328
368
  # Create bucket
329
369
  #--
330
- def create_bucket(credentials, name, opts=nil)
370
+ def create_bucket(credentials, name, opts={})
331
371
  check_credentials(credentials)
332
372
  bucket = {
373
+ :id => name,
333
374
  :name=>name,
334
375
  :size=>'0',
335
376
  :blob_list=>[]
336
377
  }
337
- File.open( "#{@storage_root}/buckets/#{name}.yml", 'w' ) {|b| YAML.dump( bucket, b )}
378
+ File.open( File::join(@storage_root, 'buckets', "#{name}.yml"), 'w') {|b| YAML.dump( bucket, b )}
338
379
  Bucket.new(bucket)
339
380
  end
340
381
 
341
382
  #--
342
383
  # Delete bucket
343
384
  #--
344
- def delete_bucket(credentials, name, opts=nil)
385
+ def delete_bucket(credentials, name, opts={})
386
+ check_credentials(credentials)
345
387
  bucket = bucket(credentials, {:id => name})
346
388
  unless (bucket.size == "0")
347
389
  raise Deltacloud::BackendError.new(403, self.class.to_s, "bucket-not-empty", "delete operation not valid for non-empty bucket")
348
390
  end
349
391
  safely do
350
- File.delete("#{@storage_root}/buckets/#{name}.yml")
392
+ File.delete(File::join(@storage_root, 'buckets', "#{name}.yml"))
351
393
  end
352
394
  end
353
395
 
354
396
  #--
355
397
  # Blobs
356
398
  #--
357
- def blobs(credentials, opts = nil)
399
+ def blobs(credentials, opts = {})
358
400
  check_credentials(credentials)
359
401
  blobs=[]
360
- Dir[ "#{@storage_root}/buckets/blobs/*.yml" ].each do |blob_file|
361
- blob = YAML.load( File.read( blob_file ) )
362
- blob[:id] = File.basename( blob_file, ".yml" )
402
+ blobfile = File::join("#{@storage_root}", "buckets", "blobs", "#{opts[:id]}.yml")
403
+ safely do
404
+ blob = YAML.load_file(blobfile)
405
+ return [] unless blob[:bucket] == opts['bucket'] #can't return nil since base_driver invokes .first on return
406
+ blob[:id] = File.basename( blobfile, ".yml" )
363
407
  blob[:name] = blob[:id]
364
408
  blobs << Blob.new( blob )
409
+ blobs = filter_on( blobs, :id, opts )
365
410
  end
366
- blobs = filter_on( blobs, :id, opts )
367
- blobs
368
411
  end
369
412
 
370
413
  #--
371
414
  # Blob content
372
415
  #--
373
- def blob_data(credentials, bucket_id, blob_id, opts = nil)
416
+ def blob_data(credentials, bucket_id, blob_id, opts = {})
374
417
  check_credentials(credentials)
375
418
  blob=nil
376
- Dir[ "#{@storage_root}/buckets/blobs/*.yml" ].each do |blob_file|
377
- if File.basename(blob_file, ".yml") == blob_id
378
- blob = YAML.load(File.read(blob_file))
379
- blob[:content].each {|part| yield part}
419
+ safely do
420
+ blobfile = File::join("#{@storage_root}", "buckets", "blobs", "#{opts['blob']}.yml")
421
+ blob = YAML.load_file(blobfile)
422
+ end
423
+ blob[:content].each {|part| yield part}
424
+ end
425
+
426
+ #--
427
+ # Create blob
428
+ #--
429
+ def create_blob(credentials, bucket_id, blob_id, blob_data, opts={})
430
+ check_credentials(credentials)
431
+ blob_meta = {}
432
+ opts.inject({}){|result, (k,v)| blob_meta[k] = v if k.match(/X[_-]Deltacloud[_-]Blobmeta[_-]/i)} #select{|k,v| k.match(/X[_-]Deltacloud[_-]Blobmeta[_-]/i)}
433
+ blob = {
434
+ :id => blob_id,
435
+ :bucket => bucket_id,
436
+ :content_length => blob_data[:tempfile].length,
437
+ :content_type => blob_data[:type],
438
+ :last_modified => Time.now,
439
+ :user_metadata => blob_meta.gsub_keys('X_Deltacloud_Blobmeta_', ''),
440
+ :content => blob_data[:tempfile].read
441
+ }
442
+ File.open( File::join("#{@storage_root}", "buckets", "blobs", "#{blob_id}.yml"), 'w' ) {|b| YAML.dump( blob, b )}
443
+ Blob.new(blob)
444
+ end
445
+
446
+ #--
447
+ # Delete blob
448
+ #--
449
+ def delete_blob(credentials, bucket_id, blob_id, opts={})
450
+ check_credentials(credentials)
451
+ blobfile = File::join("#{@storage_root}", "buckets", "blobs", "#{blob_id}.yml")
452
+ safely do
453
+ unless File.exists?(blobfile)
454
+ raise Deltacloud::BackendError.new(500, self.class.to_s, "blob #{blob_id} doesn't exist", "cannot delete non existant blob")
380
455
  end
456
+ File.delete(blobfile)
457
+ end
458
+ end
459
+
460
+ #--
461
+ # Get metadata
462
+ #--
463
+ def blob_metadata(credentials, opts={})
464
+ check_credentials(credentials)
465
+ blobfile = File::join("#{@storage_root}", "buckets", "blobs", "#{opts[:id]}.yml")
466
+ #safely do - mechanism not suitable here since head requests don't return a body response
467
+ begin
468
+ blob = YAML.load_file(blobfile)
469
+ rescue Errno::ENOENT
470
+ return nil #server.rb picks this up and gives 404
471
+ end
472
+ blob[:user_metadata]
473
+ end
474
+
475
+ #--
476
+ # Update metadata
477
+ #--
478
+ def update_blob_metadata(credentials, opts={})
479
+ check_credentials(credentials)
480
+ blobfile = File::join("#{@storage_root}", "buckets", "blobs", "#{opts[:id]}.yml")
481
+ safely do
482
+ blob = YAML.load_file(blobfile)
483
+ return false unless blob
484
+ blob[:user_metadata] = opts['meta_hash'].gsub_keys('HTTP[-_]X[-_]Deltacloud[-_]Blobmeta[-_]', '')
485
+ File.open(File::join("#{@storage_root}", "buckets", "blobs", "#{opts[:id]}.yml"), 'w' ) {|b| YAML.dump( blob, b )}
381
486
  end
382
487
  end
383
488
 
@@ -393,15 +498,18 @@ class MockDriver < Deltacloud::BaseDriver
393
498
  private
394
499
 
395
500
  def check_credentials(credentials)
396
- if ( credentials.user != 'mockuser' )
397
- raise Deltacloud::AuthException.new
398
- end
399
-
400
- if ( credentials.password != 'mockpassword' )
501
+ if ( credentials.user != 'mockuser' ) or ( credentials.password != 'mockpassword' )
401
502
  raise Deltacloud::AuthException.new
402
503
  end
403
504
  end
404
505
 
506
+ def catched_exceptions_list
507
+ {
508
+ :auth => [],
509
+ :error => [ /Deltacloud::BackendError/, /Errno::ENOENT/ ],
510
+ :glob => [ /Error/ ]
511
+ }
512
+ end
405
513
 
406
514
  end
407
515
 
@@ -1,21 +1,19 @@
1
- #--------------------------------------------------------------------------- #
2
- # Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
3
- # Complutense de Madrid (dsa-research.org)
4
1
  #
5
- # This library is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU Lesser General Public
7
- # License as published by the Free Software Foundation; either
8
- # version 2.1 of the License, or (at your option) any later version.
2
+ # Licensed to the Apache Software Foundation (ASF) under one or more
3
+ # contributor license agreements. See the NOTICE file distributed with
4
+ # this work for additional information regarding copyright ownership. The
5
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance with the
7
+ # License. You may obtain a copy of the License at
9
8
  #
10
- # This library is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
- # Lesser General Public License for more details.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations
15
+ # under the License.
14
16
  #
15
- # You should have received a copy of the GNU Lesser General Public
16
- # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
- #--------------------------------------------------------------------------- #
19
17
 
20
18
  require 'rubygems'
21
19
  require 'uri'
@@ -1,21 +1,19 @@
1
- #--------------------------------------------------------------------------- #
2
- # Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
3
- # Complutense de Madrid (dsa-research.org)
4
1
  #
5
- # This library is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU Lesser General Public
7
- # License as published by the Free Software Foundation; either
8
- # version 2.1 of the License, or (at your option) any later version.
2
+ # Licensed to the Apache Software Foundation (ASF) under one or more
3
+ # contributor license agreements. See the NOTICE file distributed with
4
+ # this work for additional information regarding copyright ownership. The
5
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance with the
7
+ # License. You may obtain a copy of the License at
9
8
  #
10
- # This library is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
- # Lesser General Public License for more details.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations
15
+ # under the License.
14
16
  #
15
- # You should have received a copy of the GNU Lesser General Public
16
- # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
- #--------------------------------------------------------------------------- #
19
17
 
20
18
  require 'rubygems'
21
19
  require 'uri'
@@ -1,21 +1,19 @@
1
- #--------------------------------------------------------------------------- #
2
- # Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
3
- # Complutense de Madrid (dsa-research.org)
4
1
  #
5
- # This library is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU Lesser General Public
7
- # License as published by the Free Software Foundation; either
8
- # version 2.1 of the License, or (at your option) any later version.
2
+ # Licensed to the Apache Software Foundation (ASF) under one or more
3
+ # contributor license agreements. See the NOTICE file distributed with
4
+ # this work for additional information regarding copyright ownership. The
5
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance with the
7
+ # License. You may obtain a copy of the License at
9
8
  #
10
- # This library is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
- # Lesser General Public License for more details.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations
15
+ # under the License.
14
16
  #
15
- # You should have received a copy of the GNU Lesser General Public
16
- # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
- #--------------------------------------------------------------------------- #
19
17
 
20
18
  require 'deltacloud/base_driver'
21
19
 
@@ -1,6 +1,4 @@
1
1
  #
2
- # Copyright (C) 2009 Red Hat, Inc.
3
- #
4
2
  # Licensed to the Apache Software Foundation (ASF) under one or more
5
3
  # contributor license agreements. See the NOTICE file distributed with
6
4
  # this work for additional information regarding copyright ownership. The
@@ -17,8 +15,9 @@
17
15
  # under the License.
18
16
 
19
17
  require 'deltacloud/base_driver'
20
- require 'deltacloud/drivers/rackspace/rackspace_client'
21
18
  require 'cloudfiles'
19
+ require 'cloudservers'
20
+ require 'base64'
22
21
 
23
22
  module Deltacloud
24
23
  module Drivers
@@ -27,20 +26,22 @@ module Deltacloud
27
26
  class RackspaceDriver < Deltacloud::BaseDriver
28
27
 
29
28
  feature :instances, :user_name
29
+ feature :instances, :authentication_password
30
+ feature :instances, :user_files
30
31
 
31
32
  def supported_collections
32
- DEFAULT_COLLECTIONS + [ :buckets ]
33
+ DEFAULT_COLLECTIONS + [ :buckets ] - [ :storage_snapshots, :storage_volumes ]
33
34
  end
34
35
 
35
- def hardware_profiles(credentials, opts = nil)
36
- racks = new_client( credentials )
37
- results=""
36
+ def hardware_profiles(credentials, opts = {})
37
+ rs = new_client( credentials )
38
+ results = []
38
39
  safely do
39
- results = racks.list_flavors.map do |flav|
40
- HardwareProfile.new(flav["id"].to_s) do
40
+ results = rs.list_flavors.collect do |f|
41
+ HardwareProfile.new(f[:id].to_s) do
41
42
  architecture 'x86_64'
42
- memory flav["ram"].to_i
43
- storage flav["disk"].to_i
43
+ memory f[:ram].to_i
44
+ storage f[:disk].to_i
44
45
  end
45
46
  end
46
47
  end
@@ -48,22 +49,21 @@ class RackspaceDriver < Deltacloud::BaseDriver
48
49
  end
49
50
 
50
51
  def images(credentials, opts=nil)
51
- racks = new_client( credentials )
52
- results=""
52
+ rs = new_client(credentials)
53
+ results = []
53
54
  safely do
54
- results = racks.list_images.map do |img|
55
- Image.new( {
56
- :id=>img["id"].to_s,
57
- :name=>img["name"],
58
- :description => img["name"] + " " + img["status"] + "",
59
- :owner_id=>"root",
60
- :architecture=>'x86_64'
61
- } )
55
+ results = rs.list_images.collect do |img|
56
+ Image.new(
57
+ :id => img[:id].to_s,
58
+ :name => img[:name],
59
+ :description => img[:name],
60
+ :owner_id => credentials.user,
61
+ :state => img[:status],
62
+ :architecture => 'x86_64'
63
+ )
62
64
  end
63
65
  end
64
- results.sort_by{|e| [e.description]}
65
- results = filter_on( results, :id, opts )
66
- results
66
+ filter_on( results, :id, opts )
67
67
  end
68
68
 
69
69
  #rackspace does not at this stage have realms... its all US/TX, all the time (at least at time of writing)
@@ -75,67 +75,99 @@ class RackspaceDriver < Deltacloud::BaseDriver
75
75
  } )]
76
76
  end
77
77
 
78
- def reboot_instance(credentials, id)
79
- racks = new_client(credentials)
78
+ #
79
+ # create instance. Default to flavor 1 - really need a name though...
80
+ # In rackspace, all flavors work with all images.
81
+ #
82
+ def create_instance(credentials, image_id, opts)
83
+ rs = new_client( credentials )
84
+ result = nil
85
+ params = extract_personality(opts)
86
+ params[:name] = opts[:name] || Time.now.to_s
87
+ params[:imageId] = image_id.to_i
88
+ params[:flavorId] = (opts[:hwp_id] && opts[:hwp_id].length>0) ? opts[:hwp_id].to_i : 1
80
89
  safely do
81
- racks.reboot_server(id)
90
+ server = rs.create_server(params)
91
+ result = convert_instance_after_create(server, credentials.user, server.adminPass)
82
92
  end
83
- Instance.new( {
84
- :id => id,
85
- :state => "RUNNING",
86
- :actions => instance_actions_for( "RUNNING" ),
87
- } )
93
+ result
88
94
  end
89
95
 
90
- def stop_instance(credentials, id)
91
- destroy_instance(credentials, id)
96
+ def create_image(credentials, opts={})
97
+ rs = new_client(credentials)
98
+ safely do
99
+ server = rs.get_server(opts[:id].to_i)
100
+ image = server.create_image(opts[:name])
101
+ Image.new(
102
+ :id => image.id.to_s,
103
+ :name => image.name,
104
+ :description => image.name,
105
+ :owner_id => credentials.user,
106
+ :state => image.status,
107
+ :architecture => 'x86_64'
108
+ )
109
+ end
92
110
  end
93
111
 
94
- def destroy_instance(credentials, id)
95
- racks = new_client(credentials)
112
+ def run_on_instance(credentials, opts={})
113
+ target = instance(credentials, :id => opts[:id])
114
+ param = {}
115
+ param[:credentials] = {
116
+ :username => 'root',
117
+ :password => opts[:password]
118
+ }
119
+ param[:port] = opts[:port] || '22'
120
+ param[:ip] = target.public_addresses.first
96
121
  safely do
97
- racks.delete_server(id)
122
+ Deltacloud::Runner.execute(opts[:cmd], param)
98
123
  end
99
- Instance.new( {
100
- :id => id,
101
- :state => "STOPPED",
102
- :actions => instance_actions_for( "STOPPED" ),
103
- } )
104
124
  end
105
125
 
126
+ def reboot_instance(credentials, instance_id)
127
+ rs = new_client(credentials)
128
+ safely do
129
+ server = rs.get_server(instance_id.to_i)
130
+ server.reboot!
131
+ convert_instance_after_create(server, credentials.user)
132
+ end
133
+ end
106
134
 
107
- #
108
- # create instance. Default to flavor 1 - really need a name though...
109
- # In rackspace, all flavors work with all images.
110
- #
111
- def create_instance(credentials, image_id, opts)
112
- racks = new_client( credentials )
113
- hwp_id = opts[:hwp_id] || 1
114
- name = Time.now.to_s
115
- if (opts[:name]) then name = opts[:name] end
135
+ def destroy_instance(credentials, instance_id)
136
+ rs = new_client(credentials)
116
137
  safely do
117
- return convert_srv_to_instance(racks.start_server(image_id, hwp_id, name))
138
+ server = rs.get_server(instance_id.to_i)
139
+ server.delete!
140
+ convert_instance_after_create(server, credentials.user)
118
141
  end
119
142
  end
120
143
 
144
+ alias_method :stop_instance, :destroy_instance
145
+
121
146
  #
122
147
  # Instances
123
148
  #
124
- def instances(credentials, opts=nil)
125
- racks = new_client(credentials)
126
- instances = []
149
+ def instances(credentials, opts={})
150
+
151
+ rs = new_client(credentials)
152
+ insts = []
153
+
127
154
  safely do
128
- if (opts.nil?)
129
- instances = racks.list_servers.map do |srv|
130
- convert_srv_to_instance(srv)
155
+ begin
156
+ if opts[:id]
157
+ server = rs.get_server(opts[:id].to_i)
158
+ insts << convert_instance_after_create(server, credentials.user)
159
+ else
160
+ insts = rs.list_servers_detail.collect do |server|
161
+ convert_instance(server, credentials.user)
162
+ end
131
163
  end
132
- else
133
- instances << convert_srv_to_instance(racks.load_server_details(opts[:id]))
164
+ rescue CloudServers::Exception::ItemNotFound
134
165
  end
135
166
  end
136
- instances = filter_on( instances, :id, opts )
137
- instances = filter_on( instances, :state, opts )
138
- instances
167
+
168
+ insts = filter_on( insts, :id, opts )
169
+ insts = filter_on( insts, :state, opts )
170
+ insts
139
171
  end
140
172
 
141
173
  def valid_credentials?(credentials)
@@ -147,42 +179,38 @@ class RackspaceDriver < Deltacloud::BaseDriver
147
179
  true
148
180
  end
149
181
 
150
-
151
-
152
-
153
182
  define_instance_states do
154
183
  start.to( :pending ) .on( :create )
155
-
156
184
  pending.to( :running ) .automatically
157
-
158
185
  running.to( :running ) .on( :reboot )
159
186
  running.to( :shutting_down ) .on( :stop )
160
-
161
187
  shutting_down.to( :stopped ) .automatically
162
-
163
188
  stopped.to( :finish ) .automatically
164
189
  end
165
190
 
166
191
  #--
167
192
  # Buckets
168
193
  #--
169
- def buckets(credentials, opts)
194
+ def buckets(credentials, opts = {})
170
195
  bucket_list = []
171
196
  cf = cloudfiles_client(credentials)
172
197
  safely do
173
- cf.containers.each do |container_name|
174
- current = cf.container(container_name)
175
- bucket_list << convert_container(current)
176
- end #containers.each
198
+ unless (opts[:id].nil?)
199
+ bucket = cf.container(opts[:id])
200
+ bucket_list << convert_container(bucket)
201
+ else
202
+ cf.containers.each do |container_name|
203
+ bucket_list << Bucket.new({:id => container_name, :name => container_name})
204
+ end #containers.each
205
+ end #unless
177
206
  end #safely
178
- bucket_list = filter_on(bucket_list, :id, opts)
179
- bucket_list
207
+ filter_on(bucket_list, :id, opts)
180
208
  end
181
209
 
182
210
  #--
183
211
  # Create Bucket
184
212
  #--
185
- def create_bucket(credentials, name, opts)
213
+ def create_bucket(credentials, name, opts = {})
186
214
  bucket = nil
187
215
  cf = cloudfiles_client(credentials)
188
216
  safely do
@@ -195,7 +223,7 @@ class RackspaceDriver < Deltacloud::BaseDriver
195
223
  #--
196
224
  # Delete Bucket
197
225
  #--
198
- def delete_bucket(credentials, name, opts)
226
+ def delete_bucket(credentials, name, opts = {})
199
227
  cf = cloudfiles_client(credentials)
200
228
  safely do
201
229
  cf.delete_container(name)
@@ -205,7 +233,7 @@ class RackspaceDriver < Deltacloud::BaseDriver
205
233
  #--
206
234
  # Blobs
207
235
  #--
208
- def blobs(credentials, opts)
236
+ def blobs(credentials, opts = {})
209
237
  cf = cloudfiles_client(credentials)
210
238
  blobs = []
211
239
  safely do
@@ -221,7 +249,7 @@ class RackspaceDriver < Deltacloud::BaseDriver
221
249
  #-
222
250
  # Blob data
223
251
  #-
224
- def blob_data(credentials, bucket_id, blob_id, opts)
252
+ def blob_data(credentials, bucket_id, blob_id, opts = {})
225
253
  cf = cloudfiles_client(credentials)
226
254
  cf.container(bucket_id).object(blob_id).data_stream do |chunk|
227
255
  yield chunk
@@ -231,18 +259,25 @@ class RackspaceDriver < Deltacloud::BaseDriver
231
259
  #--
232
260
  # Create Blob
233
261
  #--
234
- def create_blob(credentials, bucket_id, blob_id, blob_data, opts=nil)
262
+ def create_blob(credentials, bucket_id, blob_id, blob_data, opts={})
235
263
  cf = cloudfiles_client(credentials)
236
- #must first create the object using cloudfiles_client.create_object
237
- #then can write using object.write(data)
238
- object = cf.container(bucket_id).create_object(blob_id)
239
- #blob_data is a construct with data in .tempfile and content-type in {:type}
240
- res = object.write(blob_data[:tempfile], {'Content-Type' => blob_data[:type]})
264
+ #insert ec2-specific header for user metadata ... X-Object-Meta-KEY = VALUE
265
+ opts.gsub_keys("HTTP_X_Deltacloud_Blobmeta_", "X-Object-Meta-")
266
+ opts['Content-Type'] = blob_data[:type]
267
+ object = nil
268
+ safely do
269
+ #must first create the object using cloudfiles_client.create_object
270
+ #then can write using object.write(data)
271
+ object = cf.container(bucket_id).create_object(blob_id)
272
+ #blob_data is a construct with data in .tempfile and content-type in {:type}
273
+ res = object.write(blob_data[:tempfile], opts)
274
+ end
241
275
  Blob.new( { :id => object.name,
242
276
  :bucket => object.container.name,
243
277
  :content_length => blob_data[:tempfile].length,
244
278
  :content_type => blob_data[:type],
245
- :last_modified => ''
279
+ :last_modified => '',
280
+ :user_metadata => opts.select{|k,v| k.match(/^X-Object-Meta-/i)}
246
281
  }
247
282
  )
248
283
  end
@@ -250,32 +285,45 @@ class RackspaceDriver < Deltacloud::BaseDriver
250
285
  #--
251
286
  # Delete Blob
252
287
  #--
253
- def delete_blob(credentials, bucket_id, blob_id, opts=nil)
288
+ def delete_blob(credentials, bucket_id, blob_id, opts={})
289
+ cf = cloudfiles_client(credentials)
290
+ safely do
291
+ cf.container(bucket_id).delete_object(blob_id)
292
+ end
293
+ end
294
+
295
+ #-
296
+ # Blob Metadada
297
+ #-
298
+ def blob_metadata(credentials, opts = {})
299
+ cf = cloudfiles_client(credentials)
300
+ meta = {}
301
+ safely do
302
+ meta = cf.container(opts['bucket']).object(opts[:id]).metadata
303
+ end
304
+ meta
305
+ end
306
+
307
+ #-
308
+ # Update Blob Metahash
309
+ #-
310
+ def update_blob_metadata(credentials, opts={})
254
311
  cf = cloudfiles_client(credentials)
255
- cf.container(bucket_id).delete_object(blob_id)
312
+ meta_hash = opts['meta_hash']
313
+ #the set_metadata method actually places the 'X-Object-Meta-' prefix for us:
314
+ meta_hash.gsub_keys('HTTP_X_Deltacloud_Blobmeta_', '')
315
+ safely do
316
+ blob = cf.container(opts['bucket']).object(opts[:id])
317
+ blob.set_metadata(meta_hash)
318
+ end
256
319
  end
257
320
 
258
321
  private
259
322
 
260
- def convert_srv_to_instance(srv)
261
- inst = Instance.new(:id => srv["id"].to_s,
262
- :owner_id => "root",
263
- :realm_id => "us")
264
- inst.name = srv["name"]
265
- inst.state = srv["status"] == "ACTIVE" ? "RUNNING" : "PENDING"
266
- inst.actions = instance_actions_for(inst.state)
267
- inst.image_id = srv["imageId"].to_s
268
- inst.instance_profile = InstanceProfile.new(srv["flavorId"].to_s)
269
- if srv["addresses"]
270
- inst.public_addresses = srv["addresses"]["public"]
271
- inst.private_addresses = srv["addresses"]["private"]
272
- end
273
- inst
274
- end
275
323
 
276
324
  def new_client(credentials)
277
325
  safely do
278
- return RackspaceClient.new(credentials.user, credentials.password)
326
+ CloudServers::Connection.new(:username => credentials.user, :api_key => credentials.password)
279
327
  end
280
328
  end
281
329
 
@@ -292,24 +340,87 @@ private
292
340
  :bucket => cf_object.container.name,
293
341
  :content_length => cf_object.bytes,
294
342
  :content_type => cf_object.content_type,
295
- :last_modified => cf_object.last_modified
343
+ :last_modified => cf_object.last_modified,
344
+ :user_metadata => cf_object.metadata
296
345
  })
297
346
  end
298
347
 
348
+ def convert_instance_after_create(server, user_name, password='')
349
+ inst = Instance.new(
350
+ :id => server.id.to_s,
351
+ :realm_id => 'us',
352
+ :owner_id => user_name,
353
+ :description => server.name,
354
+ :name => server.name,
355
+ :state => (server.status == 'ACTIVE') ? 'RUNNING' : 'PENDING',
356
+ :architecture => 'x86_64',
357
+ :image_id => server.imageId.to_s,
358
+ :instance_profile => InstanceProfile::new(server.flavorId.to_s),
359
+ :public_addresses => server.addresses[:public],
360
+ :private_addresses => server.addresses[:private],
361
+ :username => 'root',
362
+ :password => password ? password : nil
363
+ )
364
+ inst.actions = instance_actions_for(inst.state)
365
+ inst.create_image = 'RUNNING'.eql?(inst.state)
366
+ inst
367
+ end
368
+
369
+ def convert_instance(server, user_name = '')
370
+ inst = Instance.new(
371
+ :id => server[:id].to_s,
372
+ :realm_id => 'us',
373
+ :owner_id => user_name,
374
+ :description => server[:name],
375
+ :name => server[:name],
376
+ :state => (server[:status] == 'ACTIVE') ? 'RUNNING' : 'PENDING',
377
+ :architecture => 'x86_64',
378
+ :image_id => server[:imageId].to_s,
379
+ :instance_profile => InstanceProfile::new(server[:flavorId].to_s),
380
+ :public_addresses => server[:addresses][:public],
381
+ :private_addresses => server[:addresses][:private]
382
+ )
383
+ inst.create_image = 'RUNNING'.eql?(inst.state)
384
+ inst.actions = instance_actions_for(inst.state)
385
+ inst
386
+ end
387
+
299
388
  def cloudfiles_client(credentials)
300
389
  safely do
301
390
  CloudFiles::Connection.new(:username => credentials.user, :api_key => credentials.password)
302
391
  end
303
392
  end
304
393
 
305
- def safely(&block)
306
- begin
307
- block.call
308
- rescue Exception => e
309
- raise Deltacloud::BackendError.new(500, e.class.to_s, e.message, e.backtrace)
310
- end
394
+ def catched_exceptions_list
395
+ {
396
+ :auth => [ /Authentication failed/ ],
397
+ :error => [ /Error/ ],
398
+ :glob => [ /CloudServers::Exception::(\w+)/, /Deltacloud::Runner::(\w+)/ ]
399
+ }
311
400
  end
312
401
 
402
+ private
403
+
404
+ def extract_personality(opts)
405
+ # This relies on an undocumented feature of the cloudservers gem:
406
+ # create_server allows passing in strings for the file contents
407
+ # directly if :personality maps to an array of hashes
408
+ ary = opts.inject([]) do |a, e|
409
+ k, v = e
410
+ if k.to_s =~ /^path([0-9]+)/
411
+ a << {
412
+ :path => v,
413
+ :contents => Base64.decode64(opts[:"content#{$1}"])
414
+ }
415
+ end
416
+ a
417
+ end
418
+ if ary.empty?
419
+ {}
420
+ else
421
+ { :personality => ary }
422
+ end
423
+ end
313
424
  end
314
425
 
315
426
  end