patriot-workflow-scheduler 0.6.1
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.
- 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
|