bbrowning-deltacloud-core 0.0.3.1 → 0.0.4

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/Rakefile CHANGED
@@ -37,15 +37,22 @@ Rake::TestTask.new("test") { |t|
37
37
  'tests/images_test.rb',
38
38
  'tests/instances_test.rb',
39
39
  'tests/instance_states_test.rb',
40
+ 'tests/url_for_test.rb'
40
41
  ]
41
42
  t.verbose = true
42
43
  t.warning = false
43
44
  }
44
45
 
45
- load 'deltacloud-core.gemspec'
46
46
 
47
- Rake::GemPackageTask.new(@spec) do |pkg|
48
- pkg.need_tar = true
47
+ @specs = ['ruby', 'java'].inject({}) do |hash, spec_platform|
48
+ $platform = spec_platform
49
+ hash.update(spec_platform => Gem::Specification.load('deltacloud-core.gemspec'))
50
+ end
51
+
52
+ @specs.values.each do |spec|
53
+ Rake::GemPackageTask.new(spec) do |pkg|
54
+ pkg.need_tar = true
55
+ end
49
56
  end
50
57
 
51
58
  desc "Install API"
data/bin/deltacloudd CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'optparse'
5
- require 'thin'
6
5
 
7
6
  options = {
8
7
  :env => 'development'
@@ -44,45 +43,78 @@ end
44
43
  ENV["API_HOST"] = "localhost" unless ENV["API_HOST"]
45
44
  ENV["API_PORT"] = "3001" unless ENV["API_PORT"]
46
45
 
46
+ puts "Starting Deltacloud API :: #{ENV["API_DRIVER"]} :: http://#{ENV["API_HOST"]}:#{ENV["API_PORT"]}/api"
47
+ puts
48
+
47
49
  dirname="#{File.dirname(__FILE__)}/.."
50
+ platform = RUBY_PLATFORM[/java/] || 'ruby'
48
51
 
49
- argv_opts = ARGV.clone
50
- argv_opts << ['start'] unless Thin::Runner.commands.include?(options[0])
51
- argv_opts << ['--address', ENV["API_HOST"] ]
52
- argv_opts << ['--port', ENV["API_PORT"] ]
53
- argv_opts << ['--rackup', 'config.ru' ]
54
- argv_opts << ['--chdir', dirname ]
55
- argv_opts << ['-e', options[:env] ]
56
- argv_opts << ['--threaded', '-D', '--stats', '/stats']
57
-
58
- argv_opts.flatten!
59
-
60
- if options[:env] == "development"
61
- use_rerun = false
62
- begin
63
- require "rerun"
64
- use_rerun = true
65
- rescue
66
- # Do nothing
67
- end
68
- end
52
+ if platform == 'java'
53
+ require 'rack'
69
54
 
70
- puts "Starting Deltacloud API :: #{ENV["API_DRIVER"]} :: http://#{ENV["API_HOST"]}:#{ENV["API_PORT"]}/api"
71
- puts
55
+ # We can't chdir with webrick so add our root directory
56
+ # onto the load path
57
+ $: << dirname
58
+
59
+ # Read in config.ru and convert it to an instance of Rack::Builder
60
+ cfgfile = File.read(File.join(dirname, 'config.ru'))
61
+ inner_app = eval("Rack::Builder.new {(" + cfgfile + "\n )}.to_app",
62
+ nil, 'config.ru')
72
63
 
73
- if use_rerun
74
- argv_opts.unshift "thin"
75
- command = argv_opts.join(" ")
76
- topdir = File::expand_path(File::join(File::dirname(__FILE__), ".."))
77
- rerun = Rerun::Runner.new(command, :dir => topdir)
78
- rerun.start
79
- rerun.join
64
+ app = Rack::Builder.new {
65
+ use Rack::CommonLogger # apache-like logging
66
+ use Rack::Reloader if options[:env] == "development"
67
+ set :root, dirname # Set Sinatra root since we can't chdir to ../
68
+ run inner_app
69
+ }.to_app
70
+
71
+ # There's a bug with string ports on JRuby so convert to int
72
+ # http://jira.codehaus.org/browse/JRUBY-4868
73
+ port = ENV["API_PORT"].to_i
74
+
75
+ puts "=> Ctrl-C to shutdown server"
76
+ Rack::Handler::WEBrick.run(app,
77
+ :Host => ENV["API_HOST"],
78
+ :Port => port,
79
+ :AccessLog => [])
80
80
  else
81
- thin = Thin::Runner.new(argv_opts)
81
+ require 'thin'
82
+
83
+ argv_opts = ARGV.clone
84
+ argv_opts << ['start'] unless Thin::Runner.commands.include?(options[0])
85
+ argv_opts << ['--address', ENV["API_HOST"] ]
86
+ argv_opts << ['--port', ENV["API_PORT"] ]
87
+ argv_opts << ['--rackup', 'config.ru' ]
88
+ argv_opts << ['--chdir', dirname ]
89
+ argv_opts << ['-e', options[:env] ]
90
+ argv_opts << ['--threaded', '-D', '--stats', '/stats']
91
+
92
+ argv_opts.flatten!
93
+
94
+ if options[:env] == "development"
95
+ use_rerun = false
96
+ begin
97
+ require "rerun"
98
+ use_rerun = true
99
+ rescue
100
+ # Do nothing
101
+ end
102
+ end
103
+
104
+ if use_rerun
105
+ argv_opts.unshift "thin"
106
+ command = argv_opts.join(" ")
107
+ topdir = File::expand_path(File::join(File::dirname(__FILE__), ".."))
108
+ rerun = Rerun::Runner.new(command, :dir => topdir)
109
+ rerun.start
110
+ rerun.join
111
+ else
112
+ thin = Thin::Runner.new(argv_opts)
82
113
 
83
- begin
84
- thin.run!
85
- rescue Exception => e
86
- puts "ERROR: #{e.message}"
114
+ begin
115
+ thin.run!
116
+ rescue Exception => e
117
+ puts "ERROR: #{e.message}"
118
+ end
87
119
  end
88
120
  end
data/config.ru CHANGED
@@ -1,5 +1,5 @@
1
- require 'sinatra'
2
1
  require 'rubygems'
2
+ require 'sinatra'
3
3
 
4
4
  require 'server.rb'
5
5
  run Sinatra::Application
data/deltacloud.rb CHANGED
@@ -9,6 +9,7 @@ require 'deltacloud/models/base_model'
9
9
  require 'deltacloud/models/realm'
10
10
  require 'deltacloud/models/image'
11
11
  require 'deltacloud/models/instance'
12
+ require 'deltacloud/models/key'
12
13
  require 'deltacloud/models/instance_profile'
13
14
  require 'deltacloud/models/storage_snapshot'
14
15
  require 'deltacloud/models/storage_volume'
@@ -31,6 +31,16 @@ module Deltacloud
31
31
  end
32
32
  end
33
33
 
34
+ class BackendFeatureUnsupported < StandardError
35
+ attr_reader :code, :cause, :details
36
+ def initialize(code, cause, message, details)
37
+ super(message)
38
+ @code = code
39
+ @cause = cause
40
+ @details = details
41
+ end
42
+ end
43
+
34
44
  class BaseDriver
35
45
 
36
46
  def self.define_hardware_profile(name,&block)
@@ -195,6 +205,25 @@ module Deltacloud
195
205
  return false
196
206
  end
197
207
 
208
+ def catched_exceptions_list
209
+ { :error => [], :auth => [], :glob => [] }
210
+ end
211
+
212
+ def safely(&block)
213
+ begin
214
+ block.call
215
+ rescue *catched_exceptions_list[:error] => e
216
+ raise Deltacloud::BackendError.new(502, e.class.to_s, e.message, e.backtrace)
217
+ rescue *catched_exceptions_list[:auth] => e
218
+ raise Deltacloud::AuthException.new
219
+ rescue => e
220
+ catched_exceptions_list[:glob].each do |ex|
221
+ raise Deltacloud::BackendError.new(502, e.class.to_s, e.message, e.backtrace) if e.class.name =~ ex
222
+ end
223
+ raise e
224
+ end
225
+ end
226
+
198
227
  end
199
228
 
200
229
  end
@@ -120,6 +120,13 @@ module Deltacloud
120
120
  #
121
121
  # Declaration of optional features
122
122
  #
123
+ declare_feature :images, :owner_id do
124
+ description "Filter images using owner id"
125
+ operation :index do
126
+ param :owner_id, :string, :optional, nil, "Owner ID"
127
+ end
128
+ end
129
+
123
130
  declare_feature :instances, :user_name do
124
131
  description "Accept a user-defined name on instance creation"
125
132
  operation :create do
@@ -11,10 +11,13 @@ module Mock
11
11
  [
12
12
  :describe_images,
13
13
  :describe_availability_zones,
14
+ :describe_keypairs,
15
+ :create_keypair,
14
16
  :run_instances,
15
17
  :describe_instances,
16
18
  :reboot_instances,
17
- :terminate_instances
19
+ :terminate_instances,
20
+ :delete_keypair
18
21
  ]
19
22
  end
20
23
 
@@ -35,8 +35,13 @@ module Deltacloud
35
35
  module EC2
36
36
  class EC2Driver < Deltacloud::BaseDriver
37
37
 
38
+ def supported_collections
39
+ DEFAULT_COLLECTIONS + [ :keys ]
40
+ end
41
+
38
42
  feature :instances, :user_data
39
43
  feature :instances, :authentication_key
44
+ feature :images, :owner_id
40
45
 
41
46
  define_hardware_profile('m1.small') do
42
47
  cpu 1
@@ -170,40 +175,60 @@ class EC2Driver < Deltacloud::BaseDriver
170
175
  def create_instance(credentials, image_id, opts)
171
176
  ec2 = new_client( credentials )
172
177
  realm_id = opts[:realm_id]
173
- image = image(credentials, :id => image_id )
174
- hwp = find_hardware_profile(credentials, opts[:hwp_id], image.id)
175
- ec2_instances = ec2.run_instances(
176
- :image_id => image.id,
177
- :user_data => opts[:user_data],
178
- :key_name => opts[:keyname],
179
- :availability_zone => realm_id,
180
- :monitoring_enabled => true,
181
- :instance_type => hwp.name,
182
- :disable_api_termination => false,
183
- :instance_initiated_shutdown_behavior => 'terminate'
184
- )
185
- convert_instance( ec2_instances.instancesSet.item.first, 'pending' )
178
+ safely do
179
+ image = image(credentials, :id => image_id )
180
+ hwp = find_hardware_profile(credentials, opts[:hwp_id], image.id)
181
+ ec2_instances = ec2.run_instances(
182
+ :image_id => image.id,
183
+ :user_data => opts[:user_data],
184
+ :key_name => opts[:keyname],
185
+ :availability_zone => realm_id,
186
+ :monitoring_enabled => true,
187
+ :instance_type => hwp.name,
188
+ :disable_api_termination => false,
189
+ :instance_initiated_shutdown_behavior => 'terminate'
190
+ )
191
+ return convert_instance( ec2_instances.instancesSet.item.first, 'pending' )
192
+ end
193
+ end
194
+
195
+ def generate_instance(ec2, id, backup)
196
+ begin
197
+ this_instance = ec2.describe_instances( :instance_id => id ).reservationSet.item.first.instancesSet.item.first
198
+ convert_instance(this_instance, this_instance.ownerId)
199
+ rescue Exception => e
200
+ puts "WARNING: ignored error during instance refresh: #{e.message}"
201
+ # at this point, the action has succeeded but our follow-up
202
+ # "describe_instances" failed for some reason. Create a simple Instance
203
+ # object with only the ID and new state in place
204
+ state = backup.instancesSet.item.first.currentState.name
205
+ Instance.new( {
206
+ :id => id,
207
+ :state => state,
208
+ :actions => instance_actions_for( state ),
209
+ } )
210
+ end
186
211
  end
187
212
 
188
213
  def reboot_instance(credentials, id)
189
214
  ec2 = new_client(credentials)
190
- safely do
191
- ec2.reboot_instances( :instance_id => id )
192
- end
215
+ backup = ec2.reboot_instances( :instance_id => id )
216
+
217
+ generate_instance(ec2, id, backup)
193
218
  end
194
219
 
195
220
  def stop_instance(credentials, id)
196
221
  ec2 = new_client(credentials)
197
- safely do
198
- ec2.terminate_instances( :instance_id => id )
199
- end
222
+ backup = ec2.terminate_instances( :instance_id => id )
223
+
224
+ generate_instance(ec2, id, backup)
200
225
  end
201
226
 
202
227
  def destroy_instance(credentials, id)
203
228
  ec2 = new_client(credentials)
204
- safely do
205
- ec2.terminate_instances( :instance_id => id )
206
- end
229
+ backup = ec2.terminate_instances( :instance_id => id )
230
+
231
+ generate_instance(ec2, id, backup)
207
232
  end
208
233
 
209
234
  #
@@ -252,6 +277,39 @@ class EC2Driver < Deltacloud::BaseDriver
252
277
  snapshots
253
278
  end
254
279
 
280
+ def key(credentials, opts=nil)
281
+ keys(credentials, opts).first
282
+ end
283
+
284
+ def keys(credentials, opts=nil)
285
+ ec2 = new_client( credentials )
286
+ opts[:key_name] = opts[:id] if opts and opts[:id]
287
+ keypairs = ec2.describe_keypairs(opts || {})
288
+ result = []
289
+ safely do
290
+ keypairs.keySet.item.each do |keypair|
291
+ result << convert_key(keypair)
292
+ end
293
+ end
294
+ result
295
+ end
296
+
297
+ def create_key(credentials, opts={})
298
+ key = Key.new
299
+ ec2 = new_client( credentials )
300
+ safely do
301
+ key = convert_key(ec2.create_keypair(opts))
302
+ end
303
+ return key
304
+ end
305
+
306
+ def destroy_key(credentials, opts={})
307
+ safely do
308
+ ec2 = new_client( credentials )
309
+ ec2.delete_keypair(opts)
310
+ end
311
+ end
312
+
255
313
  private
256
314
 
257
315
  def new_client(credentials)
@@ -260,7 +318,18 @@ class EC2Driver < Deltacloud::BaseDriver
260
318
  :secret_access_key => credentials.password
261
319
  }
