nuri 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +12 -0
  4. data/CHANGELOG +146 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +28 -0
  7. data/README.md +64 -0
  8. data/Rakefile +15 -0
  9. data/VERSION +1 -0
  10. data/bin/delete_modules +11 -0
  11. data/bin/install_agent +18 -0
  12. data/bin/install_module +65 -0
  13. data/bin/nuri +519 -0
  14. data/bin/nuri.old +183 -0
  15. data/bin/push_model +16 -0
  16. data/examples/.gitignore +3 -0
  17. data/examples/bonfire.sfp +95 -0
  18. data/examples/bonfire/epcc.sfp +43 -0
  19. data/examples/bonfire/epcc0.sfp +49 -0
  20. data/examples/bonfire/epcc2.sfp +52 -0
  21. data/examples/bonfire/epcc2a.sfp +25 -0
  22. data/examples/bonfire/inria.sfp +72 -0
  23. data/examples/bonfire/inria0.sfp +49 -0
  24. data/examples/bonfire/inria2.sfp +71 -0
  25. data/examples/bonfire/inria2a.sfp +44 -0
  26. data/examples/bonfire/inria2b.sfp +54 -0
  27. data/examples/bonfire/inria2c.sfp +62 -0
  28. data/examples/bonfire/inria2d.sfp +71 -0
  29. data/examples/bonfire/inria2e.sfp +80 -0
  30. data/examples/bonfire/main.sfp +33 -0
  31. data/examples/bonfire/old/bonfire-1-1-1.sfp +76 -0
  32. data/examples/bonfire/old/bonfire-1-10-1.sfp +77 -0
  33. data/examples/bonfire/old/bonfire-1-2-1.sfp +58 -0
  34. data/examples/bonfire/old/bonfire-1-3-1.sfp +61 -0
  35. data/examples/bonfire/old/bonfire-1-4-1.sfp +64 -0
  36. data/examples/bonfire/old/bonfire-1-5-1.sfp +67 -0
  37. data/examples/bonfire/old/bonfire-1-6-1.sfp +82 -0
  38. data/examples/bonfire/old/bonfire-1-7-1.sfp +82 -0
  39. data/examples/bonfire/old/bonfire-1-8-1.sfp +79 -0
  40. data/examples/bonfire/old/bonfire-1-9-1.sfp +83 -0
  41. data/examples/bonfire/old/wp-test1a.sfp +38 -0
  42. data/examples/bonfire/old/wp-test1b.sfp +18 -0
  43. data/examples/bonfire/old/wp-test1c.sfp +7 -0
  44. data/examples/bonfire/old/wp-test2.sfp +47 -0
  45. data/examples/bonfire/old3/bonfire-epcc.sfp +57 -0
  46. data/examples/bonfire/old3/bonfire-inria.sfp +72 -0
  47. data/examples/bonfire/old3/bonfire-master.sfp +18 -0
  48. data/examples/bonfire/old3/bonfire.sfp +23 -0
  49. data/examples/bonfire/old3/bonfire2.sfp +49 -0
  50. data/examples/bonfire/old3/bonfire3.sfp +76 -0
  51. data/examples/bonfire/old3/bonfire4.sfp +78 -0
  52. data/examples/bonfire/old3/bonfire5.sfp +34 -0
  53. data/examples/bonfire/old3/bonfire5b.sfp +84 -0
  54. data/examples/bonfire/old3/hpvm6.sfp +22 -0
  55. data/examples/bonfire/old3/model.json +1 -0
  56. data/examples/bonfire/old3/test0.sfp +16 -0
  57. data/examples/bonfire/old3/test1.sfp +5 -0
  58. data/examples/bonfire/old3/test10.sfp +5 -0
  59. data/examples/bonfire/old3/test2.sfp +18 -0
  60. data/examples/bonfire/old3/test3.sfp +10 -0
  61. data/examples/bonfire/old3/test4.sfp +11 -0
  62. data/examples/bonfire/old3/test5.sfp +18 -0
  63. data/examples/bonfire/old3/test6.sfp +19 -0
  64. data/examples/bonfire/old3/test7.sfp +34 -0
  65. data/examples/bonfire/old3/test8.sfp +5 -0
  66. data/examples/bonfire/old3/test9.sfp +16 -0
  67. data/examples/bonfire/old3/wordpress-test-cluster.sfp +38 -0
  68. data/examples/bonfire/old3/wordpress-test.sfp +22 -0
  69. data/examples/bonfire/old3/wp-test-2.sfp +49 -0
  70. data/examples/bonfire/test.sfp +13 -0
  71. data/examples/generator.rb +66 -0
  72. data/examples/hadoop2.sfp +20 -0
  73. data/examples/hpcloud.sfp +18 -0
  74. data/examples/run.rb +17 -0
  75. data/examples/test.inc +0 -0
  76. data/examples/test.sfp +11 -0
  77. data/lib/naas/d3.js +5 -0
  78. data/lib/naas/d3.v3.min.js +5 -0
  79. data/lib/naas/index.css +0 -0
  80. data/lib/naas/index.html +18 -0
  81. data/lib/naas/index.js +18 -0
  82. data/lib/naas/jquery-1.10.2.min.js +6 -0
  83. data/lib/naas/jquery.js +6 -0
  84. data/lib/naas/naas.rb +160 -0
  85. data/lib/nuri.rb +62 -0
  86. data/lib/nuri/choreographer.rb +151 -0
  87. data/lib/nuri/constraint_helper.rb +9 -0
  88. data/lib/nuri/directory.rb +40 -0
  89. data/lib/nuri/master.rb +725 -0
  90. data/lib/nuri/net_helper.rb +65 -0
  91. data/lib/nuri/orchestrator.rb +224 -0
  92. data/lib/nuri/server.rb +212 -0
  93. data/modules/.gitignore +4 -0
  94. data/modules/apache/apache.rb +255 -0
  95. data/modules/apache/apache.rb.old +167 -0
  96. data/modules/apache/apache.sfp +146 -0
  97. data/modules/apache/apache.sfp.future +100 -0
  98. data/modules/apache/load_balancer +20 -0
  99. data/modules/apache/model.json +1 -0
  100. data/modules/apache/test.sfp +8 -0
  101. data/modules/aptpackage/aptpackage.rb +82 -0
  102. data/modules/aptpackage/aptpackage.sfp +5 -0
  103. data/modules/bonfire/.gitignore +2 -0
  104. data/modules/bonfire/README.md +12 -0
  105. data/modules/bonfire/bonfire.rb +60 -0
  106. data/modules/bonfire/bonfire.sfp +9 -0
  107. data/modules/bonfire/config.yml +4 -0
  108. data/modules/bonfire/helper.rb +149 -0
  109. data/modules/bonfire/stresstest.rb +144 -0
  110. data/modules/bonfire/test.sfp +8 -0
  111. data/modules/client/client.rb +22 -0
  112. data/modules/client/client.sfp +14 -0
  113. data/modules/cloud/cloud.rb +11 -0
  114. data/modules/cloud/cloud.sfp +26 -0
  115. data/modules/file/file.rb +91 -0
  116. data/modules/file/file.sfp +9 -0
  117. data/modules/hadoop1/core-site.xml +17 -0
  118. data/modules/hadoop1/hadoop-env.sh +55 -0
  119. data/modules/hadoop1/hadoop1.rb +384 -0
  120. data/modules/hadoop1/hadoop1.sfp +93 -0
  121. data/modules/hadoop1/hdfs-site.xml +16 -0
  122. data/modules/hadoop1/mapred-site.xml +17 -0
  123. data/modules/hadoop2/core-site.xml +31 -0
  124. data/modules/hadoop2/hadoop-env.sh +77 -0
  125. data/modules/hadoop2/hadoop2.rb +401 -0
  126. data/modules/hadoop2/hadoop2.sfp +114 -0
  127. data/modules/hadoop2/hdfs-site.xml +47 -0
  128. data/modules/hadoop2/mapred-site.xml +71 -0
  129. data/modules/hadoop2/ports +14 -0
  130. data/modules/hadoop2/yarn-env.sh +112 -0
  131. data/modules/hadoop2/yarn-site.xml +107 -0
  132. data/modules/hpcloud/.gitignore +2 -0
  133. data/modules/hpcloud/README.md +16 -0
  134. data/modules/hpcloud/config.yml +3 -0
  135. data/modules/hpcloud/example.sfp +18 -0
  136. data/modules/hpcloud/hpcloud.rb +241 -0
  137. data/modules/hpcloud/hpcloud.sfp +22 -0
  138. data/modules/hpcloud/test.sfp +5 -0
  139. data/modules/install_module +65 -0
  140. data/modules/machine/machine.rb +95 -0
  141. data/modules/machine/machine.sfp +9 -0
  142. data/modules/mockcloud/mockcloud.rb +20 -0
  143. data/modules/mockcloud/mockcloud.sfp +6 -0
  144. data/modules/mysql/mysql.rb +118 -0
  145. data/modules/mysql/mysql.sfp +38 -0
  146. data/modules/mysql/test.sfp +3 -0
  147. data/modules/node/node.rb +8 -0
  148. data/modules/node/node.sfp +7 -0
  149. data/modules/object/object.rb +7 -0
  150. data/modules/object/object.sfp +1 -0
  151. data/modules/os/os.rb +38 -0
  152. data/modules/os/os.sfp +11 -0
  153. data/modules/package/package.rb +26 -0
  154. data/modules/package/package.sfp +22 -0
  155. data/modules/package/test.sfp +6 -0
  156. data/modules/service/model.json +1 -0
  157. data/modules/service/service.rb +50 -0
  158. data/modules/service/service.sfp +46 -0
  159. data/modules/service/test.sfp +6 -0
  160. data/modules/tarpackage/tarpackage.rb +93 -0
  161. data/modules/tarpackage/tarpackage.sfp +5 -0
  162. data/modules/vm/vm.rb +8 -0
  163. data/modules/vm/vm.sfp +18 -0
  164. data/modules/wordpress/wordpress.rb +98 -0
  165. data/modules/wordpress/wordpress.sfp +34 -0
  166. data/modules/wordpresscluster/wordpresscluster.rb +150 -0
  167. data/modules/wordpresscluster/wordpresscluster.sfp +74 -0
  168. data/nuri.gemspec +26 -0
  169. 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
@@ -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