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.
- 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
|