patriot-workflow-scheduler 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/bin/patriot +8 -0
- data/bin/patriot-init +35 -0
- data/lib/patriot.rb +11 -0
- data/lib/patriot/command.rb +71 -0
- data/lib/patriot/command/base.rb +199 -0
- data/lib/patriot/command/command_group.rb +43 -0
- data/lib/patriot/command/command_macro.rb +141 -0
- data/lib/patriot/command/composite.rb +49 -0
- data/lib/patriot/command/parser.rb +78 -0
- data/lib/patriot/command/sh_command.rb +42 -0
- data/lib/patriot/controller.rb +2 -0
- data/lib/patriot/controller/package_controller.rb +81 -0
- data/lib/patriot/controller/worker_admin_controller.rb +159 -0
- data/lib/patriot/job_store.rb +66 -0
- data/lib/patriot/job_store/base.rb +159 -0
- data/lib/patriot/job_store/factory.rb +19 -0
- data/lib/patriot/job_store/in_memory_store.rb +252 -0
- data/lib/patriot/job_store/job.rb +118 -0
- data/lib/patriot/job_store/job_ticket.rb +30 -0
- data/lib/patriot/job_store/rdb_job_store.rb +353 -0
- data/lib/patriot/tool.rb +2 -0
- data/lib/patriot/tool/batch_parser.rb +102 -0
- data/lib/patriot/tool/patriot_command.rb +48 -0
- data/lib/patriot/tool/patriot_commands/execute.rb +92 -0
- data/lib/patriot/tool/patriot_commands/job.rb +62 -0
- data/lib/patriot/tool/patriot_commands/plugin.rb +41 -0
- data/lib/patriot/tool/patriot_commands/register.rb +77 -0
- data/lib/patriot/tool/patriot_commands/upgrade.rb +24 -0
- data/lib/patriot/tool/patriot_commands/validate.rb +84 -0
- data/lib/patriot/tool/patriot_commands/worker.rb +35 -0
- data/lib/patriot/tool/patriot_commands/worker_admin.rb +60 -0
- data/lib/patriot/util.rb +14 -0
- data/lib/patriot/util/config.rb +58 -0
- data/lib/patriot/util/config/base.rb +22 -0
- data/lib/patriot/util/config/inifile_config.rb +63 -0
- data/lib/patriot/util/cron_format_parser.rb +104 -0
- data/lib/patriot/util/date_util.rb +200 -0
- data/lib/patriot/util/db_client.rb +65 -0
- data/lib/patriot/util/db_client/base.rb +142 -0
- data/lib/patriot/util/db_client/hash_record.rb +53 -0
- data/lib/patriot/util/db_client/record.rb +25 -0
- data/lib/patriot/util/logger.rb +24 -0
- data/lib/patriot/util/logger/facade.rb +33 -0
- data/lib/patriot/util/logger/factory.rb +59 -0
- data/lib/patriot/util/logger/log4r_factory.rb +111 -0
- data/lib/patriot/util/logger/webrick_log_factory.rb +47 -0
- data/lib/patriot/util/param.rb +73 -0
- data/lib/patriot/util/retry.rb +30 -0
- data/lib/patriot/util/script.rb +52 -0
- data/lib/patriot/util/system.rb +120 -0
- data/lib/patriot/worker.rb +35 -0
- data/lib/patriot/worker/base.rb +153 -0
- data/lib/patriot/worker/info_server.rb +90 -0
- data/lib/patriot/worker/job_store_server.rb +32 -0
- data/lib/patriot/worker/multi_node_worker.rb +157 -0
- data/lib/patriot/worker/servlet.rb +23 -0
- data/lib/patriot/worker/servlet/job_servlet.rb +128 -0
- data/lib/patriot/worker/servlet/worker_status_servlet.rb +44 -0
- data/skel/batch/sample/daily/test.pbc +4 -0
- data/skel/config/patriot.ini +21 -0
- data/skel/public/css/bootstrap.css +2495 -0
- data/skel/public/css/original.css +54 -0
- data/skel/public/js/bootstrap-alerts.js +124 -0
- data/skel/public/js/bootstrap-buttons.js +64 -0
- data/skel/public/js/bootstrap-dropdown.js +55 -0
- data/skel/public/js/bootstrap-modal.js +260 -0
- data/skel/public/js/bootstrap-popover.js +90 -0
- data/skel/public/js/bootstrap-scrollspy.js +107 -0
- data/skel/public/js/bootstrap-tabs.js +80 -0
- data/skel/public/js/bootstrap-twipsy.js +321 -0
- data/skel/public/js/jquery-1.6.4.min.js +4 -0
- data/skel/public/templates/_jobs.erb +97 -0
- data/skel/public/templates/job.erb +119 -0
- data/skel/public/templates/jobs.erb +21 -0
- data/skel/public/templates/jobs_deleted.erb +6 -0
- data/skel/public/templates/layout.erb +103 -0
- data/skel/public/templates/state_updated.erb +6 -0
- metadata +235 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'patriot/util'
|
2
|
+
|
3
|
+
module Patriot
|
4
|
+
module JobStore
|
5
|
+
# base class of JobStore
|
6
|
+
class Base
|
7
|
+
include Patriot::Util::Logger
|
8
|
+
|
9
|
+
# @param [String] store_id identifier of this store
|
10
|
+
# @param [Patriot::Util::Config::Base] config configuration of this store
|
11
|
+
def initialize(store_id, config)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
# register the given jobs with the given update_id
|
16
|
+
# @param [Integer] update_id
|
17
|
+
# @param [Array] jobs a list of jobs to be registered
|
18
|
+
def register(update_id, jobs)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
# check whether the command can be stored as a job to this job_store
|
23
|
+
# @param [Patriot::Command::Base] command
|
24
|
+
# @return [Boolean] true if the command can be converted to a job
|
25
|
+
def acceptable?(command)
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
# get job tickets for jobs which are ready to execute
|
30
|
+
# @param [String] host the host name of the client
|
31
|
+
# @param [Array] nodes array of nodes on the client
|
32
|
+
# @param opts [Hash]
|
33
|
+
# @option opts [Integer] :fetch_limit the max number of tickets
|
34
|
+
def get_job_tickets(host, nodes, opts = {})
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
# offer to execute a job specified with a job_ticket
|
39
|
+
# If the job is ready to execute, the state of job is set to {Patriot::JobStore::JobState::RUNNING}.
|
40
|
+
# A response of this method is a Hash including
|
41
|
+
# * :execution_id the identifier of the execution (used to identify history record)
|
42
|
+
# * :command an instance of command for the offered job
|
43
|
+
# @param [Patriot::JobStore::JobTicket] job_ticket the ticket of the job of which execution is offered.
|
44
|
+
# @return [Hash] response for the offer
|
45
|
+
def offer_to_execute(job_ticket)
|
46
|
+
raise NotImplementedError
|
47
|
+
end
|
48
|
+
|
49
|
+
# report completion status a job specified with a job_ticket
|
50
|
+
# The state of the job should be changed according to the completion status.
|
51
|
+
# @param [Patriot::JobStore::JobTicket] job_ticket the ticket of the job of which completion is reported
|
52
|
+
# @return [Boolean] true if the job exists and the state is updated, otherwise false
|
53
|
+
def report_completion_status(job_ticket)
|
54
|
+
raise NotImplementedError
|
55
|
+
end
|
56
|
+
|
57
|
+
# set jobs state
|
58
|
+
# @param [Integer] update_id
|
59
|
+
# @param [Array] job_ids list of job_ids
|
60
|
+
# @param [Integer] new_state new state of the job.
|
61
|
+
def set_state(update_id, job_ids, new_state)
|
62
|
+
raise NotImplementedError
|
63
|
+
end
|
64
|
+
|
65
|
+
# get a job
|
66
|
+
# @param [String] job_id
|
67
|
+
# @param [Hash] opts
|
68
|
+
# @option opts [String] :include_dependency include jobs with 1-hop dependency
|
69
|
+
# @return [Patrio::JobStore::Job] in case of include_dependency is true,
|
70
|
+
# jobs in dependency set to :consumers/:producers as a hash from job_id to state
|
71
|
+
def get(job_id, opts={})
|
72
|
+
job = get_job(job_id)
|
73
|
+
return if job.nil?
|
74
|
+
if opts[:include_dependency] == true
|
75
|
+
job['consumers'] = get_consumers(job[Patriot::Command::PRODUCTS_ATTR]) || {}
|
76
|
+
job['producers'] = get_producers(job[Patriot::Command::REQUISITES_ATTR]) ||{}
|
77
|
+
end
|
78
|
+
return job
|
79
|
+
end
|
80
|
+
|
81
|
+
# get a job data
|
82
|
+
# @param [String] job_id
|
83
|
+
# @return [Patriot::JobStore::Job]
|
84
|
+
def get_job(job_id)
|
85
|
+
raise NotImplementedError
|
86
|
+
end
|
87
|
+
|
88
|
+
# get producers of products
|
89
|
+
# @param [Array] products a list of product name
|
90
|
+
# @param [Hash] opts
|
91
|
+
# @option opt [Array] :include_attrs a list of attribute included in results
|
92
|
+
# @return [Hash] a hash from job_id to its state
|
93
|
+
def get_producers(products, opts = {:include_attrs => [Patriot::Command::STATE_ATTR]})
|
94
|
+
raise NotImplementedError
|
95
|
+
end
|
96
|
+
|
97
|
+
# get consumers of products
|
98
|
+
# @param [Array] products a list of product name
|
99
|
+
# @param [Hash] opts
|
100
|
+
# @option opt [Array] :include_attrs a list of attribute included in results
|
101
|
+
# @return [Hash] a hash from job_id to its state
|
102
|
+
def get_consumers(products, opts = {:include_attrs => [Patriot::Command::STATE_ATTR]})
|
103
|
+
raise NotImplementedError
|
104
|
+
end
|
105
|
+
|
106
|
+
# get execution histories of the specified job
|
107
|
+
# @param [String] job_id
|
108
|
+
# @param [Hash] opts
|
109
|
+
# @option opts [Integer] :limit a max number of history records (default 1)
|
110
|
+
# @option opts [Symbol] :order order of record [:DESC or :ASC, default is :DESC]
|
111
|
+
def get_execution_history(job_id, opts = {})
|
112
|
+
raise NotImplementedError
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param [Patriot::JobStore::JobState] state
|
116
|
+
# @param [Hash] opts
|
117
|
+
# @option ops [Integer] :limit a max nubmer of jobs fetched at once
|
118
|
+
# @option ops [Integer] :offset the number of records skipped before beginning to include to a result
|
119
|
+
# @option ops [String] :filter_exp additional condition on job_id in a LIKE expression
|
120
|
+
# @return [Array] an array of job_id which is in the given state
|
121
|
+
def find_jobs_by_state(state, opts = {})
|
122
|
+
raise NotImplementedError
|
123
|
+
end
|
124
|
+
|
125
|
+
# @param [Hash] opts
|
126
|
+
# @option [Array<Patriot::JobStore::JobState>] :ignore_states
|
127
|
+
# @return [Hash<Patriot::JobStore::JobState, Integer>] a hash from job state to the number of jobs in the state
|
128
|
+
def get_job_size(opts = {})
|
129
|
+
raise NotImplementedError
|
130
|
+
end
|
131
|
+
|
132
|
+
# delete the job from this job_store
|
133
|
+
# @param [String] job_id
|
134
|
+
def delete_job(job_id)
|
135
|
+
raise NotImplementedError
|
136
|
+
end
|
137
|
+
|
138
|
+
# Process subsequent jobs with a given block.
|
139
|
+
# The block is called for each dependency depth.
|
140
|
+
# @param job_ids [Array<String>]
|
141
|
+
# @yieldparam job_store [Patriot::JobStore::Base] this job_store
|
142
|
+
# @yieldparam jobs [Patriot::JobStore::Job] subsequet jobs
|
143
|
+
def process_subsequent(job_ids, &blk)
|
144
|
+
products = job_ids.map{|jid|
|
145
|
+
job = get_job(jid)
|
146
|
+
job.nil? ? nil : job[Patriot::Command::PRODUCTS_ATTR]
|
147
|
+
}.compact.flatten
|
148
|
+
consumers = get_consumers(products)
|
149
|
+
while !consumers.empty?
|
150
|
+
jobs = consumers.keys.map{|jid| get_job(jid)}.compact
|
151
|
+
yield self, jobs
|
152
|
+
products = jobs.map{|j| j[Patriot::Command::PRODUCTS_ATTR]}.compact.flatten
|
153
|
+
consumers = get_consumers(products)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Patriot
|
2
|
+
module JobStore
|
3
|
+
# a moulde for a factory method of JobStores
|
4
|
+
module Factory
|
5
|
+
# create JobStore for given store_id based on the configuration
|
6
|
+
# @param store_id [String] JobStore ID to identify configuration parameters
|
7
|
+
# @param config [Patriot::Util::Config::Base] configuration to create a JobStore
|
8
|
+
# @return [Patriot::JobStore::Base]
|
9
|
+
def create_jobstore(store_id, config)
|
10
|
+
cls = config.get([Patriot::JobStore::CONFIG_PREFIX, store_id, "class"].join("."))
|
11
|
+
# TODO set default store
|
12
|
+
raise "class for job store #{store_id} is not specified" if cls.nil?
|
13
|
+
job_store = eval(cls).new(store_id, config)
|
14
|
+
return job_store
|
15
|
+
end
|
16
|
+
module_function :create_jobstore
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Patriot
|
4
|
+
module JobStore
|
5
|
+
# a JobStore implementation on memory
|
6
|
+
class InMemoryStore < Patriot::JobStore::Base
|
7
|
+
include Patriot::Util::Logger
|
8
|
+
include Patriot::Util::Retry
|
9
|
+
|
10
|
+
# @see Patriot::JobStore::Base#initialize
|
11
|
+
def initialize(store_id, config)
|
12
|
+
@config = config
|
13
|
+
@logger = create_logger(config)
|
14
|
+
@mutex = Mutex.new
|
15
|
+
@jobs = {} # hash from job_id to job content in hash
|
16
|
+
# hash from state to list of job_id
|
17
|
+
@job_states = {Patriot::JobStore::JobState::INIT => [],
|
18
|
+
Patriot::JobStore::JobState::SUCCEEDED => [Patriot::JobStore::INITIATOR_JOB_ID],
|
19
|
+
Patriot::JobStore::JobState::WAIT => [],
|
20
|
+
Patriot::JobStore::JobState::RUNNING => [],
|
21
|
+
Patriot::JobStore::JobState::SUSPEND => [],
|
22
|
+
Patriot::JobStore::JobState::FAILED => []}
|
23
|
+
@producers = {} # hash from job_id to produces products
|
24
|
+
@consumers = {} # hash from job_id to referece products
|
25
|
+
@job_history = {} # hash from job_id to a array of its execution hisotry
|
26
|
+
end
|
27
|
+
|
28
|
+
# @see Patriot::JobStore::Base#register
|
29
|
+
def register(update_id, jobs)
|
30
|
+
jobs.each{|job| raise "#{job.job_id} is not acceptable" unless acceptable?(job) }
|
31
|
+
@mutex.synchronize do
|
32
|
+
jobs.each do |job|
|
33
|
+
job_id = job.job_id.to_sym
|
34
|
+
job.update_id = update_id
|
35
|
+
if @jobs.has_key?(job_id) # update
|
36
|
+
job[Patriot::Command::STATE_ATTR] ||= @jobs[job_id][Patriot::Command::STATE_ATTR]
|
37
|
+
else # insert
|
38
|
+
# set default state
|
39
|
+
job[Patriot::Command::STATE_ATTR] ||= Patriot::JobStore::JobState::INIT
|
40
|
+
end
|
41
|
+
@jobs[job_id] = job
|
42
|
+
@producers[job_id] = job[Patriot::Command::PRODUCTS_ATTR] unless job[Patriot::Command::PRODUCTS_ATTR].nil?
|
43
|
+
@consumers[job_id] = job[Patriot::Command::REQUISITES_ATTR] unless job[Patriot::Command::REQUISITES_ATTR].nil?
|
44
|
+
if job[Patriot::Command::STATE_ATTR] == Patriot::JobStore::JobState::INIT
|
45
|
+
_set_state(job_id, Patriot::JobStore::JobState::WAIT)
|
46
|
+
else
|
47
|
+
_set_state(job_id, job[Patriot::Command::STATE_ATTR])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @see Patriot::JobStore::Base#acceptable?
|
54
|
+
def acceptable?(job)
|
55
|
+
raise "invalid class #{job.class}" unless job.is_a?(Patriot::JobStore::Job)
|
56
|
+
return true
|
57
|
+
end
|
58
|
+
|
59
|
+
# @see Patriot::JobStore::Base#get_job_tickets
|
60
|
+
def get_job_tickets(host, nodes, options = {})
|
61
|
+
nodes = [nodes] unless nodes.is_a?(Array)
|
62
|
+
@mutex.synchronize do
|
63
|
+
return @job_states[Patriot::JobStore::JobState::WAIT].map do |wid|
|
64
|
+
job = @jobs[wid]
|
65
|
+
# check host and node
|
66
|
+
next unless job[Patriot::Command::EXEC_NODE_ATTR].nil? || nodes.include?(job[Patriot::Command::EXEC_NODE_ATTR])
|
67
|
+
next unless job[Patriot::Command::EXEC_HOST_ATTR].nil? || host == job[Patriot::Command::EXEC_HOST_ATTR]
|
68
|
+
next unless job[Patriot::Command::START_DATETIME_ATTR].nil? || Time.now > job[Patriot::Command::START_DATETIME_ATTR]
|
69
|
+
# check dependency
|
70
|
+
reference = @consumers[wid] || []
|
71
|
+
producers = @producers.map{|pid, prods| pid unless (prods & reference).empty?}.compact
|
72
|
+
next if !reference.empty? && producers.empty? # no producer exists
|
73
|
+
next if producers.any?{|pjid| !@job_states[Patriot::JobStore::JobState::SUCCEEDED].include?(pjid)}
|
74
|
+
JobTicket.new(wid.to_s, job.update_id, job[Patriot::Command::EXEC_NODE_ATTR])
|
75
|
+
end.compact
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# @see Patriot::JobStore::Base#offer_to_execute
|
80
|
+
def offer_to_execute(job_ticket)
|
81
|
+
job_id = job_ticket.job_id.to_sym
|
82
|
+
update_id = job_ticket.update_id
|
83
|
+
@mutex.synchronize do
|
84
|
+
unless _check_and_set_state(job_id, update_id, Patriot::JobStore::JobState::WAIT, Patriot::JobStore::JobState::RUNNING)
|
85
|
+
@logger.debug("execution of job: #{job_id} is skipped")
|
86
|
+
return
|
87
|
+
end
|
88
|
+
job = @jobs[job_id]
|
89
|
+
raise "no entry found for #{job_ticket}" if job.nil?
|
90
|
+
begin
|
91
|
+
# TODO make the max number of histories configurable and keep multiple histories
|
92
|
+
execution_id = Time.now.to_i
|
93
|
+
@job_history[job_id] = [{:id => execution_id,
|
94
|
+
:job_id => job_id.to_s,
|
95
|
+
:host => job_ticket.exec_host,
|
96
|
+
:node => job_ticket.exec_node,
|
97
|
+
:thread => job_ticket.exec_thread,
|
98
|
+
:begin_at => Time.now
|
99
|
+
}]
|
100
|
+
return {:execution_id => execution_id, :command => job.to_command(@config)}
|
101
|
+
rescue Exception => e
|
102
|
+
_check_and_set_state(job_id, update_id, Patriot::JobStore::JobState::RUNNING, Patriot::JobStore::JobState::FAILED)
|
103
|
+
raise e
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# @see Patriot::JobStore::Base#report_completion_status
|
109
|
+
def report_completion_status(job_ticket)
|
110
|
+
job_id = job_ticket.job_id.to_sym
|
111
|
+
update_id = job_ticket.update_id
|
112
|
+
exit_code = job_ticket.exit_code
|
113
|
+
raise "exit code is not set " if exit_code.nil?
|
114
|
+
state = Patriot::JobStore::EXIT_CODE_TO_STATE[exit_code]
|
115
|
+
raise "invalid exit code #{exit_code} " if state.nil?
|
116
|
+
@mutex.synchronize do
|
117
|
+
# TODO save finish_time to history server
|
118
|
+
last_history = @job_history[job_id]
|
119
|
+
raise "illegal state job_history is not set for #{job_id}" if last_history.nil? || last_history.empty?
|
120
|
+
last_history = last_history[0]
|
121
|
+
# TODO make the max number of histories configurable and keep multiple histories
|
122
|
+
@job_history[job_id] = [last_history.merge({:exit_code => exit_code, :end_at => Time.now, :description => job_ticket.description})]
|
123
|
+
return _check_and_set_state(job_id, update_id, Patriot::JobStore::JobState::RUNNING, state)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# @see Patriot::JobStore::Base#set_state
|
128
|
+
def set_state(update_id, job_ids, new_state)
|
129
|
+
@mutex.synchronize do
|
130
|
+
job_ids = job_ids.map do |jid|
|
131
|
+
@jobs[jid.to_sym][Patriot::Command::STATE_ATTR] = new_state
|
132
|
+
jid.to_sym
|
133
|
+
end
|
134
|
+
@job_states.each do |s,jobs|
|
135
|
+
next if s == new_state
|
136
|
+
@job_states[s] -= job_ids
|
137
|
+
end
|
138
|
+
@job_states[new_state] += job_ids
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# @see Patriot::JobStore::Base#get_job
|
143
|
+
def get_job(job_id)
|
144
|
+
return @jobs[job_id.to_sym]
|
145
|
+
end
|
146
|
+
|
147
|
+
# @see Patriot::JobStore::Base#get_producers
|
148
|
+
def get_producers(products, opts = {:include_attrs => [Patriot::Command::STATE_ATTR]})
|
149
|
+
opts = {:include_attrs => []}.merge(opts)
|
150
|
+
products = [products] unless products.is_a?(Array)
|
151
|
+
producers = {}
|
152
|
+
products.each{|product|
|
153
|
+
@producers.map{|pid, prods|
|
154
|
+
producers[pid.to_s] = @jobs[pid].filter_attributes(opts[:include_attrs]) if prods.include?(product)
|
155
|
+
}
|
156
|
+
}
|
157
|
+
return producers
|
158
|
+
end
|
159
|
+
|
160
|
+
# @see Patriot::JobStore::Base#get_consumers
|
161
|
+
def get_consumers(products, opts = {:include_attrs => [Patriot::Command::STATE_ATTR]})
|
162
|
+
opts = {:include_attrs => []}.merge(opts)
|
163
|
+
products = [products] unless products.is_a?(Array)
|
164
|
+
consumers = {}
|
165
|
+
products.each{|product|
|
166
|
+
@consumers.map{|pid, prods|
|
167
|
+
consumers[pid.to_s] = @jobs[pid].filter_attributes(opts[:include_attrs]) if prods.include?(product)
|
168
|
+
}
|
169
|
+
}
|
170
|
+
return consumers
|
171
|
+
end
|
172
|
+
|
173
|
+
# @see Patriot::JobStore::Base#find_jobs_by_state
|
174
|
+
def find_jobs_by_state(state, opts = {})
|
175
|
+
all_records = @job_states[state] - [Patriot::JobStore::INITIATOR_JOB_ID]
|
176
|
+
size = all_records.size
|
177
|
+
opts = {:limit => size, :offset => 0}.merge(opts)
|
178
|
+
filter = opts.has_key?(:filter_exp) ? Regexp.new(opts[:filter_exp].gsub(/(?<!\\)%/,'.*').gsub(/(?<!\\)_/,'.')) : nil
|
179
|
+
result = []
|
180
|
+
opts[:offset].upto(size).each do |i|
|
181
|
+
break if i >= size
|
182
|
+
break if result.size >= opts[:limit]
|
183
|
+
job_id = all_records[size - 1 - i].to_s
|
184
|
+
next if !filter.nil? && !filter.match(job_id)
|
185
|
+
result << job_id
|
186
|
+
end
|
187
|
+
return result
|
188
|
+
end
|
189
|
+
|
190
|
+
# @see Patriot::JobStore::Base#get_execution_history
|
191
|
+
def get_execution_history(job_id, opts = {})
|
192
|
+
opts = {:limit => 1, :order => :DESC}
|
193
|
+
return @job_history[job_id.to_sym] || []
|
194
|
+
end
|
195
|
+
|
196
|
+
# @see Patriot::JobStore::Base#get_job_size
|
197
|
+
def get_job_size(opts = {})
|
198
|
+
opts = {:ignore_states => []}.merge(opts)
|
199
|
+
sizes = {}
|
200
|
+
@job_states.each do |s,js|
|
201
|
+
next if opts[:ignore_states].include?(s)
|
202
|
+
sizes[s] = js.size
|
203
|
+
sizes[s] = sizes[s] -1 if s == Patriot::JobStore::JobState::SUCCEEDED
|
204
|
+
end
|
205
|
+
return sizes
|
206
|
+
end
|
207
|
+
|
208
|
+
# @see Patriot::JobStore::Base#delete_job
|
209
|
+
def delete_job(job_id)
|
210
|
+
job_id = job_id.to_sym
|
211
|
+
@mutex.synchronize do
|
212
|
+
@job_states.each{|s,js| js.delete(job_id)}
|
213
|
+
@jobs.delete(job_id)
|
214
|
+
@producers.delete(job_id)
|
215
|
+
@consumers.delete(job_id)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
### private
|
220
|
+
|
221
|
+
# not thread safe. should be locked around invocation
|
222
|
+
# @param [Symbol] job_id
|
223
|
+
# @param [Integer] update_id
|
224
|
+
# @param [Integer] prev_state
|
225
|
+
# @param [Integer] post_state
|
226
|
+
# @return [Boolean] true if state is changed, otherwise false
|
227
|
+
def _check_and_set_state(job_id, update_id, prev_state, post_state)
|
228
|
+
return false unless @job_states[prev_state].include?(job_id)
|
229
|
+
return false unless @jobs[job_id].update_id == update_id
|
230
|
+
_set_state(job_id, post_state)
|
231
|
+
return true
|
232
|
+
end
|
233
|
+
private :_check_and_set_state
|
234
|
+
|
235
|
+
# set job state
|
236
|
+
# @param [String] job_id
|
237
|
+
# @param [Integer] new_state new state of the job. set nil to keep_state
|
238
|
+
def _set_state(job_id, new_state)
|
239
|
+
return if new_state.nil?
|
240
|
+
job_id = job_id.to_sym
|
241
|
+
@job_states.each do |s,jobs|
|
242
|
+
deleted_id = jobs.delete(job_id)
|
243
|
+
break unless deleted_id.nil?
|
244
|
+
end
|
245
|
+
@jobs[job_id][Patriot::Command::STATE_ATTR] = new_state
|
246
|
+
@job_states[new_state] << job_id
|
247
|
+
end
|
248
|
+
private :_set_state
|
249
|
+
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Patriot
|
2
|
+
module JobStore
|
3
|
+
# a record stored in jobstore
|
4
|
+
class Job
|
5
|
+
|
6
|
+
attr_accessor :job_id, :update_id, :attributes
|
7
|
+
|
8
|
+
# @param job_id [String] the identifier of the job
|
9
|
+
def initialize(job_id)
|
10
|
+
@job_id = job_id
|
11
|
+
@attributes = {
|
12
|
+
Patriot::Command::PRIORITY_ATTR => Patriot::JobStore::DEFAULT_PRIORITY,
|
13
|
+
Patriot::Command::STATE_ATTR => Patriot::JobStore::JobState::INIT
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
# set an attribute to this job
|
18
|
+
# @param k [String] attribute name
|
19
|
+
# @param v [Object] attribute value
|
20
|
+
def []=(k,v)
|
21
|
+
raise "key #{k} should be string but #{k.class}" unless k.is_a?(String)
|
22
|
+
@attributes[k] = v
|
23
|
+
end
|
24
|
+
|
25
|
+
# get an attribute to this job
|
26
|
+
# @param k [String] attribute name
|
27
|
+
# @return [Object] the attribute value
|
28
|
+
def [](k)
|
29
|
+
raise "key #{k} should be string but #{k.class}" unless k.is_a?(String)
|
30
|
+
return @attributes[k]
|
31
|
+
end
|
32
|
+
|
33
|
+
# delete an attribute
|
34
|
+
# @param k [String] attribute name
|
35
|
+
# @return [Object] the deleted attribute value
|
36
|
+
def delete(k)
|
37
|
+
raise "key #{k} should be string but #{k.class}" unless k.is_a?(String)
|
38
|
+
return @attributes.delete(k)
|
39
|
+
end
|
40
|
+
|
41
|
+
# read the content of command
|
42
|
+
# @param command [Patriot::Command::Base] a command loaded to this job
|
43
|
+
def read_command(command)
|
44
|
+
Patriot::Command::COMMON_ATTRIBUTES.each do |attr|
|
45
|
+
value = command.instance_variable_get("@#{attr}".to_sym)
|
46
|
+
self[attr] = value unless value.nil?
|
47
|
+
end
|
48
|
+
_to_stdobj(command).each{|k,v| self[k] = v}
|
49
|
+
end
|
50
|
+
|
51
|
+
# @private
|
52
|
+
# convert a given object to an object only includes standand objects can be converted to JSON.
|
53
|
+
# in other words, convert Command instances in the object to hash
|
54
|
+
def _to_stdobj(obj)
|
55
|
+
if obj.is_a?(Patriot::Command::Base)
|
56
|
+
hash = {}
|
57
|
+
hash[Patriot::Command::COMMAND_CLASS_KEY] = obj.class.to_s.gsub(/::/, '.')
|
58
|
+
obj.class.serde_attrs.each do |attr|
|
59
|
+
value = obj.instance_variable_get("@#{attr}".to_sym)
|
60
|
+
hash[attr.to_s] = _to_stdobj(value) unless value.nil?
|
61
|
+
end
|
62
|
+
return hash
|
63
|
+
elsif obj.is_a?(Hash)
|
64
|
+
hash = {}
|
65
|
+
obj.each{|k,v| hash[k.to_s] = _to_stdobj(v)}
|
66
|
+
return hash
|
67
|
+
elsif obj.is_a?(Array)
|
68
|
+
return obj.map{|e| _to_stdobj(e)}
|
69
|
+
else
|
70
|
+
return obj
|
71
|
+
end
|
72
|
+
end
|
73
|
+
private :_to_stdobj
|
74
|
+
|
75
|
+
# @param config [Patriot::Util::Command::Base] configuration for building a command
|
76
|
+
# @return [Patriot::Command::Base] an executable for this job
|
77
|
+
def to_command(config)
|
78
|
+
raise "configuration is not set" if config.nil?
|
79
|
+
return _from_stdobj(self.attributes, config)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @private
|
83
|
+
# convert corresponding objects in the given argument into Command instances.
|
84
|
+
# @param obj [Object] a object to be deserialized
|
85
|
+
# @param config [Patriot::util::Config::Base] configuration used for deserialization
|
86
|
+
# @return [Object] a starndard object (primitive or command, or array) for the obj
|
87
|
+
def _from_stdobj(obj, config)
|
88
|
+
if obj.is_a?(Hash)
|
89
|
+
if obj.has_key?(Patriot::Command::COMMAND_CLASS_KEY)
|
90
|
+
cmd_cls = obj.delete(Patriot::Command::COMMAND_CLASS_KEY)
|
91
|
+
cmd_cls = cmd_cls.split('.').inject(Object){|c,name| c.const_get(name)}
|
92
|
+
cmd = cmd_cls.new(config)
|
93
|
+
obj.each do |k,v|
|
94
|
+
cmd.instance_variable_set("@#{k}".to_sym, _from_stdobj(v, config))
|
95
|
+
end
|
96
|
+
return cmd
|
97
|
+
else
|
98
|
+
hash = {}
|
99
|
+
obj.each{|k,v| hash[k] = _from_stdobj(v, config)}
|
100
|
+
return hash
|
101
|
+
end
|
102
|
+
elsif obj.is_a?(Array)
|
103
|
+
return obj.map{|e| _from_stdobj(e, config)}
|
104
|
+
else
|
105
|
+
return obj
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param attrs [Array<String>] a list of attribute names
|
110
|
+
# @return [Hash] a set of attribute name value pairs for specified attributes
|
111
|
+
def filter_attributes(attrs)
|
112
|
+
filtered = {}
|
113
|
+
attrs.each{|a| filtered[a] = self[a]}
|
114
|
+
return filtered
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|