262
320
  opts[:server] = ENV['DCLOUD_EC2_URL'] if ENV['DCLOUD_EC2_URL']
263
- AWS::EC2::Base.new(opts)
321
+ safely do
322
+ AWS::EC2::Base.new(opts)
323
+ end
324
+ end
325
+
326
+ def convert_key(key)
327
+ Key.new({
328
+ :id => key['keyName'],
329
+ :fingerprint => key['keyFingerprint'],
330
+ :credential_type => :key,
331
+ :pem_rsa_key => key['keyMaterial']
332
+ })
264
333
  end
265
334
 
266
335
  def convert_image(ec2_image)
@@ -326,14 +395,12 @@ class EC2Driver < Deltacloud::BaseDriver
326
395
  } )
327
396
  end
328
397
 
329
- def safely(&block)
330
- begin
331
- block.call
332
- rescue AWS::AuthFailure => e
333
- raise Deltacloud::AuthException.new
334
- rescue Exception => e
335
- puts "ERROR: #{e.message}\n#{e.backtrace.join("\n")}"
336
- end
398
+ def catched_exceptions_list
399
+ {
400
+ :auth => [ AWS::AuthFailure ],
401
+ :error => [],
402
+ :glob => [ /AWS::(\w+)/ ]
403
+ }
337
404
  end
