steamcannon-deltacloud-core 0.0.8.1 → 0.1.1.1

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.
Files changed (51) hide show
  1. data/Rakefile +3 -9
  2. data/bin/deltacloudd +17 -15
  3. data/deltacloud.rb +1 -0
  4. data/lib/deltacloud/backend_capability.rb +21 -0
  5. data/lib/deltacloud/base_driver/base_driver.rb +6 -0
  6. data/lib/deltacloud/base_driver/features.rb +22 -0
  7. data/lib/deltacloud/base_driver/mock_driver.rb +24 -27
  8. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +432 -494
  9. data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +4 -7
  10. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +116 -3
  11. data/lib/deltacloud/drivers/mock/mock_driver.rb +56 -2
  12. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +5 -19
  13. data/lib/deltacloud/helpers/application_helper.rb +26 -3
  14. data/lib/deltacloud/models/image.rb +2 -1
  15. data/lib/deltacloud/models/instance.rb +22 -5
  16. data/lib/deltacloud/models/key.rb +17 -0
  17. data/lib/deltacloud/models/load_balancer.rb +39 -0
  18. data/lib/deltacloud/models/storage_volume.rb +2 -0
  19. data/lib/sinatra/rabbit.rb +4 -7
  20. data/public/javascripts/application.js +10 -24
  21. data/public/stylesheets/compiled/application.css +2 -0
  22. data/server.rb +214 -75
  23. data/views/blobs/new.html.haml +10 -0
  24. data/views/blobs/show.html.haml +21 -15
  25. data/views/buckets/index.html.haml +1 -1
  26. data/views/buckets/show.html.haml +5 -2
  27. data/views/errors/backend_capability_failure.html.haml +11 -0
  28. data/views/errors/backend_capability_failure.xml.haml +4 -0
  29. data/views/errors/backend_error.html.haml +3 -0
  30. data/views/errors/not_allowed.html.haml +6 -0
  31. data/views/errors/not_allowed.xml.haml +2 -0
  32. data/views/instances/index.html.haml +1 -1
  33. data/views/instances/new.html.haml +8 -0
  34. data/views/instances/show.html.haml +1 -1
  35. data/views/keys/show.xml.haml +2 -0
  36. data/views/load_balancers/index.html.haml +33 -0
  37. data/views/load_balancers/index.xml.haml +5 -0
  38. data/views/load_balancers/new.html.haml +38 -0
  39. data/views/load_balancers/show.html.haml +37 -0
  40. data/views/load_balancers/show.xml.haml +21 -0
  41. data/views/realms/index.html.haml +4 -7
  42. data/views/storage_snapshots/index.html.haml +3 -0
  43. data/views/storage_snapshots/index.xml.haml +0 -2
  44. data/views/storage_snapshots/new.html.haml +9 -0
  45. data/views/storage_snapshots/show.xml.haml +0 -2
  46. data/views/storage_volumes/attach.html.haml +20 -0
  47. data/views/storage_volumes/index.html.haml +16 -1
  48. data/views/storage_volumes/index.xml.haml +1 -20
  49. data/views/storage_volumes/new.html.haml +17 -0
  50. data/views/storage_volumes/show.xml.haml +13 -19
  51. metadata +53 -99
@@ -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.5')
12
+ version='1.6')
13
13
  @server = server
14
14
  @secret = secret
15
15
  @default_params = {'format'=>format, 'v'=>version,'api_key' => apikey}
@@ -35,12 +35,9 @@ class GoGridClient
35
35
  @default_params['v'] = version
36
36
  else
37
37
  @default_params['v'] = '1.5'
38
- end
39
- begin
40
- JSON::parse(sendAPIRequest(method, params))
41
- rescue Exception => e
42
- STDERR.puts("ERROR: #{e.message}")
43
- end
38
+ end
39
+ request = sendAPIRequest(method, params)
40
+ JSON::parse(request)
44
41
  end
45
42
 
46
43
  def encode_params(params)
@@ -45,7 +45,7 @@ class GogridDriver < Deltacloud::BaseDriver
45
45
 
46
46
  def supported_collections
47
47
  DEFAULT_COLLECTIONS.reject! { |c| [ :storage_volumes, :storage_snapshots ].include?(c) }
48
- DEFAULT_COLLECTIONS + [ :keys ]
48
+ DEFAULT_COLLECTIONS + [ :keys, :load_balancers ]
49
49
  end
