poolparty 1.4.7 → 1.4.8
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/VERSION.yml +2 -2
- data/bin/cloud +7 -2
- data/bin/cloud-console +25 -12
- data/lib/cloud_providers/cloud_provider.rb +3 -2
- data/lib/cloud_providers/ec2/ec2.rb +58 -13
- data/lib/cloud_providers/ec2/ec2_instance.rb +5 -2
- data/lib/cloud_providers/ec2/helpers/ec2_helper.rb +1 -1
- data/lib/cloud_providers/ec2/helpers/elastic_block_device_mapping.rb +7 -0
- data/lib/cloud_providers/ec2/helpers/elastic_block_store.rb +62 -5
- data/lib/cloud_providers/ec2/helpers/elastic_block_store_group.rb +73 -0
- data/lib/cloud_providers/remote_instance.rb +3 -3
- metadata +4 -2
data/VERSION.yml
CHANGED
data/bin/cloud
CHANGED
|
@@ -31,7 +31,12 @@ EOS
|
|
|
31
31
|
$DEBUGGING = true if command[:debug]
|
|
32
32
|
$VERY_DEBUGGING = true if command[:very_debug]
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
begin
|
|
35
|
+
require command[:clouds_dot_rb]
|
|
36
|
+
rescue LoadError => e
|
|
37
|
+
puts "Failed loading #{command[:clouds_dot_rb]}, try using -c to specify the location of your clouds.rb"
|
|
38
|
+
exit
|
|
39
|
+
end
|
|
35
40
|
|
|
36
41
|
@loaded_pool = pool
|
|
37
42
|
@loaded_clouds = command[:name] ? [pool.clouds[command[:name]]] : pool.clouds.map {|name,cld|cld}
|
|
@@ -47,4 +52,4 @@ EOS
|
|
|
47
52
|
|
|
48
53
|
See 'cloud help COMMAND' for more information on a specific command"
|
|
49
54
|
end
|
|
50
|
-
end
|
|
55
|
+
end
|
data/bin/cloud-console
CHANGED
|
@@ -16,18 +16,31 @@ EOS
|
|
|
16
16
|
|
|
17
17
|
run do |command|
|
|
18
18
|
|
|
19
|
-
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
|
20
|
-
|
|
21
|
-
libs = " -r irb/completion"
|
|
22
|
-
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
|
23
|
-
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
|
24
|
-
libs << " -r #{File.dirname(__FILE__) + '/../lib/poolparty.rb'}"
|
|
25
|
-
f = command[:clouds_dot_rb]
|
|
26
|
-
File.open("/tmp/console.rb", "w") {|f| f << "require '#{command[:clouds_dot_rb]}'\npool.auto_execute = false" }
|
|
27
|
-
libs << " -r /tmp/console.rb"
|
|
28
|
-
|
|
29
19
|
puts "Loading PoolParty console..."
|
|
30
|
-
exec "#{irb} #{libs} --simple-prompt"
|
|
20
|
+
#exec "#{irb} #{libs} --simple-prompt"
|
|
21
|
+
require 'irb/completion'
|
|
22
|
+
require command[:clouds_dot_rb]
|
|
23
|
+
require "#{File.dirname(__FILE__) + '/../lib/poolparty.rb'}"
|
|
24
|
+
require 'irb'
|
|
25
|
+
pool.auto_execute = false
|
|
31
26
|
|
|
27
|
+
IRB.setup(nil)
|
|
28
|
+
IRB.conf[:IRB_NAME]="cloud"
|
|
29
|
+
IRB.conf[:PROMPT_MODE]=:CLOUD
|
|
30
|
+
IRB.conf[:PROMPT][:CLOUD]={
|
|
31
|
+
:PROMPT_I => "%N> ",
|
|
32
|
+
:PROMPT_S => "%l> ",
|
|
33
|
+
:PROMPT_C => "* ",
|
|
34
|
+
:PROMPT_N => ">> ",
|
|
35
|
+
:RETURN => "=>%s\n" }
|
|
36
|
+
irb=IRB::Irb.new
|
|
37
|
+
IRB.conf[:IRB_RC].call(irb.context) if IRB.conf[:IRB_RC]
|
|
38
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
|
39
|
+
trap("SIGINT") do
|
|
40
|
+
irb.signal_handle
|
|
41
|
+
end
|
|
42
|
+
catch(:IRB_EXIT) do
|
|
43
|
+
irb.eval_input
|
|
44
|
+
end
|
|
32
45
|
end
|
|
33
|
-
end
|
|
46
|
+
end
|
|
@@ -62,12 +62,12 @@ module CloudProviders
|
|
|
62
62
|
return {:access_key => @access_key, :secret_access_key => @secret_access_key} if @access_key and @secret_access_key
|
|
63
63
|
return {} if filename.nil? or not File.exists?(filename)
|
|
64
64
|
puts("Reading keys from file: #{filename}")
|
|
65
|
-
File.open(filename).each_line {|line|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
File.open(filename).each_line { |line|
|
|
66
|
+
if line =~ /AWSAccessKeyId=([a-zA-Z0-9]+)$/
|
|
67
|
+
@access_key=$1.chomp
|
|
68
|
+
elsif line =~ /AWSSecretKey=([^ ]+)$/
|
|
69
|
+
@secret_access_key=$1.chomp
|
|
70
|
+
end
|
|
71
71
|
}
|
|
72
72
|
return {:access_key => @access_key, :secret_access_key => @secret_access_key}
|
|
73
73
|
end
|
|
@@ -92,8 +92,7 @@ module CloudProviders
|
|
|
92
92
|
:addressing_type => nil,
|
|
93
93
|
:kernel_id => nil,
|
|
94
94
|
:ramdisk_id => nil,
|
|
95
|
-
:
|
|
96
|
-
:ebs_volumes => [] # The volume id of an ebs volume # TODO: ensure this is consistent with :block_device_mappings
|
|
95
|
+
:block_device_mapping => [{}]
|
|
97
96
|
)
|
|
98
97
|
|
|
99
98
|
# Called when the create command is called on the cloud
|
|
@@ -174,6 +173,8 @@ module CloudProviders
|
|
|
174
173
|
end
|
|
175
174
|
|
|
176
175
|
assign_elastic_ips
|
|
176
|
+
puts "Attaching EBS volumes"
|
|
177
|
+
assign_ebs_volumes # Assign EBS volumes
|
|
177
178
|
end
|
|
178
179
|
|
|
179
180
|
def teardown
|
|
@@ -194,7 +195,8 @@ module CloudProviders
|
|
|
194
195
|
:instance_type => instance_type,
|
|
195
196
|
:availability_zone => availability_zones.first,
|
|
196
197
|
:base64_encoded => true,
|
|
197
|
-
:cloud => cloud
|
|
198
|
+
:cloud => cloud,
|
|
199
|
+
:block_device_mapping => block_device_mapping
|
|
198
200
|
})
|
|
199
201
|
progress_bar_until("Waiting for node to launch...") do
|
|
200
202
|
wait_for_node(e)
|
|
@@ -283,8 +285,8 @@ module CloudProviders
|
|
|
283
285
|
# Describe instances
|
|
284
286
|
# Describe the instances that are available on this cloud
|
|
285
287
|
# @params id (optional) if present, details about the instance
|
|
286
|
-
#
|
|
287
|
-
#
|
|
288
|
+
# with the id given will be returned
|
|
289
|
+
# if not given, details for all instances will be returned
|
|
288
290
|
def describe_instances(id=nil)
|
|
289
291
|
begin
|
|
290
292
|
@describe_instances = ec2.describe_instances.reservationSet.item.map do |r|
|
|
@@ -303,6 +305,10 @@ module CloudProviders
|
|
|
303
305
|
|
|
304
306
|
# Extras!
|
|
305
307
|
|
|
308
|
+
def block_device_mapping(o=[], given_name=cloud.proper_name )
|
|
309
|
+
@mappings ||= o
|
|
310
|
+
end
|
|
311
|
+
|
|
306
312
|
def load_balancer(given_name=cloud.proper_name, o={}, &block)
|
|
307
313
|
load_balancers << ElasticLoadBalancer.new(given_name, sub_opts.merge(o || {}), &block)
|
|
308
314
|
end
|
|
@@ -362,6 +368,25 @@ module CloudProviders
|
|
|
362
368
|
@elastic_ips ||= []
|
|
363
369
|
end
|
|
364
370
|
|
|
371
|
+
def ebs_volume_groups
|
|
372
|
+
@ebs_volume_groups ||= []
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# dsl method for EBS volumes. E.G.:
|
|
376
|
+
# ebs_volumes do
|
|
377
|
+
# volumes "vol-001248ff", "vol-01ff4b85" # use existing volumes, not mandatory
|
|
378
|
+
# device "/dev/sdf"
|
|
379
|
+
# snapshot_id "snap-602030dd"
|
|
380
|
+
# size 200
|
|
381
|
+
# end
|
|
382
|
+
def ebs_volumes(name=nil, &block)
|
|
383
|
+
ebs_volume_groups << ElasticBlockStoreGroup.new(sub_opts,&block)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def assign_ebs_volumes
|
|
387
|
+
ebs_volume_groups.each{|ebs_volume_group| ebs_volume_group.attach(nodes)}
|
|
388
|
+
end
|
|
389
|
+
|
|
365
390
|
def rds_instances
|
|
366
391
|
@rds_instances ||= []
|
|
367
392
|
end
|
|
@@ -371,11 +396,29 @@ module CloudProviders
|
|
|
371
396
|
@nodes = @describe_instances = nil
|
|
372
397
|
end
|
|
373
398
|
|
|
399
|
+
# Get existing volumes on EC2. filters is a hash of filters, either single valued or multivalued (value is an array of possible values).
|
|
400
|
+
# The function will return volumes matching *all* filters. A volume is a filter match if *any* one of the filter values equals the volume parameter value.
|
|
401
|
+
def list_ec2_volumes(filters=nil)
|
|
402
|
+
@volumes_on_ec2=ec2.describe_volumes.volumeSet.item unless @volumes_on_ec2
|
|
403
|
+
return @volumes_on_ec2 if filters.nil? # no filter to check, so return at once
|
|
404
|
+
@volumes_on_ec2.select{|vol| # select volumes for which no filter failed
|
|
405
|
+
not filters.map {|filter_key, filter_val|
|
|
406
|
+
filter_key=filter_key.to_s if filter_key.is_a?(Symbol) # filter_key may be given as a symbol
|
|
407
|
+
raise ArgumentError, "Filter key #{filter_key} is invalid" unless vol.has_key?(filter_key)
|
|
408
|
+
if filter_val.is_a?(Array) # Deal with multiple filter values
|
|
409
|
+
filter_val.map{|val| val.is_a?(String) ? val : val.to_s}.member?(vol[filter_key]) # make sure fiter_val array values are Strings before checking for match
|
|
410
|
+
else
|
|
411
|
+
filter_val.is_a?(String) ? filter_val : filter_val.to_s==vol[filter_key] # make sure fiter_val is a String before comparing
|
|
412
|
+
end
|
|
413
|
+
}.member?(false) # Check if a filter failed, the 'not' statement at the beginning of the map block negates this so 'select' will choose only when no filter failed
|
|
414
|
+
}.compact # remove nil results from volume set.
|
|
415
|
+
end
|
|
416
|
+
|
|
374
417
|
# Read credentials from credential_file if one exists
|
|
375
418
|
def credential_file(file=nil)
|
|
376
419
|
unless file.nil?
|
|
377
|
-
|
|
378
|
-
|
|
420
|
+
dsl_options[:credential_file]=file
|
|
421
|
+
dsl_options.merge((Ec2.load_keys_from_credential_file(file)))
|
|
379
422
|
else
|
|
380
423
|
fetch(:credential_file)
|
|
381
424
|
end
|
|
@@ -410,7 +453,9 @@ require "#{File.dirname(__FILE__)}/helpers/ec2_helper"
|
|
|
410
453
|
%w( security_group
|
|
411
454
|
authorize
|
|
412
455
|
elastic_auto_scaler
|
|
456
|
+
elastic_block_device_mapping
|
|
413
457
|
elastic_block_store
|
|
458
|
+
elastic_block_store_group
|
|
414
459
|
elastic_load_balancer
|
|
415
460
|
elastic_ip
|
|
416
461
|
rds_instance
|
|
@@ -9,7 +9,8 @@ module CloudProviders
|
|
|
9
9
|
:public_ip => nil,
|
|
10
10
|
:key_name => nil,
|
|
11
11
|
:launch_time => nil,
|
|
12
|
-
:availability_zones => []
|
|
12
|
+
:availability_zones => [],
|
|
13
|
+
:block_device_mapping => [{}]
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
def initialize(raw_response={})
|
|
@@ -25,6 +26,7 @@ module CloudProviders
|
|
|
25
26
|
self.launch_time = raw_response["launchTime"] rescue nil
|
|
26
27
|
self.availability_zones = raw_response["placement"]["availabilityZone"] rescue nil
|
|
27
28
|
self.status = raw_response["instanceState"]["name"] rescue nil
|
|
29
|
+
self.block_device_mapping = raw_response["blockDeviceMapping"] rescue nil
|
|
28
30
|
super
|
|
29
31
|
end
|
|
30
32
|
|
|
@@ -53,6 +55,7 @@ module CloudProviders
|
|
|
53
55
|
:user_data => user_data,
|
|
54
56
|
:instance_type => instance_type,
|
|
55
57
|
:availability_zone => availability_zone,
|
|
58
|
+
:block_device_mapping => block_device_mapping,
|
|
56
59
|
:base64_encoded => true)
|
|
57
60
|
r.instancesSet.item.map do |i|
|
|
58
61
|
inst_options = i.merge(r.merge(:cloud => cloud)).merge(cloud.cloud_provider.dsl_options)
|
|
@@ -130,4 +133,4 @@ module CloudProviders
|
|
|
130
133
|
end
|
|
131
134
|
|
|
132
135
|
end
|
|
133
|
-
end
|
|
136
|
+
end
|
|
@@ -1,9 +1,66 @@
|
|
|
1
1
|
module CloudProviders
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
class ElasticBlockStore < Ec2Helper
|
|
3
|
+
|
|
4
|
+
# instance methods
|
|
5
|
+
attr_accessor :volumeId, :size, :snapshotId, :status, :attachments, :device, :availabilityZone, :instance_id
|
|
6
|
+
attr_reader :createTime
|
|
7
|
+
|
|
8
|
+
alias :volume_id :volumeId
|
|
9
|
+
alias :snapshot_id :snapshotId
|
|
10
|
+
alias :availability_zone :availabilityZone
|
|
11
|
+
alias :create_time :createTime
|
|
12
|
+
|
|
13
|
+
def createTime(create_time)
|
|
14
|
+
unless create_time.class==DateTime
|
|
15
|
+
@create_time=(DateTime.new(create_time) rescue nil)
|
|
16
|
+
else
|
|
17
|
+
@createTime=create_time
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
def initialize(raw_response,init_opts={},&block)
|
|
21
|
+
parse_raw_response(raw_response)
|
|
22
|
+
super(volumeId,init_opts,&block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def parse_raw_response(raw_response)
|
|
26
|
+
@raw_respons = raw_response
|
|
27
|
+
raw_response.each{|k,v| send k+"=", v if respond_to?(k+"=") }
|
|
28
|
+
if raw_response.attachmentSet.respond_to?(:item)
|
|
29
|
+
@attachments=raw_response.attachmentSet.item
|
|
30
|
+
@attachments.each{|attch| instance_id=attch.instanceId if attch.status=="attached"}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def attached?(fn_instance_id=nil)
|
|
35
|
+
return false unless @status=="in-use" or @status=="attaching"
|
|
36
|
+
return true if fn_instance_id.nil?
|
|
37
|
+
return true if fn_instance_id == instance_id
|
|
38
|
+
return false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def available?
|
|
42
|
+
@status=="available"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def attach(ec2_instance,device)
|
|
46
|
+
ec2.attach_volume(:volume_id => volume_id, :instance_id => ec2_instance.instance_id, :device => device).return=="true"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def detach
|
|
50
|
+
ec2.detach_volume(:volume_id => volume_id).return=="true"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def detach!
|
|
54
|
+
ec2.detach_volume(:volume_id => volume_id, :force => true).return=="true"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def delete!
|
|
58
|
+
ec2.delete(:volume_id => volume_id).return == "true"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def update!
|
|
62
|
+
parse_raw_response ec2.describe_volumes(:volume_id => volume_id)
|
|
6
63
|
end
|
|
7
64
|
|
|
8
65
|
end
|
|
9
|
-
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
|
|
2
|
+
module CloudProviders
|
|
3
|
+
# ElasticBlockStoreGroup class allows easy manipulation of EBS volumes matching defined criterias.
|
|
4
|
+
# Existing volumes in *cloud*'s availability zones that match the criterias will be selected for the group. When cloud instances need to attach EBS volumes from the group, the attach method should called.
|
|
5
|
+
# When attaching volumes the ElasticBlockStoreGroup will select existing (unattached) volumes until there are non, afterwhich the group will create new volumes according to the criterias given as needed.
|
|
6
|
+
#
|
|
7
|
+
# Currently, EBS volumes will not be deleted when tearing down a cloud. This is because poolparty is stateless and thus deleting drives from it will probably result in catastroph (deletions will be too general and delete stuff you don't want deleted).
|
|
8
|
+
# Hopefully, we will come up with a scheme for a deletion flag of some sort to solve this situation.
|
|
9
|
+
class ElasticBlockStoreGroup < Ec2Helper
|
|
10
|
+
|
|
11
|
+
default_options(:device => nil, :size => 0, :snapshot_id => nil)
|
|
12
|
+
alias :snapshotId :snapshot_id
|
|
13
|
+
|
|
14
|
+
def initialize(name=cloud.proper_name, init_opts={}, &block)
|
|
15
|
+
@volumes=[]
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
def after_initialized
|
|
19
|
+
unless @volumes.size > 0
|
|
20
|
+
filters={:size => size, :availabilityZone => availability_zones}
|
|
21
|
+
filters[:snapshotId]=snapshot_id if snapshot_id
|
|
22
|
+
@volumes=cloud.list_ec2_volumes filters
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
def volumes(*volume_ids)
|
|
26
|
+
return @volumes if volume_ids.size==0
|
|
27
|
+
volume_ids.each{|volume_id| @volumes << cloud.list_ec2_volumes(:volumeId => volume_id)}
|
|
28
|
+
end
|
|
29
|
+
def volumes_attached_to(instanceId)
|
|
30
|
+
@volumes.select {|vol| vol.attached?(instanceId)}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# get volumes that are not attached
|
|
34
|
+
def free_volumes(availability_zone)
|
|
35
|
+
@volumes.select{|vol| vol.available? and vol.availability_zone == availability_zone}
|
|
36
|
+
end
|
|
37
|
+
# Get a free volume from existing volumes in group or create a new one
|
|
38
|
+
def get_free_volume(availability_zone)
|
|
39
|
+
free=free_volumes(availability_zone)
|
|
40
|
+
if free.size>=1
|
|
41
|
+
return free[0]
|
|
42
|
+
end
|
|
43
|
+
create(availability_zone)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Create new volume on availability_zone
|
|
47
|
+
def create(availability_zone)
|
|
48
|
+
options={:availability_zone => availability_zone, :size => size.to_s}
|
|
49
|
+
options[:snapshot_id]=snapshot_id if snapshot_id
|
|
50
|
+
vol=ElasticBlockStore.new(ec2.create_volume(options),:cloud => cloud)
|
|
51
|
+
@volumes<<vol
|
|
52
|
+
vol
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def attach(nodes)
|
|
56
|
+
nodes.each{|node|
|
|
57
|
+
# Check no volumes are attached to node on device
|
|
58
|
+
skip_node=false
|
|
59
|
+
cloud.list_ec2_volumes.each{|vol|
|
|
60
|
+
if vol.attached?(node.instance_id)and vol.device == device
|
|
61
|
+
warn "A volume is allready attached to device #{device} of instance #{node.instance_id}"
|
|
62
|
+
skip_node = true
|
|
63
|
+
end
|
|
64
|
+
}
|
|
65
|
+
unless skip_node
|
|
66
|
+
vol=get_free_volume(node.zone)
|
|
67
|
+
vol.device=device
|
|
68
|
+
vol.attach(node,device)
|
|
69
|
+
end
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -47,8 +47,8 @@ module CloudProviders
|
|
|
47
47
|
'apt-get autoremove -y',
|
|
48
48
|
'apt-get install -y ruby ruby-dev rubygems git-core',
|
|
49
49
|
'gem sources -a http://gems.opscode.com',
|
|
50
|
-
'gem install chef ohai --no-rdoc --no-ri'
|
|
51
|
-
|
|
50
|
+
'gem install chef ohai --no-rdoc --no-ri' ] +
|
|
51
|
+
bootstrap_gems.collect { |gem| "gem install #{gem} --no-rdoc --no-ri" } )
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -138,4 +138,4 @@ module CloudProviders
|
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
end
|
|
141
|
-
end
|
|
141
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: poolparty
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.4.
|
|
4
|
+
version: 1.4.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ari Lerner
|
|
@@ -11,7 +11,7 @@ autorequire:
|
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
13
|
|
|
14
|
-
date: 2010-01-
|
|
14
|
+
date: 2010-01-27 00:00:00 -08:00
|
|
15
15
|
default_executable:
|
|
16
16
|
dependencies: []
|
|
17
17
|
|
|
@@ -250,7 +250,9 @@ files:
|
|
|
250
250
|
- lib/cloud_providers/ec2/helpers/authorize.rb
|
|
251
251
|
- lib/cloud_providers/ec2/helpers/ec2_helper.rb
|
|
252
252
|
- lib/cloud_providers/ec2/helpers/elastic_auto_scaler.rb
|
|
253
|
+
- lib/cloud_providers/ec2/helpers/elastic_block_device_mapping.rb
|
|
253
254
|
- lib/cloud_providers/ec2/helpers/elastic_block_store.rb
|
|
255
|
+
- lib/cloud_providers/ec2/helpers/elastic_block_store_group.rb
|
|
254
256
|
- lib/cloud_providers/ec2/helpers/elastic_ip.rb
|
|
255
257
|
- lib/cloud_providers/ec2/helpers/elastic_load_balancer.rb
|
|
256
258
|
- lib/cloud_providers/ec2/helpers/rds_instance.rb
|