338
405
 
339
406
  end
@@ -9,7 +9,7 @@ class GoGridClient
9
9
  apikey='YOUR API KEY',
10
10
  secret='YOUR SHARED SECRET',
11
11
  format='json',
12
- version='1.4')
12
+ version='1.5')
13
13
  @server = server
14
14
  @secret = secret
15
15
  @default_params = {'format'=>format, 'v'=>version,'api_key' => apikey}
@@ -30,7 +30,12 @@ class GoGridClient
30
30
  open(getRequestURL(method,params)).read
31
31
  end
32
32
 
33
- def request(method, params={})
33
+ def request(method, params={}, version=nil)
34
+ if version
35
+ @default_params['v'] = version
36
+ else
37
+ @default_params['v'] = '1.5'
38
+ end
34
39
  begin
35
40
  JSON::parse(sendAPIRequest(method, params))
36
41
  rescue Exception => e
@@ -44,20 +44,8 @@ class GogridDriver < Deltacloud::BaseDriver
44
44
  end
45
45
 
46
46
  def supported_collections
47
- DEFAULT_COLLECTIONS.reject { |c| [ :storage_volumes, :storage_snapshots ].include?(c) }
48
- end
49
-
50
- # The only valid option for flavors is server RAM for now
51
- def flavors(credentials, opts=nil)
52
- flavors = []
53
- safely do
54
- flavors=new_client(credentials).request('common/lookup/list', { 'lookup' => 'server.ram' })['list'].collect do |flavor|
55
- convert_flavor(flavor)
56
- end
57
- end
58
- flavors = filter_on( flavors, :id, opts )
59
- flavors = filter_on( flavors, :architecture, opts )
60
- flavors
47
+ DEFAULT_COLLECTIONS.reject! { |c| [ :storage_volumes, :storage_snapshots ].include?(c) }
48
+ DEFAULT_COLLECTIONS + [ :keys ]
61
49
  end
