patriot-workflow-scheduler 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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