pmux-gw 0.1.0

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 (38) hide show
  1. data/Gemfile +4 -0
  2. data/LICENSE.txt +22 -0
  3. data/Makefile +18 -0
  4. data/README.md +145 -0
  5. data/Rakefile +4 -0
  6. data/bin/pmux-gw +6 -0
  7. data/examples/password +4 -0
  8. data/examples/pmux-gw.conf +15 -0
  9. data/lib/pmux-gw/application.rb +226 -0
  10. data/lib/pmux-gw/client_context.rb +40 -0
  11. data/lib/pmux-gw/history.rb +149 -0
  12. data/lib/pmux-gw/http_handler.rb +454 -0
  13. data/lib/pmux-gw/logger_wrapper.rb +75 -0
  14. data/lib/pmux-gw/pmux_handler.rb +86 -0
  15. data/lib/pmux-gw/static/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  16. data/lib/pmux-gw/static/css/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  17. data/lib/pmux-gw/static/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  18. data/lib/pmux-gw/static/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  19. data/lib/pmux-gw/static/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  20. data/lib/pmux-gw/static/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  21. data/lib/pmux-gw/static/css/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  22. data/lib/pmux-gw/static/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  23. data/lib/pmux-gw/static/css/images/ui-icons_222222_256x240.png +0 -0
  24. data/lib/pmux-gw/static/css/images/ui-icons_2e83ff_256x240.png +0 -0
  25. data/lib/pmux-gw/static/css/images/ui-icons_454545_256x240.png +0 -0
  26. data/lib/pmux-gw/static/css/images/ui-icons_888888_256x240.png +0 -0
  27. data/lib/pmux-gw/static/css/images/ui-icons_cd0a0a_256x240.png +0 -0
  28. data/lib/pmux-gw/static/css/jquery-ui-1.9.2.custom.css +462 -0
  29. data/lib/pmux-gw/static/js/jquery-1.8.3.js +9472 -0
  30. data/lib/pmux-gw/static/js/jquery-ui-1.9.2.custom.js +14879 -0
  31. data/lib/pmux-gw/syslog_wrapper.rb +58 -0
  32. data/lib/pmux-gw/template/history.tmpl +101 -0
  33. data/lib/pmux-gw/version.rb +5 -0
  34. data/lib/pmux-gw.rb +12 -0
  35. data/pmux-gw.gemspec +26 -0
  36. data/rpm/Makefile +19 -0
  37. data/rpm/pmux-gw.spec +100 -0
  38. metadata +173 -0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pmux-gw.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Hiroyuki Kakine
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Makefile ADDED
@@ -0,0 +1,18 @@
1
+ RUBY_VERID?=default
2
+
3
+ default: build
4
+
5
+ build: clean
6
+ rake build
7
+
8
+ install: clean
9
+ rake install
10
+
11
+ rpmbuild: clean
12
+ cd rpm && make clean
13
+ rake build
14
+ cd rpm && make RUBY_VERID=$(RUBY_VERID)
15
+
16
+ clean:
17
+ cd rpm && make clean
18
+ rm -rf pkg
data/README.md ADDED
@@ -0,0 +1,145 @@
1
+ ## Pmux::Gateway
2
+
3
+ Pmux gateway is an executor for Pmux (https://github.com/iij/pmux) through HTTP request.
4
+
5
+ ## Requirements
6
+
7
+ * ruby 1.8.7, 1.9.3 or higher
8
+ * pmux
9
+ * gflocator
10
+ * eventmachine
11
+ * em_pessimistic
12
+ * eventmacnine/evma_httpserver https://github.com/eventmachine/evma_httpserver.git
13
+
14
+ ## Installation
15
+
16
+ ### Install dependency programs
17
+
18
+ gem install gflocator
19
+ gem install pmux
20
+ gem install eventmachine
21
+ gem install em_pessimistic
22
+ git clone https://github.com/eventmachine/evma_httpserver.git
23
+ cd evma_httpserver
24
+ rake gem:build
25
+ gem install eventmachine_httpserver-0.2.1.gem
26
+ (I do not recommend "gem install eventmachine_httpserver")
27
+
28
+ ### Install pmux-gw
29
+
30
+ gem install pmux-gw
31
+
32
+ ## Usage
33
+
34
+ pmux-gw [-c config] [-F] [-h]
35
+ -c : specified config file
36
+ -F : foreground mode
37
+ -h : print usage
38
+
39
+ ## Quick start
40
+
41
+ ### Install glusterfs client (http://www.gluster.org/)
42
+
43
+ #### CentOS/RHEL
44
+
45
+ rpm -ivh glusterfs-3.3.1-1.el6.x86_64.rpm
46
+ rpm -ivh glusterfs-fuse-3.3.1-1.el6.x86_64.rpm
47
+
48
+ ### Create an environment that can use pmux
49
+
50
+ mkdir /mnt/volume
51
+ mount -t glusterfs gfsnode1:volume /mnt/volume
52
+ gflocator
53
+ useradd -m admin
54
+ sudo -u admin ssh-keygen
55
+ (copy publickey to glusterfs nodes)
56
+
57
+ ### Make sure that the following command will work
58
+
59
+ sudo -u admin pmux --status -h 127.0.0.1
60
+ sudo -u admin pmux --mapper="ls -al" --storage=glusterfs --locator-host=127.0.0.1 /mnt/volume
61
+
62
+ ### Create an environment that can use pmux-gw
63
+
64
+ mkdir /etc/pmux-gw
65
+ cp <pmux-geteway-install-path>/examples/pmux-gw.conf /etc/pmux-gw/pmux-gw.conf
66
+ cp <pmux-geteway-install-path>/examples/password /etc/pmux-gw/password
67
+ chown -R admin:admin /etc/pmux-gw/password
68
+ chmod -R 600 /etc/pmux-gw/password
69
+ pmux-gw
70
+ curl --basic -u user:pass -iv 'http://127.0.0.1:18080/pmux?mapper=ls&file=/'
71
+
72
+ ## Resource URL
73
+
74
+ ### http://<server_name>:18080/pmux
75
+
76
+ Resource to execute the Pmux
77
+
78
+ #### method
79
+
80
+ * GET
81
+ * POST
82
+
83
+ #### parameters
84
+
85
+ * mapper
86
+ * Specified as a string to exected mapper command.
87
+ * Must be placed on the glusterfs mapper program
88
+ * Be distributed to each glusterfs nodes using ship-file is possible mapper program on glusterfs
89
+ * file
90
+ * Specifies the file to be processed
91
+ * This parameter can be specified multiple
92
+ * Can also be used with file-glob
93
+ * The default program is not distributed reducer
94
+ * Specifies the path on the volume that was mounted
95
+ * file-glob
96
+ * Specified in the expression pattern of the shell files to be processed
97
+ * Specification of the pattern, according to the specifications of the pattern glob (3)
98
+ * This parameter can be specified multiple
99
+ * Can also be used with file-glob
100
+ * The default program is not distributed reducer
101
+ * Specifies the path on the volume that was mounted
102
+ * ship-file
103
+ * Specifies the path of the file to be distributed to each node of the pmux
104
+ * You use this if you want to distribute to each node in the mapper program on the glusterfs pmux
105
+ * Can be distributed more files than mapper script (configuration files, for example)
106
+ * This parameter can be specified multiple
107
+ * Specifies the path on the volume that was mounted
108
+ * reducer
109
+ * specified as a string to be executed reducer command
110
+ * Reducer is not used the program default
111
+ * num-r
112
+ * Reducer to run the program number.
113
+ * Default 0 is used
114
+ * ff
115
+ * Number of tasks if you want to be lumped fine fusion task
116
+ * I do not fusion when omitted
117
+ * storage
118
+ * Specify the type of storage you want to use
119
+ * Default is "glusterfs"
120
+ * locator-host
121
+ * Specifies the hostname or address of the locator to return the position of the entity in the file
122
+ * default is "127.0.0.1"
123
+ * locator-port
124
+ * Specifies the port number of the locator that returns the position of the entity in the file
125
+ * Default is automatically set according to the type of storage
126
+ * detect-error
127
+ * Specifies the on / off
128
+ * if on, Return a response waiting for the completion of the execution of pmux, it checks whether an error occurred.
129
+ * The default is off
130
+
131
+ ### http://<server_name>:18080/history
132
+
133
+ Resources of the request to refer to the history
134
+
135
+ ### http://<server_name>:18080/existence
136
+
137
+ Resources to monitor the presence of process
138
+
139
+ ## Links
140
+ * Glusterfs
141
+ * http://www.gluster.org/
142
+ * Gflocator
143
+ * https://github.com/iij/gflocator
144
+ * Pmux
145
+ * https://github.com/iij/pmux
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new
data/bin/pmux-gw ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pmux-gw'
4
+
5
+ app = Pmux::Gateway::Application.new()
6
+ app.run()
data/examples/password ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ user:
3
+ volume-prefix: volume
4
+ password: pass
@@ -0,0 +1,15 @@
1
+ ---
2
+ bind_address: "0.0.0.0"
3
+ bind_port: 18080
4
+ user: "admin"
5
+ group: "admin"
6
+ glusterfs_root: "/mnt"
7
+ max_tasks: 512
8
+ max_content_size: 1048576
9
+ use_basic_auth: true
10
+ password_file_path: "/etc/pmux-w/password"
11
+ history_file_path: "/var/log/pmux-gw/history"
12
+ log_file_path: "/var/log/pmux-w/pmux-gw.log"
13
+ log_level: "info"
14
+ use_syslog: false
15
+ syslog_facility: "local0"
@@ -0,0 +1,226 @@
1
+ require 'yaml'
2
+ require 'etc'
3
+ require 'optparse'
4
+ require 'fileutils'
5
+
6
+ module Pmux
7
+ module Gateway
8
+ class Application
9
+ def initialize
10
+ @config_file_path = "/etc/pmux-gw/pmux-gw.conf"
11
+ @foreground = false
12
+ @reload = false
13
+ @term = false
14
+ @logger = nil
15
+ end
16
+
17
+ def argparse
18
+ OptionParser.new do |opt|
19
+ opt.on('-c [config_file_path]', '--config [config_file_path]') {|v| @config_file_path = v}
20
+ opt.on('-F', '--foreground') {|v| @foreground = true}
21
+ opt.parse!(ARGV)
22
+ end
23
+ end
24
+
25
+ def daemonize
26
+ if !@foreground
27
+ exit!(0) if Process.fork
28
+ Process.setsid
29
+ exit!(0) if Process.fork
30
+ STDIN.reopen("/dev/null", "r")
31
+ STDOUT.reopen("/dev/null", "w")
32
+ STDERR.reopen("/dev/null", "w")
33
+ end
34
+ end
35
+
36
+ def load_config
37
+ # コンフィグのロード、リロード処理
38
+ # グローバル変数 $config はここで作る
39
+ begin
40
+ $config = YAML.load_file(@config_file_path)
41
+ rescue Errno::ENOENT
42
+ if @logger.nil?
43
+ puts "not found config file (#{@config_file_path})"
44
+ else
45
+ @logger.logging("error", "not found config file (#{@config_file_path})")
46
+ end
47
+ rescue Errno::EACCES
48
+ if @logger.nil?
49
+ puts "can not access config file (#{@config_file_path})"
50
+ else
51
+ @logger.logging("error", "can not access config file (#{@config_file_path})")
52
+ end
53
+ rescue Exception => e
54
+ # コンフィグ読み込み中に予期しないエラーが起きた
55
+ if @logger.nil?
56
+ puts "error occurred in config loading: #{e}"
57
+ puts e.backtrace.join("\n")
58
+ else
59
+ @logger.logging("error", "error occurred in config loading: #{e}")
60
+ @logger.logging("error", e.backtrace.join("\n"))
61
+ end
62
+ end
63
+ # 必要なディレクトリを作る
64
+ # 作れなければ、ログに残して続行
65
+ begin
66
+ histdir = File.dirname($config["history_file_path"])
67
+ logdir = File.dirname($config["log_file_path"])
68
+ FileUtils.mkdir_p(histdir) unless File.exist?(histdir)
69
+ FileUtils.mkdir_p(logdir) unless File.exist?(logdir)
70
+ FileUtils.chown_R($config["user"], $config["group"], histdir)
71
+ FileUtils.chown_R($config["user"], $config["group"], logdir)
72
+ rescue Exception => e
73
+ if @logger.nil?
74
+ puts "error occurred in directory creating: #{e}"
75
+ puts e.backtrace.join("\n")
76
+ else
77
+ @logger.logging("error", "error occurred in directory creating: #{e}")
78
+ @logger.logging("error", e.backtrace.join("\n"))
79
+ end
80
+ end
81
+ end
82
+
83
+ def load_userdb
84
+ $userdb = {} if $userdb.nil?
85
+ # パスワードファイルの読み込み
86
+ # パーミッションをチェックする
87
+ # グローバル変数 $userdb はここで作る
88
+ # $userdbがnilなら空のマップにする
89
+ # 読み込みに何らかの理由で失敗したらログに残し、前の状態から変更しない
90
+ if $config["use_basic_auth"]
91
+ return false if $config["password_file_path"].nil? || $config["password_file_path"] == ""
92
+ stat = File.stat($config["password_file_path"])
93
+ mode = "%o" % stat.mode
94
+ if mode[-3, 3] != "600"
95
+ if @logger.nil?
96
+ puts "password file permission is not 600 (#{$config['password_file_path']})"
97
+ else
98
+ @logger.logging("error", "password file permission is not 600 (#{$config['password_file_path']})")
99
+ end
100
+ return false
101
+ end
102
+ begin
103
+ $userdb = YAML.load_file($config["password_file_path"])
104
+ rescue Errno::ENOENT
105
+ if @logger.nil?
106
+ puts "not found password file (#{$config['password_file_path']})"
107
+ else
108
+ @logger.logging("error", "not found password file (#{$config['password_file_path']})")
109
+ end
110
+ rescue Errno::EACCES
111
+ if @logger.nil?
112
+ puts "can not access password file (#{$config['password_file_path']})"
113
+ else
114
+ @logger.logging("error", "can not access password file (#{$config['password_file_path']})")
115
+ end
116
+ rescue Exception => e
117
+ # password読み込み中に予期しないエラーが起きた
118
+ if @logger.nil?
119
+ puts "error occurred in password loading: #{e}"
120
+ puts e.backtrace.join("\n")
121
+ else
122
+ @logger.logging("error", "error occurred in password loading: #{e}")
123
+ @logger.logging("error", e.backtrace.join("\n"))
124
+ end
125
+ end
126
+ end
127
+ return true
128
+ end
129
+
130
+ def run
131
+ argparse()
132
+ daemonize()
133
+
134
+ # シグナルを受るとフラグを設定するようにする
135
+ Signal.trap(:INT) { @term = true }
136
+ Signal.trap(:TERM) { @term = true }
137
+ Signal.trap(:HUP) { @reload = true }
138
+
139
+ # コンフィグの読み込み
140
+ load_config()
141
+
142
+ # ユーザーとグループの情報を取得する
143
+ user = Etc.getpwnam($config["user"])
144
+ group = Etc.getgrnam($config["group"])
145
+
146
+ # ユーザー情報読み込み
147
+ exit(1) if !load_userdb()
148
+
149
+ # syslogラッパーインスタンス作成
150
+ @syslog = SyslogWrapper.instance()
151
+
152
+ # ロガーラッパーインスタンスの作成と初期化
153
+ @logger = LoggerWrapper.instance()
154
+ @logger.init(@foreground)
155
+
156
+ # 履歴処理インスタンスの作成と初期化
157
+ @history = History.instance()
158
+ @history.init($config["history_file_path"], @logger)
159
+
160
+ begin
161
+ EM.run do
162
+ # httpサーバーを開始する
163
+ EM.start_server($config["bind_address"], $config["bind_port"], HttpHandler)
164
+
165
+ # ユーザー権限を変更
166
+ Process::GID.change_privilege(group.gid)
167
+ Process::UID.change_privilege(user.uid)
168
+
169
+ # pmuxが読み込む環境変数を上書き
170
+ ENV["USER"] = $config["user"]
171
+ ENV["LOGNAME"] = $config["user"]
172
+ ENV["HOME"] = user.dir
173
+
174
+ # リソースリミットの設定
175
+ soft, hard = Process.getrlimit(Process::RLIMIT_NPROC)
176
+ proc_margin = 32
177
+ if soft - proc_margin < $config["max_tasks"] || hard - proc_margin < $config["max_tasks"]
178
+ proc_limit = $config["max_tasks"] + proc_margin
179
+ Process.setrlimit(Process::RLIMIT_NPROC, proc_limit, proc_limit)
180
+ end
181
+
182
+ # syslogのオープン
183
+ @syslog.open($config["use_syslog"], $config["syslog_facility"])
184
+
185
+ # ロガーのオープン
186
+ @logger.open($config["log_file_path"], $config["log_level"])
187
+
188
+ # シグナル受信フラグを処理する定期実行タイマー
189
+ @periodic_timer = EM::PeriodicTimer.new(1) do
190
+ # reloadフラグが立っていればリロードする
191
+ if @reload
192
+ @logger.logging("info", "config reloading...")
193
+ load_config()
194
+ load_userdb()
195
+ # syslogとロガーをリオープンと履歴処理インスタンスのリセット
196
+ @syslog.open($config["use_syslog"], $config["syslog_facility"])
197
+ @logger.open($config["log_file_path"], $config["log_level"])
198
+ @history.reset($config["history_file_path"])
199
+ @reload = false
200
+ end
201
+ # 終了フラグが立っていると終了処理
202
+ if @term
203
+ HttpHandler.set_term()
204
+ if HttpHandler.get_task_cnt() == 0:
205
+ @logger.logging("info", "shutdown...")
206
+ @periodic_timer.cancel()
207
+ @history.finish()
208
+ @logger.close()
209
+ EM.stop()
210
+ end
211
+ end
212
+ sleep(1)
213
+ end
214
+ end
215
+ rescue Exception => e
216
+ # event machine内で予期せぬ例外で死んだ場合ログに残す
217
+ @logger.logging("error", "error occurred in eventmachine: #{e}")
218
+ @logger.logging("error", e.backtrace.join("\n"))
219
+ @history.finish()
220
+ @logger.close()
221
+ raise e
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,40 @@
1
+ module Pmux
2
+ module Gateway
3
+ class ClientContext
4
+ # 処理に必要なクライアントの情報を保持するクラス
5
+ def initialize request, response, mapper, command, detect_error
6
+ @request = request
7
+ @response = response
8
+ @mapper = mapper
9
+ @command = command
10
+ @detect_error = detect_error
11
+ @stdout_data = ""
12
+ @stderr_data = ""
13
+ @pmux_terminated = false
14
+ @force_pmux_terminated = false
15
+ @content_too_big = false
16
+ @pid = nil
17
+ @start_datetime = nil
18
+ @end_datetime = nil
19
+ @peername = nil
20
+ @user = nil
21
+ @status = "init"
22
+ end
23
+
24
+ def set_pmux_handler pmux_handler
25
+ @pmux_handler = pmux_handler
26
+ end
27
+
28
+ def append_stdout_data data
29
+ @stdout_data << data
30
+ end
31
+
32
+ def append_stderr_data data
33
+ @stderr_data << data
34
+ end
35
+
36
+ attr_reader :request, :response, :pmux_handler, :mapper, :command, :detect_error, :stdout_data, :stderr_data
37
+ attr_accessor :pmux_terminated, :force_pmux_terminated, :content_too_big, :pid, :start_datetime, :end_datetime, :status, :peername, :user
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,149 @@
1
+ require 'singleton'
2
+ require 'date'
3
+ require 'cgi'
4
+
5
+ module Pmux
6
+ module Gateway
7
+ class History
8
+ include Singleton
9
+
10
+ @@date_format = "%Y_%m_%d"
11
+
12
+ def init history_file_path, logger
13
+ @syslog_wrapper = SyslogWrapper.instance()
14
+ @history_file_path = history_file_path
15
+ @logger = logger
16
+ @last_rotate = Date.today()
17
+ @file_path = build_file_path(@last_rotate)
18
+ @fp = nil
19
+ @reset = false
20
+ end
21
+
22
+ def reset history_file_path
23
+ @history_file_path = history_file_path
24
+ @reset = true
25
+ end
26
+
27
+ def finish
28
+ @fp.close() if !@fp.nil?
29
+ end
30
+
31
+ def build_file_path d
32
+ return "#{@history_file_path}-#{d.strftime(@@date_format)}"
33
+ end
34
+
35
+ def build_id cc
36
+ # peername, pid, mapper, start_datetimeからユニークなidを作る
37
+ # 内部的に使うだけなので文字列をつなげただけのもの
38
+ return "#{cc.peername}#{cc.pid}#{cc.mapper}#{cc.start_datetime.to_s}"
39
+ end
40
+
41
+ def rotate
42
+ # 日付が変わっていればローテート処理
43
+ if @fp.nil?
44
+ begin
45
+ @fp = open(@file_path, "a")
46
+ rescue Errno::ENOENT => e
47
+ @logger.logging("error", "not found history file (#{@file_path})")
48
+ @logger.logging("error", "error: #{e}")
49
+ rescue Errno::EACCES => e
50
+ @logger.logging("error", "can not access history file (#{@file_path})")
51
+ @logger.logging("error", "error: #{e}")
52
+ end
53
+ end
54
+ new_last_rotate = Date.today()
55
+ if new_last_rotate.day != @last_rotate.day || @reset
56
+ @file_path = build_file_path(@last_rotate)
57
+ if !@fp.nil?
58
+ @fp.close()
59
+ @fp = nil
60
+ else
61
+ @logger.logging("error", "can not close file, file object is nil")
62
+ end
63
+ begin
64
+ @fp = open(@file_path, "a")
65
+ @last_rotate = new_last_rotate
66
+ @reset = false
67
+ rescue Errno::ENOENT => e
68
+ @logger.logging("error", "not found history file (#{@file_path})")
69
+ @logger.logging("error", "error: #{e}")
70
+ rescue Errno::EACCES => e
71
+ @logger.logging("error", "can not access history file (#{@file_path})")
72
+ @logger.logging("error", "error: #{e}")
73
+ end
74
+ end
75
+ end
76
+
77
+ def save cc
78
+ # idを作成する
79
+ id = build_id(cc)
80
+ # rotateを行う
81
+ rotate()
82
+ # historyに追加書き込み
83
+ # フォーマット(タブ区切り):
84
+ # id\tpeername\tpid\tmapper\tstart_datetime\tend_datetime\tstatus\tcommand\n
85
+ if !cc.end_datetime.nil?
86
+ elapsed = ((cc.end_datetime - cc.start_datetime) * 86400).to_f
87
+ else
88
+ elapsed = nil
89
+ end
90
+ if !@fp.nil?
91
+ msg = "#{id}\t#{cc.peername}\t#{cc.user}\t#{cc.pid}\t#{cc.mapper}\t#{cc.start_datetime.to_s}\t#{cc.end_datetime.to_s}\t#{elapsed}\t#{cc.status}\t#{cc.command}\n"
92
+ @fp.write(msg)
93
+ @fp.flush()
94
+ @syslog_wrapper.logging("history", "info", msg);
95
+ else
96
+ @logger.logging("error", "can not write file, file object is nil")
97
+ end
98
+ end
99
+
100
+ def is_skip elems, peername, pid, mapper, start_datetime
101
+ return true if !peername.nil? && peername != "" && /#{peername}/ !~ elems[1]
102
+ return true if !pid.nil? && pid != "" && /#{pid}/ !~ elems[3]
103
+ return true if !mapper.nil? && mapper != "" && /#{mapper}/ !~ elems[4]
104
+ return true if !start_datetime.nil? && start_datetime != "" && start_datetime != elems[5]
105
+ end
106
+
107
+ def load peername, pid, mapper, start_datetime, start_date, end_date, need_command = false, html_escape = false
108
+ # 指定された期間のログファイルからデータを読み込む
109
+ # フォーマット(タブ区切り):
110
+ # id\tpeername\tuser\tpid\tmapper\tstart_datetime\tend_datetime\telapsed\tstatus\tcommand\n
111
+ # ヒストリの順番はhistory_id_orderに保存し
112
+ # ヒストリの内容はhistoryに保存する
113
+ history_id_order = []
114
+ history = {}
115
+ while (end_date - start_date).to_i >= 0 do
116
+ file_path = build_file_path(start_date)
117
+ begin
118
+ open(file_path) {|file|
119
+ while line = file.gets() do
120
+ elems = line.chomp().split("\t")
121
+ next if is_skip(elems, peername, pid, mapper, start_datetime)
122
+ id = elems.shift()
123
+ command = elems.pop() if !need_command
124
+ elems.unshift(start_date.to_s)
125
+ elems.each_with_index { |e, i| elems[i] = CGI.escapeHTML(e) } if html_escape
126
+ if history.key?(id)
127
+ # すでにidが存在しているのでmapだけを更新
128
+ history[id] = elems
129
+ else
130
+ # idが存在していないのでmapを更新してリストにidを追加
131
+ history_id_order << id
132
+ history[id] = elems
133
+ end
134
+ end
135
+ }
136
+ rescue Errno::ENOENT => e
137
+ @logger.logging("info", "not found history file (#{file_path})")
138
+ @logger.logging("info", "error: #{e}")
139
+ rescue Errno::EACCES => e
140
+ @logger.logging("info", "can not access history file (#{file_path})")
141
+ @logger.logging("info", "error: #{e}")
142
+ end
143
+ start_date += 1
144
+ end
145
+ return [ history, history_id_order.reverse! ]
146
+ end
147
+ end
148
+ end
149
+ end