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.
Files changed (79) hide show
  1. checksums.yaml +15 -0
  2. data/bin/patriot +8 -0
  3. data/bin/patriot-init +35 -0
  4. data/lib/patriot.rb +11 -0
  5. data/lib/patriot/command.rb +71 -0
  6. data/lib/patriot/command/base.rb +199 -0
  7. data/lib/patriot/command/command_group.rb +43 -0
  8. data/lib/patriot/command/command_macro.rb +141 -0
  9. data/lib/patriot/command/composite.rb +49 -0
  10. data/lib/patriot/command/parser.rb +78 -0
  11. data/lib/patriot/command/sh_command.rb +42 -0
  12. data/lib/patriot/controller.rb +2 -0
  13. data/lib/patriot/controller/package_controller.rb +81 -0
  14. data/lib/patriot/controller/worker_admin_controller.rb +159 -0
  15. data/lib/patriot/job_store.rb +66 -0
  16. data/lib/patriot/job_store/base.rb +159 -0
  17. data/lib/patriot/job_store/factory.rb +19 -0
  18. data/lib/patriot/job_store/in_memory_store.rb +252 -0
  19. data/lib/patriot/job_store/job.rb +118 -0
  20. data/lib/patriot/job_store/job_ticket.rb +30 -0
  21. data/lib/patriot/job_store/rdb_job_store.rb +353 -0
  22. data/lib/patriot/tool.rb +2 -0
  23. data/lib/patriot/tool/batch_parser.rb +102 -0
  24. data/lib/patriot/tool/patriot_command.rb +48 -0
  25. data/lib/patriot/tool/patriot_commands/execute.rb +92 -0
  26. data/lib/patriot/tool/patriot_commands/job.rb +62 -0
  27. data/lib/patriot/tool/patriot_commands/plugin.rb +41 -0
  28. data/lib/patriot/tool/patriot_commands/register.rb +77 -0
  29. data/lib/patriot/tool/patriot_commands/upgrade.rb +24 -0
  30. data/lib/patriot/tool/patriot_commands/validate.rb +84 -0
  31. data/lib/patriot/tool/patriot_commands/worker.rb +35 -0
  32. data/lib/patriot/tool/patriot_commands/worker_admin.rb +60 -0
  33. data/lib/patriot/util.rb +14 -0
  34. data/lib/patriot/util/config.rb +58 -0
  35. data/lib/patriot/util/config/base.rb +22 -0
  36. data/lib/patriot/util/config/inifile_config.rb +63 -0
  37. data/lib/patriot/util/cron_format_parser.rb +104 -0
  38. data/lib/patriot/util/date_util.rb +200 -0
  39. data/lib/patriot/util/db_client.rb +65 -0
  40. data/lib/patriot/util/db_client/base.rb +142 -0
  41. data/lib/patriot/util/db_client/hash_record.rb +53 -0
  42. data/lib/patriot/util/db_client/record.rb +25 -0
  43. data/lib/patriot/util/logger.rb +24 -0
  44. data/lib/patriot/util/logger/facade.rb +33 -0
  45. data/lib/patriot/util/logger/factory.rb +59 -0
  46. data/lib/patriot/util/logger/log4r_factory.rb +111 -0
  47. data/lib/patriot/util/logger/webrick_log_factory.rb +47 -0
  48. data/lib/patriot/util/param.rb +73 -0
  49. data/lib/patriot/util/retry.rb +30 -0
  50. data/lib/patriot/util/script.rb +52 -0
  51. data/lib/patriot/util/system.rb +120 -0
  52. data/lib/patriot/worker.rb +35 -0
  53. data/lib/patriot/worker/base.rb +153 -0
  54. data/lib/patriot/worker/info_server.rb +90 -0
  55. data/lib/patriot/worker/job_store_server.rb +32 -0
  56. data/lib/patriot/worker/multi_node_worker.rb +157 -0
  57. data/lib/patriot/worker/servlet.rb +23 -0
  58. data/lib/patriot/worker/servlet/job_servlet.rb +128 -0
  59. data/lib/patriot/worker/servlet/worker_status_servlet.rb +44 -0
  60. data/skel/batch/sample/daily/test.pbc +4 -0
  61. data/skel/config/patriot.ini +21 -0
  62. data/skel/public/css/bootstrap.css +2495 -0
  63. data/skel/public/css/original.css +54 -0
  64. data/skel/public/js/bootstrap-alerts.js +124 -0
  65. data/skel/public/js/bootstrap-buttons.js +64 -0
  66. data/skel/public/js/bootstrap-dropdown.js +55 -0
  67. data/skel/public/js/bootstrap-modal.js +260 -0
  68. data/skel/public/js/bootstrap-popover.js +90 -0
  69. data/skel/public/js/bootstrap-scrollspy.js +107 -0
  70. data/skel/public/js/bootstrap-tabs.js +80 -0
  71. data/skel/public/js/bootstrap-twipsy.js +321 -0
  72. data/skel/public/js/jquery-1.6.4.min.js +4 -0
  73. data/skel/public/templates/_jobs.erb +97 -0
  74. data/skel/public/templates/job.erb +119 -0
  75. data/skel/public/templates/jobs.erb +21 -0
  76. data/skel/public/templates/jobs_deleted.erb +6 -0
  77. data/skel/public/templates/layout.erb +103 -0
  78. data/skel/public/templates/state_updated.erb +6 -0
  79. metadata +235 -0
