nuri 0.5.2 → 0.5.3

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.
@@ -3,29 +3,31 @@ require 'thread'
3
3
  module Nuri::Orchestrator
4
4
  include Nuri::Helper
5
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]
6
+ def execute_plan(options={})
7
+ raise Exception, "Plan file is not exist!" if not File.exist?(options[:execute].to_s) and !options[:plan]
8
+ raise Exception, "Plan is not exist (parameter :plan must be given)!" if !options[:plan]
9
9
 
10
10
  push_agents_list
11
11
 
12
12
  success = false
13
13
  benchmark = Benchmark.measure {
14
- plan = (p[:plan] ? p[:plan] : JSON[File.read(p[:execute])])
15
- raise Exception, "Invalid plan!" if plan['workflow'].nil?
14
+ plan = (options[:plan] ? options[:plan] : JSON[File.read(options[:execute])])
15
+ raise Exception, "No plan." if plan['workflow'].nil?
16
16
  if plan.is_a?(Hash) and plan['type'] == 'sequential'
17
- success = execute_sequential_plan(plan, p)
17
+ success = execute_sequential_plan(plan, options)
18
18
  elsif plan.is_a?(Hash) and plan['type'] == 'parallel'
19
- success = execute_parallel_plan(plan, p)
19
+ success = execute_parallel_plan(plan, options)
20
+ else
21
+ raise Exception, "Invalid plan."
20
22
  end
21
23
  }
22
- puts "Execution time (s): #{benchmark}"
24
+ puts "Execution " + format_benchmark(benchmark)
23
25
 
24
26
  success
25
27
  end
26
28
 
27
29
  protected
28
- def send_action_data(action, address, port)
30
+ def send_action_data(action, address, port, options={})
29
31
  action['parameters'].each do |name,value|
30
32
  next if !value.is_a?(String) or !value.isref or value.split('.').length > 2
31
33
  _, target_name = value.split('.', 2)
@@ -38,7 +40,8 @@ module Nuri::Orchestrator
38
40
  rescue
39
41
  end
40
42
  if code != '200'
41
- $stderr.puts "Sending action data of #{value} to #{address}:#{port} [Failed]".red
43
+ $stderr.print "Sending action data of #{value} to #{address}:#{port} "
44
+ $stderr.puts (options[:color] ? "[Failed]".red : "[Failed]")
42
45
  return false
43
46
  end
44
47
  end
@@ -46,7 +49,7 @@ module Nuri::Orchestrator
46
49
  true
47
50
  end
48
51
 
49
- def execute_action(action)
52
+ def execute_action(action, options={})
50
53
  _, agent_name, _ = action['name'].split('.', 3)
51
54
  agents = get_agents
52
55
 
@@ -58,7 +61,7 @@ module Nuri::Orchestrator
58
61
  raise Exception, "Cannot find address:port of agent #{agent_name}" if
59
62
  address.length <= 0 or port.length <= 0
60
63
 
61
- send_action_data(action, address, port)
64
+ send_action_data(action, address, port, options)
62
65
 
63
66
  data = {'action' => JSON.generate(action)}
64
67
  code, _ = post_data(address, port, '/execute', data)
@@ -97,22 +100,22 @@ module Nuri::Orchestrator
97
100
  }
98
101
  end
99
102
 
100
- def execute_sequential_plan(plan, p)
103
+ def execute_sequential_plan(plan, options)
101
104
  begin
102
105
  index = 1
103
106
  plan['workflow'].each do |action|
104
- print "#{index}. #{action['name']} #{JSON.generate(action['parameters'])}... "
105
- if execute_action(action)
106
- puts "[OK]".green
107
+ puts "#{index}. #{action['name']} #{JSON.generate(action['parameters'])} " + (options[:color] ? "[Wait]".yellow : "[Wait]")
108
+ if execute_action(action, options)
109
+ puts "#{index}. #{action['name']} #{JSON.generate(action['parameters'])} " + (options[:color] ? "[OK]".green : "[OK]")
107
110
  else
108
- puts "[Failed]".red
111
+ puts "#{index}. #{action['name']} #{JSON.generate(action['parameters'])} " + (options[:color] ? "[Failed]".red : "[Failed]")
109
112
  return false
110
113
  end
111
114
  index += 1
112
115
  end
113
116
  return true
114
117
  rescue Exception => e
115
- puts "#{e}\n#{e.backtrace.join("\n")}".red
118
+ $stderr.puts "#{e}\n#{e.backtrace.join("\n")}"
116
119
  end
117
120
  false
118
121
  end
@@ -140,27 +143,28 @@ module Nuri::Orchestrator
140
143
  id
