sfp 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'thread'
5
+
6
+ module Sfp
7
+ module Executor
8
+ class ExecutionException < Exception; end
9
+ class ParallelExecutionException < Exception; end
10
+ class SequentialExecutionException < Exception; end
11
+
12
+ # @param :plan the plan to be executed
13
+ # @param :owner an object that implement the action
14
+ # @param :retry number of retries (default: 2) when execution is failed
15
+ #
16
+ def execute_plan(params={})
17
+ if params[:plan].nil? or not params[:plan].is_a?(Hash)
18
+ raise ExecutionException, 'Plan is not available.'
19
+ elsif params[:plan]['type'].to_s == 'parallel' or
20
+ params[:plan][:type].to_s == 'parallel'
21
+ return self.execute_parallel_plan(params)
22
+ elsif params[:plan]['type'].to_s == 'sequential' or
23
+ params[:plan][:type].to_s == 'sequential'
24
+ return self.execute_sequential_plan(params)
25
+ else
26
+ raise ExecutionException, 'Unknown type of plan!'
27
+ end
28
+ false
29
+ end
30
+
31
+ # @param :plan the plan to be executed
32
+ # @param :owner an object that implement the action
33
+ # @param :retry number of retries (default: 2) when execution is failed
34
+ #
35
+ def execute_parallel_plan(params={})
36
+ def assign_action_with_id(id)
37
+ thread_id = next_thread_id
38
+ action = @actions[id]
39
+ action[:executor] = thread_id
40
+ self.thread_execute_action(thread_id, action)
41
+ end
42
+
43
+ def next_thread_id
44
+ id = 0
45
+ @mutex.synchronize { @thread_id = id = @thread_id + 1 }
46
+ id
47
+ end
48
+
49
+ def action_to_string(action)
50
+ "#{action['id']}:#{action['name']}#{JSON.generate(action['parameters'])}"
51
+ end
52
+
53
+ def thread_execute_action(tid, action)
54
+ t = Thread.new {
55
+ @mutex.synchronize { @threads << tid }
56
+
57
+ while not @failed and not action[:executed]
58
+ # execute the action
59
+ op_str = action_to_string(action)
60
+ #Nuri::Util.puts "[ExecutorThread: #{tid}] #{op_str}"
61
+ success = false
62
+ num = @retry
63
+ begin
64
+ success = @owner.execute_action { action }
65
+ num -= 1
66
+ end while not success and num > 0
67
+
68
+ # check if execution failed
69
+ if success
70
+ next_actions = []
71
+ @mutex.synchronize {
72
+ # set executed
73
+ action[:executed] = true
74
+ # select next action to be executed from all predecessor actions
75
+ # if each action has not been assigned to any thread yet
76
+ if action['successors'].length > 0
77
+ action['successors'].each { |id|
78
+ if @actions[id][:executor].nil?
79
+ predecessors_ok = true
80
+ @actions[id]['predecessors'].each { |pid|
81
+ predecessors_ok = (predecessors_ok and @actions[pid][:executed])
82
+ }
83
+ next_actions << id if predecessors_ok
84
+ end
85
+ }
86
+ end
87
+ next_actions.each { |id| @actions[id][:executor] = tid }
88
+ }
89
+ if next_actions.length > 0
90
+ # execute next actions
91
+ action = @actions[next_actions[0]]
92
+ if next_actions.length > 1
93
+ for i in 1..(next_actions.length-1)
94
+ assign_action_with_id(next_actions[i])
95
+ end
96
+ end
97
+ end
98
+
99
+ else
100
+ Nuri::Util.error "Failed executing #{op_str}!"
101
+ @mutex.synchronize {
102
+ @failed = true # set global flag
103
+ @actions_failed << action
104
+ }
105
+ end
106
+ end
107
+
108
+ @mutex.synchronize { @threads.delete(tid) }
109
+ }
110
+ end
111
+
112
+ if params[:plan].nil? or not params[:plan].is_a?(Hash)
113
+ raise ParallelExecutionException, 'Plan is not available.'
114
+ elsif params[:plan]['type'].to_s == 'parallel' or
115
+ params[:plan][:type].to_s == 'parallel'
116
+ else
117
+ raise ParallelExecutionException, 'Not a parallel plan.'
118
+ end
119
+
120
+ @owner = params[:owner]
121
+ @retry = (params[:retry].nil? ? 2 : params[:retry].to_i)
122
+
123
+ @actions = params[:plan]['workflow']
124
+ @actions.sort! { |x,y| x['id'] <=> y['id'] }
125
+ @actions.each { |op| op[:executed] = false; op[:executor] = nil; }
126
+
127
+ @threads = []
128
+ @actions_failed = []
129
+ @mutex = Mutex.new
130
+ @failed = false
131
+ @thread_id = 0
132
+
133
+ params[:plan]['init'].each { |op_id| assign_action_with_id(op_id) }
134
+
135
+ begin
136
+ sleep 1
137
+ end while @threads.length > 0
138
+
139
+ Nuri::Util.log "Using #{@thread_id} threads in execution."
140
+
141
+ return (not @failed)
142
+ end
143
+
144
+ # @param :plan the plan to be executed
145
+ # @param :owner an object that implement the action
146
+ # @param :retry number of retries (default: 2) when execution is failed
147
+ #
148
+ def execute_sequential_plan(params={})
149
+ if params[:plan].nil? or not params[:plan].is_a?(Hash)
150
+ raise ParallelExecutionException, 'Plan is not available.'
151
+ elsif params[:plan]['type'].to_s == 'sequential' or
152
+ params[:plan][:type].to_s == 'sequential'
153
+ else
154
+ raise ParallelExecutionException, 'Not a parallel plan.'
155
+ end
156
+
157
+ @owner = params[:owner]
158
+ @retry = (params[:retry].nil? ? 2 : params[:retry].to_i)
159
+ params[:plan]['workflow'].each { |action|
160
+ success = false
161
+ num = @retry
162
+ begin
163
+ success, data = @owner.execute_action { action }
164
+ puts data.to_s if params[:print_output]
165
+ num -= 1
166
+ end while not success and num > 0
167
+ return false if not success
168
+ }
169
+ true
170
+ end
171
+ end
172
+
173
+ class RubyExecutor
174
+ def execute_plan(params={})
175
+ exec = Object.new
176
+ exec.extend(Sfp::Executor)
177
+ params[:owner] = self
178
+ exec.execute_plan(params)
179
+ end
180
+
181
+ def execute_action
182
+ # TODO
183
+ action = yield
184
+ puts "Exec: #{action.inspect}"
185
+ [true, nil]
186
+ end
187
+ end
188
+
189
+ class BashExecutor < RubyExecutor
190
+ def execute_action
191
+ # TODO
192
+ action = yield
193
+ module_dir = (ENV.has_key?("SFP_HOME") ? ENV['SFP_HOME'] : ".")
194
+ script_path = "#{action['name'].sub!(/^\$\./, '')}"
195
+ script_path = "#{module_dir}/#{script_path.gsub!(/\./, '/')}"
196
+ cmd = "/bin/bash #{script_path}"
197
+ action['parameters'].each { |p| cmd += " '#{p}'" }
198
+ begin
199
+ data = `#{cmd}`
200
+ rescue Exception => exp
201
+ $stderr.puts "#{exp}\n#{exp.backtrace}"
202
+ [false, nil]
203
+ end
204
+ [true, data]
205
+ end
206
+ end
207
+ end
@@ -296,12 +296,19 @@ module Sfp
296
296
 
