Class: PoolParty::Remote::Ec2

Inherits:
RemoterBase show all
Defined in:
lib/poolparty/net/remoter_bases/ec2/ec2.rb

Instance Attribute Summary

Attributes inherited from RemoterBase

#cloud

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from RemoterBase

#after_launched, #before_shutdown, describe_instance, describe_instances, #initialize, #launch_instance!, launch_instance!, ping_port, terminate_instance!

Methods included from PoolParty::Remote

#_nodes, #are_any_nodes_exceeding_minimum_runtime?, #are_too_few_instances_running?, #are_too_many_instances_running?, #commands, #execute!, #is_master_running?, #list_of_instances, #list_of_nodes_exceeding_minimum_runtime, #master, #netssh, #nodes, #remote_rsync_command, #remote_ssh_array, #remote_ssh_string, #rsync, #rsync_command, #rsync_storage_files_to, #rsync_storage_files_to_command, #rsync_to, #rsync_to_command, #run_command_on, #run_command_on_command, #run_command_on_instance_number, #run_local, #run_remote, #scp_array, #scp_to_command, #simplest_run_remote, #ssh_array, #ssh_command, #ssh_into, #ssh_into_instance_number, #ssh_options, #ssh_string, #target_host

Methods included from Pinger

included

Constructor Details

This class inherits a constructor from PoolParty::Remote::RemoterBase

Class Method Details

.ec2(o) ⇒ Object



106
107
108
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 106

def self.ec2(o)
  @ec2 ||= self.class.ec2(o)
end

.launch_new_instance!(parent_cloud, o) ⇒ Object

Requires a hash of options



49
50
51
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 49

def self.launch_new_instance!(parent_cloud, o)
  new(parent_cloud, o).launch_new_instance!
end

Instance Method Details

#after_install_tasks_for(o) ⇒ Object



234
235
236
237
238
239
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 234

def after_install_tasks_for(o)
  [
    # "cd /var/poolparty && wget http://rubyforge.org/frs/download.php/43666/amazon-ec2-0.3.1.gem -O amazon-ec2.gem 2>&1",
    # "/usr/bin/gem install --no-ri --no-rdoc amazon-ec2.gem 2>&1"
  ]
end

#after_launch_instance(inst) ⇒ Object



135
136
137
138
139
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 135

def after_launch_instance(inst)
  if inst
    associate_address(inst)
  end
end

#associate_address(instance = nil) ⇒ Object

Associate an address with the instance using ec2 Get the next_unused_elastic_ip and if there is one, associate the instance to the public ip



155
156
157
158
159
160
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 155

def associate_address(instance=nil)
  if ip = next_unused_elastic_ip
    vputs "Associating #{instance.instance_id} with #{ip}"
    ec2.associate_address(:instance_id => instance.instance_id, :public_ip => ip)
  end
end

#attach_volume(instance = nil) ⇒ Object

Attach a volume to the instance DEPRECATE this relies on master. master will be removed in next major release. This method will be in ec2_remote_instance instead, or require an instance id



143
144
145
146
147
148
149
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 143

def attach_volume(instance=nil)
  if ebs_volume_id
    vputs "Attaching volume #{ebs_volume_id} to the master at #{ebs_volume_device}"
    instance = master        
    ec2.attach_volume(:volume_id => ebs_volume_id, :instance_id => instance.instance_id, :device => ebs_volume_device) if ebs_volume_id && ebs_volume_mount_point
  end
end

#aws_keysObject

Class method helpers



125
126
127
128
129
130
131
132
133
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 125

def aws_keys
  unless @access_key && @secret_access_key          
    aws_keys = {}
    aws_keys = YAML::load( File.open('/etc/poolparty/aws_keys.yml') ) rescue 'No aws_keys.yml file.   Will try to use enviornment variables'
    @access_key ||= aws_keys[:access_key] || ENV['AMAZON_ACCESS_KEY_ID'] || ENV['AWS_ACCESS_KEY']
    @secret_access_key ||= aws_keys[:secret_access_key] || ENV['AMAZON_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_ACCESS_KEY']
  end
  [@access_key, @secret_access_key]
end

#create_keypairObject

Help create a keypair for the cloud This is a helper to create the keypair and add them to the cloud for you



190
191
192
193
194
195
196
197
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 190

def create_keypair
  return false unless keypair
  unless ::File.exists?( new_keypair_path )
    FileUtils.mkdir_p ::File.dirname( new_keypair_path )
    vputs "Creating keypair: #{keypair} in #{new_keypair_path}"
    Kernel.system "ec2-add-keypair #{keypair} > #{new_keypair_path} && chmod 600 #{new_keypair_path}"
  end
end

#create_snapshotObject

wrapper for remote base to perform a snapshot backup for the ebs volume



200
201
202
203
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 200

def create_snapshot
  return nil if ebs_volume_id.nil?
  ec2.create_snapshot(:volume_id => ebs_volume_id)
end

#custom_configure_tasks_for(o) ⇒ Object



241
242
243
244
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 241

def custom_configure_tasks_for(o)
  [
  ]
end

#custom_install_tasks_for(o) ⇒ Object

Hook TODO#: Change this so they match with the cap tasks



225
226
227
228
229
230
231
232
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 225

def custom_install_tasks_for(o)        
  [
    # "if [ -z $(grep -v '#' /etc/hosts | grep '#{o.name}') ]; then echo \"$(curl http://169.254.169.254/latest/meta-data/public-ipv4) #{o.name}\" >> /etc/hosts; fi",
    "if [ -z \"$(grep -v '#' /etc/hosts | grep '#{o.name}')\" ]; then echo '127.0.0.1 #{o.name}' >> /etc/hosts; fi",
    "hostname #{o.name}",
    "echo #{o.name} > /etc/hostname"
  ]
end

#custom_minimum_runnable_optionsObject



219
220
221
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 219

def custom_minimum_runnable_options
  [:ami, :availabilty_zone, :security_group]
end

#describe_instance(o = {}) ⇒ Object

Describe an instance’s status



73
74
75
76
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 73

def describe_instance(o={})
  return describe_instances.first if o[:instance_id].nil?
  describe_instances.detect {|a| a[:name] == o[:instance_id] || a[:ip] == o[:instance_id] || a[:instance_id] == o[:instance_id] }
end

#describe_instances(o = {}) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 77

def describe_instances(o={})
  id = 0
  get_instances_description(options.merge(o)).each_with_index do |h,i|          
    if h[:status] == "running"
      inst_name = id == 0 ? "master" : "node#{id}"
      id += 1
    else
      inst_name = "#{h[:status]}_node#{i}"
    end
    h.merge!({
      :name => inst_name,
      :hostname => h[:ip],
      :ip => h[:ip].convert_from_ec2_to_ip,
      :index => i,  #TODO MF get the instance id from the aws result instead
      :launching_time => (h[:launching_time])
    })
  end.compact.sort {|a,b| a[:index] <=> b[:index] }
end

#ec2(o = {}) ⇒ Object

return or create a new base EC2 connection object that will actually connect to ec2



101
102
103
104
105
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 101

def ec2(o={})
  @ec2 ||= EC2::Base.new( :access_key_id => o[:access_key], 
                          :secret_access_key => o[:secret_access_key]
                        )
end

#get_descriptions(o = {}) ⇒ Object



116
117
118
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 116

def get_descriptions(o={})
  self.class.get_descriptions(o)
end

#get_instances_description(o = {}) ⇒ Object

Get the ec2 description for the response in a hash format



111
112
113
114
115
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 111

def get_instances_description(o={})
  #TODO: only use keypair.full_filepath
  key_hash = {:keypair => ::File.basename(keypair.is_a?(String) ? keypair : keypair.full_filepath)}
  EC2ResponseObject.get_descriptions(ec2(o).describe_instances).select_with_hash(key_hash)
end

#has_cert_and_key?Boolean

Returns:

  • (Boolean)


205
206
207
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 205

def has_cert_and_key?
  pub_key && private_key
end

#keypairObject



120
121
122
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 120

def keypair
  cloud.keypair
end

#launch_new_instance!(o = {}) ⇒ Object

TODO: Fix the key_name issue Start a new instance with the given options



55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 55

def launch_new_instance!(o={})
  raise "You must pass a keypair to launch an instance, or else you will not be able to login. options = #{o.inspect}" if !cloud.keypair
  o.merge!( options ).merge!(:key_name=>keypair.basename)
  instance = ec2(o).run_instances(o)
  begin
    h = EC2ResponseObject.get_hash_from_response(instance.instancesSet.item.first)
    #h = instance.instancesSet.item.first
  rescue Exception => e
    h = EC2ResponseObject.get_hash_from_response(instance) rescue instance
    # h = instance
  end
  h
end

#next_unused_elastic_ipObject

Get the next usable elastic ip First, get the list of addresses from ec2 that the client has access to, then select only the ones that are not associated with an instance. If the cloud has set elastic_ips to use, then, using the intersection of the unused ips and those, find the first one available and return that, otherwise, return the first elastic ip available



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 169

def next_unused_elastic_ip
  # [{"instanceId"=>nil, "publicIp"=>"174.129.212.93"}, {"instanceId"=>nil, "publicIp"=>"174.129.212.94"}]
  if addressesSet = ec2(options).describe_addresses["addressesSet"]
    begin
      empty_addresses = addressesSet["item"].select {|i| i["instanceId"].nil? }
      ips = empty_addresses.map {|addr| addr["publicIp"]}
      if cloud.elastic_ips?
        ips_to_use = cloud.elastic_ips & ips
        ips_to_use.first
      else
        ips.first
      end
    rescue Exception => e
      puts "Error: #{e}"
      nil
    end          
  end
end

#private_keyObject

Private key



215
216
217
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 215

def private_key
  @private_key ||= ENV["EC2_PRIVATE_KEY"] ? ENV["EC2_PRIVATE_KEY"] : nil
end

#pub_keyObject

The keys are used only for puppet certificates and are only used for EC2. Public key



211
212
213
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 211

def pub_key
  @pub_key ||= ENV["EC2_CERT"] ? ENV["EC2_CERT"] : nil
end

#reset_base!Object



246
247
248
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 246

def reset_base!
  @describe_instances = @cached_descriptions = nil
end

#terminate_instance!(o = {}) ⇒ Object

Terminate an instance by id



69
70
71
# File 'lib/poolparty/net/remoter_bases/ec2/ec2.rb', line 69

def terminate_instance!(o={})
  ec2(o).terminate_instances(:instance_id => o[:instance_id])
end