nuri 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|