sfpagent 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sfpagent might be problematic. Click here for more details.
- data/README.md +2 -1
- data/VERSION +1 -0
- data/bin/sfpagent +3 -3
- data/lib/sfpagent/bsig.rb +22 -0
- data/lib/sfpagent/module.rb +2 -2
- data/lib/sfpagent/runtime.rb +18 -32
- data/lib/sfpagent.rb +3 -1
- data/sfpagent.gemspec +2 -2
- metadata +6 -5
- data/lib/sfpagent/executor.rb +0 -207
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
SFP Agent for Ruby
|
2
2
|
==================
|
3
3
|
- Author: Herry (herry13@gmail.com)
|
4
|
-
- Version
|
4
|
+
- [Version](https://github.com/herry13/sfpagent/blob/master/VERSION)
|
5
5
|
- License: [BSD License](https://github.com/herry13/sfpagent/blob/master/LICENSE)
|
6
6
|
|
7
7
|
A Ruby script and API of an SFP agent. The agent could be accessed through HTTP RESTful API.
|
@@ -23,6 +23,7 @@ Requirements
|
|
23
23
|
To install
|
24
24
|
----------
|
25
25
|
|
26
|
+
$ apt-get install git make gcc ruby1.9.1 ruby1.9.1-dev libz-dev libaugeas-ruby1.9.1 libxml2-dev libxslt-dev
|
26
27
|
$ gem install sfpagent
|
27
28
|
|
28
29
|
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.5
|
data/bin/sfpagent
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
require "#{
|
3
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
4
|
+
require "#{dir}/../lib/sfpagent"
|
5
5
|
|
6
6
|
opts = Trollop::options do
|
7
|
-
version "sfpagent
|
7
|
+
version "sfpagent " + File.read("#{dir}/../VERSION").sub(/\n/, '') + " (c) 2013 Herry"
|
8
8
|
banner <<-EOS
|
9
9
|
SFP Agent that provides a Ruby framework for managing system configurations. The configurations are modelled in SFP language.
|
10
10
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sfp::BSig
|
2
|
+
end
|
3
|
+
|
4
|
+
module Sfp::BSig::Main
|
5
|
+
def execute_model
|
6
|
+
# TODO -- implement
|
7
|
+
end
|
8
|
+
|
9
|
+
def achieve_local_goals(version, goals, operators, pi)
|
10
|
+
# TODO -- implement
|
11
|
+
end
|
12
|
+
|
13
|
+
def achieve_remote_goals(version, goals, pi)
|
14
|
+
# TODO -- implement
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Sfp::BSig::Satisfier
|
19
|
+
def receive_goals(agent, version, goals, pi)
|
20
|
+
# TODO -- implement
|
21
|
+
end
|
22
|
+
end
|
data/lib/sfpagent/module.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# predefined methods: update_state, apply, reset, resolve
|
3
3
|
#
|
4
4
|
module Sfp::Resource
|
5
|
-
attr_accessor :parent
|
5
|
+
attr_accessor :parent, :synchronized
|
6
6
|
attr_reader :state, :model
|
7
7
|
|
8
8
|
def init(model, default)
|
@@ -10,7 +10,7 @@ module Sfp::Resource
|
|
10
10
|
model.each { |k,v| @model[k] = v }
|
11
11
|
@state = {}
|
12
12
|
@default = {}
|
13
|
-
|
13
|
+
@synchronized = []
|
14
14
|
end
|
15
15
|
|
16
16
|
def update_state
|
data/lib/sfpagent/runtime.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
1
3
|
class Sfp::Runtime
|
2
4
|
attr_reader :modules
|
3
5
|
|
4
6
|
def initialize(parser)
|
7
|
+
@mutex_procedure = Mutex.new
|
5
8
|
@parser = parser
|
6
9
|
@root = @parser.root
|
7
10
|
@modules = nil
|
@@ -22,7 +25,13 @@ class Sfp::Runtime
|
|
22
25
|
raise Exception, "Cannot execute #{action['name']}!" if not mod.respond_to?(method_name)
|
23
26
|
|
24
27
|
params = normalise_parameters(action['parameters'])
|
25
|
-
mod.
|
28
|
+
if mod.synchronized.rindex(method_name)
|
29
|
+
@mutex_procedure.synchronize { mod.send method_name.to_sym, params }
|
30
|
+
else
|
31
|
+
mod.send method_name.to_sym, params
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO - check post-execution state for verification
|
26
35
|
end
|
27
36
|
|
28
37
|
def get_state(as_sfp=false)
|
@@ -54,6 +63,14 @@ class Sfp::Runtime
|
|
54
63
|
default = cleanup(root.at?(model['_isa']))
|
55
64
|
ruby_model = cleanup(model)
|
56
65
|
mod.init(ruby_model, default)
|
66
|
+
|
67
|
+
# update synchronized list of procedures
|
68
|
+
model.each { |k,v|
|
69
|
+
next if k[0,1] == '_' or not (v.is_a?(Hash) and v['_context'] == 'procedure')
|
70
|
+
mod.synchronized << k if v['_synchronized']
|
71
|
+
}
|
72
|
+
|
73
|
+
# return the object instant
|
57
74
|
mod
|
58
75
|
end
|
59
76
|
|
@@ -113,42 +130,11 @@ class Sfp::Runtime
|
|
113
130
|
state
|
114
131
|
end
|
115
132
|
|
116
|
-
def execute_plan(plan)
|
117
|
-
plan = JSON[plan]
|
118
|
-
if plan['type'] == 'sequential'
|
119
|
-
execute_sequential_plan(plan)
|
120
|
-
else
|
121
|
-
raise Exception, "Not implemented yet!"
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
133
|
protected
|
126
|
-
class SfpState
|
127
|
-
def visit(name, value, parent)
|
128
|
-
parent.delete(name) if name[0,1] == '_' or
|
129
|
-
(value.is_a?(Hash) and value['_context'] != 'object')
|
130
|
-
true
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
134
|
ParentGenerator = Object.new
|
135
135
|
def ParentGenerator.visit(name, value, parent)
|
136
136
|
value['_parent'] = parent if value.is_a?(Hash)
|
137
137
|
true
|
138
138
|
end
|
139
139
|
|
140
|
-
def execute_sequential_plan(plan)
|
141
|
-
puts 'Execute a sequential plan...'
|
142
|
-
|
143
|
-
plan['workflow'].each_index { |index|
|
144
|
-
action = plan['workflow'][index]
|
145
|
-
print "#{index+1}) #{action['name']} "
|
146
|
-
if not execute_action(action)
|
147
|
-
puts '[Failed]'
|
148
|
-
return false
|
149
|
-
end
|
150
|
-
puts '[OK]'
|
151
|
-
}
|
152
|
-
true
|
153
|
-
end
|
154
140
|
end
|
data/lib/sfpagent.rb
CHANGED
@@ -6,11 +6,13 @@ require 'sfp'
|
|
6
6
|
module Nuri
|
7
7
|
end
|
8
8
|
|
9
|
+
module Sfp
|
10
|
+
end
|
11
|
+
|
9
12
|
# internal dependencies
|
10
13
|
libdir = File.expand_path(File.dirname(__FILE__))
|
11
14
|
|
12
15
|
require libdir + '/sfpagent/net_helper.rb'
|
13
|
-
require libdir + '/sfpagent/executor.rb'
|
14
16
|
require libdir + '/sfpagent/runtime.rb'
|
15
17
|
require libdir + '/sfpagent/module.rb'
|
16
18
|
require libdir + '/sfpagent/agent.rb'
|
data/sfpagent.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'sfpagent'
|
3
|
-
s.version =
|
3
|
+
s.version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).sub(/\n/, '')
|
4
4
|
s.date = '2013-08-13'
|
5
5
|
s.summary = 'SFP Agent'
|
6
6
|
s.description = 'A Ruby implementation of SFP agent.'
|
@@ -16,5 +16,5 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.homepage = 'https://github.com/herry13/sfpagent'
|
17
17
|
s.rubyforge_project = 'sfpagent'
|
18
18
|
|
19
|
-
s.add_dependency 'sfp', '~> 0.3.
|
19
|
+
s.add_dependency 'sfp', '~> 0.3.9'
|
20
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sfpagent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,15 +13,15 @@ date: 2013-08-13 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sfp
|
16
|
-
requirement: &
|
16
|
+
requirement: &20504460 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.3.
|
21
|
+
version: 0.3.9
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *20504460
|
25
25
|
description: A Ruby implementation of SFP agent.
|
26
26
|
email: herry13@gmail.com
|
27
27
|
executables:
|
@@ -32,11 +32,12 @@ files:
|
|
32
32
|
- .gitignore
|
33
33
|
- LICENSE
|
34
34
|
- README.md
|
35
|
+
- VERSION
|
35
36
|
- bin/cert.rb
|
36
37
|
- bin/sfpagent
|
37
38
|
- lib/sfpagent.rb
|
38
39
|
- lib/sfpagent/agent.rb
|
39
|
-
- lib/sfpagent/
|
40
|
+
- lib/sfpagent/bsig.rb
|
40
41
|
- lib/sfpagent/module.rb
|
41
42
|
- lib/sfpagent/net_helper.rb
|
42
43
|
- lib/sfpagent/runtime.rb
|
data/lib/sfpagent/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
|