tweek 0.0.1 → 0.9.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c0c0a75d22fc70c3d22e74318e3d957a286d597f
4
- data.tar.gz: 5928676675e148473ec09ca0358fd73e6a67d494
3
+ metadata.gz: bc8acd5816827fe451b4f223f31c70be9862dc25
4
+ data.tar.gz: 114a4da6ae11f12d5fa23edf7b43419fc0d1a78a
5
5
  SHA512:
6
- metadata.gz: f1bb49f5fff276b2cbc85ab7b31210e95e1af0e8d77f3762cae9f7cfca08010d06a31fdc3a006ad405aea0ad22a5334a9da699ecf52bd0ef15daa5e17983178c
7
- data.tar.gz: 922d61a6cd6c6891f5a6ce8b623a73bd9733c6956c905ba4f332f59d4f3ca248dadc6da2d9b551822212928efbf621d8ac24cfb48c434a00dda4b242dde3797b
6
+ metadata.gz: 6c69ab3d791fad607f25d4c6361387d3d59ac99467b9d79b2f6a94cf02c29541912c2f8af85fa430ad464b94cdf50a52e84d15f6012d35e91a2ea1753422f58d
7
+ data.tar.gz: 3b20cc0973d271c4d4a5c06aa4c6d247817cc9cd71d08a22fb8604e97d70dee21508d65b8467f8984c18ca3d643130cc126810fbfdd0215568f726e485845e10
data/bin/tweek ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # A script to check a variety of Linux system settings
4
+ # See the sample config after __END__ for a description of these.
5
+ # By Nick Townsend, June 2017
6
+ #
7
+ require 'rubygems'
8
+
9
+ require 'optparse'
10
+ require 'ostruct'
11
+ require 'tweek'
12
+ require 'tweek/app'
13
+ require 'tweek/version'
14
+
15
+ trap('INT') do
16
+ print "\n"
17
+ exit(1)
18
+ end
19
+
20
+ gflag = false
21
+ distro = nil
22
+ distro_ver = nil
23
+ kernel_ver = nil
24
+ OptionParser.new do |o|
25
+ o.banner = "Usage: #{$0} [options] [settings_file...]"
26
+ o.separator ""
27
+ o.separator "Check the system parameters specified in the settings files"
28
+ o.separator "(If not supplied then built-in defaults are used)"
29
+ o.separator "Options:"
30
+ o.on('--distro=NAME', "Set Distro (default uses lsb_release)" ) do |d|
31
+ distro = d
32
+ end
33
+ o.on('--distro-ver=X.Y.Z', "Set Distro version (default uses lsb_release)" ) do |dv|
34
+ distro_ver = dv
35
+ end
36
+ o.on('--kernel-ver=X.Y.Z', "Set Kernel version (default uses uname)" ) do |k|
37
+ kernel_ver = k
38
+ end
39
+ o.on( '-g', '--generate', "Generate a copy of the input file(s)", "containing current values on STDOUT" ) do
40
+ gflag = true
41
+ end
42
+ o.on( '-s', '--show', "Write the default parameters to STDOUT",
43
+ "Use as a fully documented starting point"
44
+ ) do
45
+ DATA.each_line{|l| STDOUT.puts l} # no copy_stream in 1.8.7
46
+ exit
47
+ end
48
+ o.on('-v', "--version", "Show version") do
49
+ puts Tweek::VERSION
50
+ exit
51
+ end
52
+ o.on( '-?', '--help', 'Display this screen' ) do
53
+ puts o
54
+ exit
55
+ end
56
+ o.separator <<MSG
57
+
58
+ Returns:
59
+ 0: If no mismatches, >0 number of mismatches
60
+ MSG
61
+ o.parse! rescue (puts "Error: #{$!}"; puts o; exit)
62
+ end
63
+ if distro.nil? or distro_ver.nil?
64
+ if RUBY_PLATFORM !~ /-linux-gnu$/
65
+ STDERR.puts "Tweek only runs on Linux!"
66
+ exit 2
67
+ end
68
+ begin
69
+ distro ||= `lsb_release -i`.partition(':').last.strip
70
+ distro_ver ||= `lsb_release -r`.partition(':').last.strip
71
+ rescue
72
+ puts "'lsb_release' not found! Install or specify --distro and --distro-ver on command line"
73
+ exit 2
74
+ end
75
+ end
76
+ if kernel_ver.nil?
77
+ begin
78
+ kernel_ver ||= `uname -r`.partition('-').first
79
+ rescue
80
+ puts "'uname' not found! Install or specify --kernel-ver on command line"
81
+ exit 2
82
+ end
83
+ end
84
+ cs = Tweek::App.new(gflag, true, distro, distro_ver, kernel_ver)
85
+ if ARGV.empty?
86
+ fh = DATA
87
+ fh.lineno -= 1
88
+ else
89
+ fh = ARGF
90
+ end
91
+ cs.read_sections(fh)
92
+ exit cs.results
93
+
94
+ __END__
95
+ # A documentation-rich sample for the parameter checking script
96
+ #
97
+ # Comments begin with # and extend to the end of the line
98
+ # Block comments are Ruby style =begin and =end
99
+ # Conditionals use 'k' for kernel version, 'v' for distro version and 'd' for distro
100
+ # Version conditionals use the http://guides.rubygems.org/patterns/#semantic-versioning
101
+ # Distro conditionals follow Ruby syntax and you may use string or regex comparisons
102
+ #
103
+ BLKDEV xvda # list one or more block devices here (filename globs OK)
104
+ read_ahead_kb = 1024 # to drive more merged IO
105
+ scheduler = noop # less CPU wasted stacking an elevator (for SSDs - still for EBS?)
106
+ rq_affinity = 2 # 1 to the CPU group, 2 to the actual CPU
107
+ nr_requests = 256
108
+
109
+ CLOCKSOURCE # Use cat /sys/devices/system/cl*/cl*/available_clocksource to see what's available
110
+ # xen is the default as supported on all instance types
111
+ # tsc is hardware supported and is significantly quicker, should be used on all post Sandybridge CPUs
112
+ clocksource0 = tsc
113
+
114
+ KERNEL # Kernel command line parameters
115
+ # true must be present, false must be absent, anything else is a value to be matched
116
+ xen_nopvspin = true
117
+ maxcpus = 63 # Set the number of physical cores, eliminates the 'B' hyperthreads
118
+ # This stops cores idling, eliminating turbo, but decrease latency of core spin-up
119
+ # intel_idle.max_cstate=1
120
+ numa = off if k>3.9 # won't move memory around between NUMA zones, useful with large working set
121
+
122
+ SYSCTL
123
+ vm.min_free_kbytes = 838608
124
+ vm.zone_reclaim = 1 if k~>4.9.0,d=='Ubuntu'
125
+ vm.zone_reclaim_mode = 1 if k~>3.13.0, k<4.9.0
126
+
127
+ # These parameters taken from Brendan Gregg's Re:Invent talk 2014
128
+ net.core.somaxconn = 1000
129
+ net.core.netdev_max_backlog = 5000
130
+ net.core.rmem_max = 16777216
131
+ net.core.wmem_max = 16777216
132
+ net.ipv4.tcp_wmem = 4096 12582912 16777216
133
+ net.ipv4.tcp_rmem = 4096 12582912 16777216
134
+ net.ipv4.tcp_max_syn_backlog = 8096
135
+
136
+ net.ipv4.tcp_slow_start_after_idle = 0
137
+
138
+ # When making lots of connections to a server enables reuse of a conn in time-wait status,
139
+ # if you run out of slots because of this then packets can get dropped!
140
+ #
141
+ net.ipv4.tcp_tw_reuse = 1
142
+ net.ipv4.ip_local_port_range = 1024 65535
143
+ net.ipv4.tcp_abort_on_overflow = 1 # allows a connection that is queued to be reset, not just dropped.
144
+ net.ipv4.tcp_fin_timeout = 50 if d=~/Amazon|RedHat/i
145
+
146
+ # Smoother page cache flushing: background flush earlier, aggressive later
147
+ # Brendan Gregg, Re:Invent 2014
148
+ #
149
+ vm.dirty_ratio = 80
150
+ vm.dirty_background_ratio = 5
151
+ vm.dirty_expire_centisecs = 12000
152
+
153
+ =begin
154
+ # These are all the recommended RedHat TCP settings
155
+ # not yet validated, so used as demo of multiline comments
156
+ net.ipv4.tcp_syn_retries = 0
157
+ net.ipv4.tcp_synack_retries = 0
158
+ net.ipv4.tcp_timestamps = 0
159
+ =end
160
+
161
+ NET eth0 # Define one or more network devices
162
+ # Check the IRQ strategy:
163
+ # aws: Use first numa node, split RSS from RPS
164
+ # RSS allocated across 0-7 (node 0), RPS on first numa node
165
+ # pin: Differs depending on number of cores
166
+ # 32 Apportion across the single node as follows:
167
+ # RPS allocated across cores 10-15 and 18-31
168
+ # RSS allocated across 0-7
169
+ # 64 Use both NUMA nodes, leave first core free on each node
170
+ # RPS allocated across remaining cores on each node
171
+ # RSS allocated across 1-4 (node 0) then 17-20 (node 1)
172
+ # null: Ignore this adapter
173
+ irqs = pin
174
+ driver = ena
175
+ # Check values in /sys/class/net/<device>/ tree
176
+ mtu = 9001
177
+ device/driver/module/version = 1.1.3
178
+
179
+ EXT4 /dev/xvda1 # Define one or more EXT4 devices or mountpoints (filename globs OK)
180
+ # Available parameters are those exposed in /proc/fs/ext4/<device>/options
181
+
182
+ barrier=true
183
+ stripe=0
184
+
185
+ XFS /mnt/xfstest # Define one or more XFS devices or mountpoints (filename globs OK)
186
+ # XFS parameters in the form "x.y" where x is log|data|naming|realtime
187
+ # and y is a valid name, eg. data.sunit or log.sunit
188
+
189
+ data.swidth = 2048
190
+ data.sunit = 128
191
+ data.bsize = 4096
192
+ log.sunit = 1
193
+
194
+ # vim: ft=ruby ts=8 sw=2 sts=2 et
data/lib/tweek/app.rb ADDED
@@ -0,0 +1,146 @@
1
+ require 'rubygems'
2
+ require 'optparse'
3
+ require 'ostruct'
4
+ require 'tweek'
5
+ require 'tweek/section'
6
+
7
+ class Tweek::App
8
+
9
+ attr_reader :distro, :distro_version, :kernel_version
10
+
11
+ def initialize( gflag, tty, distro, distro_version, kernel_version)
12
+ @distro = distro
13
+ @distro_version = Gem::Version.new(distro_version)
14
+ @kernel_version = Gem::Version.new(kernel_version)
15
+ @nparams = 0
16
+ @mismatches = []
17
+ @errors = []
18
+ @skips = []
19
+ @warns = []
20
+ @generated = []
21
+ @gflag = gflag
22
+ @tty = tty
23
+ end
24
+
25
+ # Define data-collection methods so we can easily stub them for tests
26
+ #
27
+ def self.bootline
28
+ @bootline ||= File.open("/proc/cmdline", &:read).chomp rescue 'nowt'
29
+ end
30
+
31
+ def generate type, e
32
+ return unless @gflag
33
+ case type
34
+ when :line
35
+ @generated.push "#{e.line}#{e.comment}"
36
+ when :section
37
+ @generated.push "#{e.type} #{e.list} #{e.comment}"
38
+ when :entry
39
+ cond = e.cond.nil? ? "": " if #{e.cond}"
40
+ comment = e.comment.nil? ? "#": e.comment
41
+ if e.actual
42
+ @generated.push "#{e.param} = #{e.actual}#{cond} #{comment} [expected #{e.value}]"
43
+ else
44
+ @generated.push "#{e.param} = #{e.value}#{cond} #{comment} [skipped]"
45
+ end
46
+ end
47
+ end
48
+
49
+ def error msg
50
+ @errors.push msg unless @errors.first == msg
51
+ end
52
+
53
+ def messages
54
+ strings = @errors + @mismatches
55
+ return strings.size == 0 ? "": strings.join("\n")
56
+ end
57
+
58
+ def warn msg
59
+ @warns.push msg unless @warns.first == msg
60
+ end
61
+
62
+ def skipcond entry
63
+ @skips.push entry
64
+ end
65
+
66
+ def condfail cond
67
+ return false unless cond
68
+ failures = 0
69
+ reqs = cond.split(/\s*,\s*/)
70
+ reqs.each do |req|
71
+ ok = case var = req.slice!(0)
72
+ when 'k'
73
+ Gem::Requirement.new(req).satisfied_by?(kernel_version)
74
+ when 'v'
75
+ Gem::Requirement.new(req).satisfied_by?(distro_version)
76
+ when 'd'
77
+ op = req.slice!(/^[<>~!=]+/)
78
+ begin
79
+ eval "distro #{op} #{req}"
80
+ rescue Exception => e
81
+ raise ArgumentError.new("entry has condition error: #{e.message}")
82
+ end
83
+ else
84
+ raise ArgumentError.new("entry has invalid condition variable: #{var}")
85
+ end
86
+ failures += 1 unless ok
87
+ end
88
+ return failures > 0
89
+ end
90
+
91
+ def assert_equal expected, actual, msg = ''
92
+ @nparams += 1
93
+ if expected === actual
94
+ STDERR.print "." if @tty
95
+ return 0
96
+ else
97
+ STDERR.print "F" if @tty
98
+ @mismatches.push "#{msg}: Expected #{expected}\n#{" "*(msg.size+4)}Actual #{actual}"
99
+ end
100
+ return 1
101
+ end
102
+
103
+ # Read the entire file and split into sections
104
+ #
105
+ def read_sections handle
106
+ section = Tweek::Section.new(0,'<initial>','','')
107
+ while line = handle.gets
108
+ line.chomp!
109
+ if (line =~ /^=begin/)..(line =~ /^=end/)
110
+ section.push OpenStruct.new( :line => line, :lineno => handle.lineno )
111
+ next
112
+ end
113
+
114
+ comment = line.slice!(/#.*$/)
115
+ if line.empty?
116
+ section.push OpenStruct.new( :line => line, :lineno => handle.lineno, :comment => comment )
117
+ next
118
+ end
119
+
120
+ if /^\s*([A-Z0-9]+)\s*(.*?)\s*$/ =~ line # We've hit a new section
121
+ section.process(self)
122
+ section = Tweek::Section.new(handle.lineno, $1, $2, comment)
123
+ next
124
+ end
125
+
126
+ if /^\s*(.+?)\s*=\s*(.*?)\s*(if\s+(.*))?$/ =~ line
127
+ section.push OpenStruct.new( :lineno => handle.lineno, :param => $1, :value => $2,
128
+ :cond => $4, :comment => comment )
129
+ next
130
+ end
131
+ error "#{handle.lineno}: Unrecognized line: #{line}"
132
+ end
133
+ section.process(self)
134
+
135
+ end
136
+
137
+ def results
138
+ STDERR.puts "\nDistro: #{@distro} Version: #{@distro_version} Kernel: #{@kernel_version}"
139
+ STDERR.puts "\n#{@nparams} parameters checked, #{@skips.size} skipped, #{@mismatches.size} mismatches, #{@warns.size} warnings, #{@errors.size} errors"
140
+ STDERR.puts "\n#{@errors.join("\n")}" unless @errors.empty?
141
+ STDERR.puts "\n#{@warns.join("\n")}" unless @warns.empty?
142
+ STDERR.puts "\n#{@mismatches.join("\n")}" unless @mismatches.empty?
143
+ @generated.each { |l| STDOUT.puts l}
144
+ return @mismatches.size
145
+ end
146
+ end
@@ -0,0 +1,252 @@
1
+ # A section is an array of the parsed lines, each element is an OpenStruct
2
+ # There are the following types of Entry
3
+ # With line attributes - single lines
4
+ # With section type and list attributes
5
+ # With parameter value and conditional attributes
6
+ # All entries may have an optional comment attribute
7
+ #
8
+ class Tweek::Section < Array
9
+
10
+ attr_reader :lineno, :type, :list, :comment
11
+ def initialize lineno, type, list, comment
12
+ @lineno = lineno
13
+ @type = type
14
+ @list = list
15
+ @comment = comment
16
+ end
17
+
18
+ def section_entry
19
+ OpenStruct.new(:lineno => lineno, :type => type, :list => list, :comment => comment )
20
+ end
21
+
22
+ # Iterate through each entry in the section.
23
+ # If it's a comment then pass to the generate method
24
+ # If it has a condition which is not met then pass to generate
25
+ # If it passes yield the entry
26
+ #
27
+ def each_entry cs, &block
28
+ self.each do |entry|
29
+ if entry.line
30
+ cs.generate :line, entry
31
+ next
32
+ end
33
+ begin
34
+ if cs.condfail entry.cond
35
+ cs.generate :entry, entry
36
+ cs.skipcond entry
37
+ next
38
+ end
39
+ yield entry
40
+ rescue Exception => e
41
+ cs.error "#{entry.lineno}: #{e.message}"
42
+ end
43
+ end
44
+ end
45
+
46
+ def process cs
47
+
48
+ cs.generate :section, section_entry unless type == '<initial>'
49
+
50
+ return if self.empty?
51
+
52
+ case type
53
+ when '<initial>'
54
+ each_entry (cs) do |entry|
55
+ end
56
+
57
+ when 'BLKDEV' # BLOCK DEVICE PARAMETERS
58
+
59
+ Dir.chdir('/dev')
60
+ devices = Dir.glob(self.list.split(' ').map{|dev| dev.gsub(/^\/dev\//,'')})
61
+
62
+ cs.warn "Block device check skipped: no devices found" if devices.empty?
63
+
64
+ devices.each do |blk|
65
+ each_entry (cs) do |entry|
66
+ entry.actual = File.open("/sys/block/#{blk}/queue/#{entry.param}", &:read).chomp rescue $!.message
67
+ if entry.param == 'scheduler'
68
+ entry.actual = entry.actual.slice(/\[(.*?)\]/,1) unless entry.actual == 'none'
69
+ end
70
+ cs.assert_equal entry.value, entry.actual, "/dev/#{blk} #{entry.param}"
71
+ cs.generate :entry, entry
72
+ end
73
+ end
74
+
75
+ when 'KERNEL' # KERNEL PARAMETERS
76
+ each_entry (cs) do |entry|
77
+ actual = /\b(#{entry.param})(=\S+)?\b/.match(Tweek::App.bootline)
78
+ case
79
+ when (entry.value == 'true' and actual)
80
+ observed = actual.to_s
81
+ expected = entry.param
82
+ when (entry.value == 'true' and actual.nil?)
83
+ expected = 'to be found'
84
+ observed = 'was not found'
85
+ when (entry.value == 'false' and actual)
86
+ expected = 'to not be found'
87
+ observed = 'was found'
88
+ when (entry.value == 'false' and actual.nil?)
89
+ expected = nil
90
+ observed = nil
91
+ else
92
+ expected = entry.value
93
+ observed = actual.nil? ? 'was not found' : actual[2][1..-1]
94
+ end
95
+ cs.assert_equal expected, observed, "Kernel parameter #{entry.param}"
96
+ entry.actual = !actual.nil?
97
+ cs.generate :entry, entry
98
+ end
99
+
100
+ when 'CLOCKSOURCE'
101
+ each_entry (cs) do |entry|
102
+ entry.actual=File.open("/sys/devices/system/clocksource/#{entry.param}/current_clocksource",&:read).chomp rescue $!.message
103
+ cs.assert_equal entry.value, entry.actual, entry.param
104
+ cs.generate :entry, entry
105
+ end
106
+
107
+ when 'SYSCTL' # SYSCTL SETTINGS
108
+ each_entry (cs) do |entry|
109
+ file = entry.param.gsub(/\./,'/')
110
+ entry.actual = File.open("/proc/sys/#{file}", &:read).chomp.gsub(/\s+/,' ') rescue $!.message
111
+ cs.assert_equal entry.value, entry.actual, entry.param
112
+ cs.generate :entry, entry
113
+ end
114
+
115
+ when 'NET' # NETWORK DEVICES
116
+ devices = self.list.split(' ').map{|dev| dev.gsub(/^\/dev\//,'')}
117
+ cs.warn "Network device check skipped: no devices found" if devices.empty?
118
+
119
+ devices.each do |netdev|
120
+ each_entry (cs) do |entry|
121
+
122
+ case entry.param
123
+ when 'driver'
124
+ entry.actual = File.basename(File.readlink("/sys/class/net/#{netdev}/device/driver"))
125
+ cs.assert_equal entry.value, entry.actual, entry.param
126
+
127
+ when 'irqs'
128
+
129
+ irqs=File.open("/proc/interrupts", &:readlines).grep(/\b#{netdev}\b/).map{|m| m.partition(':')[0].strip}
130
+
131
+ if irqs.empty?
132
+ cs.warn "#{netdev} IRQ check skipped: none found"
133
+ entry.actual = 'null'
134
+ cs.generate :entry, entry
135
+ next
136
+ end
137
+
138
+ ncores = `getconf _NPROCESSORS_ONLN`.chomp.to_i rescue 0
139
+ mismatches = 0
140
+ irqs.each_with_index do |irq, ix|
141
+ case entry.value
142
+ when 'null','none','ignore'
143
+ next
144
+
145
+ when 'aws'
146
+ expected_rps = ("00000000," * 3) + "0000ff00"
147
+ expected_rss = ("00000000," * 3) + sprintf("%08x", 1<<ix)
148
+
149
+ when 'pin'
150
+ case ncores
151
+ when 32 # RPS on cores 10-15 and 18-31, RSS tied to 0-7
152
+ case ix
153
+ when 0..7
154
+ expected_rps = ("00000000," * 3) + "fffefe00"
155
+ expected_rss = ("00000000," * 3) + sprintf("%08x", 1<<(ix+1))
156
+ else
157
+ cs.warn "#{netdev} IRQ strategy '#{entry.value}' can't handle IRQ#{ix}"
158
+ end
159
+
160
+ when 64 # Split the work across the two nodes
161
+ case ix
162
+ when 0..3 # alloc on node 0 (cpus 0-3)
163
+ expected_rps = ("00000000," * 2) + "0000ffff," + "0000ffe0"
164
+ expected_rss = ("00000000," * 3) + sprintf("%08x", 1<<(ix+1))
165
+ when 4..7 # alloc on node 1 (
166
+ expected_rps = ("00000000," * 2) + "ffff0000," + "ffe00000"
167
+ expected_rss = ("00000000," * 3) + sprintf("%08x", 1<<(ix + 9))
168
+ else
169
+ cs.warn "#{netdev} IRQ strategy '#{entry.value}' can't handle IRQ#{ix}"
170
+ end
171
+
172
+ else
173
+ cs.error "#{entry.lineno}: Can't handle #{ncores} cores in IRQ strategy '#{entry.value}'"
174
+ end
175
+
176
+ else
177
+ cs.error "#{entry.lineno}: Unrecognized #{netdev} IRQ strategy '#{entry.value}'"
178
+ end
179
+ actual_rss = File.open("/proc/irq/#{irq}/smp_affinity",&:read).chomp rescue $!.message
180
+ actual_rps = File.open("/sys/class/net/#{netdev}/queues/rx-#{ix}/rps_cpus", &:read).chomp rescue $!.message
181
+ mismatches += cs.assert_equal expected_rss, actual_rss, "#{netdev}_irq_#{irq}_rss"
182
+ mismatches += cs.assert_equal expected_rps, actual_rps, "#{netdev}_irq_#{irq}_rps"
183
+ end
184
+ entry.actual = 'null' if mismatches > 0
185
+
186
+ else # try getting the name
187
+ begin
188
+ entry.actual = File.open("/sys/class/net/#{netdev}/#{entry.param}", &:read).chomp.gsub(/\s+/,' ')
189
+ cs.assert_equal entry.value, entry.actual, entry.param
190
+
191
+ rescue
192
+ cs.error "#{entry.lineno}: Network parameter #{entry.param} not handled: #{$!.message}"
193
+ next
194
+ end
195
+ end
196
+ cs.generate :entry, entry
197
+ end
198
+ end
199
+
200
+ when 'EXT4' # EXT4 filesystems info via `dumpe2fs -h /dev/#{device}`
201
+ mounted = File.open("/proc/mounts", &:readlines).map{|m| m.split(' ')}
202
+ mounts = Dir.glob(self.list.split(' '))
203
+ mounts.each do |mount|
204
+ unless this = mounted.select{|m| (mount == m[0] or mount == m[1]) and m[2] == 'ext4'}.first
205
+ cs.warn "EXT4 path #{mount} is not mounted or is not mounted as ext4"
206
+ next
207
+ end
208
+ device = this[0].gsub('/dev/','')
209
+ optstring = File.open("/proc/fs/ext4/#{device}/options",&:read)
210
+ options = Hash[optstring.scan(/([^=\s]+)=([^=\s,]+)/)] # options a=b
211
+ optstring.split(/\n/).reject{|o| o.index('=')}.each{|o| options[o] = 'true'}
212
+ each_entry (cs) do |entry|
213
+ entry.actual = options[entry.param] || "<not found>"
214
+ cs.assert_equal entry.value, entry.actual, "EXT4 #{mount}: #{entry.param}"
215
+ cs.generate :entry, entry
216
+ end
217
+ end
218
+
219
+ when 'XFS' # XFS filesystems info via `xfs_info`
220
+ # Dynamically via: /proc/fs/xfs/... ?
221
+ mounts = self.list.split(' ')
222
+ if mounts.empty?
223
+ cs.warn "#{self.lineno}: XFS device check skipped: no mountpoints found"
224
+ end
225
+ mounts.each do |mount|
226
+ xfsinfo = `xfs_info #{mount} 2>/dev/null`.chomp
227
+ if $?.exitstatus > 0
228
+ cs.warn "No XFS filesystem at #{mount}"
229
+ each_entry (cs) { |entry| cs.generate :entry, entry }
230
+ next
231
+ end
232
+ data = Hash[xfsinfo.slice!(/^meta-data.*(?=^naming)/m).scan(/([^=\s]+)=([^=\s,]+)/)]
233
+ naming = Hash[xfsinfo.slice!(/^naming.*(?=^log)/m).scan(/([^=\s]+)=([^=\s,]+)/)]
234
+ log = Hash[xfsinfo.slice!(/^log.*(?=^realtime)/m).scan(/([^=\s]+)=([^=\s,]+)/)]
235
+ realtime = Hash[xfsinfo.slice!(/^realtime.*$/m).scan(/([^=\s]+)=([^=\s,]+)/)]
236
+ xfsparms = { 'data' => data, 'naming' => naming, 'log' => log, 'realtime' => realtime }
237
+
238
+ each_entry (cs) do |entry|
239
+ parameter = entry.param.split('.',2)
240
+ entry.actual = xfsparms[parameter[0]][parameter[1]] rescue "<invalid name>"
241
+ cs.assert_equal entry.value, entry.actual, "XFS #{mount}: #{entry.param}"
242
+ cs.generate :entry, entry
243
+ end
244
+ end
245
+
246
+ else
247
+ cs.error "#{self.lineno}: Unknown type #{self.type}"
248
+ end
249
+
250
+ end
251
+ end
252
+
data/lib/tweek/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tweek
2
- VERSION = "0.0.1"
2
+ VERSION = "0.9.5"
3
3
  end
data/lib/tweek.rb CHANGED
@@ -1,5 +1,3 @@
1
- require "tweek/version"
2
-
3
1
  module Tweek
4
- # Your code goes here...
2
+ # nothing yet
5
3
  end
data/tweek.gemspec CHANGED
@@ -12,8 +12,6 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = %q{Read and compare Linux parameters for performance tweaking}
13
13
  spec.license = "MIT"
14
14
 
15
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
16
- # to allow pushing to a single host or delete this section to allow pushing to any host.
17
15
  if spec.respond_to?(:metadata)
18
16
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
19
17
  else
@@ -24,8 +22,8 @@ Gem::Specification.new do |spec|
24
22
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
23
  f.match(%r{^(test|spec|features)/})
26
24
  end
27
- spec.bindir = "exe"
28
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.bindir = "bin"
26
+ spec.executables = ['tweek']
29
27
  spec.require_paths = ["lib"]
30
28
 
31
29
  spec.add_development_dependency "bundler", "~> 1.15"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tweek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.9.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Townsend
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-14 00:00:00.000000000 Z
11
+ date: 2017-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -55,7 +55,8 @@ dependencies:
55
55
  description:
56
56
  email:
57
57
  - nick.townsend@mac.com
58
- executables: []
58
+ executables:
59
+ - tweek
59
60
  extensions: []
60
61
  extra_rdoc_files: []
61
62
  files:
@@ -68,7 +69,10 @@ files:
68
69
  - Rakefile
69
70
  - bin/console
70
71
  - bin/setup
72
+ - bin/tweek
71
73
  - lib/tweek.rb
74
+ - lib/tweek/app.rb
75
+ - lib/tweek/section.rb
72
76
  - lib/tweek/version.rb
73
77
  - tweek.gemspec
74
78
  homepage: