add nodes#create and nodes#ping
[arvados.git] / app / models / node.rb
1 class Node < ActiveRecord::Base
2   include AssignUuid
3   serialize :info, Hash
4   before_validation :ensure_ping_secret
5
6   MAX_SLOTS = 64
7
8   def info
9     @info ||= Hash.new
10     super
11   end
12
13   def ping(o)
14     raise "must have :ip and :ping_secret" unless o[:ip] and o[:ping_secret]
15
16     if o[:ping_secret] != self.info[:ping_secret]
17       logger.info "Ping: secret mismatch: received \"#{o[:ping_secret]}\" != \"#{self.info[:ping_secret]}\""
18       return nil
19     end
20     self.last_ping_at = Time.now
21
22     # Record IP address
23     if self.ip_address.nil?
24       logger.info "#{self.uuid} ip_address= #{o[:ip]}"
25       self.ip_address = o[:ip]
26       self.first_ping_at = Time.now
27     end
28
29     # Record instance ID if not already known
30     self.info[:ec2_instance_id] ||= o[:ec2_instance_id]
31
32     # Assign hostname
33     if self.slot_number.nil?
34       try_slot = 0
35       begin
36         self.slot_number = try_slot
37         try_slot += 1
38         break if self.save rescue nil
39         raise "No available node slots" if try_slot == MAX_SLOTS
40       end while true
41       self.hostname = "compute#{self.slot_number}"
42     end
43
44     save
45   end
46
47   def start!(ping_url_method)
48     ping_url = ping_url_method.call({ uuid: self.uuid, ping_secret: self.info[:ping_secret] })
49     cmd = ["ec2-run-instances",
50            "--user-data '#{ping_url}'",
51            "-t c1.xlarge -n 1 -g orvos-compute",
52            "ami-68ca6901"
53           ].join(' ')
54     self.info[:ec2_start_command] = cmd
55     logger.info "#{self.uuid} ec2_start_command= #{cmd.inspect}"
56     result = `#{cmd} 2>&1`
57     self.info[:ec2_start_result] = result
58     logger.info "#{self.uuid} ec2_start_result= #{result.inspect}"
59     result.match(/INSTANCE\s*(i-[0-9a-f]+)/) do |m|
60       self.info[:ec2_instance_id] = m[1]
61       self.save!
62     end
63   end
64
65   protected
66
67   def ensure_ping_secret
68     self.info[:ping_secret] ||= rand(2**256).to_s(36)
69   end
70 end