nuri 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.travis.yml +12 -0
- data/CHANGELOG +146 -0
- data/Gemfile +3 -0
- data/LICENSE +28 -0
- data/README.md +64 -0
- data/Rakefile +15 -0
- data/VERSION +1 -0
- data/bin/delete_modules +11 -0
- data/bin/install_agent +18 -0
- data/bin/install_module +65 -0
- data/bin/nuri +519 -0
- data/bin/nuri.old +183 -0
- data/bin/push_model +16 -0
- data/examples/.gitignore +3 -0
- data/examples/bonfire.sfp +95 -0
- data/examples/bonfire/epcc.sfp +43 -0
- data/examples/bonfire/epcc0.sfp +49 -0
- data/examples/bonfire/epcc2.sfp +52 -0
- data/examples/bonfire/epcc2a.sfp +25 -0
- data/examples/bonfire/inria.sfp +72 -0
- data/examples/bonfire/inria0.sfp +49 -0
- data/examples/bonfire/inria2.sfp +71 -0
- data/examples/bonfire/inria2a.sfp +44 -0
- data/examples/bonfire/inria2b.sfp +54 -0
- data/examples/bonfire/inria2c.sfp +62 -0
- data/examples/bonfire/inria2d.sfp +71 -0
- data/examples/bonfire/inria2e.sfp +80 -0
- data/examples/bonfire/main.sfp +33 -0
- data/examples/bonfire/old/bonfire-1-1-1.sfp +76 -0
- data/examples/bonfire/old/bonfire-1-10-1.sfp +77 -0
- data/examples/bonfire/old/bonfire-1-2-1.sfp +58 -0
- data/examples/bonfire/old/bonfire-1-3-1.sfp +61 -0
- data/examples/bonfire/old/bonfire-1-4-1.sfp +64 -0
- data/examples/bonfire/old/bonfire-1-5-1.sfp +67 -0
- data/examples/bonfire/old/bonfire-1-6-1.sfp +82 -0
- data/examples/bonfire/old/bonfire-1-7-1.sfp +82 -0
- data/examples/bonfire/old/bonfire-1-8-1.sfp +79 -0
- data/examples/bonfire/old/bonfire-1-9-1.sfp +83 -0
- data/examples/bonfire/old/wp-test1a.sfp +38 -0
- data/examples/bonfire/old/wp-test1b.sfp +18 -0
- data/examples/bonfire/old/wp-test1c.sfp +7 -0
- data/examples/bonfire/old/wp-test2.sfp +47 -0
- data/examples/bonfire/old3/bonfire-epcc.sfp +57 -0
- data/examples/bonfire/old3/bonfire-inria.sfp +72 -0
- data/examples/bonfire/old3/bonfire-master.sfp +18 -0
- data/examples/bonfire/old3/bonfire.sfp +23 -0
- data/examples/bonfire/old3/bonfire2.sfp +49 -0
- data/examples/bonfire/old3/bonfire3.sfp +76 -0
- data/examples/bonfire/old3/bonfire4.sfp +78 -0
- data/examples/bonfire/old3/bonfire5.sfp +34 -0
- data/examples/bonfire/old3/bonfire5b.sfp +84 -0
- data/examples/bonfire/old3/hpvm6.sfp +22 -0
- data/examples/bonfire/old3/model.json +1 -0
- data/examples/bonfire/old3/test0.sfp +16 -0
- data/examples/bonfire/old3/test1.sfp +5 -0
- data/examples/bonfire/old3/test10.sfp +5 -0
- data/examples/bonfire/old3/test2.sfp +18 -0
- data/examples/bonfire/old3/test3.sfp +10 -0
- data/examples/bonfire/old3/test4.sfp +11 -0
- data/examples/bonfire/old3/test5.sfp +18 -0
- data/examples/bonfire/old3/test6.sfp +19 -0
- data/examples/bonfire/old3/test7.sfp +34 -0
- data/examples/bonfire/old3/test8.sfp +5 -0
- data/examples/bonfire/old3/test9.sfp +16 -0
- data/examples/bonfire/old3/wordpress-test-cluster.sfp +38 -0
- data/examples/bonfire/old3/wordpress-test.sfp +22 -0
- data/examples/bonfire/old3/wp-test-2.sfp +49 -0
- data/examples/bonfire/test.sfp +13 -0
- data/examples/generator.rb +66 -0
- data/examples/hadoop2.sfp +20 -0
- data/examples/hpcloud.sfp +18 -0
- data/examples/run.rb +17 -0
- data/examples/test.inc +0 -0
- data/examples/test.sfp +11 -0
- data/lib/naas/d3.js +5 -0
- data/lib/naas/d3.v3.min.js +5 -0
- data/lib/naas/index.css +0 -0
- data/lib/naas/index.html +18 -0
- data/lib/naas/index.js +18 -0
- data/lib/naas/jquery-1.10.2.min.js +6 -0
- data/lib/naas/jquery.js +6 -0
- data/lib/naas/naas.rb +160 -0
- data/lib/nuri.rb +62 -0
- data/lib/nuri/choreographer.rb +151 -0
- data/lib/nuri/constraint_helper.rb +9 -0
- data/lib/nuri/directory.rb +40 -0
- data/lib/nuri/master.rb +725 -0
- data/lib/nuri/net_helper.rb +65 -0
- data/lib/nuri/orchestrator.rb +224 -0
- data/lib/nuri/server.rb +212 -0
- data/modules/.gitignore +4 -0
- data/modules/apache/apache.rb +255 -0
- data/modules/apache/apache.rb.old +167 -0
- data/modules/apache/apache.sfp +146 -0
- data/modules/apache/apache.sfp.future +100 -0
- data/modules/apache/load_balancer +20 -0
- data/modules/apache/model.json +1 -0
- data/modules/apache/test.sfp +8 -0
- data/modules/aptpackage/aptpackage.rb +82 -0
- data/modules/aptpackage/aptpackage.sfp +5 -0
- data/modules/bonfire/.gitignore +2 -0
- data/modules/bonfire/README.md +12 -0
- data/modules/bonfire/bonfire.rb +60 -0
- data/modules/bonfire/bonfire.sfp +9 -0
- data/modules/bonfire/config.yml +4 -0
- data/modules/bonfire/helper.rb +149 -0
- data/modules/bonfire/stresstest.rb +144 -0
- data/modules/bonfire/test.sfp +8 -0
- data/modules/client/client.rb +22 -0
- data/modules/client/client.sfp +14 -0
- data/modules/cloud/cloud.rb +11 -0
- data/modules/cloud/cloud.sfp +26 -0
- data/modules/file/file.rb +91 -0
- data/modules/file/file.sfp +9 -0
- data/modules/hadoop1/core-site.xml +17 -0
- data/modules/hadoop1/hadoop-env.sh +55 -0
- data/modules/hadoop1/hadoop1.rb +384 -0
- data/modules/hadoop1/hadoop1.sfp +93 -0
- data/modules/hadoop1/hdfs-site.xml +16 -0
- data/modules/hadoop1/mapred-site.xml +17 -0
- data/modules/hadoop2/core-site.xml +31 -0
- data/modules/hadoop2/hadoop-env.sh +77 -0
- data/modules/hadoop2/hadoop2.rb +401 -0
- data/modules/hadoop2/hadoop2.sfp +114 -0
- data/modules/hadoop2/hdfs-site.xml +47 -0
- data/modules/hadoop2/mapred-site.xml +71 -0
- data/modules/hadoop2/ports +14 -0
- data/modules/hadoop2/yarn-env.sh +112 -0
- data/modules/hadoop2/yarn-site.xml +107 -0
- data/modules/hpcloud/.gitignore +2 -0
- data/modules/hpcloud/README.md +16 -0
- data/modules/hpcloud/config.yml +3 -0
- data/modules/hpcloud/example.sfp +18 -0
- data/modules/hpcloud/hpcloud.rb +241 -0
- data/modules/hpcloud/hpcloud.sfp +22 -0
- data/modules/hpcloud/test.sfp +5 -0
- data/modules/install_module +65 -0
- data/modules/machine/machine.rb +95 -0
- data/modules/machine/machine.sfp +9 -0
- data/modules/mockcloud/mockcloud.rb +20 -0
- data/modules/mockcloud/mockcloud.sfp +6 -0
- data/modules/mysql/mysql.rb +118 -0
- data/modules/mysql/mysql.sfp +38 -0
- data/modules/mysql/test.sfp +3 -0
- data/modules/node/node.rb +8 -0
- data/modules/node/node.sfp +7 -0
- data/modules/object/object.rb +7 -0
- data/modules/object/object.sfp +1 -0
- data/modules/os/os.rb +38 -0
- data/modules/os/os.sfp +11 -0
- data/modules/package/package.rb +26 -0
- data/modules/package/package.sfp +22 -0
- data/modules/package/test.sfp +6 -0
- data/modules/service/model.json +1 -0
- data/modules/service/service.rb +50 -0
- data/modules/service/service.sfp +46 -0
- data/modules/service/test.sfp +6 -0
- data/modules/tarpackage/tarpackage.rb +93 -0
- data/modules/tarpackage/tarpackage.sfp +5 -0
- data/modules/vm/vm.rb +8 -0
- data/modules/vm/vm.sfp +18 -0
- data/modules/wordpress/wordpress.rb +98 -0
- data/modules/wordpress/wordpress.sfp +34 -0
- data/modules/wordpresscluster/wordpresscluster.rb +150 -0
- data/modules/wordpresscluster/wordpresscluster.sfp +74 -0
- data/nuri.gemspec +26 -0
- metadata +281 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Nuri::Net
|
5
|
+
end
|
6
|
+
|
7
|
+
module Nuri::Net::Helper
|
8
|
+
def post_data(address, port, path, data, open_timeout=5, read_timeout=1800)
|
9
|
+
uri = create_uri(address, port, path)
|
10
|
+
req = Net::HTTP::Post.new(uri.path)
|
11
|
+
req.set_form_data(data)
|
12
|
+
http_request(uri, req, open_timeout, read_timeout)
|
13
|
+
end
|
14
|
+
|
15
|
+
def put_data(address, port, path, data, open_timeout=5, read_timeout=1800)
|
16
|
+
uri = create_uri(address, port, path)
|
17
|
+
req = Net::HTTP::Put.new(uri.path)
|
18
|
+
req.set_form_data(data)
|
19
|
+
http_request(uri, req, open_timeout, read_timeout)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_data(address, port, path, open_timeout=5, read_timeout=1800)
|
23
|
+
uri = create_uri(address, port, path)
|
24
|
+
req = Net::HTTP::Get.new(uri.path)
|
25
|
+
http_request(uri, req, open_timeout, read_timeout)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_data(address, port, path, open_timeout=5, read_timeout=1800)
|
29
|
+
uri = create_uri(address, port, path)
|
30
|
+
req = Net::HTTP::Delete.new(uri.path)
|
31
|
+
http_request(uri, req, open_timeout, read_timeout)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def create_uri(address, port, path)
|
36
|
+
address = address.to_s.strip
|
37
|
+
port = port.to_s.strip
|
38
|
+
path = path.to_s.strip
|
39
|
+
raise Exception, "Invalid parameters [address:#{address},port:#{port},path:#{path}]" if
|
40
|
+
address.length <= 0 or port.length <= 0 or path.length <= 0
|
41
|
+
path.sub!(/^\/+/, '')
|
42
|
+
URI.parse("http://#{address}:#{port}/#{path}")
|
43
|
+
end
|
44
|
+
|
45
|
+
def use_http_proxy?(uri)
|
46
|
+
ENV['no_proxy'].to_s.split(',').each { |pattern|
|
47
|
+
pattern.chop! if pattern[-1] == '*'
|
48
|
+
return false if uri.host[0,pattern.length] == pattern
|
49
|
+
}
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def http_request(uri, request, open_timeout=5, read_timeout=1800)
|
54
|
+
if ENV['http_proxy'].to_s.strip != '' and use_http_proxy?(uri)
|
55
|
+
proxy = URI.parse(ENV['http_proxy'])
|
56
|
+
http = Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port)
|
57
|
+
else
|
58
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
59
|
+
end
|
60
|
+
http.open_timeout = open_timeout
|
61
|
+
http.read_timeout = read_timeout
|
62
|
+
http.start
|
63
|
+
http.request(request) { |res| return [res.code, res.body] }
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Nuri::Orchestrator
|
4
|
+
include Nuri::Net::Helper
|
5
|
+
|
6
|
+
def execute_plan(p={})
|
7
|
+
raise Exception, "Plan file is not exist!" if not File.exist?(p[:execute].to_s) and !p[:plan]
|
8
|
+
raise Exception, "Plan is not exist (parameter :plan must be given)!" if !p[:plan]
|
9
|
+
|
10
|
+
push_agents_list
|
11
|
+
|
12
|
+
success = false
|
13
|
+
benchmark = Benchmark.measure {
|
14
|
+
plan = (p[:plan] ? p[:plan] : JSON[File.read(p[:execute])])
|
15
|
+
raise Exception, "Invalid plan!" if plan['workflow'].nil?
|
16
|
+
if plan.is_a?(Hash) and plan['type'] == 'sequential'
|
17
|
+
success = execute_sequential_plan(plan, p)
|
18
|
+
elsif plan.is_a?(Hash) and plan['type'] == 'parallel'
|
19
|
+
success = execute_parallel_plan(plan, p)
|
20
|
+
end
|
21
|
+
}
|
22
|
+
puts "Execution time (s): #{benchmark}"
|
23
|
+
|
24
|
+
success
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
def send_action_data(action, address, port)
|
29
|
+
action['parameters'].each do |name,value|
|
30
|
+
next if !value.is_a?(String) or !value.isref or value.split('.').length > 2
|
31
|
+
_, target_name = value.split('.', 2)
|
32
|
+
data = {'model' => Sfp::Helper.deep_clone(@model[target_name])}
|
33
|
+
data.accept(Sfp::Visitor::ParentEliminator.new)
|
34
|
+
data = {'model' => JSON.generate(data)}
|
35
|
+
code = nil
|
36
|
+
begin
|
37
|
+
code, _ = put_data(address, port, "/model/cache/#{target_name}", data)
|
38
|
+
rescue
|
39
|
+
end
|
40
|
+
if code != '200'
|
41
|
+
$stderr.puts "Sending action data of #{value} to #{address}:#{port} [Failed]".red
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def execute_action(action)
|
50
|
+
_, agent_name, _ = action['name'].split('.', 3)
|
51
|
+
agents = get_agents
|
52
|
+
|
53
|
+
return false if !agents[agent_name]['sfpAddress'].is_a?(String)
|
54
|
+
|
55
|
+
address = agents[agent_name]['sfpAddress'].to_s
|
56
|
+
port = agents[agent_name]['sfpPort'].to_s
|
57
|
+
|
58
|
+
raise Exception, "Cannot find address:port of agent #{agent_name}" if
|
59
|
+
address.length <= 0 or port.length <= 0
|
60
|
+
|
61
|
+
send_action_data(action, address, port)
|
62
|
+
|
63
|
+
data = {'action' => JSON.generate(action)}
|
64
|
+
code, _ = post_data(address, port, '/execute', data)
|
65
|
+
if code.to_i == 200
|
66
|
+
# if the action is "create_vm" or "delete_vm", then
|
67
|
+
# update the VMs' address, and then push agents list
|
68
|
+
postprocess_create_or_delete_vm(action) if action['name'] =~ /^\$(\.[a-zA-Z0-9_]+)*\.(create_vm|delete_vm)/
|
69
|
+
true
|
70
|
+
else
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def postprocess_create_or_delete_vm(action)
|
76
|
+
@mutex_postprocess = Mutex.new if !defined?(@mutex_postprocess)
|
77
|
+
|
78
|
+
@mutex_postprocess.synchronize {
|
79
|
+
_, agent_name, _ = action['name'].split('.', 3)
|
80
|
+
|
81
|
+
state = {agent_name => get_node_state(agent_name)}
|
82
|
+
vms1, vms2 = update_vms_address(state)
|
83
|
+
|
84
|
+
if action['name'] =~ /^\$(\.[a-zA-Z0-9_]+)*\.(create_vm)/
|
85
|
+
vm_name = action['parameters']['$.vm'].sub(/^\$\./, '')
|
86
|
+
get_node_state(vm_name, true)
|
87
|
+
|
88
|
+
elsif action['name'] =~ /^\$(\.[a-zA-Z0-9_]+)*\.(delete_vm)/
|
89
|
+
model = @model.at?(action['parameters']['$.vm'])
|
90
|
+
if model.is_a?(Hash)
|
91
|
+
model['sfpAddress'] = {'_context'=>'any_value','_isa'=>'$.String'}
|
92
|
+
model['sfpPort'] = {'_context'=>'any_value','_isa'=>'$.Number'}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
push_agents_list
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def execute_sequential_plan(plan, p)
|
101
|
+
begin
|
102
|
+
index = 1
|
103
|
+
plan['workflow'].each do |action|
|
104
|
+
print "#{index}. #{action['name']} #{JSON.generate(action['parameters'])}... "
|
105
|
+
if execute_action(action)
|
106
|
+
puts "[OK]".green
|
107
|
+
else
|
108
|
+
puts "[Failed]".red
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
index += 1
|
112
|
+
end
|
113
|
+
return true
|
114
|
+
rescue Exception => e
|
115
|
+
puts "#{e}\n#{e.backtrace.join("\n")}".red
|
116
|
+
end
|
117
|
+
false
|
118
|
+
end
|
119
|
+
|
120
|
+
def execute_parallel_plan(plan, options)
|
121
|
+
@retries = 2
|
122
|
+
|
123
|
+
@actions = plan['workflow']
|
124
|
+
@actions.sort! { |x,y| x['id'] <=> y['id'] }
|
125
|
+
@actions.each { |op|
|
126
|
+
op[:executed] = false
|
127
|
+
op[:executor] = nil
|
128
|
+
op[:string] = "#{op['id'] + 1}: #{op['name']} #{JSON.generate(op['parameters'])}"
|
129
|
+
}
|
130
|
+
|
131
|
+
@threads = []
|
132
|
+
@actions_failed = []
|
133
|
+
@mutex = Mutex.new
|
134
|
+
@failed = false
|
135
|
+
@thread_id = 0
|
136
|
+
|
137
|
+
def next_thread_id
|
138
|
+
id = 0
|
139
|
+
@mutex.synchronize { @thread_id = id = @thread_id + 1 }
|
140
|
+
id
|
141
|
+
end
|
142
|
+
|
143
|
+
def assign_action_with_id(id)
|
144
|
+
thread_id = next_thread_id
|
145
|
+
action = @actions[id]
|
146
|
+
action[:executor] = thread_id
|
147
|
+
self.thread_execute_action(thread_id, action)
|
148
|
+
end
|
149
|
+
|
150
|
+
def thread_execute_action(tid, action)
|
151
|
+
t = Thread.new {
|
152
|
+
# Register the thread
|
153
|
+
@mutex.synchronize { @threads << tid }
|
154
|
+
|
155
|
+
while not @failed and not action[:executed]
|
156
|
+
# Try to execute the action
|
157
|
+
puts "Executing #{action[:string]} - thread ##{tid} [WAIT]".yellow
|
158
|
+
success = false
|
159
|
+
1.upto(@retries) do |i|
|
160
|
+
begin
|
161
|
+
success = execute_action(action)
|
162
|
+
rescue Exception => exp
|
163
|
+
puts "Executing(#{i}) #{action[:string]} - thread ##{tid} [FAILED]\n#{exp}\n#{exp.backtrace.join("\n")}"
|
164
|
+
end
|
165
|
+
break if success
|
166
|
+
end
|
167
|
+
|
168
|
+
# Check if execution failed
|
169
|
+
if success
|
170
|
+
# Execution was success
|
171
|
+
puts "Executing #{action[:string]} - thread ##{tid} [OK]".green
|
172
|
+
next_actions = []
|
173
|
+
@mutex.synchronize {
|
174
|
+
action[:executed] = true # set executed
|
175
|
+
# select next action to be executed from successor actions list
|
176
|
+
# select a successor action that has not been assigned to any thread yet
|
177
|
+
if action['successors'].length > 0
|
178
|
+
action['successors'].each { |id|
|
179
|
+
if @actions[id][:executor].nil?
|
180
|
+
# check if all predecessors actions have been executed
|
181
|
+
predecessors_ok = true
|
182
|
+
@actions[id]['predecessors'].each { |pid|
|
183
|
+
predecessors_ok = (predecessors_ok and @actions[pid][:executed])
|
184
|
+
}
|
185
|
+
# assign this action to be executed by this thread if all predecessors
|
186
|
+
# have been executed
|
187
|
+
next_actions << id if predecessors_ok
|
188
|
+
end
|
189
|
+
}
|
190
|
+
end
|
191
|
+
next_actions.each { |id| @actions[id][:executor] = tid }
|
192
|
+
}
|
193
|
+
if next_actions.length > 0
|
194
|
+
# execute the first next actions to this thread
|
195
|
+
action = @actions[next_actions[0]]
|
196
|
+
if next_actions.length > 1
|
197
|
+
# execute other next actions to other threads
|
198
|
+
for i in 1..(next_actions.length-1)
|
199
|
+
assign_action_with_id(next_actions[i])
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
else
|
205
|
+
# Execution was failed
|
206
|
+
puts "Executing #{action[:string]} - thread ##{tid} [FAILED]".red
|
207
|
+
@mutex.synchronize {
|
208
|
+
@failed = true # set global flag to stop the execution
|
209
|
+
@actions_failed << action
|
210
|
+
}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
@mutex.synchronize { @threads.delete(tid) }
|
215
|
+
}
|
216
|
+
end
|
217
|
+
|
218
|
+
plan['init'].each { |id| assign_action_with_id(id) }
|
219
|
+
begin
|
220
|
+
sleep 1
|
221
|
+
end while @threads.length > 0
|
222
|
+
(not @failed)
|
223
|
+
end
|
224
|
+
end
|
data/lib/nuri/server.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'logger'
|
3
|
+
require 'webrick'
|
4
|
+
|
5
|
+
module Nuri::Server
|
6
|
+
VarDir = (Process.euid == 0 ? '/var/nuri' : File.expand_path('~/.nuri'))
|
7
|
+
Dir.mkdir(VarDir, 0700) if not File.exist?(VarDir)
|
8
|
+
|
9
|
+
PIDFile = "#{VarDir}/nuri.pid"
|
10
|
+
PlanFile = "#{VarDir}/plan.json"
|
11
|
+
LogFile = "#{VarDir}/nuri.log"
|
12
|
+
ModelFile = "#{VarDir}/main.sfp"
|
13
|
+
ModulesDir = "#{VarDir}/modules"
|
14
|
+
Dir.mkdir(ModulesDir, 0700) if not File.exist?(ModulesDir)
|
15
|
+
|
16
|
+
ConfigDir = (Process.euid == 0 ? '/etc/nuri' : File.expand_path('~/.nuri'))
|
17
|
+
ConfigFile = "#{ConfigDir}/nuri.yml"
|
18
|
+
|
19
|
+
DefaultIntervalTime = 5
|
20
|
+
DefaultPort = 1313
|
21
|
+
|
22
|
+
def init
|
23
|
+
Dir.mkdir(ConfigDir, '0755') if not File.exist?(ConfigDir)
|
24
|
+
|
25
|
+
if not File.exist?(ConfigFile)
|
26
|
+
# generate and save default configuration file
|
27
|
+
config = { "port" => DefaultPort,
|
28
|
+
"interval_time" => 5 }
|
29
|
+
File.open(ConfigFile, 'w', 0644) { |f| f.write(YAML.dump(config)) }
|
30
|
+
|
31
|
+
# generate an empty model file
|
32
|
+
File.open(ModelFile, 'w', 0644) { |f| f.write('') }
|
33
|
+
end
|
34
|
+
|
35
|
+
#if not File.exist?(".git")
|
36
|
+
# system("cd #{ConfigDir}; git init 1>/dev/null 2>/dev/null")
|
37
|
+
# File.open("#{ConfigDir}/.gitignore", 'w', 0644) { |f| f.write("*.pid\n*.log\n") }
|
38
|
+
# system("cd #{ConfigDir}; git add -A . 1>/dev/null 2>/dev/null; git commit -m 'nuri init' 1>/dev/null 2>/dev/null")
|
39
|
+
#end
|
40
|
+
end
|
41
|
+
|
42
|
+
def start
|
43
|
+
if not File.exist?(ConfigFile)
|
44
|
+
puts "Execute 'nuri init' first!"
|
45
|
+
exit!(1)
|
46
|
+
end
|
47
|
+
|
48
|
+
if File.exist?(PIDFile)
|
49
|
+
pid = File.read(PIDFile).to_i
|
50
|
+
begin
|
51
|
+
Process.kill 0, pid if pid > 0
|
52
|
+
# Nuri server is still running
|
53
|
+
puts "Nuri server is already running with PID #{pid}."
|
54
|
+
exit!(1)
|
55
|
+
rescue
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
@logger = WEBrick::Log.new(LogFile, WEBrick::BasicLog::INFO ||
|
60
|
+
WEBrick::BasicLog::ERROR ||
|
61
|
+
WEBrick::BasicLog::FATAL ||
|
62
|
+
WEBrick::BasicLog::WARN)
|
63
|
+
|
64
|
+
@logger.info "Reading configuration file [Wait]"
|
65
|
+
@config = YAML.load(File.read(ConfigFile))
|
66
|
+
@logger.info "Reading configuration file [OK]"
|
67
|
+
|
68
|
+
['INT', 'KILL', 'HUP'].each { |signal|
|
69
|
+
trap(signal) {
|
70
|
+
@logger.info "Shutting down Nuri server..."
|
71
|
+
@enabled = false
|
72
|
+
@web_server.shutdown
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
puts "Starting Nuri server..."
|
77
|
+
Process.daemon
|
78
|
+
|
79
|
+
File.open("#{PIDFile}", 'w', 0644) { |f| f.write($$.to_s) }
|
80
|
+
|
81
|
+
start_web_server
|
82
|
+
|
83
|
+
start_nuri_server
|
84
|
+
end
|
85
|
+
|
86
|
+
def start_nuri_server
|
87
|
+
@enabled = true
|
88
|
+
@logger.info "Nuri server is running."
|
89
|
+
|
90
|
+
options = { :parallel => true,
|
91
|
+
:model_file => ModelFile,
|
92
|
+
:apply => true,
|
93
|
+
:push_modules => true,
|
94
|
+
:modules_dir => ModulesDir,
|
95
|
+
:silent => true }
|
96
|
+
|
97
|
+
interval_time = (@config['interval_time'] ? @config['interval_time'].to_i : DefaultIntervalTime)
|
98
|
+
|
99
|
+
while @enabled
|
100
|
+
@logger.info "Executing the model."
|
101
|
+
|
102
|
+
plan = nil
|
103
|
+
begin
|
104
|
+
#plan = get_plan if File.exist?(ModelFile)
|
105
|
+
|
106
|
+
if plan.is_a?(Hash)
|
107
|
+
File.open(PlanFile, 'w', '0644') { |f| f.write(JSON.pretty_generate(plan)) }
|
108
|
+
@logger.info "Executing the generated plan in file #{PlanFile}."
|
109
|
+
options[:plan] = plan
|
110
|
+
if execute_plan(options)
|
111
|
+
@logger.info "Execution is success!"
|
112
|
+
else
|
113
|
+
@logger.info "Execution is failed!"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
rescue Exception => e
|
118
|
+
@logger.error "Error: #{e}\n#{e.backtrace.join("\n")}"
|
119
|
+
end
|
120
|
+
|
121
|
+
sleep interval_time
|
122
|
+
end
|
123
|
+
|
124
|
+
@logger.info "Nuri server has stopped."
|
125
|
+
end
|
126
|
+
|
127
|
+
def start_web_server
|
128
|
+
Thread.new {
|
129
|
+
config = { :Host => '0.0.0.0',
|
130
|
+
:Port => (@config['port'] ? @config['port'].to_i : DefaultPort),
|
131
|
+
:ServerType => WEBrick::SimpleServer,
|
132
|
+
:Logger => @logger }
|
133
|
+
@web_server = WEBrick::HTTPServer.new(config)
|
134
|
+
@web_server.mount('/', Nuri::Server::Handler, self, @logger)
|
135
|
+
@web_server.start
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
def stop
|
140
|
+
begin
|
141
|
+
pid = File.read(PIDFile).to_i
|
142
|
+
Process.kill 'HUP', pid
|
143
|
+
puts "Stopping Nuri server..."
|
144
|
+
rescue
|
145
|
+
puts "Nuri server is not running."
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def get_model(p={})
|
150
|
+
(File.exist?(ModelFile) ? File.read(ModelFile) : '')
|
151
|
+
end
|
152
|
+
|
153
|
+
def get_log(total_lines=0)
|
154
|
+
(File.exist?(LogFile) ? `tail -n #{total_lines} #{LogFile}` : '')
|
155
|
+
end
|
156
|
+
|
157
|
+
class Handler < WEBrick::HTTPServlet::AbstractServlet
|
158
|
+
def initialize(server, master, logger)
|
159
|
+
@master = master
|
160
|
+
@logger = logger
|
161
|
+
end
|
162
|
+
|
163
|
+
def do_GET(request, response)
|
164
|
+
status = 400
|
165
|
+
content_type, body = ''
|
166
|
+
|
167
|
+
if not trusted(request.peeraddr[2])
|
168
|
+
status = 403
|
169
|
+
else
|
170
|
+
path = (request.path[-1,1] == '/' ? request.path.chop : request.path)
|
171
|
+
if path == '/model'
|
172
|
+
status, content_type, body = [200, 'text/plain', @master.get_model(:json => true)]
|
173
|
+
elsif path == '/log'
|
174
|
+
status, content_type, body = [200, 'text/plain', @master.get_log(100)]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
response.status = status
|
179
|
+
response['Content-Type'] = content_type
|
180
|
+
response.body = body
|
181
|
+
end
|
182
|
+
|
183
|
+
def do_POST(request, response)
|
184
|
+
status = 400
|
185
|
+
content_type, body = ''
|
186
|
+
|
187
|
+
response.status = status
|
188
|
+
response['Content-Type'] = content_type
|
189
|
+
response.body = body
|
190
|
+
end
|
191
|
+
|
192
|
+
def do_PUT(request, response)
|
193
|
+
status = 400
|
194
|
+
content_type, body = ''
|
195
|
+
|
196
|
+
response.status = status
|
197
|
+
response['Content-Type'] = content_type
|
198
|
+
response.body = body
|
199
|
+
end
|
200
|
+
|
201
|
+
def trusted(address)
|
202
|
+
# TODO
|
203
|
+
true
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
#nuri = Object.new
|
209
|
+
#nuri.extend(Nuri::Server)
|
210
|
+
#nuri.init
|
211
|
+
#nuri.start
|
212
|
+
#nuri.stop
|