62
50
 
63
51
  def images(credentials, opts=nil)
@@ -79,7 +67,7 @@ class GogridDriver < Deltacloud::BaseDriver
79
67
 
80
68
  def realms(credentials, opts=nil)
81
69
  safely do
82
- new_client(credentials).request('common/lookup/list', { 'lookup' => 'image.type' })['list'].collect do |realm|
70
+ new_client(credentials).request('common/lookup/list', { 'lookup' => 'ip.datacenter' })['list'].collect do |realm|
83
71
  convert_realm(realm)
84
72
  end
85
73
  end
@@ -100,7 +88,7 @@ class GogridDriver < Deltacloud::BaseDriver
100
88
  'name' => name,
101
89
  'image' => image_id,
102
90
  'server.ram' => server_ram,
103
- 'ip' => get_next_free_ip(credentials)
91
+ 'ip' => get_free_ip_from_realm(credentials, opts[:realm_id] || '1')
104
92
  })['list'].first
105
93
  if instance
106
94
  login_data = get_login_data(client, instance[:id])
@@ -132,6 +120,7 @@ class GogridDriver < Deltacloud::BaseDriver
132
120
  end
133
121
 
134
122
  def instances(credentials, opts=nil)
123
+ require 'ap'
135
124
  instances = []
136
125
  if opts and opts[:id]
