pmux-gw 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Makefile +18 -0
- data/README.md +145 -0
- data/Rakefile +4 -0
- data/bin/pmux-gw +6 -0
- data/examples/password +4 -0
- data/examples/pmux-gw.conf +15 -0
- data/lib/pmux-gw/application.rb +226 -0
- data/lib/pmux-gw/client_context.rb +40 -0
- data/lib/pmux-gw/history.rb +149 -0
- data/lib/pmux-gw/http_handler.rb +454 -0
- data/lib/pmux-gw/logger_wrapper.rb +75 -0
- data/lib/pmux-gw/pmux_handler.rb +86 -0
- data/lib/pmux-gw/static/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-icons_222222_256x240.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-icons_2e83ff_256x240.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-icons_454545_256x240.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-icons_888888_256x240.png +0 -0
- data/lib/pmux-gw/static/css/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/lib/pmux-gw/static/css/jquery-ui-1.9.2.custom.css +462 -0
- data/lib/pmux-gw/static/js/jquery-1.8.3.js +9472 -0
- data/lib/pmux-gw/static/js/jquery-ui-1.9.2.custom.js +14879 -0
- data/lib/pmux-gw/syslog_wrapper.rb +58 -0
- data/lib/pmux-gw/template/history.tmpl +101 -0
- data/lib/pmux-gw/version.rb +5 -0
- data/lib/pmux-gw.rb +12 -0
- data/pmux-gw.gemspec +26 -0
- data/rpm/Makefile +19 -0
- data/rpm/pmux-gw.spec +100 -0
- metadata +173 -0
data/Gemfile
ADDED
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
data/bin/pmux-gw
ADDED
data/examples/password
ADDED
@@ -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
|