141
144
  end
142
145
 
143
- def assign_action_with_id(id)
146
+ def assign_action_with_id(id, options={})
144
147
  thread_id = next_thread_id
145
148
  action = @actions[id]
146
149
  action[:executor] = thread_id
147
- self.thread_execute_action(thread_id, action)
150
+ self.thread_execute_action(thread_id, action, options)
148
151
  end
149
152
 
150
- def thread_execute_action(tid, action)
153
+ def thread_execute_action(tid, action, options={})
151
154
  t = Thread.new {
152
155
  # Register the thread
153
156
  @mutex.synchronize { @threads << tid }
154
157
 
155
158
  while not @failed and not action[:executed]
156
159
  # Try to execute the action
157
- puts "Executing #{action[:string]} - thread ##{tid} [WAIT]".yellow
160
+ puts "Executing #{action[:string]} - thread ##{tid} " + (options[:color] ? "[Wait]".yellow : "[Wait]")
158
161
  success = false
159
162
  1.upto(@retries) do |i|
160
163
  begin
161
- success = execute_action(action)
164
+ success = execute_action(action, options)
162
165
  rescue Exception => exp
163
- puts "Executing(#{i}) #{action[:string]} - thread ##{tid} [FAILED]\n#{exp}\n#{exp.backtrace.join("\n")}"
166
+ puts "Executing(#{i}) #{action[:string]} - thread ##{tid} " + (options[:color] ? "[Failed]".red : "[Failed]")
167
+ puts "#{exp}\n#{exp.backtrace.join("\n")}"
164
168
  end
165
169
  break if success
166
170
  end
@@ -168,10 +172,10 @@ module Nuri::Orchestrator
168
172
  # Check if execution failed
169
173
  if success
170
174
  # Execution was success
171
- puts "Executing #{action[:string]} - thread ##{tid} [OK]".green
175
+ puts "Executing #{action[:string]} - thread ##{tid} " + (options[:color] ? "[OK]".green : "[OK]")
172
176
  next_actions = []
173
177
  @mutex.synchronize {
174
- action[:executed] = true # set executed
178
+ action[:executed] = true # set executed flag
175
179
  # select next action to be executed from successor actions list
176
180
  # select a successor action that has not been assigned to any thread yet
177
181
  if action['successors'].length > 0
@@ -196,14 +200,14 @@ module Nuri::Orchestrator
196
200
  if next_actions.length > 1
197
201
  # execute other next actions to other threads
198
202
  for i in 1..(next_actions.length-1)
199
- assign_action_with_id(next_actions[i])
203
+ assign_action_with_id(next_actions[i], options)
200
204
  end
201
205
  end
202
206
  end
203
207
 
204
208
  else
205
209
  # Execution was failed
206
- puts "Executing #{action[:string]} - thread ##{tid} [FAILED]".red
210
+ puts "Executing #{action[:string]} - thread ##{tid} " + (options[:color] ? "[Failed]".red : "[Failed]")
207
211
  @mutex.synchronize {
208
212
  @failed = true # set global flag to stop the execution
209
213
  @actions_failed << action
@@ -215,7 +219,8 @@ module Nuri::Orchestrator
215
219
  }
216
220
  end
217
221
 
218
- plan['init'].each { |id| assign_action_with_id(id) }
222
+ ### execute actions without predecessor
223
+ plan['init'].each { |id| assign_action_with_id(id, options) }
219
224
  begin
220
225
  sleep 1
221
226
  end while @threads.length > 0
@@ -0,0 +1,9 @@
1
+ class Sfp::Module::Apache2
2
+ include Sfp::Resource
3
+
4
+ def update_state
5
+ @state['path'] = path
6
+ @state['installed'] = resolve(path + '.package.installed')
7
+ @state['running'] = resolve(path + '.service.running')
8
+ end
9
+ end
@@ -0,0 +1,54 @@
1
+ include "../package2/package2.sfp"
2
+ include "../service2/service2.sfp"
3
+
4
+ schema Apache2 {
5
+ installed : Bool
6
+ running : Bool
7
+
8
+ package isa Package2 {
9
+ name = "apache2"
10
+ version = "latest"
11
+ provider = "apt"
12
+
13
+ synchronized sub install {
14
+ condition {
15
+ this.parent.installed != true
16
+ }
17
+ effect {
18
+ this.parent.installed = true
19
+ this.parent.running = true
20
+ }
21
+ }
22
+ synchronized sub uninstall {
23
+ condition {
24
+ this.parent.installed = true
25
+ this.parent.running = false
26
+ }
27
+ effect {
28
+ this.parent.installed = false
29
+ }
30
+ }
31
+ }
32
+
33
+ service isa Service2 {
34
+ name = "apache2"
35
+
36
+ sub start {
37
+ condition {
38
+ this.parent.installed = true
39
+ }
40
+ effect {
41
+ this.parent.running = true
42
+ }
43
+ }
44
+
45
+ sub stop {
46
+ condition {
47
+ this.parent.running = true
48
+ }
49
+ effect {
50
+ this.parent.running = false
51
+ }
52
+ }
53
+ }
54
+ }
@@ -7,7 +7,7 @@ class Sfp::Module::File
7
7
 
8
8
  ##############################
9
9
  #
10
- # update current state method
10
+ # method to update current state
11
11
  #
12
12
  ##############################
13
13
 
@@ -1,5 +1,5 @@
1
1
  schema File {
2
- exists is true
2
+ exists : Bool
3
3
 
4
4
  final path is ""
5
5
  final content : String
@@ -4,6 +4,8 @@ class Sfp::Module::Machine
4
4
  def update_state
5
5
  to_model
6
6
 
7
+ load_kernel_modules(['acpiphp'])
8
+
7
9
  @state['sfpAddress'] = @model['sfpAddress']
8
10
  @state['sfpPort'] = @model['sfpPort']
9
11
  @state['created'] = true
@@ -29,6 +31,21 @@ class Sfp::Module::Machine
29
31
 
30
32
  protected
31
33
 
34
+ def load_kernel_modules(modules=[])
35
+ loaded = []
36
+ `lsmod`.each_line do |line|
37
+ next if line.strip.length <= 0
38
+ name, size, used, by = line.split(' ', 4)
39
+ next if name.to_s.downcase == 'module'
40
+ loaded << name
41
+ end
42
+
43
+ unloaded = modules - loaded
44
+ unloaded.each do |name|
45
+ shell "modprobe #{name}"
46
+ end
47
+ end
48
+
32
49
  # generate the disks' state, try to automatically mount the disk to target directory
33
50
  #
34
51
  def get_disk_state
@@ -0,0 +1,85 @@
1
+ require 'thread'
2
+
3
+ class Sfp::Module::Package2
4
+ include Sfp::Resource
5
+
6
+ def update_state
7
+ to_model
8
+
9
+ @state['installed'] = installed?
10
+ @state['version'] = version?
11
+ end
12
+
13
+ ##############################
14
+ #
15
+ # Action methods (see Package2.sfp)
16
+ #
17
+ ##############################
18
+
19
+ @@lock = Mutex.new
20
+
21
+ def installed?
22
+ name = @model['name'].to_s.strip
23
+ if name.length > 0
24
+ data = `/usr/bin/dpkg-query -W #{name} 2>/dev/null`.strip.chop.split(' ')
25
+ (data[0].to_s == name)
26
+ else
27
+ false
28
+ end
29
+ end
30
+
31
+ def version?
32
+ name = @model['name'].to_s.strip
33
+ if name.length > 0
34
+ data = `/usr/bin/dpkg-query -W #{name} 2>/dev/null`.strip.chop.split(' ')
35
+ (data[0].to_s == name ? data[1] : "")
36
+ else
37
+ ""
38
+ end
39
+ end
40
+
41
+ def install(p={})
42
+ name = @model['name'].to_s.strip
43
+ if name.length <= 0
44
+ false
45
+ elsif installed?
46
+ true
47
+ else
48
+ shell "dpkg --configure -a; apt-get -y --purge autoremove"
49
+ if shell "apt-get -y install #{name}"
50
+ true
51
+ else
52
+ shell "dpkg --configure -a; apt-get -y update && apt-get -y install #{name}"
53
+ end
54
+ end
55
+ end
56
+
57
+ def uninstall(package)
58
+ name = @model['name'].to_s.strip
59
+ if name.length <= 0
60
+ false
61
+ elsif not installed?
62
+ true
63
+ else
64
+ shell "dpkg --configure -a; apt-get -y --purge autoremove"
65
+ shell "apt-get -y --purge remove #{name} && apt-get -y --purge autoremove && apt-get -y --purge autoremove"
66
+ end
67
+ end
68
+
69
+ =begin
70
+ protected
71
+ def installed?
72
+ Sfp::Module::AptPackage.installed?(@model['package_name'].to_s.strip)
73
+ end
74
+
75
+ def version?
76
+ package = @model['package_name'].to_s.strip
77
+ return "" if package.length <= 0
78
+ installed = `apt-cache policy #{package} | grep Installed`.strip.split(' ', 2)[1].to_s.strip
79
+ return "" if installed.length <= 0
80
+ candidate = `apt-cache policy #{package} | grep Candidate`.strip.split(' ', 2)[1].to_s.strip
81
+ return "latest" if candidate == installed
82
+ installed
83
+ end
84
+ =end
85
+ end
@@ -0,0 +1,23 @@
1
+ schema Package2 {
2
+ installed : Bool
3
+
4
+ final name = ""
5
+ final version = "latest"
6
+ final source = "default"
7
+ final provider = "apt"
8
+
9
+ synchronized sub install {
10
+ effect {
11
+ this.installed = true
12
+ }
13
+ }
14
+
15
+ synchronized sub uninstall {
16
+ condition {
17
+ this.installed = true
18
+ }
19
+ effect {
20
+ this.installed = false
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python
2
+
3
+ import sys
4
+ import json
5
+ import os
6
+ import hashlib
7
+ import io
8
+ from subprocess import call
9
+
10
+
11
+ ###############
12
+ #
13
+ # Method for generating current state
14
+ #
15
+ ###############
16
+ def get_state(model):
17
+ state = {}
18
+ state['path'] = model['path'] if 'path' in model else ''
19
+ try:
20
+ m = hashlib.md5()
21
+ with open(state['path']) as f:
22
+ buf = f.read(128)
23
+ while len(buf) > 0:
24
+ m.update(buf)
25
+ buf = f.read(128)
26
+ state['created'] = True
27
+ state['content'] = '{md5}' + m.hexdigest()
28
+ except IOError:
29
+ state['created'] = False
30
+ state['content'] = ''
31
+
32
+ if state['created'] and 'content' in model:
33
+ m = hashlib.md5()
34
+ m.update(model['content'])
35
+ state['created'] = (('{md5}' + m.hexdigest()) == state['content'])
36
+
37
+ ### dump current state
38
+ print json.dumps(state)
39
+
40
+
41
+ ###############
42
+ #
43
+ # Implementation of procedure 'del' (see pyfile.sfp)
44
+ #
45
+ ###############
46
+ def delete(model):
47
+ if 'path' in model:
48
+ os.remove(model['path'])
49
+ else:
50
+ return False
51
+ return True
52
+
53
+
54
+ ###############
55
+ #
56
+ # Implementation of procedure 'create' (see pyfile.sfp)
57
+ #
58
+ ###############
59
+ def create(model):
60
+ if 'path' in model:
61
+ content = model['content'] if 'content' in model else ''
62
+ try:
63
+ with open(model['path'], 'w') as f:
64
+ f.write(content)
65
+ except IOError, e:
66
+ print str(e)
67
+ return False
68
+ else:
69
+ return False
70
+
71
+ if 'permission' in model:
72
+ call(["chmod", model['permission'], model['path']])
73
+
74
+ return True
75
+
76
+
77
+ ###############
78
+ #
79
+ # Function to redirect procedure execution request by
80
+ # calling target function
81
+ #
82
+ ###############
83
+ def execute(procedure, parameters, model):
84
+ success = False
85
+ description = ''
86
+ if procedure == 'del':
87
+ success = delete(model)
88
+ elif procedure == 'create':
89
+ success = create(model)
90
+ else:
91
+ description = 'unrecognized procedure ' + procedure
92
+ status = 'ok' if success else 'failed'
93
+ print json.dumps({'status': status, 'description': description})
94
+
95
+
96
+ ###############
97
+ #
98
+ # Main logic for handling any request
99
+ #
100
+ ###############
101
+ if len(sys.argv) <= 1:
102
+ ### invalid request
103
+ print '{"status":"error","description":"invalid command line argument"}'
104
+ exit(1)
105
+ else:
106
+ ### parse request data in JSON
107
+ data = json.loads(sys.argv[1])
108
+
109
+ if 'command' in data:
110
+ try:
111
+ if data['command'] == 'state' and 'model' in data:
112
+ ### request for current state
113
+ get_state(data['model'])
114
+
115
+ elif data['command'] == 'execute' and 'procedure' in data and 'parameters' in data and 'model' in data:
116
+ ### request for executing a procedure
117
+ execute(data['procedure'], data['parameters'], data['model'])
118
+
119
+ else:
120
+ ### invalid request
121
+ print '{"status":"error","description":"invalid command"}'
122
+ exit(1)
123
+
124
+ except Exception, e:
125
+ ### dump exception
126
+ print json.dumps({'status':'error','description': str(e)})
127
+
128
+ else:
129
+ ### invalid request
130
+ print '{"status":"error","description":"no command"}'
131
+ exit(1)
132
+
133
+ exit(0)