137
126
  begin
@@ -166,26 +155,41 @@ class GogridDriver < Deltacloud::BaseDriver
166
155
 
167
156
  def reboot_instance(credentials, id)
168
157
  safely do
169
- new_client(credentials).request('grid/server/power', { 'id' => id, 'power' => 'reboot'})
158
+ new_client(credentials).request('grid/server/power', { 'name' => id, 'power' => 'reboot'})
170
159
  end
171
160
  end
172
161
 
173
162
  def destroy_instance(credentials, id)
174
163
  safely do
175
- new_client(credentials).request('grid/server/delete', { 'id' => id})
164
+ new_client(credentials).request('grid/server/delete', { 'name' => id})
176
165
  end
177
166
  end
178
167
 
179
168
  def stop_instance(credentials, id)
180
169
  safely do
181
- new_client(credentials).request('grid/server/power', { 'id' => id, 'power' => 'off'})
170
+ new_client(credentials).request('grid/server/power', { 'name' => id, 'power' => 'off'})
182
171
  end
183
172
  end
184
173
 
185
174
  def start_instance(credentials, id)
186
175
  safely do
187
- new_client(credentials).request('grid/server/power', { 'id' => id, 'power' => 'on'})
176
+ new_client(credentials).request('grid/server/power', { 'name' => id, 'power' => 'on'})
177
+ end
178
+ end
179
+
180
+ def key(credentials, opts=nil)
181
+ keys(credentials, opts).first
182
+ end
183
+
184
+ def keys(credentials, opts=nil)
185
+ gogrid = new_client( credentials )
186
+ creds = []
187
+ safely do
188
+ gogrid.request('support/password/list')['list'].each do |password|
189
+ creds << convert_key(password)
190
+ end
188
191
  end