50
50
 
51
51
  def images(credentials, opts=nil)
@@ -176,6 +176,88 @@ class GogridDriver < Deltacloud::BaseDriver
176
176
  end
177
177
  end
178
178
 
179
+ def create_load_balancer(credentials, opts={})
180
+ gogrid = new_client(credentials)
181
+ balancer, l_instance = nil, nil
182
+ safely do
183
+ virtip = get_free_ip_from_realm(credentials, opts['realm_id'])
184
+ if opts['instance_id']
185
+ l_instance = instance(credentials, :id => opts['instance_id'])
186
+ real_ip = {
187
+ 'realiplist.0.port' => opts['listener_inst_port'],
188
+ 'realiplist.0.ip' => l_instance ? l_instance.public_addresses.first : ""
189
+ }
190
+ else
191
+ real_ip = false
192
+ end
193
+ request = {
194
+ 'name' => opts['name'],
195
+ 'virtualip.ip' => virtip,
196
+ 'virtualip.port' => opts['listener_lbr_port'],
197
+ }
198
+ request.merge!(real_ip) if real_ip
199
+ balancer = gogrid.request('grid/loadbalancer/add', request)['list'].first
200
+ end
201
+ balancer = convert_load_balancer(credentials, balancer)
202
+ balancer.instances = [l_instance] if l_instance
203
+ balancer
204
+ end
205
+
206
+ def destroy_load_balancer(credentials, id)
207
+ gogrid = new_client(credentials)
208
+ balancer = nil
209
+ safely do
210
+ balancer = gogrid.request('grid/loadbalancer/delete', { 'name' => id })
211
+ balancer = load_balancer(credentials, :id => id) unless balancer
212
+ end
213
+ convert_load_balancer(credentials, balancer)
214
+ end
215
+
216
+ def load_balancers(credentials, opts={})
217
+ gogrid = new_client(credentials)
218
+ balancers = []
219
+ safely do
220
+ balancer = gogrid.request('grid/loadbalancer/list', opts || {})['list'].each do |balancer|
221
+ balancers << balancer
222
+ end
223
+ end
224
+ balancers.collect { |b| convert_load_balancer(credentials, b) }
225
+ end
226
+
227
+ def load_balancer(credentials, opts={})
228
+ gogrid = new_client(credentials)
229
+ balancer = nil
230
+ begin
231
+ balancer = gogrid.request('grid/loadbalancer/get', { 'name' => opts[:id] })['list'].first
232
+ balancer['instances'] = instances(credentials)
233
+ return convert_load_balancer(credentials, balancer)
234
+ rescue OpenURI::HTTPError
235
+ balancer = load_balancers(credentials, :id => opts[:id]).first
236
+ end
237
+ end
238
+
239
+
240
+ def lb_register_instance(credentials, opts={})
241
+ client = new_client(credentials)
242
+ instance = instance(credentials, :id => opts[:instance_id])
243
+ balancer = client.request('grid/loadbalancer/get', { 'name' => opts[:id]})['list'].first
244
+ safely do
245
+ convert_load_balancer(credentials, client.request('grid/loadbalancer/edit', {
246
+ "id" => balancer['id'],
247
+ "realiplist.#{balancer['realiplist'].size}.ip" => instance.public_addresses.first,
248
+ "realiplist.#{balancer['realiplist'].size}.port" => balancer['virtualip']['port']
249
+ }))
250
+ end
251
+ end
252
+
253
+ # Move this to capabilities
254
+ def lb_unregister_instance(credentials, opts={})
255
+ raise Deltacloud::BackendFeatureUnsupported.new('501',
256
+ 'Unregistering instances from load balancer is not supported in GoGrid')
257
+ end
258
+
259
+
260
+
179
261
  def key(credentials, opts=nil)
180
262
  keys(credentials, opts).first
181
263
  end
@@ -213,8 +295,39 @@ class GogridDriver < Deltacloud::BaseDriver
213
295
 
214
296
  def new_client(credentials)
215
297
  GoGridClient.new('https://api.gogrid.com/api', credentials.user, credentials.password)
298
+ end
216
299
 
