-
Notifications
You must be signed in to change notification settings - Fork 2
/
glaucus.rb
240 lines (222 loc) · 8.96 KB
/
glaucus.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# server spawner module
# using chef and knife
# env needed : chef client install with knife and the following gems :
# net-ssh net-ssh-multi fog highline --no-rdoc --no-ri --verbose
#
require "rubygems"
require "bundler/setup"
require "fileutils"
require "yaml"
require "net/ssh"
# get all the gems in
Bundler.require(:default)
current_path = File.expand_path(File.dirname(__FILE__))
config = YAML.load_file("#{current_path}/config.yml")
def is_mac?
RUBY_PLATFORM.downcase.include?("darwin")
end
module EggApi
require 'net/http'
require "net/https"
extend self
def register(register_json)
return post("/api/server/status",register_json)
end
private
def get(request)
current_path = File.expand_path(File.dirname(__FILE__))
config = YAML.load_file("#{current_path}/config.yml")
http_r = Net::HTTP.new(config['egg_api']['host'], config['egg_api']['port'])
http_r.use_ssl = @config['egg_api']['ssl']
response = nil
begin
http_r.start() do |http|
req = Net::HTTP::Get.new('/api/web/' + request)
req.add_field("USERNAME", config['egg_api']['username'])
req.add_field("TOKEN", config['egg_api']['token'])
response = http.request(req)
end
return [response.code, response.body]
rescue Errno::ECONNREFUSED
@logger.error("front server didn't answer !")
return [503, "unavailable"]
end
end
def post(request,payload)
current_path = File.expand_path(File.dirname(__FILE__))
config = YAML.load_file("#{current_path}/config.yml")
http_r = Net::HTTP.new(config['egg_api']['host'], config['egg_api']['port'])
http_r.use_ssl = config['egg_api']['ssl']
response = nil
begin
http_r.start() do |http|
req = Net::HTTP::Post.new(request, initheader = {'Content-Type' =>'application/json'})
req.add_field("USERNAME", config['egg_api']['username'])
req.add_field("TOKEN", config['egg_api']['token'])
req.body = payload
req.set_form_data(payload)
response = http.request(req)
end
rescue Errno::ECONNREFUSED
return [503, "unavailable"]
end
return [response.code, response.body]
end
end
class Server
# flavor :
# 1 = 256MB RAM, 10GB HD
# 2 = 512MB RAM, 20GB HD
# 3 = 1024MB RAM, 40GB HD
# 4 = 2048MB RAM, 80GB HD
# image : 10194286, home made squeeze chef ready image
#
# role is a chef role
#
attr_accessor :hostname, :nodename, :image, :flavor, :role, :log, :connection
attr_accessor :redis_queue, :redis_status, :token, :status
# rackspace specific attributes
attr_accessor :provider_id, :host_id, :image_name, :flavor_name, :metadata
attr_accessor :public_ip, :private_ip, :password, :state, :progress
attr_accessor :bootstrap_log
def initialize(hostname, role, image = 10194286, flavor = 1, token)
current_path = File.expand_path(File.dirname(__FILE__))
config = YAML.load_file(current_path + "/config.yml")
@hostname = hostname
@nodename = hostname
@image = 10194286
@image = image unless(image == (nil || ""))
@flavor = 1
@flavor = flavor unless(flavor == (nil || ""))
@role = role
@provider_id = nil
@token = token # unique identifier of server, will be written to /etc/sol_token.txt
# cuddy will pick it up, sol will use it to for app deployment and key
# in some of the redis dbs
@connection = Fog::Compute.new(:provider => "Rackspace",
:rackspace_api_key => config['rackspace_token'],
:rackspace_username => config['rackspace_username'],
:rackspace_auth_url => config['rackspace_auth_host'])
@status = ""
@ready = false
@redis_queue = Redis.new(:host => config['redis']['host'], :port => config['redis']['port'], :password => config['redis']['password'], :db => config['redis']['queue_db'])
@redis_status = Redis.new(:host => config['redis']['host'], :port => config['redis']['port'], :password => config['redis']['password'], :db => config['redis']['status_db'])
end
def is_mac?
RUBY_PLATFORM.downcase.include?("darwin")
end
def create
server = connection.servers.create(
:name => hostname,
:image_id => image,
:flavor_id => flavor)
server.wait_for { ready? }
# server ready we store up stuff about the server
self.provider_id = server.id
self.host_id = server.host_id
self.image_name = server.image.name
self.flavor_name = server.flavor.name
self.metadata = server.metadata
self.public_ip = server.addresses["public"][0]
self.private_ip = server.addresses['private'][0]
self.password = server.password
self.state = server.state
self.progress = server.progress
end
def bootstrap
current_path = File.expand_path(File.dirname(__FILE__))
config = YAML.load_file(current_path + "/config.yml")
# connect using ssh to get the chef bootstrap script
result = ""
begin
Net::SSH.start(public_ip, "root", :password => password) do|ssh|
self.bootstrap_log = ssh.exec!("echo #{token} > /etc/sol_token.txt && cd /tmp && wget #{config['sol_files_host']}/client_bootstrap.sh && bash /tmp/client_bootstrap.sh www-host")
end
rescue Net::SSH::AuthenticationFailed
return false
end
return true
end
def to_h
arh = { "name" => nodename,
"image" => image,
"flavor" => flavor,
"role" => role,
"provider_id" => provider_id,
"host_id" => host_id,
"image_name" => image_name,
"flavor_name" => flavor_name,
"public_ip" => public_ip,
"private_ip" => private_ip,
"state" => state,
"progress" => progress}
return arh
end
def set_status(status_string)
# key is server token content is (jsoned) hash :
# { "name" => string, # the name of the server
# "image" => integer, # the image id used to create it
# "flavor" => integer, # the flavor id used to create it
# "role" => string, # the role used to create it
# "provider_id" => integer, # the provider id of the server (must store, need to do actions on the servers)
# "host_id" => integer, # host id, for provider needs (must store too)
# "image_name" => string, # the image name
# "flavor_name" => string, # the flavor name
# "public_ip" => string, # the public ip (must store)
# "private_ip" => string, # the private ip (must store)
# "state" => string, # the state on provider's side
# "progess" => integer, # the progress on provider's side (percentage)
# "status" => string, # one of "waiting", "spawning", "created", "running", "out"
# "started_at" => string, # time of start of the process
# "finished_at" => string, # time of finish of the status
# }
old_status = JSON.parse(redis_status.get(token)) if redis_status.get(token) != nil
start_time = Time.now.to_s
finish_time = Time.now.to_s
start_time = old_status['started_at'] if old_status != nil
arh = self.to_h
self.status = status_string
arh["status"] = status_string
arh['started_at'] = start_time
arh['finished_at'] = finish_time
redis_status.set(token, arh.to_json)
end
def run
set_status("spawning")
puts "Spawning at rackspace" if is_mac?
self.create
set_status("bootstrapping")
puts "Created at rackspace (now bootstrapping)" if is_mac?
self.bootstrap
set_status("running")
puts "Bootstrapped at rackspace" if is_mac?
end
end
# input loop
redis_queue = Redis.new(:host => config['redis']['host'], :port => config['redis']['port'], :password => config['redis']['password'], :db => config['redis']['queue_db'])
puts "Starting up" if is_mac?
while true
queue = JSON.parse(redis_queue.get("queue")) unless redis_queue.get("queue") == nil
queue = Array.new if queue == nil
while queue.size > 0
# "queue" (jsoned) array with items :
# { "name" => string, # hostname
# "role" => string, # chef role
# "image" => integer, # image id for rackspace, if nil then 10194286 (a base squeeze) is used
# "flavor" => integer, # server size 1 = 256MB, 2 = 512MB, 3 = 1024MB ..., default = 1 (if nil)
# "token" => string # unique token to identify server, will be picked up by cuddy in /etc/sol_token.txt
# }
serv = queue.pop
if (serv['token'] != nil)
puts "Spawning #{serv['name']} #{serv['token']} I: #{serv['image']} F: #{serv['flavor']} R: #{serv['role']}" if is_mac?
server = Server.new(serv['name'], serv['role'], serv['image'], serv['flavor'], serv['token'])
fork {
server.run
}
redis_queue.set("queue", queue.to_json)
else
puts "no token, no chocolate"
end
end
sleep(10)
end