sfp 0.2.1 → 0.3.4
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 +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
|