manband 0.4.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.
@@ -0,0 +1,274 @@
1
+ require 'data_mapper'
2
+ require 'dm-migrations'
3
+ require 'yaml'
4
+ require 'logger'
5
+ require 'fileutils'
6
+ require 'json'
7
+ require 'manband/flowconfig.rb'
8
+ require 'manband/bandmanager.rb'
9
+ require 'manband/job.rb'
10
+
11
+ #DataMapper.setup(:default, 'sqlite:///home/osallou/Desktop/genflow/project.db')
12
+ DataMapper.setup(:default, ENV['MYSQL_URL'])
13
+
14
+
15
+
16
+ class WorkFlow
17
+ include DataMapper::Resource
18
+
19
+ @@log = Logger.new(STDOUT)
20
+ @@log.level = Logger::DEBUG
21
+
22
+ property :id, Serial # An auto-increment integer key
23
+ property :uid, Text # user id
24
+ property :name, String # A varchar type string, for short strings
25
+ property :description, Text # A text block, for longer string data.
26
+ property :file, String # path to the worflow file definition
27
+ property :created_at, DateTime # A DateTime, for any date you might like.
28
+ property :terminated_at, DateTime # Termination date
29
+ property :terminals, Integer
30
+ property :status, Integer # Running, Suspended, Error, Finished
31
+ property :workdir, Text # Workflow work dir
32
+ property :vars, Text, :default => "{}" # Runtime vars
33
+ property :bucket, String, :default => "manband" # Bucket name for S3 storage
34
+ property :parent, Integer, :default => 0 # Parent workflow id, none by default
35
+ property :instances, Integer, :default => 0 # Number of sub workflows, if any
36
+
37
+ def isover?
38
+ # decrement terminals
39
+ @terminals = @terminals - 1
40
+ curw = nil
41
+ # Use lock if MYSQL
42
+ if ENV['MYSQL_URL'].include?("mysql")
43
+ DataMapper.repository(:default).adapter.execute("UPDATE work_flows SET terminals = terminals - 1 WHERE id="+@id.to_s);
44
+ curw = WorkFlow.get(@id)
45
+ else
46
+ curw = WorkFlow.get(@id)
47
+ curw.update(:terminals => @terminals)
48
+ end
49
+ if curw.terminals <=0
50
+ @@log.info "Workflow "+@id.to_s+" is over"
51
+ curw.update(:terminated_at => Time.now, :status => STATUS_OVER)
52
+ return true
53
+ end
54
+ return false
55
+ end
56
+
57
+ # Return an array of node names
58
+ def getnextjobs(curnode)
59
+ #fworkflow = YAML.load_file(@file)
60
+ fworkflow = BandManager.load(@file)
61
+ if fworkflow==nil
62
+ return nil
63
+ end
64
+ if fworkflow["workflow"][curnode]["next"] == nil
65
+ @@log.debug "no next node, this branch is over"
66
+ #isover?
67
+ return nil
68
+ end
69
+ nexts = fworkflow["workflow"][curnode]["next"].split(',')
70
+ if nexts[0].empty?
71
+ @@log.debug "no next node, this branch is over"
72
+ #isover?
73
+ return nil
74
+ end
75
+ return nexts
76
+ end
77
+
78
+ def parse(curnode, id = nil)
79
+ #fworkflow = YAML.load_file(@file)
80
+ fworkflow = BandManager.load(@file)
81
+ if fworkflow==nil
82
+ return nil
83
+ end
84
+ jobs = getnextjobs(curnode)
85
+ if jobs == nil
86
+ return
87
+ end
88
+ jobs.each do |job|
89
+ if Job.count(:wid => @id, :node => job) == 0
90
+ queue = ""
91
+ if fworkflow["workflow"][job]["queue"]!=nil
92
+ queue = fworkflow["workflow"][job]["queue"]
93
+ end
94
+ status = STATUS_NEW
95
+ if fworkflow["workflow"][job]["breakpoint"]!=nil
96
+ @@log.debug "Node "+job+" has a breakpoint set"
97
+ status = STATUS_SUSPEND
98
+ end
99
+ store = STORE_NO
100
+ if (!fworkflow["options"].nil? && fworkflow["options"]["store"] == "all") || fworkflow["workflow"][job]["store"] == true
101
+ @@log.debug "Add store option for job "+job
102
+ store = STORE_DO
103
+ end
104
+ workdir = FlowConfig.getjobdir(@workdir)
105
+ if curnode == "root"
106
+ workdir = self.workdir + "/root";
107
+ end
108
+ newjob = Job.new(:wid => @id, :node => job, :command => "", :status => status, :instances => 0, :maxinstances => 0, :queue => queue, :workdir => FlowConfig.getjobdir(@workdir), :store => store)
109
+ newjob.save
110
+ if id != nil
111
+ @@log.debug "Add link "+id.to_s+"->"+newjob.id.to_s+","+newjob.node
112
+ link = JobLink.new(:wid => @id, :from => id, :to => newjob.id)
113
+ link.save
114
+ end
115
+ parse(job,newjob.id)
116
+ else
117
+ if id!=nil
118
+ # Already declared, just add link
119
+ linkedjob = Job.first(:wid => @id, :node => job)
120
+ @@log.debug "Add link "+id.to_s+"->"+linkedjob.id.to_s+","+linkedjob.node
121
+ link = JobLink.new(:wid => @id, :from => id, :to => linkedjob.id)
122
+ link.save
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ # Update in command the runtime vars
129
+ # return modified command
130
+ def setruntimevars(varexpr,command)
131
+ newcommand = String.new(command)
132
+ runtimevars = JSON.parse(self.vars)
133
+ if runtimevars[varexpr]!=nil
134
+ @@log.debug "Replace in command #var."+varexpr+"# by "+runtimevars[varexpr]
135
+ newcommand["#var."+varexpr+"#"]= runtimevars[varexpr]
136
+ else
137
+ # runtime var is node defined
138
+ return nil
139
+ end
140
+ return newcommand
141
+ end
142
+
143
+ # Return command for the node in the workflow
144
+ def getnodecommand(curnode)
145
+ #fworkflow = YAML.load_file(@file)
146
+ fworkflow = BandManager.load(@file)
147
+ if fworkflow==nil
148
+ return nil
149
+ end
150
+ maincommand = fworkflow["workflow"][curnode]["command"]
151
+ # Manage node regexp
152
+ exprs = maincommand.scan(/#(node|var)\.(.*?)#/)
153
+ if exprs.length == 0
154
+ return [ fworkflow["workflow"][curnode]["command"] ]
155
+ end
156
+ subnodefilelist = Hash.new
157
+ multinode=nil
158
+ for reg in 0..exprs.length-1
159
+ @@log.debug "Expr "+": "+exprs[reg][0]+" "+exprs[reg][1]
160
+ if exprs[reg][0] == "var"
161
+ maincommand = setruntimevars(exprs[reg][1],maincommand)
162
+ if maincommand == nil
163
+ @@log.error "Runtime var "+exprs[reg][1]+" is not defined for workflow "+@id.to_s
164
+ return nil
165
+ end
166
+ end
167
+ if exprs[reg][0] == "node"
168
+ # Get regexp for this node
169
+ subnode = exprs[reg][1]
170
+ # If regexp is empty, we jsut want the directory
171
+ if fworkflow["workflow"][curnode][subnode]==nil
172
+ return nil
173
+ end
174
+ if fworkflow["workflow"][curnode][subnode]['regexp'].strip == ''
175
+ subnoderegexp = nil
176
+ else
177
+ subnoderegexp = Regexp.new(fworkflow["workflow"][curnode][subnode]['regexp'])
178
+ end
179
+ # List all files for this node regexp
180
+ if subnode.match(/local/)
181
+ # Local files reference
182
+ nodepath = fworkflow["workflow"][curnode][subnode]['url']
183
+ else
184
+ # An other node reference
185
+ if subnode == 'root'
186
+ nodepath = self.workdir+'/root';
187
+ else
188
+ subjob = Job.first(:wid => @id, :node => subnode)
189
+ nodepath = subjob.workdir
190
+ end
191
+ end
192
+
193
+ filelist = Array.new
194
+ if subnoderegexp == nil
195
+ filelist.push(nodepath+"/")
196
+ else
197
+ if !File.exists?(nodepath)
198
+ @@log.error("path does not exists!")
199
+ return nil
200
+ end
201
+ Dir.new(nodepath).entries.each do |n|
202
+ if subnoderegexp.match(n)
203
+ filelist.push(nodepath+"/"+n)
204
+ end
205
+ end
206
+ end
207
+ @@log.debug "File list "+subnode+": "+filelist.to_s
208
+ # update file list per node regexp in the command
209
+ subnodefilelist[subnode]=filelist
210
+ if filelist.length>1
211
+ if multinode!=nil
212
+ # We do not support multiple lists in same command (N*N*N*....)
213
+ return nil
214
+ else
215
+ multinode = subnode
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ # TODO manage local URI regexp (or remote)
222
+ # If remote: list then download
223
+
224
+ # Now create an array of command with file lists substitution
225
+ commands = Array.new
226
+ subnodefilelist.each do |key,slist|
227
+ if slist[0] == nil
228
+ return nil
229
+ end
230
+ if key != multinode
231
+ # replace, one match only allowed here
232
+ maincommand["#node."+key+"#"]= slist[0]
233
+ end
234
+ end
235
+ if multinode!=nil
236
+ @@log.debug "Multinode: "+multinode
237
+ subnodefilelist[multinode].each do |file|
238
+ newcommand = String.new(maincommand)
239
+ @@log.debug "Command: "+newcommand
240
+ newcommand["#node."+multinode+"#"]= file
241
+ commands.push(newcommand)
242
+ end
243
+ else
244
+ commands.push(maincommand)
245
+ end
246
+ @@log.debug "Commands: "+commands.to_s
247
+ return commands
248
+ end
249
+
250
+ def clean
251
+ workflow = WorkFlow.get(@id)
252
+ if workflow.workdir == nil
253
+ return
254
+ end
255
+ if File.directory? workflow.workdir
256
+ FileUtils.rm_rf(workflow.workdir)
257
+ end
258
+ end
259
+
260
+ end
261
+
262
+ # Table hosting workflow handler messages
263
+ class BandMessage
264
+ include DataMapper::Resource
265
+
266
+ property :id, Serial # An auto-increment integer key
267
+ property :wid, Integer # Workflow id
268
+ property :message, Text # Message text
269
+
270
+ end
271
+
272
+ DataMapper.finalize
273
+ DataMapper.auto_upgrade!
274
+
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: manband
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
+ platform: ruby
12
+ authors:
13
+ - Olivier Sallou
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-10 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: amqp
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: json
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: data_mapper
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: dm-mysql-adapter
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: zip
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ type: :runtime
89
+ version_requirements: *id005
90
+ - !ruby/object:Gem::Dependency
91
+ name: mb-minion
92
+ prerelease: false
93
+ requirement: &id006 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ type: :runtime
103
+ version_requirements: *id006
104
+ - !ruby/object:Gem::Dependency
105
+ name: mb-aws-s3
106
+ prerelease: false
107
+ requirement: &id007 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ hash: 3
113
+ segments:
114
+ - 0
115
+ version: "0"
116
+ type: :runtime
117
+ version_requirements: *id007
118
+ description:
119
+ email: olivier.sallou@irisa.fr
120
+ executables: []
121
+
122
+ extensions: []
123
+
124
+ extra_rdoc_files:
125
+ - README
126
+ - LICENSE
127
+ files:
128
+ - bin/manband.rb
129
+ - bin/remoteband.rb
130
+ - bin/jobhandler.rb
131
+ - bin/workflowhandler.rb
132
+ - bin/userband.rb
133
+ - lib/manband/store.rb
134
+ - lib/manband/bandmanager.rb
135
+ - lib/manband/user.rb
136
+ - lib/manband/workflow.rb
137
+ - lib/manband/job.rb
138
+ - lib/manband/flowconfig.rb
139
+ - README
140
+ - LICENSE
141
+ homepage: http://gforge.inria.fr/projects/manband
142
+ licenses:
143
+ - CeCILL-C
144
+ post_install_message:
145
+ rdoc_options: []
146
+
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
157
+ version: "0"
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 3
164
+ segments:
165
+ - 0
166
+ version: "0"
167
+ requirements: []
168
+
169
+ rubyforge_project:
170
+ rubygems_version: 1.8.15
171
+ signing_key:
172
+ specification_version: 3
173
+ summary: Workflow orchestrator based on RabbitMQ
174
+ test_files: []
175
+