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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cec6d985c53b3d147c6d1eb5418ae15983b1bf2ec1bcd15f6c4c035f20a93cf3
4
- data.tar.gz: b58b0565d227ccc1f99d0dc3fbb482e03b1cb5faf38c9a5326fe753f0a222efc
3
+ metadata.gz: 1afbc81ebf5fe6ab013818ac124776418b3c909bef5ac5a649210499c14ff2c9
4
+ data.tar.gz: ec4b237dcbe9f065f6645181c5cade9cc118cdfbc376460fbbf3108014d360f0
5
5
  SHA512:
6
- metadata.gz: c1f5582d4f9835d158f09ee7256771406cf57c8753b6de9170c68d5b4f774ebb7397fce7fe70c8d247eb92bedf9f46a6af6f575b783a4718323b48a2a824b521
7
- data.tar.gz: 139b59e1c25cd34e745ba799d5c323c18421d3f5b4b95fbf9c59c63b75291e6663ee9a5cb43cb3cb8ac75ea99a1a495b6c3f9b731960643a93df0fc09e3ddfc1
6
+ metadata.gz: 71a1b513b1d55e3033ce8cd90d3060a6316c48358f2a374c75f23b43ddb32693dcb0107a96706a1c961445d23b93ada33edfaf7cda37c3ff415482af58c661a1
7
+ data.tar.gz: 92d4ed8273c892e623487ec0089a606d30bb14798d46c73d7c2469d2c167c8489293776c9750b0a1812edb9d61df0c70c3d2fb1caf75243cd8ed16ee8ae3ac63
@@ -0,0 +1,13 @@
1
+ ~*
2
+ #*
3
+ *~
4
+ [._]*.s[a-w][a-z]
5
+ .DS_Store
6
+
7
+ *.gem
8
+ .bundle
9
+ Gemfile.lock
10
+ vendor
11
+ .ruby-version
12
+
13
+ test/tmp/
data/AUTHORS CHANGED
@@ -1 +1 @@
1
- TOMONORI KUBOTA <tmnr.kubota _at_ gmail.com>
1
+ TOMONORI KUBOTA <tkubota _at_ ctc-america.com>
@@ -1,16 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fluent-diagtool (0.1.2)
5
- fileutils (~> 1.0.2)
6
- json (~> 2.1.0)
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.0.2)
13
- json (2.1.0)
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.<br>
4
- The scope of data collection:<br>
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
- - network conectivity status/stats
18
- - memory information
19
- <br>
20
- (*) The diagtool automatically gather the path of td-agent configuration files and log files and use them during data collection.
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 listfile provide a file which describes a List of user-defined words (Optional : Default=None)
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
- The list of user-defined words can be specified both -e option and -f option.
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
- ## Mask Function
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
- <br>
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": {
@@ -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
+
@@ -0,0 +1,2 @@
1
+ centos8101
2
+ centos8102
@@ -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 listfile', String, 'provide a file which describes a List of user-defined words (Optional : Default=None)')
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
- diag.diagtool()
40
+ if params[:precheck]
41
+ diag.run_precheck()
42
+ else
43
+ diag.run_diagtool()
44
+ end
37
45
 
38
46
 
@@ -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", ["~> 1.0"])
23
- spec.add_runtime_dependency("json", ["~> 2.1"])
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}: [Diagutils] [#{severity}] #{msg}\n"
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 = get_tdenv()
33
- @tdconf = @tdenv['FLUENT_CONF'].split('/')[-1]
34
- @tdconf_path = @tdenv['FLUENT_CONF'].gsub(@tdconf,'')
35
- @tdlog = @tdenv['TD_AGENT_LOG_FILE'].split('/')[-1]
36
- @tdlog_path = @tdenv['TD_AGENT_LOG_FILE'].gsub(@tdlog,'')
37
-
38
- @osenv = get_osenv()
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 get_osenv()
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
- File.open(@workdir+'/os_env.output', 'w') do |f|
61
- f.puts(stdout)
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 get_tdenv()
87
+ def gen_tdenv()
67
88
  stdout, stderr, status = Open3.capture3('systemctl cat td-agent')
68
89
  env_dict = {}
69
- File.open(@workdir+'/td-agent_env.output', 'w') do |f|
70
- f.puts(stdout)
71
- end
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
- if l.include?('Environment')
74
- env_dict[l.split('=')[1]] = l.split('=')[2]
75
- end
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
- FileUtils.mkdir_p(@workdir+@tdconf_path)
94
- FileUtils.cp(@tdconf_path+@tdconf, @workdir+@tdconf_path)
95
- return @workdir+@tdconf_path+@tdconf
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
- FileUtils.mkdir_p(@workdir+@tdlog_path)
100
- FileUtils.cp_r(@tdlog_path, @workdir+@oslog_path)
101
- return Dir.glob(@workdir+@tdlog_path+@tdlog+'*')
102
- end
103
-
104
- def collect_sysctl()
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
- FileUtils.mkdir_p(@workdir+@oslog_path)
112
- FileUtils.cp(@oslog_path+@oslog, @workdir+@oslog_path)
113
- return @workdir+@oslog_path+@oslog
114
- end
115
-
116
- def collect_ulimit()
117
- output = @outdir+'/ulimit_n.output'
118
- stdout, stderr, status = Open3.capture3("ulimit -n")
119
- File.open(output, 'w') do |f|
120
- f.puts(stdout)
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 collect_meminfo()
135
- output = @outdir+'/meminfo.output'
136
- stdout, stderr, status = Open3.capture3("cat /proc/meminfo")
137
- File.open(output, 'w') do |f|
138
- f.puts(stdout)
139
- end
140
- return output
141
- end
142
-
143
- def collect_netstat_plan()
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
- end
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.collect_ntp(command="chrony")
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="ntp")
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.collect_sysctl()
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.collect_ulimit()
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
- diaglogger_info("[Mask] Masking td-agent config file : #{tdconf}...")
147
- m.mask_tdlog(tdconf, clean = true)
148
- tdlog.each do | file |
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
- end
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[:output] != nil
173
- if Dir.exist?(params[:output])
174
- options[:basedir] = params[:output]
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 '#{basedir}' does not exist"
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?("net")
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
- if @def_sysctl[key] == value
70
- @logger.info("#{key} => #{value} is correct")
71
- v[key]['value'] = value
72
- v[key]['recommend'] = @def_sysctl[key]
73
- v[key]['result'] = "correct"
74
- else
75
- @logger.warn("#{key} => #{value} is incorrect, should be #{@def_sysctl[key]}")
76
- v[key]['value'] = value
77
- v[key]['recommend'] = @def_sysctl[key]
78
- v[key]['result'] = "incorrect"
79
- end
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
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
  module Diagtool
3
- VERSION = "0.1.3"
3
+ VERSION = "0.1.8"
4
4
  end
5
5
  end
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.3
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-05-19 00:00:00.000000000 Z
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: '1.0'
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: '1.0'
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: '2.1'
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: '2.1'
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