300
+ def convert_load_balancer(credentials, loadbalancer)
301
+ if loadbalancer['datacenter']
302
+ b_realm = realm(credentials, :id => loadbalancer['datacenter']['id'])
303
+ else
304
+ # Report first Realm until loadbalancer become ready
305
+ b_realm = realm(credentials, :id => 1)
306
+ end
307
+ balancer = LoadBalancer.new({
308
+ :id => loadbalancer['name'],
309
+ :realms => [b_realm]
310
+ })
311
+ balancer.public_addresses = [loadbalancer['virtualip']['ip']['ip']] if loadbalancer['virtualip'] and loadbalancer['virtualip']['ip']
312
+ balancer.listeners = []
313
+ balancer.instances = []
314
+ instance_ips = []
315
+ loadbalancer['realiplist'].each do |instance_ip|
316
+ balancer.add_listener({
317
+ :protocol => 'TCP',
318
+ :load_balancer_port => loadbalancer['virtualip']['port'],
319
+ :instance_port => instance_ip['port']
320
+ })
321
+ instance_ips << instance_ip['ip']['ip']
322
+ end if loadbalancer['realiplist']
323
+ balancer.instances = get_load_balancer_instances(instance_ips, loadbalancer['instances'])
324
+ return balancer
217
325
  end
326
+
327
+ def get_load_balancer_instances(instance_ips, instances)
328
+ instances.select { |i| instance_ips.include?(i.public_addresses.first) } if instances
329
+ end
330
+
218
331
 
219
332
  def get_login_data(client, instance_id)
220
333
  login_data = {}
@@ -316,11 +429,11 @@ class GogridDriver < Deltacloud::BaseDriver
316
429
  state.eql?('Off') ? 'STOPPED' : 'RUNNING'
317
430
  end
318
431
 
319
- def get_free_ip_from_realm(credentials, realm_id)
432
+ def get_free_ip_from_realm(credentials, realm_id, ip_type = 1)
320
433
  ip = ""
321
434
  safely do
322
435
  ip = new_client(credentials).request('grid/ip/list', {
323
- 'ip.type' => '1',
436
+ 'ip.type' => "#{ip_type}",
324
437
  'ip.state' => '1',
325
438
  'datacenter' => realm_id
326
439
  })['list'].first['ip']
@@ -26,7 +26,7 @@ module Deltacloud
26
26
  class MockDriver < Deltacloud::BaseDriver
27
27
 
28
28
  def supported_collections
29
- DEFAULT_COLLECTIONS + [ :buckets ]
29
+ DEFAULT_COLLECTIONS + [ :buckets, :keys]
30
30
  end
31
31
 
