manband 0.4.0 → 0.5.0
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/bin/jobhandler.rb +7 -0
- data/bin/manband.rb +4 -0
- data/bin/remoteband.rb +5 -0
- data/bin/userband.rb +6 -0
- data/bin/workflowhandler.rb +10 -1
- data/lib/manband/bandmanager.rb +3 -3
- data/lib/manband/flowconfig.rb +15 -2
- data/lib/manband/job.rb +25 -11
- data/lib/manband/store.rb +14 -4
- data/lib/manband/user.rb +6 -0
- data/lib/manband/workflow.rb +14 -3
- metadata +3 -3
data/bin/jobhandler.rb
CHANGED
@@ -7,6 +7,13 @@ require 'mb-minion'
|
|
7
7
|
|
8
8
|
include Minion
|
9
9
|
|
10
|
+
# The program manages job commands. Master nodes (workflow handler) sends
|
11
|
+
# messages to jobhandler instances. Each instance manages one and
|
12
|
+
# only one message at a time. Jobhnadlers are also in charge of storage to S3.
|
13
|
+
# Author: Olivier Sallou <olivier.sallou@irisa.fr>
|
14
|
+
# Copyright: 2012, IRISA
|
15
|
+
|
16
|
+
|
10
17
|
if ENV["MYSQL_URL"]==nil
|
11
18
|
puts "MYSQL_URL environment variable is not set exiting..."
|
12
19
|
exit 1
|
data/bin/manband.rb
CHANGED
@@ -14,6 +14,10 @@ require 'manband/flowconfig.rb'
|
|
14
14
|
require 'manband/job.rb'
|
15
15
|
require 'manband/bandmanager.rb'
|
16
16
|
|
17
|
+
# The program sends a workflow execution request to manband orchestrator.
|
18
|
+
# Author: Olivier Sallou <olivier.sallou@irisa.fr>
|
19
|
+
# Copyright: 2012, IRISA
|
20
|
+
|
17
21
|
log = Logger.new(STDOUT)
|
18
22
|
log.level = Logger::INFO
|
19
23
|
|
data/bin/remoteband.rb
CHANGED
@@ -7,6 +7,11 @@ require 'mb-minion'
|
|
7
7
|
|
8
8
|
include Minion
|
9
9
|
|
10
|
+
# The program sends a workflow execution request to manband orchestrator.
|
11
|
+
# This program does not need database credentials but does not get workflow id in return
|
12
|
+
# Author: Olivier Sallou <olivier.sallou@irisa.fr>
|
13
|
+
# Copyright: 2012, IRISA
|
14
|
+
|
10
15
|
|
11
16
|
log = Logger.new(STDOUT)
|
12
17
|
log.level = Logger::INFO
|
data/bin/userband.rb
CHANGED
@@ -8,6 +8,12 @@ $:.push File.expand_path("../lib")
|
|
8
8
|
|
9
9
|
require 'manband/flowconfig.rb'
|
10
10
|
|
11
|
+
# The program is used to manage users (add, update) in the database.
|
12
|
+
# Users are used for web interface authentification and S3 credentials.
|
13
|
+
# Author: Olivier Sallou <olivier.sallou@irisa.fr>
|
14
|
+
# Copyright: 2012, IRISA
|
15
|
+
|
16
|
+
|
11
17
|
log = Logger.new(STDOUT)
|
12
18
|
log.level = Logger::INFO
|
13
19
|
|
data/bin/workflowhandler.rb
CHANGED
@@ -7,6 +7,15 @@ require 'mb-minion'
|
|
7
7
|
|
8
8
|
include Minion
|
9
9
|
|
10
|
+
# The program s the main orchestrator of the workflow.
|
11
|
+
# It starts with a workflow execution request and sends messages to job
|
12
|
+
# handlers according to the input workflow.
|
13
|
+
# It is possible to have multipe workflow handler, sharing messages for
|
14
|
+
# the same workflow, or to handler a high number of workflow requests.
|
15
|
+
# Author: Olivier Sallou <olivier.sallou@irisa.fr>
|
16
|
+
# Copyright: 2012, IRISA
|
17
|
+
|
18
|
+
|
10
19
|
if ENV["MYSQL_URL"]==nil
|
11
20
|
puts "MYSQL_URL environment variable is not set exiting..."
|
12
21
|
exit 1
|
@@ -52,7 +61,7 @@ optparse.parse!
|
|
52
61
|
|
53
62
|
userconf = File.expand_path("~")+"/.manband"
|
54
63
|
if @@options[:conf]==nil && File.exists?(userconf)
|
55
|
-
options[:conf]=userconf
|
64
|
+
@@options[:conf]=userconf
|
56
65
|
end
|
57
66
|
|
58
67
|
if @@options[:conf]!=nil
|
data/lib/manband/bandmanager.rb
CHANGED
@@ -11,14 +11,14 @@ include Minion
|
|
11
11
|
require 'manband/workflow.rb'
|
12
12
|
require 'manband/flowconfig.rb'
|
13
13
|
|
14
|
-
#
|
14
|
+
# This class is used to launch new workflows
|
15
15
|
class BandManager
|
16
16
|
|
17
17
|
@@log = Logger.new(STDOUT)
|
18
18
|
@@log.level = Logger::DEBUG
|
19
19
|
|
20
20
|
|
21
|
-
#
|
21
|
+
# Loads a workflow file
|
22
22
|
def self.load(wfile)
|
23
23
|
if !File.exists?(wfile)
|
24
24
|
@@log.error "Workflow file "+wfile+" does not exist!"
|
@@ -143,7 +143,7 @@ class BandManager
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
# Execute a workflow from an other one
|
146
|
+
# Execute a workflow from an other one (clone it)
|
147
147
|
def self.launchclone(id)
|
148
148
|
workflow = WorkFlow.get(id)
|
149
149
|
newworkflow = WorkFlow.new(:uid => workflow.uid , :name => workflow.name, :description => workflow.description, :created_at => Time.now, :file => workflow.file, :terminals => workflow.terminals, :status => STATUS_NEW, :workdir => FlowConfig.getjobdir(), :vars => workflow.vars, :bucket => workflow.bucket)
|
data/lib/manband/flowconfig.rb
CHANGED
@@ -2,6 +2,9 @@ require 'uuid'
|
|
2
2
|
require 'data_mapper'
|
3
3
|
require 'dm-migrations'
|
4
4
|
|
5
|
+
# Configuration library
|
6
|
+
|
7
|
+
# Determines the workflow and node status
|
5
8
|
STATUS_SKIP = -2
|
6
9
|
STATUS_FAKE = -1
|
7
10
|
STATUS_NEW = 0
|
@@ -10,12 +13,14 @@ STATUS_OVER = 2
|
|
10
13
|
STATUS_SUSPEND = 3 # SUSPEND mode for a job means a job is over and paused
|
11
14
|
STATUS_ERROR = 4
|
12
15
|
|
16
|
+
# Determines if a job must be stored to S3, and its current status
|
13
17
|
STORE_NO = -1
|
14
18
|
STORE_DO = 0
|
15
19
|
STORE_RUN = 1
|
16
20
|
STORE_OVER = 2
|
17
21
|
STORE_ERROR = 4
|
18
22
|
|
23
|
+
# List of message operations
|
19
24
|
OP_NEW = "new"
|
20
25
|
OP_START = "start"
|
21
26
|
OP_FINISH = "finish"
|
@@ -30,10 +35,10 @@ OP_CLEAN = "clean" # Delete work dirs of workflow
|
|
30
35
|
OP_DESTROY = "destroy" # Delete workflow and its work dirs
|
31
36
|
OP_STORE = "store" # Store result to S3
|
32
37
|
|
33
|
-
#
|
38
|
+
# This class holds the base config
|
34
39
|
class FlowConfig
|
35
40
|
@@workdir='/tmp'
|
36
|
-
@@s3host = '
|
41
|
+
@@s3host = 'localhost'
|
37
42
|
@@s3port = '8773'
|
38
43
|
@@s3path = '/services/Walrus'
|
39
44
|
|
@@ -47,6 +52,7 @@ class FlowConfig
|
|
47
52
|
return @@s3host
|
48
53
|
end
|
49
54
|
|
55
|
+
# Sets S3 storage parameters
|
50
56
|
def self.sets3(host,port= '8773',path='/services/Walrus')
|
51
57
|
@@s3host = host
|
52
58
|
@@s3port = port
|
@@ -65,14 +71,21 @@ class FlowConfig
|
|
65
71
|
return @@workdir
|
66
72
|
end
|
67
73
|
|
74
|
+
# defines upload directory for webband
|
75
|
+
# It must be accessible by the handlers
|
68
76
|
def self.setuploaddir(directory)
|
69
77
|
@@uploaddir = directory
|
70
78
|
end
|
71
79
|
|
80
|
+
# Defines the work directory. It must be shared between job
|
81
|
+
# and workflow handlers.
|
72
82
|
def self.setworkdir(directory)
|
73
83
|
@@workdir=directory
|
74
84
|
end
|
75
85
|
|
86
|
+
|
87
|
+
# Returns a work directory for a job. Directory
|
88
|
+
# is based on a unique identifier.
|
76
89
|
def self.getjobdir(workflowdir = nil)
|
77
90
|
uuid = UUID.new
|
78
91
|
if workflowdir == nil
|
data/lib/manband/job.rb
CHANGED
@@ -11,10 +11,7 @@ include Minion
|
|
11
11
|
#DataMapper.setup(:default, 'sqlite:///home/osallou/Desktop/genflow/project.db')
|
12
12
|
DataMapper.setup(:default, ENV['MYSQL_URL'])
|
13
13
|
|
14
|
-
#
|
15
|
-
# Exit code = 0 selects first node, other code select second node
|
16
|
-
# Should inform with a finish "code: exitcode" and manage it in workflowhandler to RUN selected node and SKIP other node
|
17
|
-
|
14
|
+
# This class defines the link between the nodes in the workflow
|
18
15
|
class JobLink
|
19
16
|
include DataMapper::Resource
|
20
17
|
|
@@ -25,11 +22,14 @@ class JobLink
|
|
25
22
|
|
26
23
|
end
|
27
24
|
|
25
|
+
# This class manages the job execution and its status on a job handler.
|
28
26
|
class Job
|
29
27
|
include DataMapper::Resource
|
30
28
|
|
31
29
|
@@debug = false
|
32
30
|
|
31
|
+
# Sets debug mode
|
32
|
+
# mode: boolean
|
33
33
|
def self.debug(mode)
|
34
34
|
@@debug = mode
|
35
35
|
end
|
@@ -50,7 +50,10 @@ class Job
|
|
50
50
|
property :error, Text, :default => "[]" # JSON Array of error, per instance
|
51
51
|
property :store, Integer, :default => STORE_NO # Storage status
|
52
52
|
|
53
|
-
# Update job status and launch the command
|
53
|
+
# Update job status and launch the command locally
|
54
|
+
# Sends a finish message or an error message according to the job status.
|
55
|
+
# if storage is needed, job will send a finish AND a store message. Storage
|
56
|
+
# will occur in parallel of the rest of the workflow.
|
54
57
|
def run(curhandler,instance=0)
|
55
58
|
# Send run command, possibly multiple ones according to pattern
|
56
59
|
# Command would send a message when over
|
@@ -81,7 +84,7 @@ class Job
|
|
81
84
|
end
|
82
85
|
end
|
83
86
|
|
84
|
-
# Skip treatment, just answer
|
87
|
+
# Skip treatment, just answer, for debug
|
85
88
|
def skip(curhandler)
|
86
89
|
curjob = Job.get(@id)
|
87
90
|
curjob.update(:handler => curhandler.to_s)
|
@@ -89,7 +92,10 @@ class Job
|
|
89
92
|
sendmessage(OP_FINISH,jobmsg)
|
90
93
|
end
|
91
94
|
|
92
|
-
# Execute locally the command
|
95
|
+
# Execute locally the command, creating directories and setting environment
|
96
|
+
# variables to empty string for security.
|
97
|
+
# wordir: job working directory
|
98
|
+
# instance: instance number in the list of commands
|
93
99
|
def runcommand(workdir,instance)
|
94
100
|
initcmd = "AMQP_URL="" && MYSQL_URL="" && mkdir -p "+workdir+" && cd "+workdir+" && WORKDIR="+workdir+" && "
|
95
101
|
curjob = Job.get(@id)
|
@@ -102,7 +108,8 @@ class Job
|
|
102
108
|
end
|
103
109
|
|
104
110
|
# Change instance counter
|
105
|
-
# If workflow is in suspend status, suspend the job at
|
111
|
+
# If workflow is in suspend status, suspend the job at
|
112
|
+
# the end of its treatment
|
106
113
|
def finish
|
107
114
|
if @status == STATUS_SKIP
|
108
115
|
return
|
@@ -133,7 +140,7 @@ class Job
|
|
133
140
|
end
|
134
141
|
end
|
135
142
|
|
136
|
-
#
|
143
|
+
# Compares intance counter to max instances to determine if job is over.
|
137
144
|
def isover?
|
138
145
|
if @status == STATUS_OVER
|
139
146
|
return true
|
@@ -179,7 +186,9 @@ class Job
|
|
179
186
|
end
|
180
187
|
end
|
181
188
|
end
|
182
|
-
|
189
|
+
|
190
|
+
# Sets a job in ERROR status.
|
191
|
+
# instance: job instance number in fault
|
183
192
|
def error!(instance = 0)
|
184
193
|
@status= STATUS_ERROR
|
185
194
|
curjob = Job.get(@id)
|
@@ -258,7 +267,12 @@ class Job
|
|
258
267
|
end
|
259
268
|
end
|
260
269
|
end
|
261
|
-
|
270
|
+
|
271
|
+
# Sends a message. According to the operation, message will be
|
272
|
+
# sent to master or node queues.
|
273
|
+
# operation: kind of message
|
274
|
+
# msg: message to send
|
275
|
+
# jobqueue: optional specific queue
|
262
276
|
def sendmessage(operation,msg,jobqueue='')
|
263
277
|
queue = "manband.master"
|
264
278
|
if operation == OP_RUN || operation == OP_SKIP || operation == OP_DESTROY || operation == OP_CLEAN|| operation == OP_STORE
|
data/lib/manband/store.rb
CHANGED
@@ -7,7 +7,7 @@ require 'fileutils'
|
|
7
7
|
require 'manband/flowconfig.rb'
|
8
8
|
require 'manband/user.rb'
|
9
9
|
|
10
|
-
|
10
|
+
# this class manage the storage of a job directory to S3
|
11
11
|
class Storeband
|
12
12
|
|
13
13
|
@@log = Logger.new(STDOUT)
|
@@ -29,6 +29,11 @@ class Storeband
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# Store a job workdir to S3
|
32
|
+
#
|
33
|
+
# job: current job
|
34
|
+
# uid: user identifier
|
35
|
+
# bucket: bucket name to store the zip file
|
36
|
+
# @return false in case of failure
|
32
37
|
def store(job,uid,bucket="manband")
|
33
38
|
user = User.get(uid)
|
34
39
|
if user == nil
|
@@ -50,16 +55,21 @@ class Storeband
|
|
50
55
|
compress(job.workdir, FlowConfig.workdir+"/"+zipfile)
|
51
56
|
# Send file
|
52
57
|
sends3(FlowConfig.workdir+"/"+zipfile,bucket,user.s3_access,user.s3_secret)
|
53
|
-
rescue
|
54
|
-
@@log.error "An error occured during S3 operation: "+job.id.to_s
|
58
|
+
rescue Exception => e
|
59
|
+
@@log.error "An error occured during S3 operation: "+job.id.to_s+" "+e.message
|
55
60
|
job.update(:store => STORE_ERROR)
|
56
61
|
return false
|
57
62
|
end
|
58
63
|
job.update(:store => STORE_OVER)
|
59
64
|
end
|
60
65
|
|
66
|
+
# Sends the file to the S3 bucket
|
67
|
+
# name: file name
|
68
|
+
# bucket: destination bucket name
|
69
|
+
# access: user credential access
|
70
|
+
# secret: user credential secret
|
61
71
|
def sends3(name,bucket,access,secret)
|
62
|
-
@@log.debug "connect to s3"
|
72
|
+
@@log.debug "connect to s3: "+FlowConfig.s3host+":"+FlowConfig.s3port.to_s+FlowConfig.s3path
|
63
73
|
AWS::S3::Base.establish_connection!(
|
64
74
|
:access_key_id => access,
|
65
75
|
:secret_access_key => secret,
|
data/lib/manband/user.rb
CHANGED
@@ -7,6 +7,7 @@ require 'manband/flowconfig.rb'
|
|
7
7
|
#DataMapper.setup(:default, 'sqlite:///tmp/project.db')
|
8
8
|
DataMapper.setup(:default, ENV['MYSQL_URL'])
|
9
9
|
|
10
|
+
# This class manages users in the database
|
10
11
|
class User
|
11
12
|
include DataMapper::Resource
|
12
13
|
|
@@ -17,6 +18,8 @@ class User
|
|
17
18
|
property :s3_access, String # User id key
|
18
19
|
property :s3_secret, String # Secret key
|
19
20
|
|
21
|
+
# Creates a default admin account if it does not exists.
|
22
|
+
# Default password is <b>admin</b>
|
20
23
|
def self.init
|
21
24
|
admin = User.get(@@admin)
|
22
25
|
if admin == nil
|
@@ -26,6 +29,9 @@ class User
|
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
32
|
+
# Check passwords
|
33
|
+
# password: user password
|
34
|
+
# @return: true if authentication is correct
|
29
35
|
def authenticate(password)
|
30
36
|
shapwd = Digest::SHA1.hexdigest(password)
|
31
37
|
if self.password == shapwd
|
data/lib/manband/workflow.rb
CHANGED
@@ -12,7 +12,9 @@ require 'manband/job.rb'
|
|
12
12
|
DataMapper.setup(:default, ENV['MYSQL_URL'])
|
13
13
|
|
14
14
|
|
15
|
-
|
15
|
+
# This class orchestrator the workflow status and the workflow file
|
16
|
+
# analysis. It determines if workflow is over, what are the next jobs
|
17
|
+
# to execute, ...
|
16
18
|
class WorkFlow
|
17
19
|
include DataMapper::Resource
|
18
20
|
|
@@ -34,6 +36,8 @@ class WorkFlow
|
|
34
36
|
property :parent, Integer, :default => 0 # Parent workflow id, none by default
|
35
37
|
property :instances, Integer, :default => 0 # Number of sub workflows, if any
|
36
38
|
|
39
|
+
# Checks if a workflow is over, e.g. we have reached all the
|
40
|
+
# terminal nodes (leafs).
|
37
41
|
def isover?
|
38
42
|
# decrement terminals
|
39
43
|
@terminals = @terminals - 1
|
@@ -54,7 +58,8 @@ class WorkFlow
|
|
54
58
|
return false
|
55
59
|
end
|
56
60
|
|
57
|
-
#
|
61
|
+
# Get the list of jobs to be run after current node
|
62
|
+
# @return an array of node names
|
58
63
|
def getnextjobs(curnode)
|
59
64
|
#fworkflow = YAML.load_file(@file)
|
60
65
|
fworkflow = BandManager.load(@file)
|
@@ -75,6 +80,9 @@ class WorkFlow
|
|
75
80
|
return nexts
|
76
81
|
end
|
77
82
|
|
83
|
+
# Parse workflow file and create jobs and links in the database
|
84
|
+
# curnode: current node
|
85
|
+
# id: id of the node as link originator
|
78
86
|
def parse(curnode, id = nil)
|
79
87
|
#fworkflow = YAML.load_file(@file)
|
80
88
|
fworkflow = BandManager.load(@file)
|
@@ -140,7 +148,9 @@ class WorkFlow
|
|
140
148
|
return newcommand
|
141
149
|
end
|
142
150
|
|
143
|
-
# Return
|
151
|
+
# Return a list of commands for the node in the workflow
|
152
|
+
# There is one command per input file matching regular expresssions,
|
153
|
+
# if any. Default is 1 command.
|
144
154
|
def getnodecommand(curnode)
|
145
155
|
#fworkflow = YAML.load_file(@file)
|
146
156
|
fworkflow = BandManager.load(@file)
|
@@ -247,6 +257,7 @@ class WorkFlow
|
|
247
257
|
return commands
|
248
258
|
end
|
249
259
|
|
260
|
+
# Clean a workflow directory
|
250
261
|
def clean
|
251
262
|
workflow = WorkFlow.get(@id)
|
252
263
|
if workflow.workdir == nil
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: manband
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 5
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Olivier Sallou
|