poolparty 1.4.7 → 1.4.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|