32
32
  ( REALMS = [
@@ -81,6 +81,7 @@ class MockDriver < Deltacloud::BaseDriver
81
81
  end
82
82
 
83
83
  feature :instances, :user_name
84
+ feature :instances, :authentication_key
84
85
 
85
86
  def initialize
86
87
  if ENV["DELTACLOUD_MOCK_STORAGE"]
@@ -131,11 +132,21 @@ class MockDriver < Deltacloud::BaseDriver
131
132
  # Instances
132
133
  #
133
134
 
135
+ def instance(credentials, opts={})
136
+ check_credentials( credentials )
137
+ instance_filename = File.join(@storage_root, 'instances', "#{opts[:id]}.yml")
138
+ return nil unless File.exists?(instance_filename)
139
+ instance = YAML::load_file(instance_filename)
140
+ instance[:actions] = instance_actions_for( instance[:state] )
141
+ instance[:id] = File::basename(instance_filename, ".yml")
142
+ Instance.new(instance)
143
+ end
144
+
134
145
  def instances(credentials, opts=nil)
135
146
  check_credentials( credentials )
136
147
  instances = []
137
148
  Dir[ "#{@storage_root}/instances/*.yml" ].each do |instance_file|
138
- instance = YAML.load( File.read( instance_file ) )
149
+ instance = YAML::load_file(instance_file)
139
150
  if ( instance[:owner_id] == credentials.user )
140
151
  instance[:id] = File.basename( instance_file, ".yml" )
141
152
  instance[:actions] = instance_actions_for( instance[:state] )
@@ -254,6 +265,49 @@ class MockDriver < Deltacloud::BaseDriver
254
265
  snapshots
255
266
  end
256
267
 
268
+ def keys(credentials, opts={})
269
+ check_credentials(credentials)
270
+ result = []
271
+ key_dir = File.join(@storage_root, 'keys')
272
+ Dir[key_dir + '/*.yml'].each do |key_file|
273
+ result << Key.new(YAML::load(File.read(key_file)))
274
+ end
275
+ result = filter_on( result, :id, opts )
276
+ result
277
+ end
278
+
279
+ def key(credentials, opts={})
280
+ keys(credentials, opts).first
281
+ end
282
+
283
+ def create_key(credentials, opts={})
284
+ check_credentials(credentials)
285
+ key_hash = {
286
+ :id => opts[:key_name],
287
+ :credential_type => :key,
288
+ :fingerprint => Key::generate_mock_fingerprint,
289
+ :pem_rsa_key => Key::generate_mock_pem
290
+ }
291
+ key_dir = File.join(@storage_root, 'keys')
292
+ if File.exists?(key_dir + "/#{key_hash[:id]}.yml")
293
+ raise Deltacloud::BackendError.new(403, self.class.to_s, "key-exists",
294
+ ["Key with same name already exists"])
295
+ end
296
+ FileUtils.mkdir_p(key_dir) unless File.directory?(key_dir)
297
+ File.open(key_dir + "/#{key_hash[:id]}.yml", 'w') do |f|
298
+ f.puts(YAML::dump(key_hash))
299
+ end
300
+ return Key.new(key_hash)
301
+ end
302
+
303
+ def destroy_key(credentials, opts={})
304
+ key = key(credentials, opts)
305
+ safely do
306
+ key_dir = File.join(@storage_root, 'keys')
307
+ File.delete(key_dir + "/#{key.id}.yml")
308
+ end
309
+ end
310
+
257
311
  #--
258
312
  # Buckets
259
313
  #--
@@ -35,6 +35,10 @@ class RHEVMDriver < Deltacloud::BaseDriver
35
35
 
36
36
  feature :instances, :user_name
37
37
 
38
+ def supported_collections
39
+ DEFAULT_COLLECTIONS.reject { |c| [ :storage_volumes, :storage_snapshots ].include?(c) }
40
+ end
41
+
38
42
  #
39
43
  # Execute a Powershell command, and convert the output
40
44
  # to YAML in order to get back an array of maps.
@@ -85,6 +89,7 @@ class RHEVMDriver < Deltacloud::BaseDriver
85
89
  "STOPPED"
86
90
  when "POWERING UP"
87
91
  "PENDING"
92
+ end
88
93
  end
89
94
 
90
95
  define_hardware_profile 'rhevm'
@@ -232,25 +237,6 @@ class RHEVMDriver < Deltacloud::BaseDriver
232
237
  vm = execute(credentials, "deleteVm.ps1", image_id)
233
238
  vm_to_instance(vm[0])
234
239
  end
235
-
236
- #
237
- # Storage Volumes
238
- #
239
-
240
- def storage_volumes(credentials, ids=nil)
241
- volumes = []
242
- volumes
243
- end
244
-
245
- #
246
- # Storage Snapshots
247
- #
248
-
249
- def storage_snapshots(credentials, ids=nil)
250
- snapshots = []
251
- snapshots
252
- end
253
-
254
240
  end
255
241
 
256
242
  end
@@ -38,11 +38,15 @@ module ApplicationHelper
38
38
  end
39
39
 
40
40
  def instance_action_method(action)
41
- collections[:instances].operations[action.to_sym].method
41
+ action_method(action, :instances)
42
42
  end
43
43
 
44
- def driver_has_feature?(feature_name)
45
- not driver.features(:instances).select{ |f| f.name.eql?(feature_name) }.empty?
44
+ def action_method(action, collection)
45
+ collections[collection].operations[action.to_sym].method
46
+ end
47
+
48
+ def driver_has_feature?(feature_name, collection_name = :instances)
49
+ not driver.features(collection_name).select{ |f| f.name.eql?(feature_name) }.empty?
46
50
  end
47
51
 
48
52
  def driver_has_auth_features?
@@ -102,6 +106,14 @@ module ApplicationHelper
102
106
  end
103
107
 
104
108
  def instance_action(name)
109
+ original_instance = driver.instance(credentials, :id => params[:id])
110
+
111
+ # If original instance doesn't include called action
112
+ # return with 405 error (Method is not Allowed)
113
+ unless driver.instance_actions_for(original_instance.state).include?(name.to_sym)
114
+ return report_error(405, 'not_allowed')
115
+ end
116
+
105
117
  @instance = driver.send(:"#{name}_instance", credentials, params["id"])
106
118
 
107
119
  return redirect(instances_url) if name.eql?(:destroy) or @instance.class!=Instance
@@ -122,4 +134,15 @@ module ApplicationHelper
122
134
  "<pem><![CDATA[#{text.strip}]]></pem>"
123
135
  end
124
136
 
137
+ def link_to_action(action, url, method)
138
+ capture_haml do
139
+ haml_tag :form, :method => :post, :action => url, :class => :link do
140
+ haml_tag :input, :type => :hidden, :name => '_method', :value => method
141
+ haml_tag :button, :type => :submit do
142
+ haml_concat action
143
+ end
144
+ end
145
+ end
146
+ end
147
+
125
148
  end
@@ -23,5 +23,6 @@ class Image < BaseModel
23
23
  attr_accessor :owner_id
24
24
  attr_accessor :description
25
25
  attr_accessor :architecture
26
+ attr_accessor :state
26
27
 
27
- end
28
+ end
@@ -29,10 +29,27 @@ class Instance < BaseModel
29
29
  attr_accessor :private_addresses
30
30
  attr_accessor :instance_profile
31
31
  attr_accessor :launch_time
32
- def initialize(init=nil)
33
- super(init)
34
- self.actions = [] if self.actions.nil?
35
- self.public_addresses = [] if self.public_addresses.nil?
36
- self.private_addresses = [] if self.private_addresses.nil?
32
+ attr_accessor :keyname
33
+ attr_accessor :authn_error
34
+
35
+ def initialize(init=nil)
36
+ super(init)
37
+ self.actions = [] if self.actions.nil?
38
+ self.public_addresses = [] if self.public_addresses.nil?
39
+ self.private_addresses = [] if self.private_addresses.nil?
40
+ end
41
+
42
+ def method_missing(name, *args)
43
+ if name =~ /is_(\w+)\?/
44
+ return true if self.state.downcase.eql?($1)
45
+ else
46
+ raise NoMethodError
47
+ end
37
48
  end
49
+
50
+ def authn_feature_failed?
51
+ return true unless authn_error.nil?
52
+ end
53
+
54
+
38
55
  end
@@ -23,6 +23,7 @@ class Key < BaseModel
23
23
  attr_accessor :username
24
24
  attr_accessor :password
25
25
  attr_accessor :pem_rsa_key
26
+ attr_accessor :state
26
27
 
27
28
  def is_password?
28
29
  true if @credential_type.eql?(:password)
@@ -32,4 +33,20 @@ class Key < BaseModel
32
33
  true if @credential_type.eql?(:key)
33
34
  end
34
35
 
36
+ # Mock fingerprint generator
37
+ # 1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f
38
+ def self.generate_mock_fingerprint
39
+ (0..19).map { "%02x" % (rand * 0xff) }.join(':')
40
+ end
41
+
42
+ # Mock PEM file
43
+ # NOTE: This is a fake PEM file, it will not work against SSH
44
+ def self.generate_mock_pem
45
+ chars = (('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + %w(= / + ))
46
+ pem_material = (1..21).map do
47
+ (1..75).collect{|a| chars[rand(chars.size)] }.join
48
+ end.join("\n") + "\n" + (1..68).collect{|a| chars[rand(chars.size)] }.join
49
+ "-----BEGIN RSA PRIVATE KEY-----\n"+pem_material+"-----END RSA PRIVATE KEY-----"
50
+ end
51
+
35
52
  end
@@ -0,0 +1,39 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # Licensed to the Apache Software Foundation (ASF) under one or more
5
+ # contributor license agreements. See the NOTICE file distributed with
6
+ # this work for additional information regarding copyright ownership. The
7
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance with the
9
+ # License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
+ # License for the specific language governing permissions and limitations
17
+ # under the License.
18
+
19
+
20
+ class LoadBalancer < BaseModel
21
+
22
+ attr_accessor :realms
23
+ attr_accessor :listeners
24
+ attr_accessor :instances
25
+ attr_accessor :public_addresses
26
+ attr_accessor :created_at
27
+
28
+ def add_listener(opts)
29
+ @listeners << Listener.new(opts)
30
+ end
31
+
32
+ class Listener < BaseModel
33
+ attr_accessor :protocol
34
+ attr_accessor :load_balancer_port
35
+ attr_accessor :instance_port
36
+ end
37
+
38
+ end
39
+