297
297
  def self.path
298
298
  os = `uname -s`.downcase.strip
299
- planner = case os
300
- when 'linux' then File.expand_path(File.dirname(__FILE__) + '/../../bin/solver/linux')
301
- when 'macos', 'darwin' then File.expand_path(File.dirname(__FILE__) + '/../../bin/solver/macos')
302
- else nil
299
+ machine = `uname -m`.downcase.strip
300
+ planner = nil
301
+
302
+ if os == 'linux' and machine[0,3] == 'x86'
303
+ planner = File.expand_path(File.dirname(__FILE__) + '/../../bin/solver/linux-x86')
304
+ elsif os == 'linux' and machine[0,3] == 'arm'
305
+ planner = File.expand_path(File.dirname(__FILE__) + '/../../bin/solver/linux-arm')
306
+ #Sfp::Planner::Config.set_max_memory(512)
307
+ elsif os == 'macos' or os == 'darwin'
308
+ planner = File.expand_path(File.dirname(__FILE__) + '/../../bin/solver/macos')
303
309
  end
304
- raise UnsupportedPlatformException, os + ' is not supported' if planner == nil
310
+
311
+ raise UnsupportedPlatformException, "#{os} is not supported" if planner.nil?
305
312
  planner
306
313
  end
307
314
 
