sfp 0.2.1 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +28 -76
- data/bin/sfp +12 -78
- data/lib/sfp/SfpLangLexer.rb +608 -473
- data/lib/sfp/SfpLangParser.rb +4571 -4371
- data/lib/sfp/Sfplib.rb +30 -20
- data/lib/sfp/parser.rb +14 -6
- data/lib/sfp/sas_translator.rb +3 -1
- data/lib/sfp.rb +0 -5
- data/sfp.gemspec +6 -12
- data/src/SfpLang.g +82 -13
- metadata +22 -57
- data/bin/solver/linux-x86/downward +0 -0
- data/bin/solver/linux-x86/preprocess +0 -0
- data/bin/solver/macos/downward +0 -0
- data/bin/solver/macos/preprocess +0 -0
- data/lib/sfp/executor.rb +0 -207
- data/lib/sfp/planner.rb +0 -482
- data/lib/sfp/sas.rb +0 -966
- data/test/cloud-schemas.sfp +0 -80
- data/test/future/test1.sfp +0 -22
- data/test/nd-cloud1.sfp +0 -33
- data/test/nd-cloud2.sfp +0 -41
- data/test/nd-cloud3.sfp +0 -42
- data/test/nd-service1.sfp +0 -23
- data/test/nd-service2.sfp +0 -27
- data/test/run.sh +0 -53
- data/test/s.sfp +0 -14
- data/test/service-schemas.sfp +0 -151
- data/test/test-module1-scripts.tgz +0 -0
- data/test/test-module1.sfp +0 -20
- data/test/test1.inc +0 -9
- data/test/test1.sfp +0 -13
- data/test/test2.sfp +0 -20
- data/test/test3.inc +0 -40
- data/test/test3.sfp +0 -17
- data/test/test4.inc +0 -28
- data/test/test4.sfp +0 -22
data/lib/sfp/Sfplib.rb
CHANGED
@@ -82,6 +82,13 @@ module Sfp
|
|
82
82
|
c.inherits( sclass )
|
83
83
|
c['_super'] = (sclass.has_key?('_super') ? sclass['_super'].clone : Array.new)
|
84
84
|
c['_super'] << c['_extends']
|
85
|
+
if sclass['_finals'].is_a?(Array)
|
86
|
+
if c['_finals'].is_a?(Array)
|
87
|
+
c['_finals'].concat(sclass['_finals'])
|
88
|
+
else
|
89
|
+
c['_finals'] = sclass['_finals']
|
90
|
+
end
|
91
|
+
end
|
85
92
|
}
|
86
93
|
end
|
87
94
|
|
@@ -91,22 +98,7 @@ module Sfp
|
|
91
98
|
end
|
92
99
|
|
93
100
|
def deep_clone(value)
|
94
|
-
|
95
|
-
result = value.clone
|
96
|
-
value.each { |k,v|
|
97
|
-
if k != '_parent'
|
98
|
-
result[k] = deep_clone(v)
|
99
|
-
result[k]['_parent'] = result if result[k].is_a?(Hash) and result[k].has_key?('_parent')
|
100
|
-
end
|
101
|
-
}
|
102
|
-
result
|
103
|
-
elsif value.is_a?(Array)
|
104
|
-
result = Array.new
|
105
|
-
value.each { |v| result << deep_clone(v) }
|
106
|
-
result
|
107
|
-
else
|
108
|
-
value
|
109
|
-
end
|
101
|
+
Sfp::Helper.deep_clone(value)
|
110
102
|
end
|
111
103
|
end
|
112
104
|
|
@@ -147,11 +139,20 @@ module Sfp
|
|
147
139
|
not obj.has_key?('_isa') or obj['_isa'] == nil
|
148
140
|
objclass = root.at?(obj['_isa'])
|
149
141
|
if objclass.nil? or objclass.is_a?(Sfp::Unknown) or objclass.is_a?(Sfp::Undefined)
|
150
|
-
raise Exception,
|
142
|
+
raise Exception, "Schema #{obj['_isa']} of object #{obj['_self']} is not found!"
|
151
143
|
end
|
152
144
|
obj.inherits( objclass )
|
153
145
|
obj['_classes'] = (objclass.has_key?('_super') ? objclass['_super'].clone : Array.new)
|
154
146
|
obj['_classes'] << obj['_isa']
|
147
|
+
|
148
|
+
if objclass['_finals'].is_a?(Array)
|
149
|
+
if obj['_finals'].is_a?(Array)
|
150
|
+
obj['_finals'].concat(objclass['_finals'])
|
151
|
+
else
|
152
|
+
obj['_finals'] = objclass['_finals']
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
155
156
|
return true
|
156
157
|
end
|
157
158
|
end
|
@@ -163,9 +164,14 @@ module Sfp
|
|
163
164
|
|
164
165
|
# Instance of this class will be returned as the value of a non-exist variable
|
165
166
|
class Undefined
|
166
|
-
attr_accessor :path
|
167
|
-
def initialize(path=nil
|
168
|
-
|
167
|
+
attr_accessor :path, :type
|
168
|
+
def initialize(path=nil, type=nil)
|
169
|
+
@path = path
|
170
|
+
@type = type
|
171
|
+
end
|
172
|
+
def to_s
|
173
|
+
(@path.nil? ? "<sfp::undefined>" : "<sfp::undefined[#{@path}]>")
|
174
|
+
end
|
169
175
|
end
|
170
176
|
|
171
177
|
# Instance of this class will be return as the value of an unknown variable
|
@@ -175,6 +181,10 @@ module Sfp
|
|
175
181
|
def initialize(path=nil); @path = path; end
|
176
182
|
def to_s; (@path.nil? ? "<sfp::unknown>" : "<sfp::unknown[#{@path}]>"); end
|
177
183
|
end
|
184
|
+
|
185
|
+
class Any
|
186
|
+
def to_s; '<sfp::any>'; end
|
187
|
+
end
|
178
188
|
end
|
179
189
|
|
180
190
|
# return a fullpath of reference of this context
|
data/lib/sfp/parser.rb
CHANGED
@@ -6,6 +6,7 @@ module Sfp
|
|
6
6
|
include Sfp::SasTranslator
|
7
7
|
|
8
8
|
attr_accessor :root_dir, :home_dir, :conformant
|
9
|
+
attr_reader :root
|
9
10
|
|
10
11
|
def initialize(params={})
|
11
12
|
@root_dir = (params[:root_dir].is_a?(String) ?
|
@@ -31,6 +32,19 @@ module Sfp
|
|
31
32
|
@parser_arrays = parser.arrays
|
32
33
|
end
|
33
34
|
|
35
|
+
def to_json(params={})
|
36
|
+
return 'null' if @root.nil?
|
37
|
+
return Sfp::Helper.to_pretty_json(@root) if params[:pretty]
|
38
|
+
return Sfp::Helper.to_json(@root)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse_file(filepath)
|
42
|
+
homedir = File.expand_path(File.dirname(filepath))
|
43
|
+
parser = Sfp::Parser.new({:home_dir => homedir})
|
44
|
+
parser.parse(File.read(filepath))
|
45
|
+
parser.root
|
46
|
+
end
|
47
|
+
|
34
48
|
=begin
|
35
49
|
# Parse SFP file and return its JSON representation
|
36
50
|
def self.parse_file(file)
|
@@ -103,12 +117,6 @@ module Sfp
|
|
103
117
|
return Nuri::Sfp.to_json(root)
|
104
118
|
end
|
105
119
|
=end
|
106
|
-
|
107
|
-
def to_json(params={})
|
108
|
-
return '' if @root.nil?
|
109
|
-
return Sfp::Helper.to_pretty_json(@root) if params[:pretty]
|
110
|
-
return Sfp::Helper.to_json(@root)
|
111
|
-
end
|
112
120
|
end
|
113
121
|
|
114
122
|
=begin
|
data/lib/sfp/sas_translator.rb
CHANGED
@@ -1346,7 +1346,7 @@ module Sfp
|
|
1346
1346
|
value = @init.at?(value) if isref
|
1347
1347
|
type = (isfinal ? self.isa?(value) : self.get_type(name, value, parent))
|
1348
1348
|
if type == nil
|
1349
|
-
raise Exception, "Unrecognized type of variable: #{var_name}"
|
1349
|
+
raise Exception, "Unrecognized type of variable: #{var_name}:#{value.class}"
|
1350
1350
|
else
|
1351
1351
|
value = null_value(type) if value == nil
|
1352
1352
|
isset = true if type[0,1] == '('
|
@@ -1375,6 +1375,8 @@ module Sfp
|
|
1375
1375
|
return type if type != nil
|
1376
1376
|
end
|
1377
1377
|
=end
|
1378
|
+
return value.type if value.is_a?(Sfp::Undefined)
|
1379
|
+
|
1378
1380
|
type = nil
|
1379
1381
|
if parent.has_key?('_isa')
|
1380
1382
|
isa = @main.root.at?(parent['_isa'])
|
data/lib/sfp.rb
CHANGED
data/sfp.gemspec
CHANGED
@@ -1,23 +1,17 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'sfp'
|
3
|
-
s.version = '0.
|
4
|
-
s.date = '2013-
|
5
|
-
s.summary = 'SFP Parser
|
6
|
-
s.description = 'A Ruby
|
7
|
-
'SFP planner. It also provides a planner sript to solve a planning problem ' +
|
8
|
-
'written in SFP language.'
|
3
|
+
s.version = '0.3.4'
|
4
|
+
s.date = '2013-07-14'
|
5
|
+
s.summary = 'SFP Parser'
|
6
|
+
s.description = 'A Ruby API and script for SFP language parser'
|
9
7
|
s.authors = ['Herry']
|
10
8
|
s.email = 'herry13@gmail.com'
|
11
9
|
|
12
10
|
s.executables << 'sfp'
|
13
|
-
s.files = `git ls-files`.split("\n")
|
14
|
-
s.test_files = `git ls-files -- test/*`.split("\n")
|
15
|
-
|
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 }
|
11
|
+
s.files = `git ls-files`.split("\n").select { |n| !(n =~ /^test\/.*/) }
|
19
12
|
|
20
13
|
s.require_paths = ['lib']
|
14
|
+
s.license = 'BSD'
|
21
15
|
|
22
16
|
s.homepage = 'https://github.com/herry13/sfp-ruby'
|
23
17
|
s.rubyforge_project = 'sfp'
|
data/src/SfpLang.g
CHANGED
@@ -40,11 +40,11 @@ sfp
|
|
40
40
|
: { self.init }
|
41
41
|
NL* include* header*
|
42
42
|
{ self.expand_classes }
|
43
|
-
(
|
43
|
+
(object_def NL* | state | constraint | goal_constraint | composite)*
|
44
44
|
;
|
45
45
|
|
46
46
|
|
47
|
-
mmutation
|
47
|
+
/*mmutation
|
48
48
|
: reference equals_op value NL+
|
49
49
|
{
|
50
50
|
path, var = $reference.val.extract
|
@@ -60,6 +60,7 @@ mmutation
|
|
60
60
|
parent[var] = self.null_value
|
61
61
|
}
|
62
62
|
;
|
63
|
+
*/
|
63
64
|
|
64
65
|
include
|
65
66
|
: 'include' include_file NL+
|
@@ -136,6 +137,17 @@ extends_class returns [val]
|
|
136
137
|
;
|
137
138
|
|
138
139
|
attribute
|
140
|
+
: {
|
141
|
+
@is_final = false
|
142
|
+
@now['_finals'] = [] if !@now.has_key? '_finals'
|
143
|
+
}
|
144
|
+
('final' { @is_final = true })? attribute_stmt
|
145
|
+
{
|
146
|
+
@now['_finals'] << $attribute_stmt.id if @is_final and !$attribute_stmt.id.nil?
|
147
|
+
}
|
148
|
+
;
|
149
|
+
|
150
|
+
attribute_stmt returns [id]
|
139
151
|
: ID equals_op value NL+
|
140
152
|
{
|
141
153
|
if @now.has_key?($ID.text) and @now[$ID.text].is_a?(Hash) and
|
@@ -144,11 +156,18 @@ attribute
|
|
144
156
|
else
|
145
157
|
@now[$ID.text] = $value.val
|
146
158
|
end
|
159
|
+
$id = $ID.text
|
147
160
|
}
|
148
161
|
| ID reference_type NL+
|
149
|
-
{
|
162
|
+
{
|
163
|
+
@now[$ID.text] = $reference_type.val
|
164
|
+
$id = $ID.text
|
165
|
+
}
|
150
166
|
| ID set_type NL+
|
151
|
-
{
|
167
|
+
{
|
168
|
+
@now[$ID.text] = $set_type.val
|
169
|
+
$id = $ID.text
|
170
|
+
}
|
152
171
|
| ID probability_op set_value NL+
|
153
172
|
{
|
154
173
|
@conformant = true
|
@@ -157,27 +176,72 @@ attribute
|
|
157
176
|
'_parent' => @now,
|
158
177
|
'_values' => $set_value.val
|
159
178
|
}
|
179
|
+
$id = $ID.text
|
180
|
+
}
|
181
|
+
| ID ':' path NL+
|
182
|
+
{
|
183
|
+
case $path.text
|
184
|
+
when 'String'
|
185
|
+
@now[$ID.text] = { '_context' => 'any_value',
|
186
|
+
'_isa' => '$.String'
|
187
|
+
}
|
188
|
+
when 'Bool'
|
189
|
+
@now[$ID.text] = { '_context' => 'any_value',
|
190
|
+
'_isa' => '$.Boolean'
|
191
|
+
}
|
192
|
+
when 'Int'
|
193
|
+
@now[$ID.text] = { '_context' => 'any_value',
|
194
|
+
'_isa' => '$.Number'
|
195
|
+
}
|
196
|
+
else
|
197
|
+
raise Exception, "Use isa/isref for any non-primitive type (#{$path.text})."
|
198
|
+
end
|
199
|
+
$id = $ID.text
|
160
200
|
}
|
161
201
|
| object_def NL+
|
202
|
+
{ $id = nil }
|
203
|
+
;
|
204
|
+
|
205
|
+
object_schema
|
206
|
+
: path('[' NUMBER { @now['_is_array'] = true } ']')?
|
207
|
+
{
|
208
|
+
@now['_isa'] = self.to_ref($path.text)
|
209
|
+
self.expand_object(@now)
|
210
|
+
}
|
211
|
+
;
|
212
|
+
|
213
|
+
object_schemata
|
214
|
+
: ',' object_schema
|
162
215
|
;
|
163
216
|
|
164
217
|
object_def
|
165
|
-
:
|
218
|
+
: { @use_template = false }
|
219
|
+
ID
|
220
|
+
('extends' path
|
221
|
+
{
|
222
|
+
template = @root.at?($path.text)
|
223
|
+
raise Exception, "Object template #{$path.text} is not found!" if
|
224
|
+
template.is_a?(Sfp::Unknown) or template.is_a?(Sfp::Undefined)
|
225
|
+
raise Exception, "#{$path.text} is not an object!" if
|
226
|
+
!template.is_a?(Hash) or template['_context'] != 'object'
|
227
|
+
@now[$ID.text] = Sfp::Helper.deep_clone(template)
|
228
|
+
@now[$ID.text].accept(Sfp::Visitor::ParentEliminator.new)
|
229
|
+
@now[$ID.text]['_parent'] = @now
|
230
|
+
@now[$ID.text]['_self'] = $ID.text
|
231
|
+
@now[$ID.text].accept(Sfp::Visitor::SfpGenerator.new(@root))
|
232
|
+
@use_template = true
|
233
|
+
}
|
234
|
+
)?
|
166
235
|
{
|
167
236
|
@now[$ID.text] = { '_self' => $ID.text,
|
168
237
|
'_context' => 'object',
|
169
238
|
'_parent' => @now,
|
170
239
|
'_isa' => '$.Object'
|
171
|
-
}
|
240
|
+
} if not @use_template
|
172
241
|
@now = @now[$ID.text]
|
173
242
|
@now['_is_array'] = false
|
174
243
|
}
|
175
|
-
('isa'
|
176
|
-
{
|
177
|
-
@now['_isa'] = self.to_ref($path.text)
|
178
|
-
self.expand_object(@now)
|
179
|
-
}
|
180
|
-
)?
|
244
|
+
('isa' object_schema (object_schemata)* )?
|
181
245
|
object_body?
|
182
246
|
{
|
183
247
|
if @now['_is_array']
|
@@ -280,7 +344,7 @@ op_statement
|
|
280
344
|
;
|
281
345
|
|
282
346
|
procedure
|
283
|
-
: 'procedure' ID
|
347
|
+
: ('procedure'|'sub') ID
|
284
348
|
{
|
285
349
|
@now[$ID.text] = { '_self' => $ID.text,
|
286
350
|
'_context' => 'procedure',
|
@@ -862,6 +926,11 @@ value returns [val, type]
|
|
862
926
|
$val = $set_value.val
|
863
927
|
$type = 'Set'
|
864
928
|
}
|
929
|
+
| 'any'
|
930
|
+
{
|
931
|
+
$val = Sfp::Any.new
|
932
|
+
$type = 'Any'
|
933
|
+
}
|
865
934
|
;
|
866
935
|
|
867
936
|
primitive_value returns [val, type]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sfp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-07-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 1.7.5
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.7.5
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: antlr3
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ~>
|
@@ -32,9 +37,13 @@ dependencies:
|
|
32
37
|
version: 1.8.12
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
36
|
-
|
37
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.8.12
|
46
|
+
description: A Ruby API and script for SFP language parser
|
38
47
|
email: herry13@gmail.com
|
39
48
|
executables:
|
40
49
|
- sfp
|
@@ -45,18 +54,11 @@ files:
|
|
45
54
|
- LICENSE
|
46
55
|
- README.md
|
47
56
|
- bin/sfp
|
48
|
-
- bin/solver/linux-x86/downward
|
49
|
-
- bin/solver/linux-x86/preprocess
|
50
|
-
- bin/solver/macos/downward
|
51
|
-
- bin/solver/macos/preprocess
|
52
57
|
- lib/sfp.rb
|
53
58
|
- lib/sfp/SfpLangLexer.rb
|
54
59
|
- lib/sfp/SfpLangParser.rb
|
55
60
|
- lib/sfp/Sfplib.rb
|
56
|
-
- lib/sfp/executor.rb
|
57
61
|
- lib/sfp/parser.rb
|
58
|
-
- lib/sfp/planner.rb
|
59
|
-
- lib/sfp/sas.rb
|
60
62
|
- lib/sfp/sas_translator.rb
|
61
63
|
- lib/sfp/sfw2graph.rb
|
62
64
|
- lib/sfp/trollop.rb
|
@@ -65,27 +67,9 @@ files:
|
|
65
67
|
- sfp.gemspec
|
66
68
|
- src/SfpLang.g
|
67
69
|
- src/build.sh
|
68
|
-
- test/cloud-schemas.sfp
|
69
|
-
- test/future/test1.sfp
|
70
|
-
- test/nd-cloud1.sfp
|
71
|
-
- test/nd-cloud2.sfp
|
72
|
-
- test/nd-cloud3.sfp
|
73
|
-
- test/nd-service1.sfp
|
74
|
-
- test/nd-service2.sfp
|
75
|
-
- test/run.sh
|
76
|
-
- test/s.sfp
|
77
|
-
- test/service-schemas.sfp
|
78
|
-
- test/test-module1-scripts.tgz
|
79
|
-
- test/test-module1.sfp
|
80
|
-
- test/test1.inc
|
81
|
-
- test/test1.sfp
|
82
|
-
- test/test2.sfp
|
83
|
-
- test/test3.inc
|
84
|
-
- test/test3.sfp
|
85
|
-
- test/test4.inc
|
86
|
-
- test/test4.sfp
|
87
70
|
homepage: https://github.com/herry13/sfp-ruby
|
88
|
-
licenses:
|
71
|
+
licenses:
|
72
|
+
- BSD
|
89
73
|
post_install_message:
|
90
74
|
rdoc_options: []
|
91
75
|
require_paths:
|
@@ -104,27 +88,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
88
|
version: '0'
|
105
89
|
requirements: []
|
106
90
|
rubyforge_project: sfp
|
107
|
-
rubygems_version: 1.8.
|
91
|
+
rubygems_version: 1.8.23
|
108
92
|
signing_key:
|
109
93
|
specification_version: 3
|
110
|
-
summary: SFP Parser
|
111
|
-
test_files:
|
112
|
-
- test/cloud-schemas.sfp
|
113
|
-
- test/future/test1.sfp
|
114
|
-
- test/nd-cloud1.sfp
|
115
|
-
- test/nd-cloud2.sfp
|
116
|
-
- test/nd-cloud3.sfp
|
117
|
-
- test/nd-service1.sfp
|
118
|
-
- test/nd-service2.sfp
|
119
|
-
- test/run.sh
|
120
|
-
- test/s.sfp
|
121
|
-
- test/service-schemas.sfp
|
122
|
-
- test/test-module1-scripts.tgz
|
123
|
-
- test/test-module1.sfp
|
124
|
-
- test/test1.inc
|
125
|
-
- test/test1.sfp
|
126
|
-
- test/test2.sfp
|
127
|
-
- test/test3.inc
|
128
|
-
- test/test3.sfp
|
129
|
-
- test/test4.inc
|
130
|
-
- test/test4.sfp
|
94
|
+
summary: SFP Parser
|
95
|
+
test_files: []
|
Binary file
|
Binary file
|
data/bin/solver/macos/downward
DELETED
Binary file
|
data/bin/solver/macos/preprocess
DELETED
Binary file
|
data/lib/sfp/executor.rb
DELETED
@@ -1,207 +0,0 @@
|
|
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
|