@@ -0,0 +1,49 @@
1
+ module Patriot
2
+ module Command
3
+ # a command which is composed of multiple sub commands
4
+ class CompositeCommand < Patriot::Command::CommandGroup
5
+ declare_command_name :composite_command
6
+ declare_command_name :composite_job
7
+ private_command_attr :contained_commands => []
8
+ volatile_attr :name, :name_suffix
9
+
10
+ # @return [String] the identifier of this composite command
11
+ # @see Patriot::Command::Base#job_id
12
+ def job_id
13
+ return "#{command_name}_#{@name}_#{@name_suffix}"
14
+ end
15
+
16
+ # @see Patriot::Command::Base#description
17
+ def description
18
+ first_job = @contained_commands.first
19
+ first_job = first_job.description unless first_job.nil?
20
+ return "#{first_job} ... (#{@contained_commands.size} jobs)"
21
+ end
22
+
23
+ # configure this composite command.
24
+ # pull up required/produced products from the sub commands
25
+ # @see Patriot::Command::Base#configure
26
+ def configure
27
+ @name_suffix ||= _date_
28
+ # don't do flatten to handle nested composite commands
29
+ @subcommands.map do |cmd|
30
+ cmd.build(@param).each do |cmd|
31
+ require cmd['requisites']
32
+ produce cmd['products']
33
+ @contained_commands << cmd
34
+ end
35
+ end
36
+ return self
37
+ end
38
+
39
+ # execute the contained commands
40
+ # @see Patriot::Command::Base#execute
41
+ def execute
42
+ @contained_commands.each do |c|
43
+ c.execute
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,78 @@
1
+ require "erb"
2
+ module Patriot
3
+ module Command
4
+
5
+ # namespace for DSL parser
6
+ module Parser
7
+
8
+ # create a command
9
+ # @param cls [Class] class of the command
10
+ # @yield block to set attributes to the command
11
+ def new_command(cls, &blk)
12
+ raise "configuration is not set" if @config.nil?
13
+ command = cls.new(@config)
14
+ command.target_datetime = @target_datetime
15
+ @macros.each{|n,b| command.batch_macro(n,&b)} unless @macros.nil?
16
+ command.instance_eval(&blk) if block_given?
17
+ return command
18
+ end
19
+
20
+ # parse DSL by processing the DSL description as block
21
+ # @param datetime [DateTime] the datetime for which the job works
22
+ # @return a list of command defined in the DSL description
23
+ def parse(datetime, blk)
24
+ self.target_datetime = datetime
25
+ self.instance_eval(blk)
26
+ return self.build({})
27
+ end
28
+
29
+ # load macro to be able to be used in the DSL
30
+ # @param macro_file [String] path to a macro file
31
+ def load_macro(macro_file)
32
+ macro_file = File.expand_path(macro_file,$home)
33
+ @logger.info "loading macro file #{macro_file}"
34
+ open(macro_file){|f|
35
+ self.instance_eval(f.read)
36
+ }
37
+ end
38
+
39
+ # define macro.
40
+ # this method is used in macro files
41
+ # @param name name of the macro
42
+ # @yieldreturn the result of the macro evaluation
43
+ def batch_macro(name, &blk)
44
+ raise "#{name} is invalid macro name (duplicate or defined method)" if respond_to?(name.to_sym)
45
+ eigenclass = class << self
46
+ self
47
+ end
48
+ @macros[name] = blk
49
+ eigenclass.send(:define_method, name, &blk)
50
+ end
51
+
52
+ # import an ERB template
53
+ # @param file [String] path to the ERB template
54
+ # @param _vars [Hash] a hash from variable name to its value used in the ERB
55
+ def import_erb_config(file, _vars)
56
+ file = File.expand_path(file,$home)
57
+ # set variables
58
+ erb = _vars.map{|k,v| "<%#{k} = #{v.inspect}%>"}.join("\n")
59
+ erb << "\n"
60
+
61
+ # read the ERB
62
+ exp=""
63
+ open(file) do |f|
64
+ erb << f.read
65
+ exp = ERB.new(erb).result(binding)
66
+ end
67
+ begin
68
+ self.instance_eval(exp)
69
+ rescue => e
70
+ @logger.error("failed to parse #{file}")
71
+ @logger.error(erb)
72
+ raise e
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
@@ -0,0 +1,42 @@
1
+ module Patriot
2
+ module Command
3
+ # a command which executes shell scripts
4
+ class ShCommand < Patriot::Command::Base
5
+ include Patriot::Util::System
6
+
7
+ declare_command_name :sh
8
+
9
+ command_attr :connector => '&&'
10
+ command_attr :commands do |cmd, a, v|
11
+ cmd.commands = v.is_a?(Array)? v : [v]
12
+ end
13
+ volatile_attr :name, :name_suffix
14
+ validate_existence :name
15
+
16
+ # @see Patriot::Command::Base#job_id
17
+ def job_id
18
+ return "#{command_name}_#{@name}_#{@name_suffix}"
19
+ end
20
+
21
+ # @see Patriot::Command::Base#configure
22
+ def configure
23
+ @name_suffix ||= _date_
24
+ return self
25
+ end
26
+
27
+ # @see Patriot::Command::Base#description
28
+ def description
29
+ return @commands.join(@connector)
30
+ end
31
+
32
+ # @see Patriot::Command::Base#execute
33
+ def execute
34
+ @logger.info "start shell command "
35
+ @commands.each do |c|
36
+ execute_command(c)
37
+ end
38
+ @logger.info "end shell command "
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,2 @@
1
+ require 'patriot/controller/package_controller'
2
+ require 'patriot/controller/worker_admin_controller'
@@ -0,0 +1,81 @@
1
+ require 'rubygems/installer'
2
+ require 'rubygems/version_option'
3
+ require 'rubygems/remote_fetcher'
4
+ require 'rubygems/dependency_installer'
5
+ require 'fileutils'
6
+
7
+ module Patriot
8
+ # a name space for controllers which encapsulate complicated operations
9
+ module Controller
10
+ # Controller class for remote management of workers
11
+ class PackageController
12
+ include Patriot::Util::Config
13
+ include Patriot::Util::Logger
14
+
15
+ # constructor
16
+ # @param config [Patriot::Util::Config::Base] configuration of this controller
17
+ def initialize(config)
18
+ @config = config
19
+ @logger = create_logger(config)
20
+ @plugin_dir = config.get(Patriot::Util::Config::PLUGIN_DIR_KEY, Patriot::Util::Config::DEFAULT_PLUGIN_DIR)
21
+ @plugin_dir = File.expand_path(@plugin_dir, $home)
22
+ end
23
+
24
+ # upgrade deployment
25
+ def upgrade(pkg = 'patriot-workflow-scheduler')
26
+ # upgrade plugins
27
+ plugins = @config.get(Patriot::Util::Config::PLUGIN_KEY, [])
28
+ plugins = [plugins] unless plugins.is_a?(Array)
29
+ plugins.each{|plugin| install_plugin(plugin, {:force => true})}
30
+
31
+ # upgrade core package
32
+ dependency = Gem::Dependency.new(pkg || 'patriot-workflow-scheduler')
33
+ path = dependency.name =~ /\.gem$/i ? dependency.name : Gem::RemoteFetcher.fetcher.download_to_cache(dependency)
34
+ installed_dir = Gem::Installer.new(path).dir
35
+ installer = Gem::DependencyInstaller.new
36
+ @logger.info "upgrade to #{dependency}"
37
+ installer.install path
38
+
39
+ public_dir = File.join(installed_dir, "skel", "public")
40
+ @logger.info "copy #{public_dir} to #{$home}"
41
+ FileUtils.cp_r(public_dir, $home)
42
+ FileUtils.cp(File.join(installed_dir, 'bin', 'patriot'), File.join($home, 'bin', 'patriot'))
43
+
44
+ end
45
+
46
+ # install plugin to plugin directory
47
+ # @param [String] plugin name of the plugin
48
+ # @param opts
49
+ # @option [Boolean] :force set true to overwrite the installation
50
+ def install_plugin(plugin, opts = {})
51
+ @logger.info "install #{plugin}"
52
+ dependency = Gem::Dependency.new plugin, opts[:version]
53
+ path = dependency.name =~ /\.gem$/i ? dependency.name : Gem::RemoteFetcher.fetcher.download_to_cache(dependency)
54
+ raise "Gem '#{plugin}' not fetchable." unless path
55
+ basename = File.basename path, '.gem'
56
+ # remvoe version
57
+ basename = basename.gsub(/-[\d]+\.[\d]+\.[\d]+$/, "")
58
+ target_dir = File.join(@plugin_dir, basename)
59
+ if opts[:force] == true
60
+ @logger.info "remove old #{target_dir}"
61
+ FileUtils.rm_r target_dir if File.exist?(target_dir)
62
+ else
63
+ raise "#{target_dir} alrady exist" if File.exist?(target_dir)
64
+ end
65
+ FileUtils.mkdir_p target_dir
66
+ if opts[:unpack]
67
+ installer = Gem::Installer.new(path, :unpack=>true)
68
+ installer.unpack target_dir
69
+ else
70
+ installer = Gem::DependencyInstaller.new
71
+ installer.install path
72
+ installed_dir = Gem::Installer.new(path).dir
73
+ init_rb = File.join(installed_dir, "init.rb")
74
+ FileUtils.cp(init_rb, target_dir)
75
+ end
76
+ @logger.info "#{path} installed: #{target_dir}'"
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,159 @@
1
+ require 'rest_client'
2
+
3
+ module Patriot
4
+ module Controller
5
+ # Controller class for remote management of workers
6
+ class WorkerAdminController
7
+ include Patriot::Util::Config
8
+ include Patriot::Util::Logger
9
+
10
+ # a command line used for start/stop workers
11
+ WORKER_COMMAND = File.join($home || Dir.pwd,'bin', 'patriot worker')
12
+ # a command line used for upgrade
13
+ UPGRADE_COMMAND = File.join($home || Dir.pwd,'bin', 'patriot upgrade')
14
+
15
+ # constructor
16
+ # @param config [Patriot::Util::Config::Base] configuration of this controller
17
+ def initialize(config)
18
+ @config = config
19
+ @logger = create_logger(config)
20
+ set_default_values
21
+ end
22
+
23
+ # @private
24
+ def set_default_values
25
+ @default_hosts = @config.get('worker_hosts') || []
26
+ @default_port = @config.get('info_server_port')
27
+ @user = @config.get('admin_user')
28
+ end
29
+ private :set_default_values
30
+
31
+ # execute block for each target hosts
32
+ # @param options [Hash]
33
+ # @option options :host a target host
34
+ # @option options :hosts a comman separated value of target hosts
35
+ # @option options :all set true to target all hosts in the configuration
36
+ # @return [Hash] a hash from host name to the result of the block
37
+ def request_to_target_hosts(options = {}, &blk)
38
+ hosts = []
39
+ port = options.has_key?(:port) ? options[:port] : @default_port
40
+ if options.has_key?(:host)
41
+ hosts = [options[:host]]
42
+ elsif options.has_key?(:hosts)
43
+ hosts = options[:hosts]
44
+ hosts = hosts.split(",") unless hosts.is_a?(Array)
45
+ elsif options[:all] == true
46
+ hosts = @default_hosts
47
+ hosts = [hosts] unless hosts.is_a?(Array)
48
+ else
49
+ raise "any host is not set"
50
+ end
51
+ results = {}
52
+ hosts.each{|h| results[h] = yield(h,port) }
53
+ return results
54
+ end
55
+
56
+ # get status of a worker or workers
57
+ # @param options @see {#request_to_target_hosts}
58
+ # @return [Hash]
59
+ # status of worker in String.
60
+ # nil for an unresponsive worker
61
+ def status(options = {})
62
+ return request_to_target_hosts(options){|h,p| get_worker_status(h,p)}
63
+ end
64
+
65
+ # get status of a worker
66
+ # @param host [String] host name of the target host
67
+ # @param port [String] port number of the worker process on the target host
68
+ # @return [String] status of the server @see {Patriot::Worker::Base}
69
+ def get_worker_status(host, port)
70
+ begin
71
+ return RestClient.get("http://#{host}:#{port}/worker")
72
+ rescue Errno::ECONNREFUSED, SocketError
73
+ return nil
74
+ end
75
+ end
76
+
77
+ # sleep target workers
78
+ # @param options @see {#request_to_target_hosts}
79
+ def sleep_worker(options = {})
80
+ return request_to_target_hosts(options){|h,p| put_worker_status(h,p,Patriot::Worker::Status::SLEEP)}
81
+ end
82
+
83
+ # wake up target workers
84
+ # @param options @see {#request_to_target_hosts}
85
+ def wake_worker(options = {})
86
+ return request_to_target_hosts(options){|h,p| put_worker_status(h,p,Patriot::Worker::Status::ACTIVE)}
87
+ end
88
+
89
+ # change state of a worker
90
+ # @param host [String] host name of the target host
91
+ # @param port [String] port number of the worker process on the target host
92
+ def put_worker_status(host, port, new_status)
93
+ return RestClient.put("http://#{host}:#{port}/worker", :status => new_status)
94
+ end
95
+
96
+ # start target workers
97
+ # @param options @see {#request_to_target_hosts}
98
+ def start_worker(options = {})
99
+ return request_to_target_hosts(options){|h,p| controll_worker_at(h,'start')}
100
+ end
101
+
102
+ # stop target workers
103
+ # @param options @see {#request_to_target_hosts}
104
+ def stop_worker(options = {})
105
+ return request_to_target_hosts(options){|h,p| controll_worker_at(h,'stop')}
106
+ end
107
+
108
+ # restart target workers
109
+ # @param options @see {#request_to_target_hosts}
110
+ def restart_worker(options = {})
111
+ options = {:interval => 60}.merge(options)
112
+ target_nodes = request_to_target_hosts(options){|h,p| controll_worker_at(h,'stop')}
113
+ target_nodes.keys.each{|host| target_nodes[host] = true}
114
+
115
+ port = options.has_key?(:port) ? options[:port] : @default_port
116
+ while(target_nodes.has_value?(true))
117
+ target_nodes.keys.each do |host|
118
+ next unless target_nodes[host] # skip already started
119
+ res = get_worker_status(host,port)
120
+ if res.nil?
121
+ controll_worker_at(host,'start')
122
+ target_nodes[host] = false
123
+ else
124
+ if res.code == 200
125
+ @logger.info "status code from #{host} : #{res.code}"
126
+ else
127
+ @logger.warn "status code from #{host} : #{res.code}"
128
+ end
129
+ end
130
+ end
131
+ sleep options[:interval] if target_nodes.has_value?(true)
132
+ end
133
+ end
134
+
135
+ # execute a worker command at a remote host
136
+ # @param host [String] host name of the target host
137
+ def controll_worker_at(host, cmd)
138
+ ssh_cmd = "ssh -l #{@user} #{host} sudo #{WORKER_COMMAND} #{cmd}"
139
+ @logger.info ssh_cmd
140
+ puts `#{ssh_cmd}`
141
+ end
142
+
143
+ # upgrade libraries for target workers
144
+ # @param options @see {#request_to_target_hosts}
145
+ def upgrade_worker(options = {})
146
+ return request_to_target_hosts(options){|h,p| do_upgrade_at(h)}
147
+ end
148
+
149
+ # execute upgrade commands at a remote host
150
+ # @param host [String] host name of the target host
151
+ def do_upgrade_at(host)
152
+ ssh_cmd = "ssh -l #{@user} #{host} sudo #{UPGRADE_COMMAND}"
153
+ @logger.info ssh_cmd
154
+ puts `#{ssh_cmd}`
155
+ end
156
+
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,66 @@
1
+ module Patriot
2
+ # namespace for JobStore
3
+ module JobStore
4
+ # state of jobs in JobStore
5
+ module JobState
6
+ # unneeded and discarded (i.e, trash)
7
+ DISCARDED = -2
8
+ # initiating
9
+ INIT = -1
10
+ # successfully finished
11
+ SUCCEEDED = 0
12
+ # waiting to be executed
13
+ WAIT = 1
14
+ # running currently
15
+ RUNNING = 2
16
+ # suspended and not to be executed
17
+ SUSPEND = 3
18
+ # executed but failed
19
+ FAILED = 4
20
+
21
+ # get name of the state
22
+ # @param [Patriot::JobStore::JobState] state
23
+ # @return[String]
24
+ def name_of(state)
25
+ state = state.to_i
26
+ return case state
27
+ when -2 then "DISCARDED"
28
+ when -1 then "INIT"
29
+ when 0 then "SUCCEEDED"
30
+ when 1 then "WAIT"
31
+ when 2 then "RUNNING"
32
+ when 3 then "SUSPEND"
33
+ when 4 then "FAILED"
34
+ else raise "unknown state #{state}"
35
+ end
36
+ end
37
+ module_function :name_of
38
+ end
39
+
40
+ # mapping from exit code to job state
41
+ # @see Patriot::Command::ExitCode
42
+ EXIT_CODE_TO_STATE = {
43
+ Patriot::Command::ExitCode::SUCCEEDED => Patriot::JobStore::JobState::SUCCEEDED,
44
+ Patriot::Command::ExitCode::FAILURE_SKIPPED => Patriot::JobStore::JobState::SUCCEEDED,
45
+ Patriot::Command::ExitCode::FAILED => Patriot::JobStore::JobState::FAILED
46
+ }
47
+
48
+ # a prefix for configuration key
49
+ CONFIG_PREFIX = "jobstore"
50
+ # root (default) store_id
51
+ ROOT_STORE_ID = "root"
52
+
53
+ # the job identifier for initiator (always succeeded)
54
+ # jobs without any required products to be configured to depend on the initiator
55
+ INITIATOR_JOB_ID = "INITIATOR"
56
+ # default job priority
57
+ DEFAULT_PRIORITY=1
58
+
59
+ require 'patriot/job_store/job_ticket'
60
+ require 'patriot/job_store/job'
61
+ require 'patriot/job_store/base'
62
+ require 'patriot/job_store/factory'
63
+ require 'patriot/job_store/in_memory_store'
64
+ require 'patriot/job_store/rdb_job_store'
65
+ end
66
+ end