@@ -314,6 +321,21 @@ module Sfp
314
321
  when 'cg' then '--search "lazy_greedy(cg(cost_type=2))"'
315
322
  when 'cea' then '--search "lazy_greedy(cea(cost_type=2))"'
316
323
  when 'mad' then '--search "lazy_greedy(mad())"'
324
+ when 'lama2011' then ' \
325
+ --heuristic "hlm1,hff1=lm_ff_syn(lm_rhw(
326
+ reasonable_orders=true,lm_cost_type=1,cost_type=1))" \
327
+ --heuristic "hlm2,hff2=lm_ff_syn(lm_rhw(
328
+ reasonable_orders=true,lm_cost_type=2,cost_type=2))" \
329
+ --search "iterated([
330
+ lazy_greedy([hff1,hlm1],preferred=[hff1,hlm1],
331
+ cost_type=1,reopen_closed=false),
332
+ lazy_greedy([hff2,hlm2],preferred=[hff2,hlm2],
333
+ reopen_closed=false),
334
+ lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=5),
335
+ lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=3),
336
+ lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=2),
337
+ lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=1)],
338
+ repeat_last=true,continue_on_fail=true)"'
317
339
  else '--search "lazy_greedy(ff(cost_type=0))"'
318
340
  end
319
341
  end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'sfp'
3
- s.version = '0.1.1'
4
- s.date = '2013-04-20'
3
+ s.version = '0.1.3'
4
+ s.date = '2013-05-06'
5
5
  s.summary = 'SFP Parser and Planner'
6
6
  s.description = 'A Ruby gem that provides a Ruby API to SFP language parser and ' +
7
7
  'SFP planner. It also provides a planner sript to solve a planning problem ' +
@@ -13,13 +13,14 @@ Gem::Specification.new do |s|
13
13
  s.files = `git ls-files`.split("\n")
14
14
  s.test_files = `git ls-files -- test/*`.split("\n")
15
15
 
16
- `git ls-files -- bin/*`.split("\n") { |f|
17
- s.executables << f
18
- }
16
+ s.files.delete_if { |f| f =~ /linux\-arm/ }
17
+
18
+ `git ls-files --exclude=bin/solver/linux-arm/* -- bin/*`.split("\n") { |f| s.executables << f }
19
19
 
20
20
  s.require_paths = ['lib']
21
21
 
22
22
  s.homepage = 'https://github.com/herry13/sfp-ruby'
23
+ s.rubyforge_project = 'sfp'
23
24
 
24
25
  s.add_dependency 'json', '~> 1.7.5'
25
26
  s.add_dependency 'antlr3', '~> 1.8.12'
@@ -104,7 +104,7 @@ composite
104
104
  ;
105
105
 
106
106
  class_definition