192
+ return creds
189
193
  end
190
194
 
191
195
  define_instance_states do
@@ -201,6 +205,7 @@ class GogridDriver < Deltacloud::BaseDriver
201
205
 
202
206
  def new_client(credentials)
203
207
  GoGridClient.new('https://api.gogrid.com/api', credentials.user, credentials.password)
208
+
204
209
  end
205
210
 
206
211
  def get_login_data(client, instance_id)
@@ -219,6 +224,15 @@ class GogridDriver < Deltacloud::BaseDriver
219
224
  return login_data
220
225
  end
221
226
 
227
+ def convert_key(password)
228
+ Key.new({
229
+ :id => password['id'],
230
+ :username => password['username'],
231
+ :password => password['password'],
232
+ :credential_type => :password
233
+ })
234
+ end
235
+
222
236
  def convert_image(gg_image, owner_id=nil)
223
237
  Image.new( {
224
238
  :id=>gg_image['id'],
@@ -237,15 +251,6 @@ class GogridDriver < Deltacloud::BaseDriver
237
251
  end
238
252
  end
239
253
 
240
- def convert_flavor(flavor)
241
- Flavor.new(
242
- :id => flavor['id'],
243
- :architecture => 'x86',
244
- :memory => flavor['name'].tr('G', ''),
245
- :storage => '1'
246
- )
247
- end
248
-
249
254
  def convert_realm(realm)
250
255
  Realm.new(
251
256
  :id => realm['id'],
@@ -279,10 +284,9 @@ class GogridDriver < Deltacloud::BaseDriver
279
284
  :id => instance['name'],
280
285
  :owner_id => owner_id,
281
286
  :image_id => instance['image']['id'],
282
- :flavor_id => instance['ram']['id'],
283
287
  :instance_profile => prof,
284
288
  :name => instance['name'],
285
- :realm_id => instance['type']['id'],
289
+ :realm_id => instance['ip']['datacenter']['id'],
286
290
  :state => convert_server_state(instance['state']['name'], instance['id']),
287
291
  :actions => instance_actions_for(convert_server_state(instance['state']['name'], instance['id'])),
288
292
  :public_addresses => [ instance['ip']['ip'] ],
@@ -301,12 +305,13 @@ class GogridDriver < Deltacloud::BaseDriver
301
305
  state.eql?('Off') ? 'STOPPED' : 'RUNNING'
302
306
  end
303
307
 
304
- def get_next_free_ip(credentials)
308
+ def get_free_ip_from_realm(credentials, realm_id)
305
309
  ip = ""
306
310
  safely do
307
311
  ip = new_client(credentials).request('grid/ip/list', {
308
312
  'ip.type' => '1',
309
- 'ip.state' => '1'
313
+ 'ip.state' => '1',
314
+ 'datacenter' => realm_id
310
315
  })['list'].first['ip']
311
316
  end
312
317
  return ip
@@ -316,7 +321,7 @@ class GogridDriver < Deltacloud::BaseDriver
316
321
  begin
317
322
  block.call
318
323
  rescue Exception => e
319
- puts "ERROR: #{e.message}"
324
+ raise Deltacloud::BackendError.new(500, e.class.to_s, e.message, e.backtrace)
320
325
  end
321
326
  end
322
327