sfpagent 0.1.13 → 0.1.14
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/VERSION +1 -1
- data/bin/install_module +63 -0
- data/bin/sfpagent +1 -2
- data/lib/sfpagent/agent.rb +245 -71
- data/lib/sfpagent/bsig.rb +196 -27
- data/lib/sfpagent/module.rb +6 -0
- data/lib/sfpagent/net_helper.rb +2 -2
- data/lib/sfpagent/sfplanner.rb +158 -26
- metadata +4 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.14
|
data/bin/install_module
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
DefaultPort=1314
|
4
|
+
|
5
|
+
# verify the arguments
|
6
|
+
if [[ "$1" == "" ]] || [[ "$2" == "" ]]; then
|
7
|
+
echo "Usage: install_module <address> [port] <module-name> ..."
|
8
|
+
exit 1
|
9
|
+
fi
|
10
|
+
|
11
|
+
# set the agent's address
|
12
|
+
address=$1
|
13
|
+
shift
|
14
|
+
|
15
|
+
# set the agent's port number
|
16
|
+
re='^[0-9]+$'
|
17
|
+
if [[ $1 =~ $re ]]; then
|
18
|
+
port=$1
|
19
|
+
shift
|
20
|
+
else
|
21
|
+
port=$DefaultPort
|
22
|
+
fi
|
23
|
+
|
24
|
+
# set a template command for sending the modules
|
25
|
+
cmd="curl -s -i -X PUT $address:$port/modules"
|
26
|
+
|
27
|
+
# for every module in the arguments:
|
28
|
+
# - archive the module's files to a temporary file
|
29
|
+
# - update the sending command by adding the module
|
30
|
+
# if the module is not exist then the program will
|
31
|
+
# set missing_module flag and then break from the loop
|
32
|
+
missing_module=0
|
33
|
+
for module in "$@" ; do
|
34
|
+
if [[ -d "$module" ]]; then
|
35
|
+
tar cvzhf /tmp/$module.tgz $module 1>/dev/null
|
36
|
+
cmd="$cmd -F $module=@/tmp/$module.tgz"
|
37
|
+
else
|
38
|
+
echo "Module $module is not exist!"
|
39
|
+
missing_module=`expr $missing_module + 1`
|
40
|
+
fi
|
41
|
+
done
|
42
|
+
|
43
|
+
if [[ $missing_module == 0 ]]; then
|
44
|
+
# execute the sending command there is no missing module
|
45
|
+
result=`$cmd`
|
46
|
+
re='.*HTTP/1\.1 200 OK.*'
|
47
|
+
if ! [[ $result =~ $re ]]; then
|
48
|
+
missing_module=`expr $missing_module + 1`
|
49
|
+
fi
|
50
|
+
fi
|
51
|
+
|
52
|
+
# delete temporary archive files
|
53
|
+
for module in "$@"; do
|
54
|
+
rm -f /tmp/$module.tgz
|
55
|
+
done
|
56
|
+
|
57
|
+
if [[ $missing_module == 0 ]]; then
|
58
|
+
echo "status: ok"
|
59
|
+
else
|
60
|
+
echo "status: failed"
|
61
|
+
fi
|
62
|
+
|
63
|
+
exit $missing_module
|
data/bin/sfpagent
CHANGED
data/lib/sfpagent/agent.rb
CHANGED
@@ -11,21 +11,23 @@ require 'digest/md5'
|
|
11
11
|
|
12
12
|
module Sfp
|
13
13
|
module Agent
|
14
|
-
NetHelper = Object.new.extend(
|
14
|
+
NetHelper = Object.new.extend(Sfp::Net::Helper)
|
15
15
|
|
16
|
-
|
17
|
-
Dir.mkdir(
|
16
|
+
CacheDir = (Process.euid == 0 ? '/var/sfpagent' : File.expand_path('~/.sfpagent'))
|
17
|
+
Dir.mkdir(CacheDir, 0700) if not File.exist?(CacheDir)
|
18
18
|
|
19
19
|
DefaultPort = 1314
|
20
20
|
|
21
|
-
PIDFile = "#{
|
22
|
-
LogFile = "#{
|
23
|
-
ModelFile = "#{
|
24
|
-
AgentsDataFile = "#{
|
21
|
+
PIDFile = "#{CacheDir}/sfpagent.pid"
|
22
|
+
LogFile = "#{CacheDir}/sfpagent.log"
|
23
|
+
ModelFile = "#{CacheDir}/sfpagent.model"
|
24
|
+
AgentsDataFile = "#{CacheDir}/sfpagent.agents"
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
CacheModelFile = "#{CacheDir}/cache.model"
|
27
|
+
|
28
|
+
BSigFile = "#{CacheDir}/bsig.model"
|
29
|
+
BSigPIDFile = "#{CacheDir}/bsig.pid"
|
30
|
+
BSigThreadsLockFile = "#{CacheDir}/bsig.threads.lock.#{Time.now.nsec}"
|
29
31
|
|
30
32
|
@@logger = WEBrick::Log.new(LogFile, WEBrick::BasicLog::INFO ||
|
31
33
|
WEBrick::BasicLog::ERROR ||
|
@@ -47,6 +49,10 @@ module Sfp
|
|
47
49
|
@@logger
|
48
50
|
end
|
49
51
|
|
52
|
+
def self.config
|
53
|
+
@@config
|
54
|
+
end
|
55
|
+
|
50
56
|
# Start the agent.
|
51
57
|
#
|
52
58
|
# options:
|
@@ -60,11 +66,11 @@ module Sfp
|
|
60
66
|
Sfp::Agent.logger.info "Starting SFP Agent daemons..."
|
61
67
|
puts "Starting SFP Agent daemons..."
|
62
68
|
|
63
|
-
Process.daemon
|
69
|
+
Process.daemon if p[:daemon]
|
64
70
|
|
65
71
|
begin
|
66
72
|
# check modules directory, and create it if it's not exist
|
67
|
-
p[:modules_dir] = File.expand_path(p[:modules_dir].to_s.strip != '' ? p[:modules_dir].to_s : "#{
|
73
|
+
p[:modules_dir] = File.expand_path(p[:modules_dir].to_s.strip != '' ? p[:modules_dir].to_s : "#{CacheDir}/modules")
|
68
74
|
Dir.mkdir(p[:modules_dir], 0700) if not File.exist?(p[:modules_dir])
|
69
75
|
@@config = p
|
70
76
|
|
@@ -97,6 +103,10 @@ module Sfp
|
|
97
103
|
trap(signal) {
|
98
104
|
Sfp::Agent.logger.info "Shutting down web server and BSig engine..."
|
99
105
|
bsig_engine.stop
|
106
|
+
loop do
|
107
|
+
break if bsig_engine.status == :stopped
|
108
|
+
sleep 1
|
109
|
+
end
|
100
110
|
server.shutdown
|
101
111
|
}
|
102
112
|
}
|
@@ -121,19 +131,23 @@ module Sfp
|
|
121
131
|
puts "Stopping SFP Agent with PID #{pid}..."
|
122
132
|
Process.kill 'HUP', pid
|
123
133
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
134
|
+
begin
|
135
|
+
sleep (Sfp::BSig::SleepTime + 0.25)
|
136
|
+
Process.kill 0, pid
|
137
|
+
Sfp::Agent.logger.info "SFP Agent daemon is still running."
|
138
|
+
puts "SFP Agent daemon is still running."
|
139
|
+
return false
|
140
|
+
rescue
|
141
|
+
Sfp::Agent.logger.info "SFP Agent daemon has stopped."
|
142
|
+
puts "SFP Agent daemon has stopped."
|
143
|
+
File.delete(PIDFile) if File.exist?(PIDFile)
|
144
|
+
end
|
131
145
|
rescue
|
132
146
|
puts "SFP Agent is not running."
|
147
|
+
File.delete(PIDFile) if File.exist?(PIDFile)
|
133
148
|
end
|
134
149
|
|
135
|
-
|
136
|
-
File.delete(PIDFile) if File.exist?(PIDFile)
|
150
|
+
true
|
137
151
|
end
|
138
152
|
|
139
153
|
# Print the status of the agent.
|
@@ -149,6 +163,39 @@ module Sfp
|
|
149
163
|
end
|
150
164
|
end
|
151
165
|
|
166
|
+
def self.get_cache_model(name)
|
167
|
+
model = JSON[File.read(CacheModelFile)]
|
168
|
+
(model.has_key?(name) ? model[name] : nil)
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.set_cache_model(p={})
|
172
|
+
File.open(CacheModelFile, File::RDWR|File::CREAT, 0600) do |f|
|
173
|
+
f.flock(File::LOCK_EX)
|
174
|
+
json = f.read
|
175
|
+
model = (json.length >= 2 ? JSON[json] : {})
|
176
|
+
|
177
|
+
if p[:name]
|
178
|
+
if p[:model]
|
179
|
+
model[p[:name]] = p[:model]
|
180
|
+
Sfp::Agent.logger.info "Setting cache model for #{p[:name]}..."
|
181
|
+
else
|
182
|
+
model.delete(p[:name]) if model.has_key?(p[:name])
|
183
|
+
Sfp::Agent.logger.info "Deleting cache model for #{p[:name]}..."
|
184
|
+
end
|
185
|
+
else
|
186
|
+
model = {}
|
187
|
+
Sfp::Agent.logger.info "Deleting all cache model..."
|
188
|
+
end
|
189
|
+
|
190
|
+
f.rewind
|
191
|
+
f.write(JSON.generate(model))
|
192
|
+
f.flush
|
193
|
+
f.truncate(f.pos)
|
194
|
+
end
|
195
|
+
|
196
|
+
true
|
197
|
+
end
|
198
|
+
|
152
199
|
# Save given model to cached file, and then reload the model.
|
153
200
|
#
|
154
201
|
def self.set_model(model)
|
@@ -342,7 +389,7 @@ module Sfp
|
|
342
389
|
Sfp::Agent.logger.info "Successfully loading #{counter} modules."
|
343
390
|
end
|
344
391
|
|
345
|
-
def self.
|
392
|
+
def self.get_sfp(module_name)
|
346
393
|
dir = @@config[:modules_dir]
|
347
394
|
|
348
395
|
filepath = "#{dir}/#{module_name}/#{module_name}.sfp"
|
@@ -372,6 +419,23 @@ module Sfp
|
|
372
419
|
data
|
373
420
|
end
|
374
421
|
|
422
|
+
# Push a list of modules to an agent using a script in $SFPAGENT_HOME/bin/install_module.
|
423
|
+
#
|
424
|
+
# parameters:
|
425
|
+
# :address => address of target agent
|
426
|
+
# :port => port of target agent
|
427
|
+
# :modules => an array of modules' name that will be pushed
|
428
|
+
#
|
429
|
+
def self.push_modules(p={})
|
430
|
+
fail "Incomplete parameters." if !p[:modules] or !p[:address] or !p[:port]
|
431
|
+
|
432
|
+
install_module = File.expand_path('../../../bin/install_module', __FILE__)
|
433
|
+
modules = p[:modules].join(' ')
|
434
|
+
cmd = "cd #{@@config[:modules_dir]}; #{install_module} #{p[:address]} #{p[:port]} #{modules}"
|
435
|
+
result = `#{cmd}`
|
436
|
+
(result =~ /status: ok/)
|
437
|
+
end
|
438
|
+
|
375
439
|
def self.uninstall_all_modules(p={})
|
376
440
|
return true if @@config[:modules_dir] == ''
|
377
441
|
if system("rm -rf #{@@config[:modules_dir]}/*")
|
@@ -397,8 +461,16 @@ module Sfp
|
|
397
461
|
result
|
398
462
|
end
|
399
463
|
|
400
|
-
def self.
|
401
|
-
return false if
|
464
|
+
def self.install_modules(modules)
|
465
|
+
modules.each { |name,data| return false if not install_module(name, data, false) }
|
466
|
+
|
467
|
+
load_modules(@@config)
|
468
|
+
|
469
|
+
true
|
470
|
+
end
|
471
|
+
|
472
|
+
def self.install_module(name, data, reload=true)
|
473
|
+
return false if @@config[:modules_dir].to_s == '' or data.nil?
|
402
474
|
|
403
475
|
if !File.directory? @@config[:modules_dir]
|
404
476
|
File.delete @@config[:modules_dir] if File.exist? @@config[:modules_dir]
|
@@ -422,11 +494,9 @@ module Sfp
|
|
422
494
|
end
|
423
495
|
system("cd #{module_dir}; rm data.tgz")
|
424
496
|
}
|
425
|
-
load_modules(@@config)
|
426
|
-
|
427
|
-
# rebuild the model
|
428
|
-
update_model({:rebuild => true})
|
429
497
|
|
498
|
+
load_modules(@@config) if reload
|
499
|
+
|
430
500
|
Sfp::Agent.logger.info "Installing module #{name} [OK]"
|
431
501
|
|
432
502
|
true
|
@@ -441,22 +511,52 @@ module Sfp
|
|
441
511
|
end
|
442
512
|
end
|
443
513
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
514
|
+
# parameter:
|
515
|
+
# :data => To delete an agent: { "agent_name" => nil }
|
516
|
+
# To add/modify an agent: { "agent_name" => { "sfpAddress" => "10.0.0.1", "sfpPort" => 1314 } }
|
517
|
+
#
|
518
|
+
def self.set_agents(data)
|
519
|
+
data.each { |name,agent|
|
520
|
+
return false if agent.is_a?(Hash) and (not agent['sfpAddress'].is_a?(String) or
|
521
|
+
agent['sfpAddress'].strip == '' or agent['sfpPort'].to_i <= 0)
|
522
|
+
}
|
523
|
+
|
524
|
+
updated = false
|
525
|
+
agents = nil
|
526
|
+
File.open(AgentsDataFile, File::RDWR|File::CREAT, 0644) { |f|
|
527
|
+
f.flock(File::LOCK_EX)
|
528
|
+
json = f.read
|
529
|
+
agents = (json == '' ? {} : JSON[json])
|
530
|
+
current_hash = agents.hash
|
531
|
+
data.each { |k,v|
|
532
|
+
if !agents.has_key?(k) or v.nil? or agents[k].hash != v.hash
|
533
|
+
agents[k] = v
|
534
|
+
end
|
535
|
+
}
|
536
|
+
agents.keys.each { |k| agents.delete(k) if agents[k].nil? }
|
537
|
+
|
538
|
+
if current_hash != agents.hash
|
539
|
+
updated = true
|
540
|
+
f.rewind
|
541
|
+
f.write(JSON.generate(agents))
|
542
|
+
f.flush
|
543
|
+
f.truncate(f.pos)
|
544
|
+
end
|
545
|
+
}
|
546
|
+
|
547
|
+
if updated # if updated then broadcast to other agents
|
548
|
+
http_data = {'agents' => JSON.generate(data)}
|
549
|
+
|
550
|
+
agents.each { |name,agent|
|
551
|
+
begin
|
552
|
+
code, _ = NetHelper.put_data(agent['sfpAddress'], agent['sfpPort'], '/agents', http_data, 5, 20)
|
553
|
+
raise Exception if code != '200'
|
554
|
+
rescue Exception => e
|
555
|
+
Sfp::Agent.logger.warn "Push agents list to #{agent['sfpAddress']}:#{agent['sfpPort']} [Failed]"
|
556
|
+
end
|
456
557
|
}
|
457
|
-
f.write(JSON.generate(buffer))
|
458
|
-
f.flush
|
459
558
|
end
|
559
|
+
|
460
560
|
true
|
461
561
|
end
|
462
562
|
|
@@ -479,15 +579,15 @@ module Sfp
|
|
479
579
|
# /pid => save daemon's PID to a file (only requested from localhost)
|
480
580
|
# /state => return the current state
|
481
581
|
# /model => return the current model
|
482
|
-
# /
|
582
|
+
# /sfp => return the SFP description of a module
|
483
583
|
# /modules => return a list of available modules
|
484
584
|
# /agents => return a list of agents database
|
485
585
|
# /log => return last 100 lines of log file
|
486
586
|
#
|
487
587
|
def do_GET(request, response)
|
488
588
|
status = 400
|
489
|
-
content_type
|
490
|
-
if not trusted(request
|
589
|
+
content_type = body = ''
|
590
|
+
if not trusted(request)
|
491
591
|
status = 403
|
492
592
|
else
|
493
593
|
path = (request.path[-1,1] == '/' ? request.path.chop : request.path)
|
@@ -509,11 +609,14 @@ module Sfp
|
|
509
609
|
elsif path == '/model'
|
510
610
|
status, content_type, body = get_model
|
511
611
|
|
612
|
+
elsif path =~ /\/model\/cache\/.+/
|
613
|
+
status, content_type, body = self.get_cache_model({:name => path[13, path.length-13]})
|
614
|
+
|
512
615
|
elsif path == '/bsig'
|
513
616
|
status, content_Type, body = get_bsig
|
514
617
|
|
515
|
-
elsif path =~ /^\/
|
516
|
-
status, content_type, body =
|
618
|
+
elsif path =~ /^\/sfp\/.+/
|
619
|
+
status, content_type, body = get_sfp({:module => path[10, path.length-10]})
|
517
620
|
|
518
621
|
elsif path == '/modules'
|
519
622
|
status, content_type, body = [200, 'application/json', JSON.generate(Sfp::Agent.get_modules)]
|
@@ -540,7 +643,7 @@ module Sfp
|
|
540
643
|
def do_POST(request, response)
|
541
644
|
status = 400
|
542
645
|
content_type, body = ''
|
543
|
-
if not self.trusted(request
|
646
|
+
if not self.trusted(request)
|
544
647
|
status = 403
|
545
648
|
else
|
546
649
|
path = (request.path[-1,1] == '/' ? ryyequest.path.chop : request.path)
|
@@ -557,36 +660,40 @@ module Sfp
|
|
557
660
|
# Handle HTTP Put request
|
558
661
|
#
|
559
662
|
# uri:
|
560
|
-
# /model => receive a new model and save
|
663
|
+
# /model => receive a new model and then save it
|
664
|
+
# /model/cache => receive a "cache" model and then save it
|
561
665
|
# /modules => save the module if parameter "module" is provided
|
562
|
-
# delete the module if parameter "module" is not provided
|
563
666
|
# /agents => save the agents' list if parameter "agents" is provided
|
564
|
-
# delete all agents if parameter "agents" is not provided
|
565
667
|
# /bsig => receive BSig model and receive it in cached directory
|
566
668
|
# /bsig/satisfier => receive goal request from other agents and then start
|
567
669
|
# a satisfier thread to try to achieve it
|
670
|
+
#
|
568
671
|
def do_PUT(request, response)
|
569
672
|
status = 400
|
570
|
-
content_type
|
571
|
-
if not self.trusted(request
|
673
|
+
content_type = body = ''
|
674
|
+
if not self.trusted(request)
|
572
675
|
status = 403
|
573
676
|
else
|
574
677
|
path = (request.path[-1,1] == '/' ? ryyequest.path.chop : request.path)
|
575
678
|
|
576
|
-
if path == '/model'
|
679
|
+
if path == '/model' and request.query.has_key?('model')
|
577
680
|
status, content_type, body = self.set_model({:query => request.query})
|
578
681
|
|
579
|
-
elsif path =~ /\/
|
682
|
+
elsif path =~ /\/model\/cache\/.+/ and request.query.length > 0
|
683
|
+
status, content_type, body = self.set_cache_model({:name => path[13, path.length-13],
|
684
|
+
:query => request.query})
|
685
|
+
|
686
|
+
elsif path =~ /\/modules\/.+/ and request.query.length > 0
|
580
687
|
status, content_type, body = self.manage_modules({:name => path[9, path.length-9],
|
581
688
|
:query => request.query})
|
582
689
|
|
583
|
-
elsif path == '/modules'
|
584
|
-
status, content_type, body = self.manage_modules({:
|
690
|
+
elsif path == '/modules' and request.query.length > 0
|
691
|
+
status, content_type, body = self.manage_modules({:query => request.query})
|
585
692
|
|
586
|
-
elsif path == '/agents'
|
587
|
-
status, content_type, body = self.
|
693
|
+
elsif path == '/agents' and request.query.has_key?('agents')
|
694
|
+
status, content_type, body = self.set_agents({:query => request.query})
|
588
695
|
|
589
|
-
elsif path == '/bsig'
|
696
|
+
elsif path == '/bsig' and request.query.has_key?('bsig')
|
590
697
|
status, content_type, body = self.set_bsig({:query => request.query})
|
591
698
|
|
592
699
|
elsif path == '/bsig/satisfier'
|
@@ -600,9 +707,52 @@ module Sfp
|
|
600
707
|
response.body = body
|
601
708
|
end
|
602
709
|
|
603
|
-
|
710
|
+
# Handle HTTP Put request
|
711
|
+
#
|
712
|
+
# uri:
|
713
|
+
# /model => delete existing model
|
714
|
+
# /modules => delete a module with name specified in parameter "module", or
|
715
|
+
# delete all modules if parameter "module" is not provided
|
716
|
+
# /agents => delete agents database
|
717
|
+
# /bsig => delete existing BSig model
|
718
|
+
#
|
719
|
+
def do_DELETE(request, response)
|
720
|
+
status = 400
|
721
|
+
content_type = body = ''
|
722
|
+
if not self.trusted(request)
|
723
|
+
status = 403
|
724
|
+
else
|
725
|
+
path = (request.path[-1,1] == '/' ? ryyequest.path.chop : request.path)
|
726
|
+
|
727
|
+
if path == '/model'
|
728
|
+
status, content_type, body = self.set_model
|
729
|
+
|
730
|
+
elsif path == '/model/cache'
|
731
|
+
status, content_type, body = self.set_cache_model
|
732
|
+
|
733
|
+
elsif path =~ /\/model\/cache\/.+/
|
734
|
+
status, content_type, body = self.set_cache_model({:name => path[13, path.length-13]})
|
735
|
+
|
736
|
+
elsif path == '/modules'
|
737
|
+
status, content_type, body = self.manage_modules({:deleteall => true})
|
738
|
+
|
739
|
+
elsif path =~ /\/modules\/.+/
|
740
|
+
status, content_type, body = self.manage_modules({:name => path[9, path.length-9]})
|
741
|
+
|
742
|
+
elsif path == '/agents'
|
743
|
+
status, content_type, body = self.set_agents
|
744
|
+
|
745
|
+
elsif path == '/bsig'
|
746
|
+
status, content_type, body = self.set_bsig
|
747
|
+
|
748
|
+
end
|
749
|
+
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
def set_agents(p={})
|
604
754
|
begin
|
605
|
-
if p[:query].has_key?('agents')
|
755
|
+
if p[:query] and p[:query].has_key?('agents')
|
606
756
|
return [200, '', ''] if Sfp::Agent.set_agents(JSON[p[:query]['agents']])
|
607
757
|
else
|
608
758
|
return [200, '', ''] if Sfp::Agent.set_agents({})
|
@@ -614,23 +764,47 @@ module Sfp
|
|
614
764
|
end
|
615
765
|
|
616
766
|
def manage_modules(p={})
|
617
|
-
if p[:
|
618
|
-
|
619
|
-
else
|
620
|
-
p[:name], _ = p[:name].split('/', 2)
|
621
|
-
if p[:query].has_key?('module')
|
767
|
+
if p[:name]
|
768
|
+
if p[:query]
|
622
769
|
return [200, '', ''] if Sfp::Agent.install_module(p[:name], p[:query]['module'])
|
623
770
|
else
|
624
771
|
return [200, '', ''] if Sfp::Agent.uninstall_module(p[:name])
|
625
772
|
end
|
773
|
+
elsif p[:query].length > 0
|
774
|
+
return [200, '', ''] if Sfp::Agent.install_modules(p[:query])
|
775
|
+
else
|
776
|
+
return [200, '', ''] if Sfp::Agent.uninstall_all_modules
|
626
777
|
end
|
778
|
+
|
627
779
|
[500, '', '']
|
628
780
|
end
|
629
781
|
|
630
|
-
def
|
782
|
+
def get_cache_model(p={})
|
783
|
+
model = Sfp::Agent.get_cache_model(p[:name])
|
784
|
+
if model
|
785
|
+
[200, 'application/json', JSON.generate(model)]
|
786
|
+
else
|
787
|
+
[404, '', '']
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
def set_cache_model(p={})
|
792
|
+
p[:model] = JSON[p[:query]['model']] if p[:query].is_a?(Hash) and p[:query]['model']
|
793
|
+
|
794
|
+
if p[:name]
|
795
|
+
return [200, '', ''] if Sfp::Agent.set_cache_model(p)
|
796
|
+
else
|
797
|
+
return [200, '', ''] if Sfp::Agent.set_cache_model
|
798
|
+
end
|
799
|
+
|
800
|
+
[500, '', '']
|
801
|
+
end
|
802
|
+
|
803
|
+
|
804
|
+
def get_sfp(p={})
|
631
805
|
begin
|
632
806
|
module_name, _ = p[:module].split('/', 2)
|
633
|
-
return [200, 'application/json', Sfp::Agent.
|
807
|
+
return [200, 'application/json', Sfp::Agent.get_sfp(module_name)]
|
634
808
|
rescue Exception => e
|
635
809
|
@logger.error "Sending schemata [Failed]\n#{e}"
|
636
810
|
end
|
@@ -680,7 +854,7 @@ module Sfp
|
|
680
854
|
end
|
681
855
|
|
682
856
|
def execute(p={})
|
683
|
-
return [400, '', ''] if not p[:query].has_key?('action')
|
857
|
+
return [400, '', ''] if not (p[:query] and p[:query].has_key?('action'))
|
684
858
|
begin
|
685
859
|
return [200, '', ''] if Sfp::Agent.execute_action(JSON[p[:query]['action']])
|
686
860
|
rescue
|
@@ -733,7 +907,7 @@ module Sfp
|
|
733
907
|
[500, '', '']
|
734
908
|
end
|
735
909
|
|
736
|
-
def trusted(
|
910
|
+
def trusted(request)
|
737
911
|
true
|
738
912
|
end
|
739
913
|
|
data/lib/sfpagent/bsig.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
3
|
class Sfp::BSig
|
4
|
-
include
|
4
|
+
include Sfp::Net::Helper
|
5
5
|
|
6
6
|
SleepTime = 5
|
7
7
|
MaxTries = 5
|
8
8
|
|
9
9
|
SatisfierPath = '/bsig/satisfier'
|
10
|
-
|
11
|
-
SatisfierLockFile = "#{
|
10
|
+
CacheDir = (Process.euid == 0 ? '/var/sfpagent' : File.expand_path('~/.sfpagent'))
|
11
|
+
SatisfierLockFile = "#{CacheDir}/bsig.satisfier.lock.#{Time.now.nsec}"
|
12
12
|
|
13
13
|
attr_reader :enabled, :status, :mode
|
14
14
|
|
@@ -16,6 +16,7 @@ class Sfp::BSig
|
|
16
16
|
@lock = Mutex.new
|
17
17
|
@enabled = false
|
18
18
|
@status = :stopped
|
19
|
+
@lock_postprocess = Mutex.new
|
19
20
|
end
|
20
21
|
|
21
22
|
def stop
|
@@ -32,7 +33,7 @@ class Sfp::BSig
|
|
32
33
|
Thread.new {
|
33
34
|
register_satisfier_thread(:reset)
|
34
35
|
|
35
|
-
system("rm -f #{
|
36
|
+
system("rm -f #{CacheDir}/operator.*.lock")
|
36
37
|
|
37
38
|
Sfp::Agent.logger.info "[main] BSig engine is running."
|
38
39
|
|
@@ -107,7 +108,8 @@ class Sfp::BSig
|
|
107
108
|
operator = select_operator(flaws, operators, pi)
|
108
109
|
return :failure if operator.nil?
|
109
110
|
|
110
|
-
#
|
111
|
+
# debugging
|
112
|
+
#Sfp::Agent.logger.info "[#{mode}] Flaws: #{JSON.generate(flaws)}"
|
111
113
|
|
112
114
|
return :ongoing if not lock_operator(operator)
|
113
115
|
|
@@ -116,7 +118,8 @@ class Sfp::BSig
|
|
116
118
|
next_pi = operator['pi'] + 1
|
117
119
|
pre_local, pre_remote = split_preconditions(operator)
|
118
120
|
|
119
|
-
#
|
121
|
+
# debugging
|
122
|
+
#Sfp::Agent.logger.info "[#{mode}] local-flaws: #{JSON.generate(pre_local)}, remote-flaws: #{JSON.generate(pre_remote)}"
|
120
123
|
|
121
124
|
status = nil
|
122
125
|
tries = MaxTries
|
@@ -146,17 +149,29 @@ class Sfp::BSig
|
|
146
149
|
end
|
147
150
|
|
148
151
|
def achieve_remote_goal(id, goal, pi, mode)
|
152
|
+
|
149
153
|
if goal.length > 0
|
150
154
|
agents = Sfp::Agent.get_agents
|
151
155
|
split_goal_by_agent(goal).each do |agent_name,agent_goal|
|
152
|
-
|
153
|
-
|
154
|
-
|
156
|
+
if agents.has_key?(agent_name)
|
157
|
+
return false if agents[agent_name]['sfpAddress'].to_s == ''
|
158
|
+
return false if not send_goal_to_agent(agents[agent_name], id, agent_goal, pi, agent_name, mode)
|
159
|
+
else
|
160
|
+
return false if not verify_state_of_not_exist_agent(agent_name, agent_goal)
|
161
|
+
end
|
155
162
|
end
|
156
163
|
end
|
157
164
|
true
|
158
165
|
end
|
159
166
|
|
167
|
+
def verify_state_of_not_exist_agent(name, goal)
|
168
|
+
state = { name => { 'created' => false } }
|
169
|
+
goal.each { |var,val|
|
170
|
+
return false if state.at?(var) != val
|
171
|
+
}
|
172
|
+
true
|
173
|
+
end
|
174
|
+
|
160
175
|
def receive_goal_from_agent(id, goal, pi)
|
161
176
|
register_satisfier_thread
|
162
177
|
|
@@ -187,7 +202,7 @@ class Sfp::BSig
|
|
187
202
|
unregister_satisfier_thread
|
188
203
|
end
|
189
204
|
|
190
|
-
protected
|
205
|
+
#protected
|
191
206
|
def register_satisfier_thread(mode=nil)
|
192
207
|
File.open(SatisfierLockFile, File::RDWR|File::CREAT, 0644) { |f|
|
193
208
|
f.flock(File::LOCK_EX)
|
@@ -212,7 +227,7 @@ class Sfp::BSig
|
|
212
227
|
|
213
228
|
def lock_operator(operator)
|
214
229
|
@lock.synchronize {
|
215
|
-
operator_lock_file = "#{
|
230
|
+
operator_lock_file = "#{CacheDir}/operator.#{operator['name']}.lock"
|
216
231
|
return false if File.exist?(operator_lock_file)
|
217
232
|
File.open(operator_lock_file, 'w') { |f| f.write('1') }
|
218
233
|
return true
|
@@ -221,17 +236,17 @@ class Sfp::BSig
|
|
221
236
|
|
222
237
|
def unlock_operator(operator)
|
223
238
|
@lock.synchronize {
|
224
|
-
operator_lock_file = "#{
|
239
|
+
operator_lock_file = "#{CacheDir}/operator.#{operator['name']}.lock"
|
225
240
|
File.delete(operator_lock_file) if File.exist?(operator_lock_file)
|
226
241
|
}
|
227
242
|
end
|
228
243
|
|
229
244
|
def split_goal_by_agent(goal)
|
230
|
-
agents = Sfp::Agent.get_agents
|
245
|
+
#agents = Sfp::Agent.get_agents
|
231
246
|
agent_goal = {}
|
232
247
|
goal.each { |var,val|
|
233
248
|
_, agent_name, _ = var.split('.', 3)
|
234
|
-
fail "Agent #{agent_name} is not in database!" if not agents.has_key?(agent_name)
|
249
|
+
#fail "Agent #{agent_name} is not in database!" if not agents.has_key?(agent_name)
|
235
250
|
agent_goal[agent_name] = {} if not agent_goal.has_key?(agent_name)
|
236
251
|
agent_goal[agent_name][var] = val
|
237
252
|
}
|
@@ -239,17 +254,21 @@ class Sfp::BSig
|
|
239
254
|
end
|
240
255
|
|
241
256
|
def send_goal_to_agent(agent, id, g, pi, agent_name='', mode)
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
257
|
+
begin
|
258
|
+
data = {'id' => id,
|
259
|
+
'goal' => JSON.generate(g),
|
260
|
+
'pi' => pi}
|
261
|
+
|
262
|
+
Sfp::Agent.logger.info "[#{mode}] Request goal to: #{agent_name} [WAIT]"
|
263
|
+
|
264
|
+
code, _ = put_data(agent['sfpAddress'], agent['sfpPort'], SatisfierPath, data)
|
265
|
+
|
266
|
+
Sfp::Agent.logger.info "[#{mode}] Request goal to: #{agent_name} - status: #{code}"
|
267
|
+
|
268
|
+
(code == '200')
|
269
|
+
rescue
|
270
|
+
false
|
271
|
+
end
|
253
272
|
end
|
254
273
|
|
255
274
|
def get_current_state
|
@@ -262,7 +281,13 @@ class Sfp::BSig
|
|
262
281
|
return goal.clone if current.nil?
|
263
282
|
flaws = {}
|
264
283
|
goal.each { |var,val|
|
265
|
-
|
284
|
+
_, agent_name, _ = var.split('.', 3)
|
285
|
+
if agent_name != Sfp::Agent.whoami?
|
286
|
+
current_value = Sfp::Resource.resolve(var)
|
287
|
+
else
|
288
|
+
current_value = current.at?(var)
|
289
|
+
end
|
290
|
+
|
266
291
|
if current_value.is_a?(Sfp::Undefined)
|
267
292
|
flaws[var] = val if not val.is_a?(Sfp::Undefined)
|
268
293
|
else
|
@@ -312,6 +337,150 @@ class Sfp::BSig
|
|
312
337
|
|
313
338
|
def invoke(operator, mode)
|
314
339
|
Sfp::Agent.logger.info "[#{mode}] Invoking #{operator['name']}"
|
315
|
-
|
340
|
+
|
341
|
+
begin
|
342
|
+
status = Sfp::Agent.execute_action(operator)
|
343
|
+
if status
|
344
|
+
if operator['name'] =~ /^\$(\.[a-zA-Z0-9_]+)*\.(create_vm)/
|
345
|
+
postprocess_create_vm(operator)
|
346
|
+
elsif operator['name'] =~ /^\$(\.[a-zA-Z0-9_]+)*\.(delete_vm)/
|
347
|
+
postprocess_delete_vm(operator)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
rescue Exception => e
|
351
|
+
Sfp::Agent.logger.error "Error in invoking operator #{operator['name']}\n#{e}\n#{e.backtrace.join("\n")}"
|
352
|
+
return false
|
353
|
+
end
|
354
|
+
|
355
|
+
status
|
356
|
+
end
|
357
|
+
|
358
|
+
def postprocess_delete_vm(operator)
|
359
|
+
@lock_postprocess.synchronize {
|
360
|
+
_, agent_name, _ = operator['name'].split('.', 3)
|
361
|
+
|
362
|
+
Sfp::Agent.logger.info "Postprocess delete VM #{agent_name}"
|
363
|
+
|
364
|
+
# update agents database (automatically broadcast to other agents)
|
365
|
+
Sfp::Agent.set_agents({agent_name => nil})
|
366
|
+
}
|
367
|
+
end
|
368
|
+
|
369
|
+
def postprocess_create_vm(operator)
|
370
|
+
@lock_postprocess.synchronize {
|
371
|
+
refs = operator['name'].split('.')
|
372
|
+
vms_ref = refs[0..-2].join('.') + '.vms'
|
373
|
+
|
374
|
+
_, agent_name, _ = operator['parameters']['$.vm'].split('.', 3)
|
375
|
+
|
376
|
+
Sfp::Agent.logger.info "Postprocess create VM #{agent_name}"
|
377
|
+
|
378
|
+
# update proxy component's state
|
379
|
+
state = Sfp::Agent.get_state
|
380
|
+
return false if not state.is_a?(Hash)
|
381
|
+
|
382
|
+
# get VM's address
|
383
|
+
vms = state.at?(vms_ref)
|
384
|
+
return false if !vms.is_a?(Hash) or !vms[agent_name].is_a?(Hash) or vms[agent_name]['ip'].to_s.strip == ''
|
385
|
+
data = {agent_name => {'sfpAddress' => vms[agent_name]['ip'], 'sfpPort' => Sfp::Agent::DefaultPort}}
|
386
|
+
|
387
|
+
# update agents database
|
388
|
+
Sfp::Agent.set_agents(data)
|
389
|
+
|
390
|
+
# get new agent's model and BSig model from cache database
|
391
|
+
model = Sfp::Agent.get_cache_model(agent_name)
|
392
|
+
model['model']['in_cloud'] = refs[0..-2].join('.')
|
393
|
+
|
394
|
+
if not model.nil?
|
395
|
+
address = data[agent_name]['sfpAddress']
|
396
|
+
port = data[agent_name]['sfpPort']
|
397
|
+
|
398
|
+
# push required modules
|
399
|
+
push_modules(model, address, port)
|
400
|
+
|
401
|
+
# push agent database to new agent
|
402
|
+
code, _ = put_data(address, port, '/agents', {'agents' => JSON.generate(Sfp::Agent.get_agents)})
|
403
|
+
|
404
|
+
# push new agent's model
|
405
|
+
code, _ = put_data(address, port, '/model', {'model' => JSON.generate({agent_name => model['model']})})
|
406
|
+
|
407
|
+
# push new agent's BSig model
|
408
|
+
code, _ = put_data(address, port, '/bsig', {'bsig' => JSON.generate(model['bsig'])}) if code == '200'
|
409
|
+
|
410
|
+
return (code == '200')
|
411
|
+
end
|
412
|
+
}
|
413
|
+
false
|
316
414
|
end
|
415
|
+
|
416
|
+
def push_modules(agent_model, address, port)
|
417
|
+
name = agent_model['_self']
|
418
|
+
finder = Sfp::Helper::SchemaCollector.new
|
419
|
+
{:agent => agent_model}.accept(finder)
|
420
|
+
schemata = finder.schemata.uniq.map { |x| x.sub(/^\$\./, '').downcase }
|
421
|
+
|
422
|
+
modules_dir = Sfp::Agent.config[:modules_dir]
|
423
|
+
install_module = File.expand_path('../../../bin/install_module', __FILE__)
|
424
|
+
|
425
|
+
begin
|
426
|
+
# get modules list
|
427
|
+
code, body = get_data(address, port, '/modules')
|
428
|
+
raise Exception, "Unable to get modules list from #{name}" if code.to_i != 200
|
429
|
+
|
430
|
+
modules = JSON[body]
|
431
|
+
list = ''
|
432
|
+
schemata.each { |m|
|
433
|
+
list += "#{m} " if m != 'object' and File.exist?("#{modules_dir}/#{m}") and
|
434
|
+
(not modules.has_key?(m) or modules[m] != get_local_module_hash(m, modules_dir).to_s)
|
435
|
+
}
|
436
|
+
|
437
|
+
return true if list == ''
|
438
|
+
|
439
|
+
if system("cd #{modules_dir}; #{install_module} #{address} #{port} #{list} 1>/dev/null 2>/tmp/install_module.error")
|
440
|
+
Sfp::Agent.logger.info "Push modules #{list}to #{name} [OK]"
|
441
|
+
else
|
442
|
+
Sfp::Agent.logger.warn "Push modules #{list}to #{name} [Failed]"
|
443
|
+
end
|
444
|
+
|
445
|
+
return true
|
446
|
+
|
447
|
+
rescue Exception => e
|
448
|
+
Sfp::Agent.logger.warn "[WARN] Cannot push module to #{name} - #{e}"
|
449
|
+
end
|
450
|
+
|
451
|
+
false
|
452
|
+
end
|
453
|
+
|
454
|
+
# return the list of Hash value of all modules
|
455
|
+
#
|
456
|
+
def get_local_module_hash(name, modules_dir)
|
457
|
+
module_dir = File.expand_path("#{modules_dir}/#{name}")
|
458
|
+
if File.directory? module_dir
|
459
|
+
if `which md5sum`.strip.length > 0
|
460
|
+
return `find #{module_dir} -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | awk '{print $1}'`.strip
|
461
|
+
elsif `which md5`.strip.length > 0
|
462
|
+
return `find #{module_dir} -type f -exec md5 {} + | awk '{print $4}' | sort | md5`.strip
|
463
|
+
end
|
464
|
+
end
|
465
|
+
nil
|
466
|
+
end
|
467
|
+
|
317
468
|
end
|
469
|
+
|
470
|
+
module Sfp::Helper
|
471
|
+
end
|
472
|
+
|
473
|
+
class Sfp::Helper::SchemaCollector
|
474
|
+
attr_reader :schemata
|
475
|
+
def initialize
|
476
|
+
@schemata = []
|
477
|
+
end
|
478
|
+
|
479
|
+
def visit(name, value, parent)
|
480
|
+
if value.is_a?(Hash) and value.has_key?('_classes')
|
481
|
+
value['_classes'].each { |s| @schemata << s }
|
482
|
+
end
|
483
|
+
true
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
data/lib/sfpagent/module.rb
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
# predefined methods: update_state, apply, reset, resolve
|
3
3
|
#
|
4
4
|
module Sfp::Resource
|
5
|
+
@@resource = Object.new.extend(Sfp::Resource)
|
6
|
+
|
7
|
+
def self.resolve(path)
|
8
|
+
@@resource.resolve(path)
|
9
|
+
end
|
10
|
+
|
5
11
|
attr_accessor :parent, :synchronized
|
6
12
|
attr_reader :state, :model
|
7
13
|
|
data/lib/sfpagent/net_helper.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'net/http'
|
3
3
|
|
4
|
-
module
|
4
|
+
module Sfp::Net
|
5
5
|
end
|
6
6
|
|
7
|
-
module
|
7
|
+
module Sfp::Net::Helper
|
8
8
|
def http_request(uri, request, open_timeout=5, read_timeout=1800)
|
9
9
|
http = Net::HTTP.new(uri.host, uri.port)
|
10
10
|
http.open_timeout = open_timeout
|
data/lib/sfpagent/sfplanner.rb
CHANGED
@@ -1,85 +1,217 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
class Planner
|
4
|
+
def initialize(p={})
|
3
5
|
# TODO
|
4
6
|
# - build from SAS string
|
5
|
-
# - generate image dependencies and
|
7
|
+
# - generate image dependencies and joints graph
|
8
|
+
|
9
|
+
@vars = []
|
10
|
+
@variables = {}
|
11
|
+
@ops = []
|
12
|
+
@operators = {}
|
13
|
+
@init = @goal = nil
|
14
|
+
|
15
|
+
lines = p[:sas].split("\n")
|
16
|
+
i = 0
|
17
|
+
while i < lines.length
|
18
|
+
if lines[i] == 'begin_variable'
|
19
|
+
i, var = Variable.read(i, lines)
|
20
|
+
@vars << var
|
21
|
+
@variables[var.sym] = var
|
22
|
+
elsif lines[i] == 'begin_operator'
|
23
|
+
i, op = Operator.read(i, lines, @vars)
|
24
|
+
@ops << op
|
25
|
+
@operators[op.sym] = op
|
26
|
+
elsif lines[i] == 'begin_state'
|
27
|
+
i, @init = State.read(i, lines, @vars)
|
28
|
+
elsif lines[i] == 'begin_goal'
|
29
|
+
i, @goal = State.read(i, lines, @vars)
|
30
|
+
end
|
31
|
+
i += 1
|
32
|
+
end
|
33
|
+
|
34
|
+
@ops.each { |op| op.update_variables_joints_and_dependencies(@variables) }
|
35
|
+
|
36
|
+
puts "#{@vars.length} variables"
|
37
|
+
puts "#{@ops.length} operators"
|
38
|
+
puts "#{@init.length} initial state"
|
39
|
+
puts "#{@goal.length} goal state"
|
40
|
+
|
41
|
+
@vars.each { |v|
|
42
|
+
puts v.to_s
|
43
|
+
if v.dependencies.length > 0
|
44
|
+
print "\tdep|"
|
45
|
+
v.dependencies.each { |k,v| print "#{k}:#{v.length}|" }
|
46
|
+
puts ''
|
47
|
+
end
|
48
|
+
if v.joints.length > 0
|
49
|
+
print "\tjoint|"
|
50
|
+
v.joints.each { |k,v| print "#{k}:#{v.length}|" }
|
51
|
+
puts ''
|
52
|
+
end
|
53
|
+
}
|
54
|
+
@ops.each { |op| puts op.inspect }
|
55
|
+
puts @init.inspect
|
56
|
+
puts @goal.inspect
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_image(p={})
|
6
60
|
end
|
7
61
|
|
8
62
|
class Variable < Array
|
9
|
-
|
10
|
-
|
63
|
+
attr_reader :name, :sym
|
64
|
+
attr_accessor :init, :goal, :joints, :dependencies
|
65
|
+
|
66
|
+
def self.read(i, lines)
|
67
|
+
var = Variable.new(lines[i+1])
|
68
|
+
i += 4
|
69
|
+
i.upto(lines.length) do |j|
|
70
|
+
i = j
|
71
|
+
break if lines[j] == 'end_variable'
|
72
|
+
var << lines[j].to_sym
|
73
|
+
end
|
74
|
+
fail "Cannot find end_variable" if lines[i] != 'end_variable'
|
75
|
+
[i, var]
|
76
|
+
end
|
11
77
|
|
12
78
|
def initialize(name, init=nil, goal=nil)
|
13
79
|
@name = name
|
80
|
+
@sym = @name.to_sym
|
14
81
|
@values = []
|
15
82
|
@map = {}
|
16
83
|
@init = init
|
17
84
|
@goal = goal
|
18
|
-
@
|
85
|
+
@joints = {}
|
19
86
|
@dependencies = {}
|
20
87
|
end
|
88
|
+
|
89
|
+
alias :super_to_s :to_s
|
90
|
+
def to_s
|
91
|
+
@name + " " + super_to_s
|
92
|
+
end
|
21
93
|
end
|
22
94
|
|
23
95
|
class Operator
|
24
|
-
attr_reader :name, :
|
96
|
+
attr_reader :name, :sym
|
97
|
+
attr_accessor :cost, :preconditions, :postconditions, :variables
|
98
|
+
|
99
|
+
def self.read(i, lines, variables)
|
100
|
+
op = Operator.new(lines[i+1])
|
101
|
+
i += 2
|
102
|
+
last = nil
|
103
|
+
i.upto(lines.length) do |j|
|
104
|
+
i = j
|
105
|
+
break if lines[j] == 'end_operator'
|
106
|
+
parts = lines[j].split(' ')
|
107
|
+
if parts.length > 1
|
108
|
+
var = variables[parts[1].to_i]
|
109
|
+
pre = (parts[2] == '-1' ? nil : var[parts[2].to_i])
|
110
|
+
post = (parts[3].nil? ? nil : var[parts[3].to_i])
|
111
|
+
op.param var, pre, post
|
112
|
+
end
|
113
|
+
last = lines[j]
|
114
|
+
end
|
115
|
+
op.cost = last.to_i
|
116
|
+
fail "Cannot find end_operator" if lines[i] != 'end_operator'
|
117
|
+
[i, op]
|
118
|
+
end
|
25
119
|
|
26
120
|
def initialize(name, cost=1)
|
27
121
|
@name = name
|
122
|
+
@sym = @name.to_sym
|
28
123
|
@cost = cost
|
29
124
|
@preconditions = {}
|
30
125
|
@postconditions = {}
|
31
126
|
@variables = {}
|
32
127
|
end
|
33
128
|
|
34
|
-
def
|
129
|
+
def param(variable, pre=nil, post=nil)
|
35
130
|
return if variable.nil? or (pre.nil? and post.nil?)
|
36
131
|
if !pre.nil?
|
37
132
|
fail "Invalid precondition #{variable.name}:#{pre}" if !variable.index(pre)
|
38
|
-
@preconditions[variable] = pre
|
133
|
+
@preconditions[variable.sym] = pre
|
39
134
|
end
|
40
135
|
if !post.nil?
|
41
136
|
fail "Invalid postcondition #{variable.name}:#{post}" if !variable.index(post)
|
42
|
-
@postconditions[variable] = post
|
137
|
+
@postconditions[variable.sym] = post
|
43
138
|
end
|
44
|
-
@variables[variable.
|
139
|
+
@variables[variable.sym] = variable
|
45
140
|
end
|
46
141
|
|
47
142
|
def applicable(state)
|
48
|
-
@preconditions.each { |var,pre| return false if state[var] != pre }
|
143
|
+
@preconditions.each { |var,pre| return false if state[var.sym] != pre }
|
49
144
|
true
|
50
145
|
end
|
51
146
|
|
52
147
|
def apply(state)
|
53
|
-
@postconditions.each { |var,post| state[var] = post }
|
148
|
+
@postconditions.each { |var,post| state[var.sym] = post }
|
54
149
|
end
|
55
150
|
|
56
|
-
def
|
57
|
-
@postconditions.each_key { |
|
58
|
-
|
59
|
-
|
60
|
-
if
|
61
|
-
|
151
|
+
def update_variables_joints_and_dependencies(variables)
|
152
|
+
@postconditions.each_key { |post|
|
153
|
+
var_post = variables[post]
|
154
|
+
@preconditions.each_key { |pre|
|
155
|
+
next if post == pre
|
156
|
+
var_pre = variables[pre]
|
157
|
+
if !var_post.dependencies.has_key?(pre)
|
158
|
+
var_post.dependencies[pre] = [self]
|
62
159
|
else
|
63
|
-
var_post.dependencies[
|
160
|
+
var_post.dependencies[pre] << self
|
64
161
|
end
|
65
162
|
}
|
66
|
-
@postconditions.each_key { |
|
67
|
-
next if
|
68
|
-
|
69
|
-
|
163
|
+
@postconditions.each_key { |post2|
|
164
|
+
next if post == post2
|
165
|
+
var_post2 = variables[post2]
|
166
|
+
if !var_post.joints.has_key?(post2)
|
167
|
+
var_post.joints[post2] = [self]
|
70
168
|
else
|
71
|
-
var_post.
|
169
|
+
var_post.joints[post2] << self
|
72
170
|
end
|
73
171
|
}
|
74
172
|
}
|
75
173
|
end
|
174
|
+
|
175
|
+
def to_s
|
176
|
+
@name + " pre:" + @preconditions.inspect + " post:" + @postconditions.inspect
|
177
|
+
end
|
76
178
|
end
|
77
179
|
|
78
180
|
class State < Hash
|
79
181
|
attr_reader :id
|
80
|
-
|
182
|
+
|
183
|
+
def self.read(i, lines, variables)
|
184
|
+
state = State.new(lines[i] == 'begin_state' ? 'init' : 'goal')
|
185
|
+
if state.id == 'init'
|
186
|
+
i += 1
|
187
|
+
var_index = 0
|
188
|
+
i.upto(lines.length) do |j|
|
189
|
+
i = j
|
190
|
+
break if lines[j] == 'end_state'
|
191
|
+
var = variables[var_index]
|
192
|
+
state[var.sym] = var[lines[j].to_i]
|
193
|
+
var_index += 1
|
194
|
+
end
|
195
|
+
fail "Cannot find end_state" if lines[i] != 'end_state'
|
196
|
+
[i, state]
|
197
|
+
elsif state.id == 'goal'
|
198
|
+
i += 2
|
199
|
+
i.upto(lines.length) do |j|
|
200
|
+
i = j
|
201
|
+
break if lines[j] == 'end_goal'
|
202
|
+
parts = lines[j].split(' ')
|
203
|
+
var = variables[parts[0].to_i]
|
204
|
+
state[var.sym] = var[parts[1].to_i]
|
205
|
+
end
|
206
|
+
fail "Cannot find end_goal" if lines[i] != 'end_goal'
|
207
|
+
[i, state]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
81
211
|
def initialize(id)
|
82
212
|
@id = id
|
83
213
|
end
|
84
214
|
end
|
85
215
|
end
|
216
|
+
|
217
|
+
Planner.new(:sas => File.read(ARGV[0]))
|
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.14
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ 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: &13903480 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: 0.3.12
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *13903480
|
25
25
|
description: A Ruby implementation of SFP agent.
|
26
26
|
email: herry13@gmail.com
|
27
27
|
executables:
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- README.md
|
35
35
|
- VERSION
|
36
36
|
- bin/cert.rb
|
37
|
+
- bin/install_module
|
37
38
|
- bin/sfpagent
|
38
39
|
- lib/sfpagent.rb
|
39
40
|
- lib/sfpagent/agent.rb
|