tengine_job 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile.lock +78 -48
- data/bin/tengine_job +71 -0
- data/examples/0004_retry_one_layer.rb +10 -7
- data/examples/0027_parallel_ssh_job +9 -0
- data/examples/0027_parallel_ssh_jobs.rb +14 -0
- data/lib/tengine/job.rb +19 -49
- data/lib/tengine/job/dsl.rb +13 -0
- data/lib/tengine/job/{dsl_binder.rb → dsl/binder.rb} +4 -4
- data/lib/tengine/job/{dsl_evaluator.rb → dsl/evaluator.rb} +2 -2
- data/lib/tengine/job/{dsl_loader.rb → dsl/loader.rb} +20 -22
- data/lib/tengine/job/runtime.rb +32 -0
- data/lib/tengine/job/{drivers → runtime/drivers}/job_control_driver.rb +46 -92
- data/lib/tengine/job/{drivers → runtime/drivers}/job_execution_driver.rb +14 -10
- data/lib/tengine/job/runtime/drivers/jobnet_control_driver.rb +240 -0
- data/lib/tengine/job/{drivers → runtime/drivers}/schedule_driver.rb +4 -4
- data/lib/tengine/job/{edge.rb → runtime/edge.rb} +79 -25
- data/lib/tengine/job/{executable.rb → runtime/executable.rb} +35 -15
- data/lib/tengine/job/{execution.rb → runtime/execution.rb} +19 -11
- data/lib/tengine/job/runtime/job_base.rb +5 -0
- data/lib/tengine/job/runtime/jobnet.rb +283 -0
- data/lib/tengine/job/runtime/junction.rb +44 -0
- data/lib/tengine/job/runtime/named_vertex.rb +95 -0
- data/lib/tengine/job/runtime/root_jobnet.rb +81 -0
- data/lib/tengine/job/{signal.rb → runtime/signal.rb} +99 -13
- data/lib/tengine/job/runtime/ssh_job.rb +486 -0
- data/lib/tengine/job/{jobnet → runtime}/state_transition.rb +6 -4
- data/lib/tengine/job/runtime/stoppable.rb +64 -0
- data/lib/tengine/job/runtime/vertex.rb +50 -0
- data/lib/tengine/job/structure.rb +20 -0
- data/lib/tengine/job/{category.rb → structure/category.rb} +9 -5
- data/lib/tengine/job/{jobnet/builder.rb → structure/edge_builder.rb} +11 -7
- data/lib/tengine/job/{element_selector_notation.rb → structure/element_selector_notation.rb} +15 -11
- data/lib/tengine/job/structure/jobnet_builder.rb +83 -0
- data/lib/tengine/job/structure/jobnet_finder.rb +60 -0
- data/lib/tengine/job/{name_path.rb → structure/name_path.rb} +2 -2
- data/lib/tengine/job/structure/tree.rb +20 -0
- data/lib/tengine/job/structure/visitor.rb +67 -0
- data/lib/tengine/job/template.rb +24 -0
- data/lib/tengine/job/template/edge.rb +37 -0
- data/lib/tengine/job/template/expansion.rb +24 -0
- data/lib/tengine/job/template/generator.rb +111 -0
- data/lib/tengine/job/template/jobnet.rb +83 -0
- data/lib/tengine/job/template/junction.rb +14 -0
- data/lib/tengine/job/{job.rb → template/named_vertex.rb} +3 -5
- data/lib/tengine/job/{root_jobnet_template.rb → template/root_jobnet.rb} +12 -26
- data/lib/tengine/job/template/ssh_job.rb +80 -0
- data/lib/tengine/job/template/vertex.rb +97 -0
- metadata +127 -93
- data/lib/tengine/job/connectable.rb +0 -43
- data/lib/tengine/job/drivers/jobnet_control_driver.rb +0 -249
- data/lib/tengine/job/end.rb +0 -32
- data/lib/tengine/job/expansion.rb +0 -37
- data/lib/tengine/job/fork.rb +0 -6
- data/lib/tengine/job/jobnet.rb +0 -184
- data/lib/tengine/job/jobnet/job_state_transition.rb +0 -167
- data/lib/tengine/job/jobnet/jobnet_state_transition.rb +0 -110
- data/lib/tengine/job/jobnet_actual.rb +0 -84
- data/lib/tengine/job/jobnet_template.rb +0 -10
- data/lib/tengine/job/join.rb +0 -6
- data/lib/tengine/job/junction.rb +0 -29
- data/lib/tengine/job/killing.rb +0 -30
- data/lib/tengine/job/mm_compatibility.rb +0 -6
- data/lib/tengine/job/mm_compatibility/connectable.rb +0 -13
- data/lib/tengine/job/root.rb +0 -16
- data/lib/tengine/job/root_jobnet_actual.rb +0 -58
- data/lib/tengine/job/script_executable.rb +0 -235
- data/lib/tengine/job/start.rb +0 -20
- data/lib/tengine/job/stoppable.rb +0 -15
- data/lib/tengine/job/vertex.rb +0 -181
@@ -1,13 +0,0 @@
|
|
1
|
-
module Tengine::Job::MmCompatibility::Connectable
|
2
|
-
extend ActiveSupport::Concern
|
3
|
-
|
4
|
-
included do
|
5
|
-
alias_method :vm_instance_name , :server_name
|
6
|
-
alias_method :vm_instance_name=, :server_name=
|
7
|
-
alias_method :instance_name , :server_name
|
8
|
-
alias_method :instance_name=, :server_name=
|
9
|
-
alias_method :user_group_credential_name , :credential_name
|
10
|
-
alias_method :user_group_credential_name=, :credential_name=
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
data/lib/tengine/job/root.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'tengine/job'
|
3
|
-
|
4
|
-
# ルートジョブネットとして必要な情報に関するモジュール
|
5
|
-
module Tengine::Job::Root
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
included do
|
9
|
-
include Tengine::Core::OptimisticLock
|
10
|
-
set_locking_field :version
|
11
|
-
|
12
|
-
belongs_to :category, :inverse_of => :root_jobnet_templates, :index => true, :class_name => "Tengine::Job::Category"
|
13
|
-
|
14
|
-
field :version, :type => Integer, :default => 0 # ジョブネット全体を更新する際の楽観的ロックのためのバージョン。更新するたびにインクリメントされます。
|
15
|
-
end
|
16
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'tengine/job'
|
3
|
-
|
4
|
-
# 実行時のルートジョブネットを表すVertex
|
5
|
-
class Tengine::Job::RootJobnetActual < Tengine::Job::JobnetActual
|
6
|
-
include Tengine::Job::Root
|
7
|
-
|
8
|
-
has_many :executions, :inverse_of => :root_jobnet, :class_name => "Tengine::Job::Execution"
|
9
|
-
|
10
|
-
|
11
|
-
def rerun(*args)
|
12
|
-
options = args.extract_options!
|
13
|
-
sender = options.delete(:sender) || Tengine::Event.default_sender
|
14
|
-
options = options.merge({
|
15
|
-
:retry => true,
|
16
|
-
:root_jobnet_id => self.id,
|
17
|
-
})
|
18
|
-
result = Tengine::Job::Execution.new(options)
|
19
|
-
result.target_actual_ids ||= []
|
20
|
-
result.target_actual_ids += args.flatten
|
21
|
-
result.with(safe: safemode(Tengine::Job::Execution.collection)).save!
|
22
|
-
sender.wait_for_connection do
|
23
|
-
sender.fire(:'start.execution.job.tengine', :properties => {
|
24
|
-
:execution_id => result.id.to_s
|
25
|
-
})
|
26
|
-
end
|
27
|
-
result
|
28
|
-
end
|
29
|
-
|
30
|
-
def update_with_lock(*args)
|
31
|
-
super(*args) do
|
32
|
-
Tengine::Job.test_harness_hook("before yield in update_with_lock")
|
33
|
-
yield if block_given?
|
34
|
-
Tengine::Job.test_harness_hook("after yield in update_with_lock")
|
35
|
-
end
|
36
|
-
Tengine::Job.test_harness_hook("after update_with_lock")
|
37
|
-
end
|
38
|
-
|
39
|
-
def fire_stop_event(options = Hash.new)
|
40
|
-
root_jobnet_id = self.id.to_s
|
41
|
-
result = Tengine::Job::Execution.create!(
|
42
|
-
options.merge(:root_jobnet_id => root_jobnet_id))
|
43
|
-
|
44
|
-
EM.run do
|
45
|
-
Tengine::Event.fire(:"stop.jobnet.job.tengine",
|
46
|
-
:source_name => name_as_resource,
|
47
|
-
:properties => {
|
48
|
-
:execution_id => result.id.to_s,
|
49
|
-
:root_jobnet_id => root_jobnet_id,
|
50
|
-
:target_jobnet_id => root_jobnet_id.to_s,
|
51
|
-
:stop_reason => "user_stop",
|
52
|
-
})
|
53
|
-
end
|
54
|
-
|
55
|
-
return result
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
@@ -1,235 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'tengine/job'
|
3
|
-
require 'tengine/resource/net_ssh'
|
4
|
-
|
5
|
-
# ジョブとして実際にスクリプトを実行する処理をまとめるモジュール。
|
6
|
-
# Tengine::Job::JobnetActualと、Tengine::Job::ScriptActualがincludeします
|
7
|
-
module Tengine::Job::ScriptExecutable
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
class Error < StandardError
|
11
|
-
end
|
12
|
-
|
13
|
-
included do
|
14
|
-
include Tengine::Core::CollectionAccessible
|
15
|
-
|
16
|
-
field :executing_pid, :type => String # 実行しているプロセスのPID
|
17
|
-
field :exit_status , :type => String # 終了したプロセスが返した終了ステータス
|
18
|
-
field :error_messages, :type => Array # エラーになった場合のメッセージを保持する配列。再実行時に追加される場合は末尾に追加されます。
|
19
|
-
array_text_accessor :error_messages, :delimeter => "\n"
|
20
|
-
end
|
21
|
-
|
22
|
-
def run(execution)
|
23
|
-
return ack(@acked_pid) if @acked_pid
|
24
|
-
cmd = build_command(execution)
|
25
|
-
# puts "cmd:\n" << cmd
|
26
|
-
execute(cmd) do |ch, data|
|
27
|
-
if signal = execution.signal
|
28
|
-
# signal.data = {:executing_pid => data.strip}
|
29
|
-
# ack(signal)
|
30
|
-
pid = data.strip
|
31
|
-
signal.callback = lambda do
|
32
|
-
signal.data = {:executing_pid => pid}
|
33
|
-
|
34
|
-
# このブロック内の処理はupdate_with_lockによって複数回実行されることがあります。
|
35
|
-
# 1回目と同じリロードされていないオブジェクトを2回目以降に使用すると、1回目の変更が残っているので
|
36
|
-
# そのオブジェクトに対して処理を行うのはNGです。
|
37
|
-
# self.ack(signal) # これはNG
|
38
|
-
|
39
|
-
# このブロックが実行されるたびに、rootからselfと同じidのオブジェクトを新たに取得する必要があります。
|
40
|
-
job = root.vertex(self.id)
|
41
|
-
job.ack(signal)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def execute(cmd)
|
48
|
-
raise "actual_server not found for #{self.name_path.inspect}" unless actual_server
|
49
|
-
Tengine.logger.info("connecting to #{actual_server.hostname_or_ipv4}")
|
50
|
-
port = actual_server.properties["ssh_port"] || 22
|
51
|
-
keys_only = actual_credential.auth_type_cd == :ssh_public_key
|
52
|
-
Net::SSH.start(actual_server.hostname_or_ipv4, actual_credential, :port => port, :logger => Tengine.logger, :keys_only => keys_only) do |ssh|
|
53
|
-
# see http://net-ssh.github.com/ssh/v2/api/classes/Net/SSH/Connection/Channel.html
|
54
|
-
ssh.open_channel do |channel|
|
55
|
-
Tengine.logger.info("now exec on ssh: " << cmd)
|
56
|
-
channel.exec(cmd.force_encoding("binary")) do |ch, success|
|
57
|
-
raise Error, "could not execute command" unless success
|
58
|
-
|
59
|
-
channel.on_close do |ch|
|
60
|
-
# puts "channel is closing!"
|
61
|
-
end
|
62
|
-
|
63
|
-
channel.on_data do |ch, data|
|
64
|
-
Tengine.logger.debug("got stdout: #{data}")
|
65
|
-
yield(ch, data) if block_given?
|
66
|
-
end
|
67
|
-
|
68
|
-
channel.on_extended_data do |ch, type, data|
|
69
|
-
self.error_messages ||= []
|
70
|
-
self.error_messages += [data]
|
71
|
-
raise Error, "Failure to execute #{self.name_path} via SSH: #{data}"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
rescue Tengine::Job::ScriptExecutable::Error
|
78
|
-
raise
|
79
|
-
rescue Mongoid::Errors::DocumentNotFound, SocketError, Net::SSH::AuthenticationFailed => src
|
80
|
-
error = Error.new("[#{src.class.name}] #{src.message}")
|
81
|
-
error.set_backtrace(src.backtrace)
|
82
|
-
raise error
|
83
|
-
rescue Exception
|
84
|
-
# puts "[#{$!.class.name}] #{$!.message}"
|
85
|
-
raise
|
86
|
-
end
|
87
|
-
|
88
|
-
def kill(execution)
|
89
|
-
lines = source_profiles
|
90
|
-
|
91
|
-
if self.executing_pid.blank?
|
92
|
-
Tengine.logger.warn("PID is blank when kill!!\n#{self.inspect}\n " << caller.join("\n "))
|
93
|
-
end
|
94
|
-
|
95
|
-
cmd = executable_command("tengine_job_agent_kill %s %d %s" % [
|
96
|
-
self.executing_pid,
|
97
|
-
self.actual_killing_signal_interval,
|
98
|
-
self.actual_killing_signals.join(","),
|
99
|
-
])
|
100
|
-
lines << cmd
|
101
|
-
cmd = lines.join(' && ')
|
102
|
-
execute(cmd)
|
103
|
-
end
|
104
|
-
|
105
|
-
# def ack(pid)
|
106
|
-
# @acked_pid = pid
|
107
|
-
# self.executing_pid = pid
|
108
|
-
# self.phase_key = :running
|
109
|
-
# self.previous_edges.each{|edge| edge.status_key = :transmitted}
|
110
|
-
# end
|
111
|
-
|
112
|
-
def build_command(execution)
|
113
|
-
result = source_profiles
|
114
|
-
mm_env = build_mm_env(execution).map{|k,v| "#{k}=#{v}"}.join(" ")
|
115
|
-
# Hadoopジョブの場合は環境変数をセットする
|
116
|
-
if is_a?(Tengine::Job::Jobnet) && (jobnet_type_key == :hadoop_job_run)
|
117
|
-
mm_env << ' ' << hadoop_job_env
|
118
|
-
end
|
119
|
-
result << "export #{mm_env}"
|
120
|
-
template_root = root_or_expansion.template
|
121
|
-
if template_root
|
122
|
-
template_job = template_root.vertex_by_name_path(self.name_path_until_expansion)
|
123
|
-
unless template_job
|
124
|
-
raise "job not found #{self.name_path_until_expansion.inspect} in #{template_root.inspect}"
|
125
|
-
end
|
126
|
-
key = Tengine::Job::DslLoader.template_block_store_key(template_job, :preparation)
|
127
|
-
preparation_block = Tengine::Job::DslLoader.template_block_store[key]
|
128
|
-
if preparation_block
|
129
|
-
preparation = instance_eval(&preparation_block)
|
130
|
-
unless preparation.blank?
|
131
|
-
result << preparation
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
unless execution.preparation_command.blank?
|
136
|
-
result << execution.preparation_command
|
137
|
-
end
|
138
|
-
# cmdはユーザーが設定したスクリプトを組み立てたもので、
|
139
|
-
# プロセスの監視/強制停止のためにtengine_job_agent/bin/tengine_job_agent_run
|
140
|
-
# からこれらを実行させるためにはcmdを編集します。
|
141
|
-
# tengine_job_agent_runは、標準出力に監視対象となる起動したプロセスのPIDを出力します。
|
142
|
-
runner_path = ENV["MM_RUNNER_PATH"] || executable_command("tengine_job_agent_run")
|
143
|
-
runner_option = ""
|
144
|
-
# 実装するべきか要検討
|
145
|
-
# runner_option << " --stdout" if execution.keeping_stdout
|
146
|
-
# runner_option << " --stderr" if execution.keeping_stderr
|
147
|
-
# script = "#{runner_path}#{runner_option} -- #{self.script}" # runnerのオプションを指定する際は -- の前に設定してください
|
148
|
-
script = "#{runner_path}#{runner_option} #{self.script}" # runnerのオプションを指定する際は -- の前に設定してください
|
149
|
-
result << script
|
150
|
-
result.join(" && ")
|
151
|
-
end
|
152
|
-
|
153
|
-
def source_profiles
|
154
|
-
# RubyのNet::SSHでは設定ファイルが読み込まれないので、ロードするようにします。
|
155
|
-
# ~/.bash_profile, ~/.bashrc などは非対応。
|
156
|
-
# ファイルが存在していたらsourceで読み込むようにしたいのですが、一旦保留します。
|
157
|
-
# http://www.syns.net/10/
|
158
|
-
["/etc/profile", "/etc/bashrc", "$HOME/.bashrc", "$HOME/.bash_profile"].map do |path|
|
159
|
-
"if [ -f #{path} ]; then source #{path}; fi"
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def executable_command(command)
|
164
|
-
if prefix = ENV["MM_CMD_PREFIX"]
|
165
|
-
"#{prefix} #{command}"
|
166
|
-
else
|
167
|
-
command
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
# MMから実行されるシェルスクリプトに渡す環境変数のHashを返します。
|
172
|
-
# MM_ACTUAL_JOB_ID : 実行される末端のジョブのMM上でのID
|
173
|
-
# MM_ACTUAL_JOB_ANCESTOR_IDS : 実行される末端のジョブの祖先のMM上でのIDをセミコロンで繋げた文字列 (テンプレートジョブ単位)
|
174
|
-
# MM_FULL_ACTUAL_JOB_ANCESTOR_IDS : 実行される末端のジョブの祖先のMM上でのIDをセミコロンで繋げた文字列 (expansionから展開した単位)
|
175
|
-
# MM_ACTUAL_JOB_NAME_PATH : 実行される末端のジョブのname_path
|
176
|
-
# MM_ACTUAL_JOB_SECURITY_TOKEN : 公開API呼び出しのためのセキュリティ用のワンタイムトークン
|
177
|
-
# MM_TEMPLATE_JOB_ID : テンプレートジョブ(=実行される末端のジョブの元となったジョブ)のID
|
178
|
-
# MM_TEMPLATE_JOB_ANCESTOR_IDS : テンプレートジョブの祖先のMM上でのIDをセミコロンで繋げたもの
|
179
|
-
# MM_SCHEDULE_ID : 実行スケジュールのID
|
180
|
-
# MM_SCHEDULE_ESTIMATED_TIME : 実行スケジュールの見積り時間。単位は分。
|
181
|
-
# MM_SCHEDULE_ESTIMATED_END : 実行スケジュールの見積り終了時刻をYYYYMMDDHHMMSS式で。(できればISO 8601など、タイムゾーンも表現できる標準的な形式の方が良い?)
|
182
|
-
# MM_MASTER_SCHEDULE_ID : マスタースケジュールがあればそのID。マスタースケジュールがない場合は環境変数は指定されません。
|
183
|
-
#
|
184
|
-
# 未実装
|
185
|
-
# MM_FAILED_JOB_ID : ジョブが失敗した場合にrecoverやfinally内のジョブを実行時に設定される、失敗したジョブのMM上でのID。
|
186
|
-
# MM_FAILED_JOB_ANCESTOR_IDS : ジョブが失敗した場合にrecoverやfinally内のジョブを実行時に設定される、失敗したジョブの祖先のMM上でのIDをセミコロンで繋げた文字列。
|
187
|
-
def build_mm_env(execution)
|
188
|
-
result = {
|
189
|
-
"MM_SERVER_NAME" => actual_server_name, # [Tengineの仕様として追加] ジョブの実行サーバ名を設定
|
190
|
-
"MM_ROOT_JOBNET_ID" => root.id.to_s,
|
191
|
-
"MM_TARGET_JOBNET_ID" => parent.id.to_s,
|
192
|
-
"MM_ACTUAL_JOB_ID" => id.to_s,
|
193
|
-
"MM_ACTUAL_JOB_ANCESTOR_IDS" => '"%s"' % ancestors_until_expansion.map(&:id).map(&:to_s).join(';'),
|
194
|
-
"MM_FULL_ACTUAL_JOB_ANCESTOR_IDS" => '"%s"' % ancestors.map(&:id).map(&:to_s).join(';'),
|
195
|
-
"MM_ACTUAL_JOB_NAME_PATH" => name_path.dump,
|
196
|
-
"MM_ACTUAL_JOB_SECURITY_TOKEN" => "", # TODO トークンの生成
|
197
|
-
"MM_SCHEDULE_ID" => execution.id.to_s,
|
198
|
-
"MM_SCHEDULE_ESTIMATED_TIME" => execution.estimated_time,
|
199
|
-
}
|
200
|
-
if estimated_end = execution.actual_estimated_end
|
201
|
-
result["MM_SCHEDULE_ESTIMATED_END"] = estimated_end.strftime("%Y%m%d%H%M%S")
|
202
|
-
end
|
203
|
-
if rjt = root.template
|
204
|
-
t = rjt.find_descendant_by_name_path(self.name_path)
|
205
|
-
unless t
|
206
|
-
template_name_parts = self.name_path_until_expansion.split(Tengine::Job::NamePath::SEPARATOR).select{|s| !s.empty?}
|
207
|
-
root_jobnet_name = template_name_parts.first
|
208
|
-
if rjt = Tengine::Job::RootJobnetTemplate.find_by_name(root_jobnet_name, :version => rjt.dsl_version)
|
209
|
-
t = rjt.find_descendant_by_name_path(self.name_path_until_expansion)
|
210
|
-
raise "template job #{path.inspect} not found in #{rjt.inspect}" unless t
|
211
|
-
else
|
212
|
-
raise "Tengine::Job::RootJobnetTemplate not found #{self.name_path_until_expansion.inspect}"
|
213
|
-
end
|
214
|
-
end
|
215
|
-
result.update({
|
216
|
-
"MM_TEMPLATE_JOB_ID" => t.id.to_s,
|
217
|
-
"MM_TEMPLATE_JOB_ANCESTOR_IDS" => '"%s"' % t.ancestors.map(&:id).map(&:to_s).join(';'),
|
218
|
-
})
|
219
|
-
end
|
220
|
-
# if ms = execution.master_schedule
|
221
|
-
# result.update({
|
222
|
-
# "MM_MASTER_SCHEDULE_ID" => ms.id.to_s,
|
223
|
-
# })
|
224
|
-
# end
|
225
|
-
result
|
226
|
-
end
|
227
|
-
|
228
|
-
def hadoop_job_env
|
229
|
-
s = children.select{|c| c.is_a?(Tengine::Job::Jobnet) && (c.jobnet_type_key == :hadoop_job)}.
|
230
|
-
map{|c| "#{c.name}\\t#{c.id.to_s}\\n"}.join
|
231
|
-
"MM_HADOOP_JOBS=\"#{s}\""
|
232
|
-
end
|
233
|
-
|
234
|
-
|
235
|
-
end
|
data/lib/tengine/job/start.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'tengine/job'
|
3
|
-
|
4
|
-
# ジョブネットの始端を表すVertex。特に状態は持たない。
|
5
|
-
class Tengine::Job::Start < Tengine::Job::Vertex
|
6
|
-
|
7
|
-
# https://cacoo.com/diagrams/hdLgrzYsTBBpV3Wj#D26C1
|
8
|
-
def transmit(signal)
|
9
|
-
activate(signal)
|
10
|
-
end
|
11
|
-
|
12
|
-
def activate(signal)
|
13
|
-
signal.leave(self)
|
14
|
-
end
|
15
|
-
|
16
|
-
def reset(signal)
|
17
|
-
signal.leave(self, :reset)
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'tengine/job'
|
3
|
-
require 'selectable_attr'
|
4
|
-
|
5
|
-
# ジョブ/ジョブネットを実行する際の情報に関するモジュール
|
6
|
-
# Tengine::Job::JobnetActual, Tengine::Job::JobnetTemplateがこのモジュールをincludeします
|
7
|
-
module Tengine::Job::Stoppable
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
included do
|
11
|
-
field :stopped_at , :type => DateTime # 停止時刻。停止を開始した時刻です。
|
12
|
-
field :stop_reason, :type => String # 停止理由。手動以外での停止ならば停止した理由が設定されます。
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
data/lib/tengine/job/vertex.rb
DELETED
@@ -1,181 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'tengine/job'
|
3
|
-
|
4
|
-
# Edgeとともにジョブネットを構成するグラフの「頂点」を表すモデル
|
5
|
-
# 自身がツリー構造を
|
6
|
-
class Tengine::Job::Vertex
|
7
|
-
include Mongoid::Document
|
8
|
-
include Mongoid::Timestamps
|
9
|
-
include Tengine::Job::Signal::Transmittable
|
10
|
-
include Tengine::Job::NamePath
|
11
|
-
|
12
|
-
self.cyclic = true
|
13
|
-
with_options(:class_name => self.name, :cyclic => true) do |c|
|
14
|
-
c.embedded_in :parent , :inverse_of => :children
|
15
|
-
c.embeds_many :children, :inverse_of => :parent , :validate => false
|
16
|
-
end
|
17
|
-
|
18
|
-
before_validation do |r|
|
19
|
-
r.children.each do |child|
|
20
|
-
child.valid?
|
21
|
-
child.errors.each do |f, error|
|
22
|
-
r.errors.add(:base, error)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# def short_inspect
|
28
|
-
# "#<%%%-30s id: %s>" % [self.class.name, self.id.to_s]
|
29
|
-
# end
|
30
|
-
# alias_method :long_inspect, :inspect
|
31
|
-
# alias_method :inspect, :short_inspect
|
32
|
-
|
33
|
-
class VertexValidations < Mongoid::Errors::Validations
|
34
|
-
def translate(key, options)
|
35
|
-
::I18n.translate(
|
36
|
-
"#{Mongoid::Errors::MongoidError::BASE_KEY}.validations",
|
37
|
-
{:errors => Tengine::Job::Vertex.flatten_errors(document).to_a.join(', ')})
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
class << self
|
43
|
-
def flatten_errors(vertex, dest = nil)
|
44
|
-
dest ||= []
|
45
|
-
children_errors = vertex.errors.messages.delete(:children)
|
46
|
-
edges_errors = vertex.errors.messages.delete(:edges)
|
47
|
-
vertex.errors.full_messages.each{|msg| dest << "#{vertex.name_path} #{msg}"}
|
48
|
-
vertex.children.each{|child| flatten_errors(child, dest)}
|
49
|
-
if vertex.respond_to?(:edges)
|
50
|
-
vertex.edges.each do|edge|
|
51
|
-
edge.errors.full_messages.each{|msg| dest << "#{edge.name_for_message} #{msg}"}
|
52
|
-
end
|
53
|
-
end
|
54
|
-
dest
|
55
|
-
end
|
56
|
-
|
57
|
-
def raise_flatten_errors
|
58
|
-
yield if block_given?
|
59
|
-
rescue Mongoid::Errors::Validations => e
|
60
|
-
raise VertexValidations, e.document
|
61
|
-
end
|
62
|
-
|
63
|
-
def create!(*args, &block)
|
64
|
-
raise_flatten_errors{ super(*args, &block) }
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def save!(*args)
|
69
|
-
self.class.raise_flatten_errors{ super(*args) }
|
70
|
-
end
|
71
|
-
def update_attributes!(*args)
|
72
|
-
self.class.raise_flatten_errors{ super(*args) }
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
def previous_edges
|
78
|
-
return nil unless parent
|
79
|
-
parent.edges.select{|edge| edge.destination_id == self.id}
|
80
|
-
end
|
81
|
-
alias_method :prev_edges, :previous_edges
|
82
|
-
|
83
|
-
def next_edges
|
84
|
-
return nil unless parent
|
85
|
-
parent.edges.select{|edge| edge.origin_id == self.id}
|
86
|
-
end
|
87
|
-
|
88
|
-
def root
|
89
|
-
(parent = self.parent) ? parent.root : self
|
90
|
-
end
|
91
|
-
|
92
|
-
def ancestors
|
93
|
-
if parent = self.parent
|
94
|
-
parent.ancestors + [parent]
|
95
|
-
else
|
96
|
-
[]
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def ancestors_until_expansion
|
101
|
-
if (parent = self.parent) && !self.was_expansion?
|
102
|
-
parent.ancestors_until_expansion + [parent]
|
103
|
-
else
|
104
|
-
[]
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
IGNORED_FIELD_NAMES = ["_type", "_id"].freeze
|
109
|
-
|
110
|
-
def actual_class; self.class; end
|
111
|
-
def generating_attrs
|
112
|
-
field_names = self.class.fields.keys - IGNORED_FIELD_NAMES
|
113
|
-
field_names.inject({}){|d, name| d[name] = send(name); d }
|
114
|
-
end
|
115
|
-
def generating_children; self.children; end
|
116
|
-
def generating_edges; respond_to?(:edges) ? self.edges : []; end
|
117
|
-
|
118
|
-
def generate(klass = actual_class)
|
119
|
-
result = klass.new(generating_attrs)
|
120
|
-
src_to_generated = {}
|
121
|
-
generating_children.each do |child|
|
122
|
-
generated = child.generate
|
123
|
-
src_to_generated[child.id] = generated.id
|
124
|
-
result.children << generated
|
125
|
-
end
|
126
|
-
generating_edges.each do |edge|
|
127
|
-
generated = edge.class.new
|
128
|
-
generated.origin_id = src_to_generated[edge.origin_id]
|
129
|
-
generated.destination_id = src_to_generated[edge.destination_id]
|
130
|
-
result.edges << generated
|
131
|
-
end
|
132
|
-
result
|
133
|
-
end
|
134
|
-
|
135
|
-
def accept_visitor(visitor)
|
136
|
-
visitor.visit(self)
|
137
|
-
end
|
138
|
-
|
139
|
-
class AnyVisitor
|
140
|
-
def initialize(&block)
|
141
|
-
@block = block
|
142
|
-
end
|
143
|
-
def visit(vertex)
|
144
|
-
if result = @block.call(vertex)
|
145
|
-
return result
|
146
|
-
end
|
147
|
-
vertex.children.each do |child|
|
148
|
-
if result = child.accept_visitor(self)
|
149
|
-
return result
|
150
|
-
end
|
151
|
-
end
|
152
|
-
return nil
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
class AllVisitor
|
157
|
-
def initialize(&block)
|
158
|
-
@block = block
|
159
|
-
end
|
160
|
-
|
161
|
-
def visit(vertex)
|
162
|
-
@block.call(vertex)
|
163
|
-
vertex.children.each do |child|
|
164
|
-
child.accept_visitor(self)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
class AllVisitorWithEdge < AllVisitor
|
170
|
-
def visit(obj)
|
171
|
-
if obj.respond_to?(:children)
|
172
|
-
super(obj)
|
173
|
-
else
|
174
|
-
@block.call(obj)
|
175
|
-
end
|
176
|
-
return unless obj.respond_to?(:edges)
|
177
|
-
obj.edges.each{|edge| edge.accept_visitor(self)}
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
end
|