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.
@@ -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