sfp 0.1.1 → 0.1.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.
- data/.gitignore +1 -0
- data/README.md +60 -20
- data/bin/sfp +23 -3
- data/bin/solver/linux-arm/downward +0 -0
- data/bin/solver/linux-arm/preprocess +0 -0
- data/bin/solver/{linux → linux-x86}/downward +0 -0
- data/bin/solver/{linux → linux-x86}/preprocess +0 -0
- data/lib/sfp.rb +2 -0
- data/lib/sfp/SfpLangLexer.rb +415 -375
- data/lib/sfp/SfpLangParser.rb +921 -915
- data/lib/sfp/executor.rb +207 -0
- data/lib/sfp/planner.rb +27 -5
- data/sfp.gemspec +6 -5
- data/src/SfpLang.g +1 -1
- data/src/build.sh +7 -0
- data/test/{cloud-classes.sfp → cloud-schemas.sfp} +7 -4
- data/test/{v1.1.sfp → future/test1.sfp} +2 -2
- data/test/{cloud1.sfp → nd-cloud1.sfp} +1 -1
- data/test/{cloud2.sfp → nd-cloud2.sfp} +1 -1
- data/test/{cloud3.sfp → nd-cloud3.sfp} +1 -1
- data/test/{service1.sfp → nd-service1.sfp} +1 -2
- data/test/{service3.sfp → nd-service2.sfp} +1 -1
- data/test/run.sh +53 -0
- data/test/{service-classes.sfp → service-schemas.sfp} +6 -6
- data/test/test-module1-scripts.tgz +0 -0
- data/test/test-module1.sfp +20 -0
- data/test/{test.inc → test1.inc} +0 -0
- data/test/test1.sfp +3 -9
- data/test/test2.sfp +15 -12
- data/test/{test2.inc → test3.inc} +0 -0
- data/test/test3.sfp +17 -0
- data/test/{types.sfp → test4.inc} +2 -2
- data/test/{task.sfp → test4.sfp} +1 -1
- metadata +97 -69
- data/test/test.sfp +0 -13
data/lib/sfp/executor.rb
ADDED
@@ -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
|
data/lib/sfp/planner.rb
CHANGED
@@ -296,12 +296,19 @@ module Sfp
|
|
296
296
|
|
297
297
|
def self.path
|
298
298
|
os = `uname -s`.downcase.strip
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
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
|
-
|
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
|
data/sfp.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'sfp'
|
3
|
-
s.version = '0.1.
|
4
|
-
s.date = '2013-
|
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
|
-
|
17
|
-
|
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'
|
data/src/SfpLang.g
CHANGED
data/src/build.sh
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
+
|
data/test/run.sh
ADDED
@@ -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
|