107
- : 'class' ID
107
+ : ('class'|'schema') ID
108
108
  {
109
109
  @now[$ID.text] = { '_self' => $ID.text,
110
110
  '_context' => 'class',
@@ -1,7 +1,14 @@
1
1
  #!/bin/sh
2
2
 
3
+ BASEDIR="$(dirname "$0")"
4
+ CURRENTDIR=`pwd`
5
+
6
+ cd $BASEDIR
7
+
3
8
  antlr4ruby SfpLang.g
4
9
 
5
10
  rm -f SfpLang.tokens
6
11
  mv -f SfpLangLexer.rb ../lib/sfp
7
12
  mv -f SfpLangParser.rb ../lib/sfp
13
+
14
+ cd $CURRENTDIR
@@ -1,4 +1,4 @@
1
- class Machine {
1
+ schema Machine {
2
2
  running is false
3
3
  address is ""
4
4
 
@@ -10,6 +10,7 @@ class Machine {
10
10
  this.running is true
11
11
  }
12
12
  }
13
+
13
14
  procedure stop {
14
15
  conditions {
15
16
  this.running is true
@@ -20,7 +21,7 @@ class Machine {
20
21
  }
21
22
  }
22
23
 
23
- class VM extends Machine {
24
+ schema VM extends Machine {
24
25
  created is false
25
26
 
26
27
  procedure start {
@@ -41,7 +42,7 @@ class VM extends Machine {
41
42
  }
42
43
  }
43
44
 
44
- class Cloud {
45
+ schema Cloud {
45
46
  running is false
46
47
 
47
48
  procedure create_vm(vm isref VM) {
@@ -64,7 +65,7 @@ class Cloud {
64
65
  }
65
66
  }
66
67
 
67
- class Client {
68
+ schema Client {
68
69
  refer isref Service
69
70
 
70
71
  procedure redirect (s isref Service) {
@@ -75,3 +76,5 @@ class Client {
75
76
  }
76
77
  }
77
78
  }
79
+
80
+
@@ -9,8 +9,8 @@ schema S {
9
9
  }
10
10
  }
11
11
 
12
- a extends S
13
- b extends S
12
+ a isa S
13
+ b isa S
14
14
 
15
15
  constraint goal {
16
16
  a.foo is false
@@ -1,4 +1,4 @@
1
- include "service-classes.sfp"
1
+ include "service-schemas.sfp"
2
2
 
3
3
  // generate all possible states with constraint solver
4
4
  initial state {
@@ -1,4 +1,4 @@
1
- include "service-classes.sfp"
1
+ include "service-schemas.sfp"
2
2
 
3
3
  // generate all possible states with constraint solver
4
4
  initial state {
@@ -1,4 +1,4 @@
1
- include "service-classes.sfp"
1
+ include "service-schemas.sfp"
2
2
 
3
3
  // generate all possible states with constraint solver
4
4
  initial state {
@@ -1,5 +1,4 @@
1
- //include "cloud-classes.sfp"
2
- include "service-classes.sfp"
1
+ include "service-schemas.sfp"
3
2
 
4
3
  initial state {
5
4
  s1 isa Service {
@@ -1,4 +1,4 @@
1
- include "service-classes.sfp"
1
+ include "service-schemas.sfp"
2
2
 
3
3
  // generate all possible states with constraint solver
4
4
  initial state {
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+
3
+ BASEDIR="$(dirname "$0")"
4
+ CURRENTDIR=`pwd`
5
+
6
+ # a function to check the execution status
7
+ function test {
8
+ "$@" 1>/dev/null
9
+ status=$?
10
+ if [ $status -ne 0 ]; then
11
+ echo "$2 [Failed]"
12
+ else
13
+ echo "$2 [OK]"
14
+ fi
15
+ return $status
16
+ }
17
+
18
+ # go to test's base dir
19
+ cd $BASEDIR
20
+
21
+ ##
22
+ # Tests use deterministic problem
23
+ ##
24
+ testcases[0]="test1.sfp"
25
+ testcases[1]="test2.sfp"
26
+ testcases[2]="test3.sfp"
27
+ testcases[3]="test4.sfp"
28
+
29
+ echo "Deterministic usecases..."
30
+ for tc in "${testcases[@]}"
31
+ do
32
+ test ../bin/sfp $tc
33
+ done
34
+
35
+ ##
36
+ # Tests use non-deterministic problems
37
+ ##
38
+ testcases[0]="nd-service1.sfp"
39
+ testcases[1]="nd-service2.sfp"
40
+ testcases[2]="nd-cloud1.sfp"
41
+ testcases[3]="nd-cloud2.sfp"
42
+ #testcases[4]="nd-cloud3.sfp"
43
+
44
+ echo "Non-Deterministic usecases..."
45
+ for tc in "${testcases[@]}"
46
+ do
47
+ test ../bin/sfp $tc
48
+ done
49
+
50
+ ##
51
+ # Back to previous directory
52
+ ##
53
+ cd $CURRENTDIR