fluent-diagtool 0.1.3 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +13 -0
- data/AUTHORS +1 -1
- data/Gemfile.lock +5 -5
- data/README.md +58 -15
- data/bin/diagtool.rb +87 -0
- data/exclude_list01 +2 -0
- data/exe/diagtool +10 -2
- data/fluent-diagtool.gemspec +2 -2
- data/lib/fluent/diagtool/collectutils.rb +136 -88
- data/lib/fluent/diagtool/diagutils.rb +99 -43
- data/lib/fluent/diagtool/validutils.rb +16 -13
- data/lib/fluent/diagtool/version.rb +1 -1
- metadata +13 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1afbc81ebf5fe6ab013818ac124776418b3c909bef5ac5a649210499c14ff2c9
|
4
|
+
data.tar.gz: ec4b237dcbe9f065f6645181c5cade9cc118cdfbc376460fbbf3108014d360f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71a1b513b1d55e3033ce8cd90d3060a6316c48358f2a374c75f23b43ddb32693dcb0107a96706a1c961445d23b93ada33edfaf7cda37c3ff415482af58c661a1
|
7
|
+
data.tar.gz: 92d4ed8273c892e623487ec0089a606d30bb14798d46c73d7c2469d2c167c8489293776c9750b0a1812edb9d61df0c70c3d2fb1caf75243cd8ed16ee8ae3ac63
|
data/.gitignore
ADDED
data/AUTHORS
CHANGED
@@ -1 +1 @@
|
|
1
|
-
TOMONORI KUBOTA <
|
1
|
+
TOMONORI KUBOTA <tkubota _at_ ctc-america.com>
|
data/Gemfile.lock
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fluent-diagtool (0.1.
|
5
|
-
fileutils
|
6
|
-
json
|
4
|
+
fluent-diagtool (0.1.7)
|
5
|
+
fileutils
|
6
|
+
json
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
11
|
diff-lcs (1.3)
|
12
|
-
fileutils (1.
|
13
|
-
json (2.
|
12
|
+
fileutils (1.4.1)
|
13
|
+
json (2.3.0)
|
14
14
|
rake (12.3.3)
|
15
15
|
rspec (3.9.0)
|
16
16
|
rspec-core (~> 3.9.0)
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Fluentd Diagnostic Tool
|
2
2
|
|
3
|
-
The diagtool enable users to automate the date collection which is required for trouble shooting. The data collected by diagtool include the configuration and log files of the td-agent and diagnostic information of operating system such as network and memory status and stats. In some cases, configuration and log files contains the security sensitive information, such as IP addresses and Hostname. The diagtool also provides the functions to generate mask on IP addresses, Hostname(in FQDN style) and user defined keywords described in the collected data
|
4
|
-
The scope of data collection
|
3
|
+
The diagtool enable users to automate the date collection which is required for trouble shooting. The data collected by diagtool include the configuration and log files of the td-agent and diagnostic information of operating system such as network and memory status and stats. In some cases, configuration and log files contains the security sensitive information, such as IP addresses and Hostname. The diagtool also provides the functions to generate mask on IP addresses, Hostname(in FQDN style) and user defined keywords described in the collected data.
|
4
|
+
The scope of data collection:
|
5
5
|
- TD Agent information
|
6
6
|
- configuration files (*)
|
7
7
|
- log files (*)
|
@@ -11,18 +11,20 @@ The scope of data collection:<br>
|
|
11
11
|
- OS log file
|
12
12
|
- OS parameters
|
13
13
|
- OS and kernel version
|
14
|
-
- time/date information
|
15
|
-
- maximum number of file descriptor(ulimit)
|
14
|
+
- time/date information(ntp -q/chronyc sources)
|
15
|
+
- maximum number of file descriptor(ulimit -n)
|
16
16
|
- kernel network parameters(sysctl)
|
17
|
-
-
|
18
|
-
-
|
19
|
-
|
20
|
-
|
17
|
+
- snapshot of current process(ps)
|
18
|
+
- network conectivity status/stats(netstat -plan/netstat -s)
|
19
|
+
- memory information(/proc/meminfo)
|
20
|
+
<br>
|
21
21
|
|
22
22
|
## Prerequisite
|
23
|
+
The diagtool provides support for td-agent based installation running on Linux OS. The td-agent is a stable distribution package of Fluentd.
|
24
|
+
The differences between Fluentd and td-agent are described in followed url:
|
25
|
+
https://www.fluentd.org/faqs
|
23
26
|
|
24
|
-
|
25
|
-
## Installation
|
27
|
+
## Diagtool Installation
|
26
28
|
|
27
29
|
```
|
28
30
|
# gem install fluent-diagtool
|
@@ -40,13 +42,54 @@ Successfully installed fluent-diagtool-0.1.2
|
|
40
42
|
```
|
41
43
|
# diagtool --help
|
42
44
|
Usage: /usr/local/bin/diagtool -o OUTPUT_DIR -m {yes | no} -w {word1,[word2...]} -f {listfile} -s {hash seed}
|
45
|
+
--precheck Run Precheck (Optional)
|
43
46
|
-o, --output DIR Output directory (Mandatory)
|
44
47
|
-m, --mask yes|no Enable mask function (Optional : Default=no)
|
45
48
|
-w, --word-list word1,word2 Provide a list of user-defined words which will to be masked (Optional : Default=None)
|
46
|
-
-f, --word-file
|
49
|
+
-f, --word-file list_file provide a file which describes a List of user-defined words (Optional : Default=None)
|
47
50
|
-s, --hash-seed seed provide a word which will be used when generate the mask (Optional : Default=None)
|
51
|
+
-c, --conf config_file provide a full path of td-agent configuration file (Optional : Default=None)
|
52
|
+
-l, --log log_file provide a full path of td-agent log file (Optional : Default=None)
|
53
|
+
```
|
54
|
+
### Pre-check
|
55
|
+
The diagtool automatically extract the path of td-agent configuration and log files from td-agent daemon and use them during data collection if the td-agent is managed as daemon. The precheck options provides the function to confirm if the diagtool could gather the td-agent information as expected.
|
56
|
+
The following command output shows the case when the diagtool successfully gather information from daemon.
|
57
|
+
```
|
58
|
+
# diagtool --precheck
|
59
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] Check OS parameters...
|
60
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] operating system = CentOS Linux 8 (Core)
|
61
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] kernel version = Linux 4.18.0-147.el8.x86_64
|
62
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] Check td-agent parameters...
|
63
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] td-agent conf path = /etc/td-agent/
|
64
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] td-agent conf file = td-agent.conf
|
65
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] td-agent log path = /var/log/td-agent/
|
66
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] td-agent log = td-agent.log
|
67
|
+
2020-05-28 00:39:02 -0400: [Diagtool] [INFO] [Precheck] Precheck completed. You can run diagtool command without -c and -l options
|
68
|
+
```
|
69
|
+
In some cases, users do not manage td-agent as daemon but use own script to run td-agent with command line options. In that cases, users need to speccify the path of td-agent configuration and log files with -c and -l options respectively.
|
70
|
+
The following example shows the precheck results when the diagtool is not able to extract the path of td-agent configuration and log files.
|
48
71
|
```
|
49
|
-
|
72
|
+
# diagtool --precheck
|
73
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] Check OS parameters...
|
74
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] operating system = CentOS Linux 8 (Core)
|
75
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] kernel version = Linux 4.18.0-147.5.1.el8_1.x86_64
|
76
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] Check td-agent parameters...
|
77
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] td-agent conf path =
|
78
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] td-agent conf file =
|
79
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] td-agent log path =
|
80
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [INFO] [Precheck] td-agent log =
|
81
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [WARN] [Precheck] can not find td-agent conf path: please run diagtool command with -c /path/to/<td-agent conf file>
|
82
|
+
2020-05-28 05:45:14 +0000: [Diagtool] [WARN] [Precheck] can not find td-agent log path: please run diagtool command with -l /path/to/<td-agent log file>
|
83
|
+
```
|
84
|
+
|
85
|
+
### Run diagtool
|
86
|
+
|
87
|
+
#### The "@include" directive in td-agent configuration file
|
88
|
+
The "@include" directive is a function to reuse configuration defined in another configuration files. The diagtool read the td-agent configuration and collect the files described in "@include" directive as well. The details of "@include" directive are described in followed url:
|
89
|
+
https://docs.fluentd.org/configuration/config-file#6-re-use-your-config-the-include-directive
|
90
|
+
|
91
|
+
#### User defined words to be masked
|
92
|
+
The user-defined words can be specified both -e option and -f option and the words are merged when both options are selected.
|
50
93
|
The format of user-defined words list file specified in -f option should be followed format.
|
51
94
|
```
|
52
95
|
# cat word_list_sample
|
@@ -113,9 +156,9 @@ NOTE: When user specified the keywork, only the exact match words will be masked
|
|
113
156
|
2020-05-12 18:21:22 -0400: [Diagtool] [INFO] [Mask] Export mask log file : ./mask_20200512182119.json
|
114
157
|
2020-05-12 18:21:22 -0400: [Diagtool] [INFO] [Collect] Generate tar file /tmp/work1/diagout-20200512182119.tar.gz
|
115
158
|
```
|
116
|
-
|
117
|
-
When run diagtool with mask option, the log of mask is also created in 'mask_{timestamp}.json' file. Users are able to confirm how the mask was generated on each files.
|
118
|
-
|
159
|
+
#### Mask Function
|
160
|
+
When run diagtool with mask option, the log of mask is also created in 'mask_{timestamp}.json' file. Users are able to confirm how the mask was generated on each files.
|
161
|
+
The diagtool provides hash-seed option with '-s'. When hash-seed is specified, the mask will be generated with original word and hash-seed so that users could use unique mask value.
|
119
162
|
#### Mask sample - IP address: IPv4_{md5hash}
|
120
163
|
```
|
121
164
|
"Line112-8": {
|
data/bin/diagtool.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'logger'
|
3
|
+
require '../lib/diagutils'
|
4
|
+
include Diagtool
|
5
|
+
|
6
|
+
logger = Logger.new(STDOUT, formatter: proc {|severity, datetime, progname, msg|
|
7
|
+
"#{datetime}: [Diagtool] [#{severity}] #{msg}\n"
|
8
|
+
})
|
9
|
+
|
10
|
+
output_dir = '../output'
|
11
|
+
mask = 'yes'
|
12
|
+
exlist= Array.new
|
13
|
+
|
14
|
+
opt = OptionParser.new
|
15
|
+
opt.banner = "Usage: #{$0} -o OUTPUT_DIR -m {yes | no} -e {word1,[word2...]} -f {listfile}"
|
16
|
+
opt.on('-o','--output DIR', String, 'Output directory (Default=./output)') { |o|
|
17
|
+
output_dir = o
|
18
|
+
}
|
19
|
+
opt.on('-m','--mask YES|NO', String, 'Enable mask function (Default=True)') { |m|
|
20
|
+
if m == 'yes' || m == 'no'
|
21
|
+
mask = m
|
22
|
+
else
|
23
|
+
logger.error("Invalid value '#{m}' : -m | --mask should be yes or no")
|
24
|
+
exit!
|
25
|
+
end
|
26
|
+
}
|
27
|
+
opt.on('-e','--exclude-list LIST', Array, 'Provide a list of exclude words which will to be masked (Default=None)') { |e| exlist += e }
|
28
|
+
opt.on('-f','--exclude-file FILE', String, 'provide a file which describes a List of exclude words (Default=None)') { |f|
|
29
|
+
if File.exist?(f)
|
30
|
+
File.readlines(f).each do |l|
|
31
|
+
exlist.append(l.gsub(/\n/,''))
|
32
|
+
end
|
33
|
+
else
|
34
|
+
logger.error("No such file or directory")
|
35
|
+
exit!
|
36
|
+
end
|
37
|
+
}
|
38
|
+
opt.parse(ARGV)
|
39
|
+
exlist = exlist.uniq
|
40
|
+
|
41
|
+
logger.info("Parsing command options...")
|
42
|
+
logger.info(" Option : Output directory = #{output_dir}")
|
43
|
+
logger.info(" Option : Mask = #{mask}")
|
44
|
+
logger.info(" Option : Exclude list = #{exlist}")
|
45
|
+
|
46
|
+
logger.info("Initializing parameters...")
|
47
|
+
node1 = Diagutils.new(output_dir,exlist, 'INFO')
|
48
|
+
|
49
|
+
logger.info("Collecting log files of td-agent...")
|
50
|
+
tdlog = node1.collect_tdlog()
|
51
|
+
logger.info("log files of td-agent are stored in #{tdlog}")
|
52
|
+
|
53
|
+
logger.info("Collecting config file of td-agent...")
|
54
|
+
tdconf = node1.collect_tdconf()
|
55
|
+
logger.info("config file is stored in #{tdconf}")
|
56
|
+
|
57
|
+
logger.info("Collecting systctl information...")
|
58
|
+
sysctl = node1.collect_sysctl()
|
59
|
+
logger.info("sysctl information is stored in #{sysctl}")
|
60
|
+
|
61
|
+
logger.info("Collecting date/time information...")
|
62
|
+
ntp = node1.collect_ntp()
|
63
|
+
logger.info("date/time information is stored in #{ntp}")
|
64
|
+
|
65
|
+
logger.info("Collecting ulimit information...")
|
66
|
+
ulimit = node1.collect_ulimit()
|
67
|
+
logger.info("ulimit information is stored in #{ulimit}")
|
68
|
+
|
69
|
+
if mask == 'yes'
|
70
|
+
logger.info("Masking td-agent config file : #{tdconf}...")
|
71
|
+
node1.mask_tdconf(tdconf)
|
72
|
+
tdlog.each do | file |
|
73
|
+
logger.info("Masking td-agent log file : #{file}...")
|
74
|
+
filename = file.split("/")[-1]
|
75
|
+
if filename.include?(".gz")
|
76
|
+
node1.mask_tdlog_gz(file)
|
77
|
+
elsif
|
78
|
+
node1.mask_tdlog(file)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
tar_file = node1.compress_output()
|
84
|
+
logger.info("Generate tar file #{tar_file}")
|
85
|
+
|
86
|
+
|
87
|
+
|
data/exclude_list01
ADDED
data/exe/diagtool
CHANGED
@@ -26,13 +26,21 @@ include Diagtool
|
|
26
26
|
params = {}
|
27
27
|
OptionParser.new do |opt|
|
28
28
|
opt.banner = "Usage: #{$0} -o OUTPUT_DIR -m {yes | no} -w {word1,[word2...]} -f {listfile} -s {hash seed}"
|
29
|
+
opt.on('--precheck', 'Run Precheck (Optional)')
|
29
30
|
opt.on('-o','--output DIR', String, 'Output directory (Mandatory)')
|
30
31
|
opt.on('-m','--mask yes|no', String, 'Enable mask function (Optional : Default=no)')
|
31
32
|
opt.on('-w','--word-list word1,word2', Array, 'Provide a list of user-defined words which will to be masked (Optional : Default=None)')
|
32
|
-
opt.on('-f','--word-file
|
33
|
+
opt.on('-f','--word-file list_file', String, 'provide a file which describes a List of user-defined words (Optional : Default=None)')
|
33
34
|
opt.on('-s','--hash-seed seed', String, 'provide a word which will be used when generate the mask (Optional : Default=None)')
|
35
|
+
opt.on('-c','--conf config_file', String, 'provide a full path of td-agent configuration file (Optional : Default=None)')
|
36
|
+
opt.on('-l','--log log_file', String, 'provide a full path of td-agent log file (Optional : Default=None)')
|
34
37
|
end.parse!(into: params)
|
38
|
+
|
35
39
|
diag = DiagUtils.new(params)
|
36
|
-
|
40
|
+
if params[:precheck]
|
41
|
+
diag.run_precheck()
|
42
|
+
else
|
43
|
+
diag.run_diagtool()
|
44
|
+
end
|
37
45
|
|
38
46
|
|
data/fluent-diagtool.gemspec
CHANGED
@@ -19,6 +19,6 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_runtime_dependency("fileutils"
|
23
|
-
spec.add_runtime_dependency("json"
|
22
|
+
spec.add_runtime_dependency("fileutils")
|
23
|
+
spec.add_runtime_dependency("json")
|
24
24
|
end
|
@@ -22,22 +22,41 @@ module Diagtool
|
|
22
22
|
class CollectUtils
|
23
23
|
def initialize(conf, log_level)
|
24
24
|
@logger = Logger.new(STDOUT, level: log_level, formatter: proc {|severity, datetime, progname, msg|
|
25
|
-
"#{datetime}: [
|
25
|
+
"#{datetime}: [Collectutils] [#{severity}] #{msg}\n"
|
26
26
|
})
|
27
|
+
@precheck = conf[:precheck]
|
27
28
|
@time_format = conf[:time]
|
28
29
|
@basedir = conf[:basedir]
|
29
30
|
@workdir = conf[:workdir]
|
30
31
|
@outdir = conf[:outdir]
|
31
|
-
|
32
|
-
@tdenv =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
|
33
|
+
@tdenv = gen_tdenv()
|
34
|
+
if not conf[:tdconf].empty?
|
35
|
+
@tdconf = conf[:tdconf].split('/')[-1]
|
36
|
+
@tdconf_path = conf[:tdconf].gsub(@tdconf,'')
|
37
|
+
elsif
|
38
|
+
if not @tdenv['FLUENT_CONF'].empty?
|
39
|
+
@tdconf = @tdenv['FLUENT_CONF'].split('/')[-1]
|
40
|
+
@tdconf_path = @tdenv['FLUENT_CONF'].gsub(@tdconf,'')
|
41
|
+
else
|
42
|
+
raise "The path of td-agent configuration file need to be specified." if conf[:precheck] == false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
if not conf[:tdlog].empty?
|
46
|
+
@tdlog = conf[:tdlog].split('/')[-1]
|
47
|
+
@tdlog_path = conf[:tdlog].gsub(@tdlog,'')
|
48
|
+
elsif
|
49
|
+
if not @tdenv['TD_AGENT_LOG_FILE'].empty?
|
50
|
+
@tdlog = @tdenv['TD_AGENT_LOG_FILE'].split('/')[-1]
|
51
|
+
@tdlog_path = @tdenv['TD_AGENT_LOG_FILE'].gsub(@tdlog,'')
|
52
|
+
else
|
53
|
+
raise "The path of td-agent log file need to be specified." if conf[:precheck] == false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
@osenv = gen_osenv()
|
39
57
|
@oslog_path = '/var/log/'
|
40
58
|
@oslog = 'messages'
|
59
|
+
@syslog = 'syslog'
|
41
60
|
@sysctl_path = '/etc/'
|
42
61
|
@sysctl = 'sysctl.conf'
|
43
62
|
|
@@ -50,29 +69,61 @@ module Diagtool
|
|
50
69
|
@logger.info(" td-agent log = #{@tdlog}")
|
51
70
|
end
|
52
71
|
|
53
|
-
def
|
72
|
+
def gen_osenv()
|
54
73
|
stdout, stderr, status = Open3.capture3('hostnamectl')
|
55
74
|
os_dict = {}
|
56
75
|
stdout.each_line { |l|
|
57
76
|
s = l.split(":")
|
58
77
|
os_dict[s[0].chomp.strip] = s[1].chomp.strip
|
59
78
|
}
|
60
|
-
|
61
|
-
|
79
|
+
if @precheck == false # SKip if precheck is true
|
80
|
+
File.open(@outdir+'/os_env.output', 'w') do |f|
|
81
|
+
f.puts(stdout)
|
82
|
+
end
|
62
83
|
end
|
63
84
|
return os_dict
|
64
85
|
end
|
65
86
|
|
66
|
-
def
|
87
|
+
def gen_tdenv()
|
67
88
|
stdout, stderr, status = Open3.capture3('systemctl cat td-agent')
|
68
89
|
env_dict = {}
|
69
|
-
|
70
|
-
|
71
|
-
|
90
|
+
if status.success?
|
91
|
+
if @precheck == false # SKip if precheck is true
|
92
|
+
File.open(@outdir+'/td-agent_env.output', 'w') do |f|
|
93
|
+
f.puts(stdout)
|
94
|
+
end
|
95
|
+
end
|
72
96
|
stdout.split().each do | l |
|
73
|
-
|
74
|
-
|
75
|
-
|
97
|
+
if l.include?('Environment')
|
98
|
+
env_dict[l.split('=')[1]] = l.split('=')[2]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
else
|
102
|
+
exe = 'fluentd'
|
103
|
+
stdout, stderr, status = Open3.capture3("ps aux | grep #{exe} | grep -v grep")
|
104
|
+
line = stdout.split(/\n/)
|
105
|
+
log_path = ''
|
106
|
+
conf_path = ''
|
107
|
+
line.each { |l|
|
108
|
+
cmd = l.split.drop(10)
|
109
|
+
i = 0
|
110
|
+
log_pos = 0
|
111
|
+
conf_pos = 0
|
112
|
+
if cmd[-1] != '--under-supervisor'
|
113
|
+
cmd.each { |c|
|
114
|
+
if c.include?("--log") || c.include?("-l")
|
115
|
+
log_pos = i + 1
|
116
|
+
log_path = cmd[log_pos]
|
117
|
+
elsif c.include?("--conf") || c.include?("-c")
|
118
|
+
conf_pos = i + 1
|
119
|
+
conf_path = cmd[conf_pos]
|
120
|
+
end
|
121
|
+
i+=1
|
122
|
+
}
|
123
|
+
end
|
124
|
+
}
|
125
|
+
env_dict['FLUENT_CONF'] = conf_path
|
126
|
+
env_dict['TD_AGENT_LOG_FILE'] = log_path
|
76
127
|
end
|
77
128
|
return env_dict
|
78
129
|
end
|
@@ -90,86 +141,83 @@ module Diagtool
|
|
90
141
|
end
|
91
142
|
|
92
143
|
def collect_tdconf()
|
93
|
-
|
94
|
-
FileUtils.
|
95
|
-
|
144
|
+
target_dir = @workdir+@tdconf_path
|
145
|
+
FileUtils.mkdir_p(target_dir)
|
146
|
+
FileUtils.cp(@tdconf_path+@tdconf, target_dir)
|
147
|
+
conf = @workdir+@tdconf_path+@tdconf
|
148
|
+
conf_list = []
|
149
|
+
conf_list.push target_dir + @tdconf
|
150
|
+
File.readlines(conf).each { |line|
|
151
|
+
if line.include? '@include'
|
152
|
+
f = line.split()[1]
|
153
|
+
if f.start_with?(/\//) # /tmp/work1/b.conf
|
154
|
+
if f.include?('*')
|
155
|
+
Dir.glob(f).each { |ff|
|
156
|
+
conf_inc = target_dir + ff.gsub(/\//,'__')
|
157
|
+
FileUtils.cp(ff, conf_inc)
|
158
|
+
conf_list.push conf_inc
|
159
|
+
}
|
160
|
+
else
|
161
|
+
conf_inc = target_dir+f.gsub(/\//,'__')
|
162
|
+
FileUtils.cp(f, conf_inc)
|
163
|
+
conf_list.push conf_inc
|
164
|
+
end
|
165
|
+
else
|
166
|
+
f = f.gsub('./','') if f.include?('./')
|
167
|
+
if f.include?('*')
|
168
|
+
Dir.glob(@tdconf_path+f).each{ |ff|
|
169
|
+
conf_inc = target_dir + ff.gsub(@tdconf_path,'').gsub(/\//,'__')
|
170
|
+
FileUtils.cp(ff, conf_inc)
|
171
|
+
conf_list.push conf_inc
|
172
|
+
}
|
173
|
+
else
|
174
|
+
conf_inc = target_dir+f.gsub(/\//,'__')
|
175
|
+
FileUtils.cp(@tdconf_path+f, conf_inc)
|
176
|
+
conf_list.push conf_inc
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
}
|
181
|
+
return conf_list
|
96
182
|
end
|
97
|
-
|
183
|
+
|
98
184
|
def collect_tdlog()
|
99
|
-
|
100
|
-
FileUtils.
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
FileUtils.mkdir_p(@workdir+@sysctl_path)
|
106
|
-
FileUtils.cp(@sysctl_path+@sysctl, @workdir+@sysctl_path)
|
107
|
-
return @workdir+@sysctl_path+@sysctl
|
185
|
+
target_dir = @workdir+@tdlog_path
|
186
|
+
FileUtils.mkdir_p(target_dir)
|
187
|
+
Dir.glob(@tdlog_path+@tdlog+'*').each{ |f|
|
188
|
+
FileUtils.cp(f, target_dir)
|
189
|
+
}
|
190
|
+
return Dir.glob(target_dir+@tdlog+'*')
|
108
191
|
end
|
109
192
|
|
110
193
|
def collect_oslog()
|
111
|
-
|
112
|
-
FileUtils.
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
194
|
+
target_dir = @workdir+@oslog_path
|
195
|
+
FileUtils.mkdir_p(target_dir)
|
196
|
+
if File.exist? @oslog_path+@oslog
|
197
|
+
FileUtils.cp(@oslog_path+@oslog, target_dir)
|
198
|
+
return target_dir+@oslog
|
199
|
+
elsif File.exist? @oslog_path+@syslog
|
200
|
+
FileUtils.cp(@oslog_path+@syslog, target_dir)
|
201
|
+
return target_dir+@syslog
|
202
|
+
else
|
203
|
+
@logger.warn("Can not find OS log file in #{oslog} or #{syslog}")
|
121
204
|
end
|
122
|
-
return output
|
123
|
-
end
|
124
|
-
|
125
|
-
def collect_ps_eo()
|
126
|
-
output = @outdir+'/ps_eo.output'
|
127
|
-
stdout, stderr, status = Open3.capture3("ps -eo pid,ppid,stime,time,%mem,%cpu,cmd")
|
128
|
-
File.open(output, 'w') do |f|
|
129
|
-
f.puts(stdout)
|
130
|
-
end
|
131
|
-
return output
|
132
205
|
end
|
133
206
|
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
output = @outdir+'/netstat_plan.output'
|
145
|
-
stdout, stderr, status = Open3.capture3("netstat -plan")
|
146
|
-
File.open(output, 'w') do |f|
|
147
|
-
f.puts(stdout)
|
148
|
-
end
|
149
|
-
return output
|
150
|
-
end
|
151
|
-
|
152
|
-
def collect_netstat_s()
|
153
|
-
output = @outdir+'/netstat_s.output'
|
154
|
-
stdout, stderr, status = Open3.capture3("netstat -s")
|
155
|
-
File.open(output, 'w') do |f|
|
156
|
-
f.puts(stdout)
|
157
|
-
end
|
158
|
-
return output
|
159
|
-
end
|
160
|
-
|
161
|
-
def collect_ntp(command)
|
162
|
-
output = @outdir+'/ntp_info.output'
|
163
|
-
stdout_date, stderr_date, status_date = Open3.capture3("date")
|
164
|
-
stdout_ntp, stderr_ntp, status_ntp = Open3.capture3("chronyc sources") if command == "chrony"
|
165
|
-
stdout_ntp, stderr_ntp, status_ntp = Open3.capture3("ntpq -p") if command == "ntp"
|
166
|
-
File.open(output, 'w') do |f|
|
167
|
-
f.puts(stdout_date)
|
168
|
-
f.puts(stdout_ntp)
|
207
|
+
def collect_cmd_output(cmd)
|
208
|
+
cmd_name = cmd.gsub(/\s/,'_').gsub(/\//,'-').gsub(',','_')
|
209
|
+
output = @outdir+'/'+cmd_name+'.txt'
|
210
|
+
stdout, stderr, status = Open3.capture3(cmd)
|
211
|
+
if status.success?
|
212
|
+
File.open(output, 'w') do |f|
|
213
|
+
f.puts(stdout)
|
214
|
+
end
|
215
|
+
else
|
216
|
+
@logger.warn("Command #{cmd} failed due to the following message - #{stderr.chomp}")
|
169
217
|
end
|
170
218
|
return output
|
171
219
|
end
|
172
|
-
|
220
|
+
|
173
221
|
def collect_tdgems()
|
174
222
|
output = @outdir+'/tdgem_list.output'
|
175
223
|
stdout, stderr, status = Open3.capture3("td-agent-gem list | grep fluent")
|
@@ -27,14 +27,48 @@ module Diagtool
|
|
27
27
|
time = Time.new
|
28
28
|
@time_format = time.strftime("%Y%m%d%0k%M%0S")
|
29
29
|
@conf = parse_diagconf(params)
|
30
|
+
@cmd_list = [
|
31
|
+
"ps -eo pid,ppid,stime,time,%mem,%cpu,cmd",
|
32
|
+
"cat /proc/meminfo",
|
33
|
+
"netstat -plan",
|
34
|
+
"netstat -s",
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
def run_precheck()
|
39
|
+
prechecklog = Logger.new(STDOUT, formatter: proc {|severity, datetime, progname, msg|
|
40
|
+
"#{datetime}: [Diagtool] [#{severity}] #{msg}\n"
|
41
|
+
})
|
42
|
+
loglevel = 'WARN'
|
43
|
+
c = CollectUtils.new(@conf, loglevel)
|
44
|
+
c_env = c.export_env()
|
45
|
+
prechecklog.info("[Precheck] Check OS parameters...")
|
46
|
+
prechecklog.info("[Precheck] operating system = #{c_env[:os]}")
|
47
|
+
prechecklog.info("[Precheck] kernel version = #{c_env[:kernel]}")
|
48
|
+
prechecklog.info("[Precheck] Check td-agent parameters...")
|
49
|
+
prechecklog.info("[Precheck] td-agent conf path = #{c_env[:tdconf_path]}")
|
50
|
+
prechecklog.info("[Precheck] td-agent conf file = #{c_env[:tdconf]}")
|
51
|
+
prechecklog.info("[Precheck] td-agent log path = #{c_env[:tdlog_path]}")
|
52
|
+
prechecklog.info("[Precheck] td-agent log = #{c_env[:tdlog]}")
|
53
|
+
if c_env[:tdconf_path] == nil || c_env[:tdconf] == nil
|
54
|
+
prechecklog.warn("[Precheck] can not find td-agent conf path: please run diagtool command with -c /path/to/<td-agent conf file>")
|
55
|
+
end
|
56
|
+
if c_env[:tdlog_path] == nil || c_env[:tdlog] == nil
|
57
|
+
prechecklog.warn("[Precheck] can not find td-agent log path: please run diagtool command with -l /path/to/<td-agent log file>")
|
58
|
+
end
|
59
|
+
if c_env[:tdconf_path] != nil && c_env[:tdconf] != nil && c_env[:tdlog_path] != nil && c_env[:tdlog] != nil
|
60
|
+
prechecklog.info("[Precheck] Precheck completed. You can run diagtool command without -c and -l options")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_diagtool()
|
30
65
|
@conf[:time] = @time_format
|
31
66
|
@conf[:workdir] = @conf[:basedir] + '/' + @time_format
|
32
67
|
@conf[:outdir] = @conf[:workdir] + '/output'
|
33
|
-
|
34
68
|
FileUtils.mkdir_p(@conf[:workdir])
|
35
69
|
FileUtils.mkdir_p(@conf[:outdir])
|
36
|
-
|
37
70
|
diaglog = @conf[:workdir] + '/diagtool.output'
|
71
|
+
|
38
72
|
@masklog = './mask_' + @time_format + '.json'
|
39
73
|
@logger = Logger.new(STDOUT, formatter: proc {|severity, datetime, progname, msg|
|
40
74
|
"#{datetime}: [Diagtool] [#{severity}] #{msg}\n"
|
@@ -47,9 +81,7 @@ module Diagtool
|
|
47
81
|
diaglogger_info(" Option : Mask = #{@conf[:mask]}")
|
48
82
|
diaglogger_info(" Option : Word list = #{@conf[:words]}")
|
49
83
|
diaglogger_info(" Option : Hash Seed = #{@conf[:seed]}")
|
50
|
-
|
51
|
-
|
52
|
-
def diagtool()
|
84
|
+
|
53
85
|
loglevel = 'WARN'
|
54
86
|
diaglogger_info("Initializing parameters...")
|
55
87
|
c = CollectUtils.new(@conf, loglevel)
|
@@ -84,39 +116,35 @@ module Diagtool
|
|
84
116
|
end
|
85
117
|
diaglogger_info("[Collect] config file is stored in #{oslog}")
|
86
118
|
|
87
|
-
diaglogger_info("[Collect] Collecting process information...")
|
88
|
-
meminfo = c.collect_ps_eo()
|
89
|
-
diaglogger_info("[Collect] process informationis stored in #{meminfo}")
|
90
|
-
|
91
|
-
diaglogger_info("[Collect] Collecting OS memory information...")
|
92
|
-
meminfo = c.collect_meminfo()
|
93
|
-
diaglogger_info("[Collect] OS memory information is stored in #{meminfo}")
|
94
|
-
|
95
119
|
diaglogger_info("[Collect] Collecting date/time information...")
|
96
120
|
if system('which chronyc > /dev/null 2>&1')
|
97
|
-
ntp = c.
|
121
|
+
ntp = c.collect_cmd_output(command="chronyc sources")
|
122
|
+
diaglogger_info("[Collect] date/time information is stored in #{ntp}")
|
98
123
|
elsif system('which ntpq > /dev/null 2>&1')
|
99
|
-
ntp = c.collect_ntp(command="
|
124
|
+
ntp = c.collect_ntp(command="ntpq -p")
|
125
|
+
diaglogger_info("[Collect] date/time information is stored in #{ntp}")
|
100
126
|
else
|
101
127
|
diaglogger_warn("[Collect] chrony/ntp does not exist. skip collectig date/time information")
|
102
128
|
end
|
103
|
-
diaglogger_info("[Collect] date/time information is stored in #{ntp}")
|
104
|
-
|
105
|
-
diaglogger_info("[Collect] Collecting netstat information...")
|
106
|
-
if system('which netstat > /dev/null 2>&1')
|
107
|
-
netstat_n = c.collect_netstat_plan()
|
108
|
-
netstat_s = c.collect_netstat_s()
|
109
|
-
if @conf[:mask] == 'yes'
|
110
|
-
diaglogger_info("[Mask] Masking netstat file : #{netstat_n}...")
|
111
|
-
netstat_n = m.mask_tdlog(netstat_n, clean = true)
|
112
|
-
end
|
113
|
-
diaglogger_info("[Collect] netstat information is stored in #{netstat_n} and #{netstat_s}")
|
114
|
-
else
|
115
|
-
diaglogger_warn("[Collect] netstat does not exist. skip collectig netstat")
|
116
|
-
end
|
117
129
|
|
130
|
+
###
|
131
|
+
# Correct OS information
|
132
|
+
###
|
133
|
+
@cmd_list.each { |cmd|
|
134
|
+
diaglogger_info("[Collect] Collecting command output : command = #{cmd}")
|
135
|
+
out = c.collect_cmd_output(cmd)
|
136
|
+
if @conf[:mask] == 'yes'
|
137
|
+
diaglogger_info("[Mask] Masking netstat file : #{out}...")
|
138
|
+
out = m.mask_tdlog(out, clean = true)
|
139
|
+
end
|
140
|
+
diaglogger_info("[Collect] Collecting command output #{cmd.split[0]} stored in #{out}")
|
141
|
+
}
|
142
|
+
|
143
|
+
###
|
144
|
+
# Correct information to be validated
|
145
|
+
###
|
118
146
|
diaglogger_info("[Collect] Collecting systctl information...")
|
119
|
-
sysctl = c.
|
147
|
+
sysctl = c.collect_cmd_output("sysctl -a")
|
120
148
|
diaglogger_info("[Collect] sysctl information is stored in #{sysctl}")
|
121
149
|
|
122
150
|
diaglogger_info("[Valid] Validating systctl information...")
|
@@ -131,7 +159,7 @@ module Diagtool
|
|
131
159
|
end
|
132
160
|
|
133
161
|
diaglogger_info("[Collect] Collecting ulimit information...")
|
134
|
-
ulimit = c.
|
162
|
+
ulimit = c.collect_cmd_output(cmd="sh -c 'ulimit -n'")
|
135
163
|
diaglogger_info("[Collect] ulimit information is stored in #{ulimit}")
|
136
164
|
|
137
165
|
diaglogger_info("[Valid] Validating ulimit information...")
|
@@ -143,9 +171,11 @@ module Diagtool
|
|
143
171
|
end
|
144
172
|
|
145
173
|
if @conf[:mask] == 'yes'
|
146
|
-
|
147
|
-
|
148
|
-
|
174
|
+
tdconf.each { | file |
|
175
|
+
diaglogger_info("[Mask] Masking td-agent config file : #{file}...")
|
176
|
+
m.mask_tdlog(file, clean = true)
|
177
|
+
}
|
178
|
+
tdlog.each { | file |
|
149
179
|
diaglogger_info("[Mask] Masking td-agent log file : #{file}...")
|
150
180
|
filename = file.split("/")[-1]
|
151
181
|
if filename.include?(".gz")
|
@@ -153,30 +183,37 @@ module Diagtool
|
|
153
183
|
elsif
|
154
184
|
m.mask_tdlog(file, clean = true)
|
155
185
|
end
|
156
|
-
|
186
|
+
}
|
157
187
|
end
|
158
188
|
|
159
189
|
if @conf[:mask] == 'yes'
|
160
190
|
diaglogger_info("[Mask] Export mask log file : #{@masklog}")
|
161
191
|
m.export_masklog(@masklog)
|
162
192
|
end
|
163
|
-
|
193
|
+
|
164
194
|
tar_file = c.compress_output()
|
165
195
|
diaglogger_info("[Collect] Generate tar file #{tar_file}")
|
166
196
|
end
|
167
197
|
|
168
198
|
def parse_diagconf(params)
|
169
199
|
options = {
|
170
|
-
:basedir => '', :mask => '', :words => [], :wfile => '', :seed => ''
|
200
|
+
:precheck => '', :basedir => '', :mask => '', :words => [], :wfile => '', :seed => '', :tdconf =>'', :tdlog => ''
|
171
201
|
}
|
172
|
-
if params[:
|
173
|
-
|
174
|
-
|
202
|
+
if params[:precheck]
|
203
|
+
options[:precheck] = params[:precheck]
|
204
|
+
else
|
205
|
+
options[:precheck] = false
|
206
|
+
end
|
207
|
+
if options[:precheck] == false
|
208
|
+
if params[:output] != nil
|
209
|
+
if Dir.exist?(params[:output])
|
210
|
+
options[:basedir] = params[:output]
|
211
|
+
else
|
212
|
+
raise "output directory '#{basedir}' does not exist"
|
213
|
+
end
|
175
214
|
else
|
176
|
-
raise "output directory '
|
215
|
+
raise "output directory '-o' must be specified"
|
177
216
|
end
|
178
|
-
else
|
179
|
-
raise "output directory '-o' must be specified"
|
180
217
|
end
|
181
218
|
if params[:mask] == nil
|
182
219
|
options[:mask] = 'no'
|
@@ -200,6 +237,25 @@ module Diagtool
|
|
200
237
|
end
|
201
238
|
options[:words] = options[:words].uniq
|
202
239
|
options[:seed] = params[:"hash-seed"] if params[:"hash-seed"] != nil
|
240
|
+
|
241
|
+
if params[:conf] != nil
|
242
|
+
f = params[:conf]
|
243
|
+
if File.exist?(f)
|
244
|
+
options[:tdconf] = params[:conf]
|
245
|
+
else
|
246
|
+
raise "#{params[:conf]} : No such file or directory"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
if params[:log] != nil
|
251
|
+
f = params[:log]
|
252
|
+
if File.exist?(f)
|
253
|
+
options[:tdlog] = params[:log]
|
254
|
+
else
|
255
|
+
raise "#{params[:log]} : No such file or directory"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
203
259
|
return options
|
204
260
|
end
|
205
261
|
|
@@ -33,7 +33,8 @@ module Diagtool
|
|
33
33
|
:net_ipv4_tcp_max_syn_backlog => "8096",
|
34
34
|
:net_ipv4_tcp_slow_start_after_idle => "0",
|
35
35
|
:net_ipv4_tcp_tw_reuse => "1",
|
36
|
-
:net_ipv4_ip_local_port_range => ["10240", "65535"]
|
36
|
+
:net_ipv4_ip_local_port_range => ["10240", "65535"]
|
37
|
+
}
|
37
38
|
@logger.debug("Initialize Validation Utils:")
|
38
39
|
@logger.debug(" Default ulimit: #{@def_ulimit}")
|
39
40
|
@logger.debug(" Default sysctl: #{@def_sysctl}")
|
@@ -57,7 +58,7 @@ module Diagtool
|
|
57
58
|
v = Hash.new { |i,j| i[j] = Hash.new(&h.default_proc) }
|
58
59
|
@logger.info("Loading sysctl file: #{sysctl_file}")
|
59
60
|
File.readlines(sysctl_file).each{ |line|
|
60
|
-
if line.include?
|
61
|
+
if line.include? "net"
|
61
62
|
line_net = line.chomp.gsub(".","_").split("=")
|
62
63
|
key = line_net[0].strip.to_sym
|
63
64
|
if line_net[1].strip! =~ /\s/
|
@@ -66,17 +67,19 @@ module Diagtool
|
|
66
67
|
value= line_net[1]
|
67
68
|
end
|
68
69
|
h[key] = value
|
69
|
-
|
70
|
-
@
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
70
|
+
if @def_sysctl.key? key
|
71
|
+
if @def_sysctl[key] == value
|
72
|
+
@logger.info("#{key} => #{value} is correct")
|
73
|
+
v[key]['value'] = value
|
74
|
+
v[key]['recommend'] = @def_sysctl[key]
|
75
|
+
v[key]['result'] = "correct"
|
76
|
+
else
|
77
|
+
@logger.warn("#{key} => #{value} is incorrect, should be #{@def_sysctl[key]}")
|
78
|
+
v[key]['value'] = value
|
79
|
+
v[key]['recommend'] = @def_sysctl[key]
|
80
|
+
v[key]['result'] = "incorrect"
|
81
|
+
end
|
82
|
+
end
|
80
83
|
end
|
81
84
|
}
|
82
85
|
if h == @sysctl
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-diagtool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kubotat
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fileutils
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: json
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
description: Bringing productivity of trouble shooting to the next level by automating
|
42
42
|
collection of Fluentd configurations, settings and OS parameters as well as masking
|
43
43
|
sensitive information in logs and configurations.
|
@@ -48,6 +48,7 @@ executables:
|
|
48
48
|
extensions: []
|
49
49
|
extra_rdoc_files: []
|
50
50
|
files:
|
51
|
+
- ".gitignore"
|
51
52
|
- AUTHORS
|
52
53
|
- Gemfile
|
53
54
|
- Gemfile.lock
|
@@ -55,8 +56,10 @@ files:
|
|
55
56
|
- README.md
|
56
57
|
- Rakefile
|
57
58
|
- bin/console
|
59
|
+
- bin/diagtool.rb
|
58
60
|
- bin/setup
|
59
61
|
- bin/word_list_sample
|
62
|
+
- exclude_list01
|
60
63
|
- exe/diagtool
|
61
64
|
- fluent-diagtool.gemspec
|
62
65
|
- lib/fluent/diagtool/collectutils.rb
|