tweek 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +51 -0
- data/bin/tweek +40 -20
- data/lib/tweek/file.rb +88 -94
- data/lib/tweek/section.rb +137 -123
- data/lib/tweek/version.rb +1 -1
- data/lib/tweek.rb +16 -10
- data/settings/sample.twk +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8c6ef673ab0665cb66d13f99dc0023475b897e7
|
4
|
+
data.tar.gz: 00de0099ebd776a88efd3179dc7ead5d5306d386
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 276719adb0d4db1ed7c326a06b477676ae9a078b561ebe5a45df8b76cb4247a31f252cb0a40234008c76f83b6ba008f9d1b91644824729f64ddbd6dddbeac6b5
|
7
|
+
data.tar.gz: 73b5560399d4374b0b13b21c39eeeffc94fc9023851cbb7e6dc344214a2ecf8f2c6c53defd6dadc6f4c07528ea1f25c14a0e54f7ca7580475b9857de2942bb1b
|
data/README.md
CHANGED
@@ -55,6 +55,8 @@ Tweek can be used in the following modes:
|
|
55
55
|
* To create a sample settings file that you can edit manually
|
56
56
|
* To generate a settings file based on the current system configuration
|
57
57
|
* To check the current system configuration against a file of settings.
|
58
|
+
* To set the current system configuration to a file of settings.<br>
|
59
|
+
__NOTE__ this is currently restricted to `sysctl` parameters only. TBD.
|
58
60
|
|
59
61
|
In the first case Tweek uses a built-in set of defaults. To check using these simply run:
|
60
62
|
|
@@ -69,6 +71,55 @@ All the other uses involve the settings file. Documentation is contained within
|
|
69
71
|
sample settings and you should read them for a full understanding of what is possible.
|
70
72
|
Use `tweek -s` to show them.
|
71
73
|
|
74
|
+
### Output
|
75
|
+
|
76
|
+
The output of Tweek is controlled by the `output` option, which takes the following
|
77
|
+
values (default `1`):
|
78
|
+
|
79
|
+
0. No output, just a return code.
|
80
|
+
|
81
|
+
1. Summary output only is written to STDERR. It consists of a string of dots (when a
|
82
|
+
parameter is OK) or the letter 'F' (when a parameter doesn't match) followed by a
|
83
|
+
summary line.
|
84
|
+
|
85
|
+
2. Only the settings that didn't match are written to STDOUT in a form that can be reused
|
86
|
+
as input. The summary is written to STDERR
|
87
|
+
|
88
|
+
3. All the settings are written to STDOUT in a form that can be reused as input.
|
89
|
+
|
90
|
+
The latter formats differ slightly depending on whether the utility is operating in query
|
91
|
+
mode, set mode or reset mode. See below.
|
92
|
+
|
93
|
+
Note that the output is colorized by default. Disable this with `--no-color`.
|
94
|
+
|
95
|
+
* red: a value that doesn't match or conditions that are not met
|
96
|
+
* yellow: expected values
|
97
|
+
* green: conditions that are met
|
98
|
+
* bold: emphasis
|
99
|
+
|
100
|
+
### Operating Mode
|
101
|
+
|
102
|
+
The actions of Tweek are controlled by the `mode` option, which takes the following values
|
103
|
+
(default `query`):
|
104
|
+
|
105
|
+
<dl>
|
106
|
+
|
107
|
+
<dt>query</dt>
|
108
|
+
|
109
|
+
<dd>the settings are checked against the actual values and mismatches are highlighted and
|
110
|
+
noted in a comment with the syntax '[expected xxx]'</dd>
|
111
|
+
|
112
|
+
<dt>set</dt>
|
113
|
+
|
114
|
+
<dd>the settings are set to the given value, mismatches are highlighted and noted in a
|
115
|
+
trailing comment with the syntax '[was xxx]'</dd>
|
116
|
+
|
117
|
+
<dt>reset</dt>
|
118
|
+
|
119
|
+
<dd>action as above but the settings are taken from the '[was xxx]' comments</dd>
|
120
|
+
|
121
|
+
</dl>
|
122
|
+
|
72
123
|
## Settings File Format
|
73
124
|
|
74
125
|
The settings file is a plaintext file encoded in UTF-8 (if you are running Ruby 2.0 or
|
data/bin/tweek
CHANGED
@@ -6,19 +6,23 @@
|
|
6
6
|
#
|
7
7
|
require 'rubygems'
|
8
8
|
|
9
|
+
require 'fileutils'
|
9
10
|
require 'optparse'
|
11
|
+
require 'open-uri'
|
10
12
|
require 'tweek'
|
11
13
|
require 'tweek/file'
|
12
14
|
require 'tweek/version'
|
15
|
+
require 'colorize'
|
13
16
|
|
14
17
|
trap('INT') do
|
15
18
|
print "\n"
|
16
19
|
exit(1)
|
17
20
|
end
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
modes = [:query, :set, :reset]
|
23
|
+
mflag = modes[0]
|
24
|
+
oflag = 1
|
25
|
+
cflag = true
|
22
26
|
distro = nil
|
23
27
|
distro_ver = nil
|
24
28
|
kernel_ver = nil
|
@@ -37,11 +41,20 @@ OptionParser.new do |o|
|
|
37
41
|
o.on('--kernel-ver=X.Y.Z', "Set Kernel version (default uses uname)" ) do |k|
|
38
42
|
kernel_ver = k
|
39
43
|
end
|
40
|
-
o.on( '-
|
41
|
-
|
44
|
+
o.on( '-c', '--[no-]color', "Colorize output (default #{cflag})" ) do |c|
|
45
|
+
cflag = c
|
42
46
|
end
|
43
|
-
o.on( '-
|
44
|
-
|
47
|
+
o.on( '-m', '--mode MODE', modes, "Select mode from: #{modes.join(',')} (default #{mflag})" ) do |m|
|
48
|
+
mflag = m
|
49
|
+
end
|
50
|
+
o.on( '-o', '--output LEVEL', Integer, "Output level (default 1)",
|
51
|
+
"0: no output", "1: progress and stats",
|
52
|
+
"2: mismatches only", "3: all" ) do |o|
|
53
|
+
oflag = o.to_i
|
54
|
+
unless (0..3).include? oflag
|
55
|
+
puts o
|
56
|
+
exit 2
|
57
|
+
end
|
45
58
|
end
|
46
59
|
o.on( '-s', '--show', "Write the default parameters to STDOUT",
|
47
60
|
"Use as a fully documented starting point"
|
@@ -49,10 +62,7 @@ OptionParser.new do |o|
|
|
49
62
|
File.open(File.expand_path('../../settings/sample.twk',__FILE__)).each_line do |l|
|
50
63
|
STDOUT.puts l # no copy_stream in 1.8.7
|
51
64
|
end
|
52
|
-
exit
|
53
|
-
end
|
54
|
-
o.on( '--set', "Set the tweak file(s)" ) do
|
55
|
-
sflag = true
|
65
|
+
exit 99
|
56
66
|
end
|
57
67
|
o.on('-v', "--version", "Show version") do
|
58
68
|
puts Tweek::VERSION
|
@@ -64,21 +74,26 @@ OptionParser.new do |o|
|
|
64
74
|
end
|
65
75
|
o.separator <<MSG
|
66
76
|
|
67
|
-
|
77
|
+
Exit code:
|
68
78
|
0: If no mismatches, >0 number of mismatches
|
69
79
|
MSG
|
70
80
|
o.parse! rescue (puts "Error: #{$!}"; puts o; exit)
|
71
81
|
end
|
82
|
+
|
83
|
+
String.disable_colorization = !cflag
|
84
|
+
|
72
85
|
if distro.nil? or distro_ver.nil?
|
73
86
|
if RUBY_PLATFORM !~ /\blinux\b/
|
74
|
-
STDERR.puts "Tweek only runs on Linux!"
|
87
|
+
STDERR.puts "Tweek only runs on Linux!".colorize(:red)
|
75
88
|
exit 2
|
76
89
|
end
|
77
90
|
begin
|
78
91
|
distro ||= `lsb_release -i`.partition(':').last.strip
|
92
|
+
raise unless $? == 0
|
79
93
|
distro_ver ||= `lsb_release -r`.partition(':').last.strip
|
94
|
+
raise unless $? == 0
|
80
95
|
rescue
|
81
|
-
puts "'lsb_release' not found! Install or specify --distro and --distro-ver on command line"
|
96
|
+
STDERR.puts "'lsb_release' not found! Install or specify --distro and --distro-ver on command line"
|
82
97
|
exit 2
|
83
98
|
end
|
84
99
|
end
|
@@ -86,19 +101,24 @@ if kernel_ver.nil?
|
|
86
101
|
begin
|
87
102
|
kernel_ver ||= `uname -r`.partition('-').first
|
88
103
|
rescue
|
89
|
-
puts "'uname' not found! Install or specify --kernel-ver on command line"
|
104
|
+
STDERR.puts "'uname' not found! Install or specify --kernel-ver on command line"
|
90
105
|
exit 2
|
91
106
|
end
|
92
107
|
end
|
93
|
-
cs = Tweek::File.new(distro, distro_ver, kernel_ver,
|
108
|
+
cs = Tweek::File.new(distro, distro_ver, kernel_ver, mflag, oflag)
|
94
109
|
if ARGV.empty?
|
95
|
-
if
|
96
|
-
puts "You can't update the system with the sample settings, specify one or more Tweek files!"
|
110
|
+
if mflag != :query
|
111
|
+
STDERR.puts "You can't update the system with the sample settings, specify one or more Tweek files!"
|
97
112
|
exit 2
|
98
113
|
end
|
99
114
|
fh = File.open(File.expand_path('../../settings/sample.twk',__FILE__))
|
100
115
|
else
|
101
|
-
|
116
|
+
dir = FileUtils.pwd
|
117
|
+
ARGV.each do |file|
|
118
|
+
FileUtils.chdir dir
|
119
|
+
open(file) do |fh|
|
120
|
+
cs.read_sections(fh)
|
121
|
+
end
|
122
|
+
end
|
102
123
|
end
|
103
|
-
cs.read_sections(fh)
|
104
124
|
exit cs.results
|
data/lib/tweek/file.rb
CHANGED
@@ -2,77 +2,77 @@ require 'rubygems'
|
|
2
2
|
require 'tweek'
|
3
3
|
require 'tweek/section'
|
4
4
|
require 'tweek/entry'
|
5
|
+
require 'colorize'
|
5
6
|
|
6
7
|
class Tweek::File
|
7
8
|
|
8
|
-
attr_reader :distro, :distro_version, :kernel_version, :
|
9
|
+
attr_reader :distro, :distro_version, :kernel_version, :mflag, :oflag, :mismatches, :skips
|
9
10
|
|
10
|
-
def initialize( distro, distro_version, kernel_version,
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
11
|
+
def initialize( distro, distro_version, kernel_version, mflag = :query, oflag = 0)
|
12
|
+
@distrov = distro
|
13
|
+
@dv = Gem::Version.new(distro_version)
|
14
|
+
@kv = Gem::Version.new(kernel_version)
|
15
|
+
@distro_version = distro_version.bold
|
16
|
+
@kernel_version = kernel_version.bold
|
17
|
+
@distro = distro.bold
|
14
18
|
@nparams = 0
|
15
|
-
@mismatches =
|
19
|
+
@mismatches = 0
|
16
20
|
@errors = []
|
17
|
-
@skips =
|
21
|
+
@skips = 0
|
18
22
|
@warns = []
|
19
23
|
@generated = []
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
@gflag = gflag
|
25
|
-
@qflag = qflag
|
26
|
-
@sflag = sflag
|
24
|
+
@mflag = mflag
|
25
|
+
@oflag = oflag
|
26
|
+
|
27
27
|
end
|
28
28
|
|
29
|
-
#
|
29
|
+
# Called for every entry, generates output as required
|
30
30
|
#
|
31
|
-
def generate
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
def generate entry
|
32
|
+
|
33
|
+
line = entry.generate
|
34
|
+
|
35
|
+
case entry.type
|
36
|
+
|
37
|
+
when :literal
|
38
|
+
@generated.push line if @oflag > 1
|
39
|
+
|
36
40
|
when :section
|
37
|
-
|
38
|
-
|
39
|
-
when :
|
40
|
-
|
41
|
-
if entry.
|
42
|
-
line
|
43
|
-
|
41
|
+
@generated.push line unless entry.section_type.nil? or @oflag < 2
|
42
|
+
|
43
|
+
when :parameter
|
44
|
+
@nparams += 1
|
45
|
+
if entry.condfail
|
46
|
+
@generated.push line if @oflag > 1
|
47
|
+
STDERR.print "s".colorize(:yellow) if @oflag == 1
|
44
48
|
else
|
45
|
-
entry.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
+
@generated.push line if @oflag == 3 or ((@oflag == 2) and entry.mismatch)
|
50
|
+
if entry.mismatch
|
51
|
+
@mismatches += 1
|
52
|
+
STDERR.print "F".colorize(:red) if @oflag == 1
|
49
53
|
else
|
50
|
-
|
54
|
+
STDERR.print ".".colorize(:green) if @oflag == 1
|
51
55
|
end
|
52
56
|
end
|
53
|
-
if entry.comment.nil?
|
54
|
-
@generated.push (note ? line + " # " + note : line)
|
55
|
-
else
|
56
|
-
@generated.push (note ? line + " " + entry.comment + " " + note : line + " " + entry.comment)
|
57
|
-
end
|
58
57
|
end
|
59
58
|
end
|
60
59
|
|
61
|
-
def error msg
|
62
|
-
|
60
|
+
def error msg, lineno = 0
|
61
|
+
line = (lineno == 0) ? msg : sprintf("%3d: #{msg}", lineno)
|
62
|
+
@errors.push line unless @errors.first == line
|
63
63
|
end
|
64
64
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
65
|
+
def warn msg, lineno = 0
|
66
|
+
line = (lineno == 0) ? msg : sprintf("%3d: #{msg}", lineno)
|
67
|
+
@warns.push line unless @warns.first == line
|
68
68
|
end
|
69
69
|
|
70
|
-
def
|
71
|
-
@
|
70
|
+
def messages
|
71
|
+
return @errors.size > 0 ? @errors.join("\n") : ""
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
75
|
-
@
|
74
|
+
def generated
|
75
|
+
return @generated.size > 0 ? @generated.join("\n") : ""
|
76
76
|
end
|
77
77
|
|
78
78
|
def condfail cond
|
@@ -81,85 +81,79 @@ class Tweek::File
|
|
81
81
|
failures = 0
|
82
82
|
reqs = cond.split(/\s*,\s*/)
|
83
83
|
reqs.each do |req|
|
84
|
-
|
84
|
+
raise ArgumentError.new("entry has invalid condition variable: #{req}") unless /^[kvd]/ =~ req
|
85
|
+
var = $&
|
86
|
+
ver = $'
|
87
|
+
ok = case var
|
85
88
|
when 'k'
|
86
|
-
Gem::Requirement.new(
|
89
|
+
Gem::Requirement.new(ver).satisfied_by?(@kv)
|
87
90
|
when 'v'
|
88
|
-
Gem::Requirement.new(
|
91
|
+
Gem::Requirement.new(ver).satisfied_by?(@dv)
|
89
92
|
when 'd'
|
90
|
-
op =
|
93
|
+
op = ver.slice!(/^[<>~!=]+/)
|
91
94
|
begin
|
92
|
-
eval "
|
95
|
+
eval "@distrov #{op} #{ver}"
|
93
96
|
rescue Exception => e
|
94
97
|
raise ArgumentError.new("entry has condition error: #{e.message}")
|
95
98
|
end
|
96
|
-
else
|
97
|
-
raise ArgumentError.new("entry has invalid condition variable: #{var}")
|
98
99
|
end
|
99
100
|
failures += 1 unless ok
|
100
101
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
def assert_equal expected, actual, msg = ''
|
105
|
-
@nparams += 1
|
106
|
-
if expected === actual
|
107
|
-
STDERR.print "." if not @qflag and STDERR.isatty
|
108
|
-
return 0
|
109
|
-
else
|
110
|
-
STDERR.print "F" if not @qflag and STDERR.isatty
|
111
|
-
@mismatches.push "#{msg}: Expected #{expected}\n#{" "*(msg.size+4)}Actual #{actual}"
|
112
|
-
end
|
113
|
-
return 1
|
102
|
+
skipped = (failures > 0)
|
103
|
+
@skips += 1 if skipped
|
104
|
+
return skipped
|
114
105
|
end
|
115
106
|
|
116
107
|
# Read the entire file and split into sections
|
117
108
|
#
|
118
109
|
def read_sections handle
|
119
|
-
section = Tweek::Section.
|
110
|
+
section = Tweek::Section.initial self
|
120
111
|
while line = handle.gets
|
121
112
|
line.chomp!
|
122
113
|
if (line =~ /^=begin/)..(line =~ /^=end/)
|
123
|
-
|
114
|
+
entry = Tweek::Entry.new(section, :literal, :line => line, :lineno => handle.lineno )
|
115
|
+
section.push entry
|
124
116
|
next
|
125
117
|
end
|
126
118
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
end
|
119
|
+
begin
|
120
|
+
entry = Tweek::Entry.parse(section, line)
|
121
|
+
entry.lineno = handle.lineno
|
122
|
+
case entry.type
|
132
123
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
124
|
+
when :literal
|
125
|
+
section.push entry
|
126
|
+
|
127
|
+
when :section
|
128
|
+
section.process
|
129
|
+
section = Tweek::Section.new(self, entry)
|
130
|
+
|
131
|
+
when :parameter
|
132
|
+
section.push entry
|
138
133
|
|
139
|
-
if /^\s*(.+?)\s*=\s*(.*?)\s*(\bif\b(.*))?$/ =~ line
|
140
|
-
if $4 and $4.strip.empty?
|
141
|
-
error "#{handle.lineno}: Missing condition after 'if'"
|
142
134
|
else
|
143
|
-
|
144
|
-
:cond => $4, :comment => comment )
|
135
|
+
error "#{handle.lineno}: Unrecognized line: #{line}"
|
145
136
|
end
|
146
|
-
|
137
|
+
rescue
|
138
|
+
error "#{handle.lineno}: #{$!.message}"
|
147
139
|
end
|
148
|
-
error "#{handle.lineno}: Unrecognized line: #{line}"
|
149
140
|
end
|
150
|
-
section.process
|
151
|
-
|
141
|
+
section.process
|
152
142
|
end
|
153
143
|
|
154
144
|
def results
|
155
|
-
unless @
|
156
|
-
STDERR.puts "
|
157
|
-
STDERR.puts "
|
158
|
-
STDERR.puts "
|
159
|
-
STDERR.puts "
|
160
|
-
STDERR.puts "
|
145
|
+
unless @oflag == 0
|
146
|
+
STDERR.puts "" if @oflag == 1
|
147
|
+
STDERR.puts "Mode: #{@mflag.to_s.bold} Distro: #{@distro} Version: #{@distro_version} Kernel: #{@kernel_version}"
|
148
|
+
STDERR.puts "#{@nparams} parameters checked, #{@skips} conditions not met, #{@mismatches} mismatches, #{@warns.size} warnings, #{@errors.size} errors"
|
149
|
+
STDERR.puts "#{@errors.join("\n")}" unless @errors.empty?
|
150
|
+
STDERR.puts "#{@warns.join("\n")}" unless @warns.empty?
|
151
|
+
if oflag > 1
|
152
|
+
@generated.unshift "# Generated at #{Time.now.strftime("%c %Z")}"
|
153
|
+
@generated.unshift "# Mode: #{@mflag} Distro: #{@distro} Version: #{@distro_version} Kernel: #{@kernel_version}"
|
154
|
+
end
|
161
155
|
@generated.each { |l| STDOUT.puts l}
|
162
156
|
end
|
163
|
-
return @mismatches
|
157
|
+
return @mismatches
|
164
158
|
end
|
165
159
|
end
|
data/lib/tweek/section.rb
CHANGED
@@ -1,224 +1,230 @@
|
|
1
|
-
# A section is an array of
|
2
|
-
#
|
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
|
1
|
+
# A section is an array of lines, each line is an Entry
|
2
|
+
# The section line itself is stored in a separate Entry object
|
7
3
|
#
|
8
4
|
class Tweek::Section < Array
|
9
5
|
|
10
|
-
attr_reader :
|
6
|
+
attr_reader :section_entry, :twf
|
11
7
|
|
12
|
-
def initialize
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@list = list
|
16
|
-
@comment = comment
|
8
|
+
def initialize twf, entry
|
9
|
+
@twf = twf
|
10
|
+
@section_entry = entry
|
17
11
|
end
|
18
12
|
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
|
13
|
+
# Return an initial section used to hold anything (such as comments) prior to
|
14
|
+
# the first real section
|
15
|
+
def self.initial twf
|
16
|
+
Tweek::Section.new(twf, Tweek::Entry.new(self, :section, :section_type => :initial) )
|
17
|
+
end
|
18
|
+
|
19
|
+
# Helper method to easily iterate through each actionable entry in the section.
|
23
20
|
#
|
24
|
-
def each_entry
|
21
|
+
def each_entry &block
|
25
22
|
self.each do |entry|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
case entry.type
|
24
|
+
when :literal
|
25
|
+
@twf.generate entry
|
26
|
+
when :parameter
|
27
|
+
begin
|
28
|
+
if entry.condfail
|
29
|
+
@twf.generate entry
|
30
|
+
else
|
31
|
+
yield entry if block_given?
|
32
|
+
end
|
33
|
+
rescue Exception => e
|
34
|
+
@twf.error e.message, entry.lineno
|
35
|
+
STDERR.puts "#{e.message}\n#{e.backtrace.join("\n")}" if ENV['DEBUG_TWEEK']
|
35
36
|
end
|
36
|
-
|
37
|
-
|
38
|
-
twf.error "#{entry.lineno}: #{e.message}"
|
37
|
+
else
|
38
|
+
raise "Unknown entry type: #{entry.type}"
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
def process
|
44
|
-
|
45
|
-
twf.generate :section, Tweek::Entry.new( :lineno => lineno, :type => type, :list => list, :comment => comment )
|
43
|
+
def process
|
46
44
|
|
47
45
|
return if self.empty?
|
48
46
|
|
49
|
-
case
|
50
|
-
when nil
|
51
|
-
each_entry (twf) do |entry|
|
52
|
-
end
|
53
|
-
|
54
|
-
when 'BLKDEV' # BLOCK DEVICE PARAMETERS
|
47
|
+
case section_entry.section_type
|
55
48
|
|
56
|
-
|
49
|
+
when :initial
|
50
|
+
self.each do |entry|
|
51
|
+
case entry.type
|
52
|
+
when :literal
|
53
|
+
@twf.generate entry
|
54
|
+
else
|
55
|
+
@twf.warn "Parameter line ignored outside section", entry.lineno
|
56
|
+
end
|
57
|
+
end
|
57
58
|
|
59
|
+
when :BLKDEV # BLOCK DEVICE PARAMETERS
|
60
|
+
@twf.warn "Set/Reset is not currently implemented for #{section_entry.section_type} section" if [:set, :reset].include? @twf.mflag
|
58
61
|
Dir.chdir('/dev')
|
59
|
-
devices = Dir.glob(
|
60
|
-
|
61
|
-
twf.warn "Block device check skipped: no devices found" if devices.empty?
|
62
|
-
|
62
|
+
devices = Dir.glob(section_entry.list.split(' ').map{|dev| dev.gsub(/^\/dev\//,'')})
|
63
|
+
@twf.warn("Block device check skipped: no devices found", section_entry.lineno) if devices.empty?
|
63
64
|
devices.each do |blk|
|
64
|
-
|
65
|
+
section_entry.list_element = blk
|
66
|
+
@twf.generate section_entry
|
67
|
+
each_entry do |entry|
|
65
68
|
entry.actual = Tweek.blk_param(blk, entry.param)
|
66
69
|
if entry.param == 'scheduler'
|
67
70
|
entry.actual = entry.actual.slice(/\[(.*?)\]/,1) unless entry.actual == 'none'
|
68
71
|
end
|
69
|
-
twf.
|
70
|
-
twf.generate :entry, entry
|
72
|
+
@twf.generate entry
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
74
|
-
when
|
75
|
-
twf.warn "Set is not currently implemented for #{
|
76
|
-
|
76
|
+
when :KERNEL # KERNEL PARAMETERS
|
77
|
+
@twf.warn "Set/Reset is not currently implemented for #{section_entry.section_type} section" if [:set, :reset].include? @twf.mflag
|
78
|
+
@twf.generate section_entry
|
79
|
+
each_entry do |entry|
|
77
80
|
actual = /\b(#{entry.param})(=\S+)?\b/.match(Tweek.bootline)
|
78
81
|
case
|
79
82
|
when (entry.value == 'true' and actual)
|
80
|
-
|
81
|
-
expected = entry.param
|
83
|
+
entry.actual = actual[2].nil? ? 'true' : actual[2][1..-1]
|
82
84
|
when (entry.value == 'true' and actual.nil?)
|
83
|
-
|
84
|
-
observed = 'was not found'
|
85
|
+
entry.actual = 'false'
|
85
86
|
when (entry.value == 'false' and actual)
|
86
|
-
|
87
|
-
observed = 'was found'
|
87
|
+
entry.actual = 'true'
|
88
88
|
when (entry.value == 'false' and actual.nil?)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
expected = entry.value
|
93
|
-
observed = actual.nil? ? 'was not found' : actual[2][1..-1]
|
89
|
+
entry.actual = 'false'
|
90
|
+
else
|
91
|
+
entry.actual = actual.nil? ? 'false' : actual[2][1..-1]
|
94
92
|
end
|
95
|
-
twf.
|
96
|
-
entry.actual = !actual.nil?
|
97
|
-
twf.generate :entry, entry
|
93
|
+
@twf.generate entry
|
98
94
|
end
|
99
95
|
|
100
|
-
when
|
101
|
-
twf.warn "Set is not currently implemented for #{
|
102
|
-
|
96
|
+
when :CLOCKSOURCE
|
97
|
+
@twf.warn "Set/Reset is not currently implemented for #{section_entry.section_type} section" if [:set, :reset].include? @twf.mflag
|
98
|
+
@twf.generate section_entry
|
99
|
+
each_entry do |entry|
|
103
100
|
entry.actual = Tweek.clocksource(entry.param)
|
104
|
-
twf.
|
105
|
-
twf.generate :entry, entry
|
101
|
+
@twf.generate entry
|
106
102
|
end
|
107
103
|
|
108
|
-
when
|
109
|
-
|
104
|
+
when :SYSCTL # SYSCTL SETTINGS
|
105
|
+
@twf.generate section_entry
|
106
|
+
each_entry do |entry|
|
110
107
|
file = entry.param.gsub(/\./,'/')
|
111
108
|
entry.actual = Tweek.sysctl(file)
|
112
|
-
twf.
|
113
|
-
|
109
|
+
case @twf.mflag
|
110
|
+
when :set
|
114
111
|
entry.value = Tweek.set_sysctl(file, entry.value)
|
115
|
-
entry.
|
112
|
+
entry.mode = :set
|
113
|
+
when :reset
|
114
|
+
if entry.was
|
115
|
+
if entry.actual == entry.value
|
116
|
+
entry.value = Tweek.set_sysctl(file, entry.was)
|
117
|
+
entry.mode = :reset
|
118
|
+
else
|
119
|
+
@twf.warn "Current value of #{entry.param.bold} does not match, cannot reset", entry.lineno
|
120
|
+
end
|
121
|
+
else
|
122
|
+
@twf.warn "Previous value of #{entry.param.bold} not found, cannot reset", entry.lineno
|
123
|
+
end
|
116
124
|
end
|
117
|
-
twf.generate
|
125
|
+
@twf.generate entry
|
118
126
|
end
|
119
127
|
|
120
|
-
when
|
121
|
-
twf.warn "Set is not currently implemented for #{
|
122
|
-
devices =
|
123
|
-
twf.warn
|
128
|
+
when :NET # NETWORK DEVICES
|
129
|
+
@twf.warn "Set/Reset is not currently implemented for #{section_entry.section_type} section" if [:set, :reset].include? @twf.mflag
|
130
|
+
devices = section_entry.list.split(' ').map{|dev| dev.gsub(/^\/dev\//,'')}
|
131
|
+
@twf.warn("Network device check skipped: no devices found", section_entry.lineno) if devices.empty?
|
124
132
|
|
125
133
|
devices.each do |netdev|
|
126
|
-
|
134
|
+
unless Tweek.net_dev_exist? netdev
|
135
|
+
@twf.warn("NET device #{netdev} not found", section_entry.lineno)
|
136
|
+
next
|
137
|
+
end
|
138
|
+
section_entry.list_element = netdev
|
139
|
+
@twf.generate section_entry
|
140
|
+
each_entry do |entry|
|
127
141
|
|
128
142
|
case entry.param
|
129
143
|
when 'driver'
|
130
144
|
entry.actual = Tweek.net_driver(netdev)
|
145
|
+
@twf.generate entry
|
131
146
|
|
132
147
|
when 'irqs'
|
133
|
-
|
134
148
|
balpid = Tweek.balance_pid
|
135
|
-
|
136
149
|
if entry.value == 'balance'
|
137
150
|
entry.actual = balpid.zero? ? 'IRQ balance daemon not running' : 'balance'
|
138
151
|
else
|
139
152
|
if balpid.zero?
|
140
|
-
handle_irqs(netdev,
|
141
|
-
next
|
153
|
+
handle_irqs(netdev, entry)
|
142
154
|
else
|
143
155
|
entry.actual = 'IRQ balance daemon running'
|
156
|
+
@twf.generate entry
|
144
157
|
end
|
145
158
|
end
|
146
159
|
|
147
160
|
else # treat the parameter as a name
|
148
|
-
|
149
|
-
|
150
|
-
rescue
|
151
|
-
twf.error "#{entry.lineno}: Network parameter #{entry.param} not handled: #{$!.message}"
|
152
|
-
next
|
153
|
-
end
|
161
|
+
entry.actual = Tweek.net_param(netdev, entry.param)
|
162
|
+
@twf.generate entry
|
154
163
|
end
|
155
164
|
|
156
|
-
twf.assert_equal entry.value, entry.actual, entry.param
|
157
|
-
twf.generate :entry, entry
|
158
165
|
end
|
159
166
|
end
|
160
167
|
|
161
|
-
when
|
162
|
-
twf.warn "Set is not currently implemented for #{
|
168
|
+
when :EXT4 # EXT4 filesystems info via `dumpe2fs -h /dev/#{device}`
|
169
|
+
@twf.warn "Set/Reset is not currently implemented for #{section_entry.section_type} section" if [:set, :reset].include? @twf.mflag
|
163
170
|
mounted = Tweek.mounted
|
164
|
-
mounts = Dir.glob(
|
171
|
+
mounts = Dir.glob(section_entry.list.split(' '))
|
172
|
+
@twf.warn("EXT4 check skipped: no mountpoints found", section_entry.lineno) if mounts.empty?
|
165
173
|
mounts.each do |mount|
|
166
174
|
unless this = mounted.select{|m| (mount == m[0] or mount == m[1]) and m[2] == 'ext4'}.first
|
167
|
-
twf.warn "EXT4 path #{mount} is not mounted or is not mounted as ext4"
|
175
|
+
@twf.warn "EXT4 path #{mount} is not mounted or is not mounted as ext4", section_entry.lineno
|
168
176
|
next
|
169
177
|
end
|
178
|
+
section_entry.list_element = mount
|
179
|
+
@twf.generate section_entry
|
170
180
|
device = this[0].gsub('/dev/','')
|
171
181
|
optstring = File.open("/proc/fs/ext4/#{device}/options",&:read)
|
172
182
|
options = Hash[optstring.scan(/([^=\s]+)=([^=\s,]+)/)] # options a=b
|
173
183
|
optstring.split(/\n/).reject{|o| o.index('=')}.each{|o| options[o] = 'true'}
|
174
|
-
each_entry
|
184
|
+
each_entry do |entry|
|
175
185
|
entry.actual = options[entry.param] || "<not found>"
|
176
|
-
twf.
|
177
|
-
twf.generate :entry, entry
|
186
|
+
@twf.generate entry
|
178
187
|
end
|
179
188
|
end
|
180
189
|
|
181
|
-
when
|
182
|
-
twf.warn "Set is not currently implemented for #{
|
190
|
+
when :XFS # XFS filesystems info via `xfs_info`
|
191
|
+
@twf.warn "Set/Reset is not currently implemented for #{section_entry.section_type} section" if [:set, :reset].include? @twf.mflag
|
183
192
|
# Dynamically via: /proc/fs/xfs/... ?
|
184
|
-
mounts =
|
185
|
-
if mounts.empty?
|
186
|
-
twf.warn "#{self.lineno}: XFS device check skipped: no mountpoints found"
|
187
|
-
end
|
193
|
+
mounts = section_entry.list.split(' ')
|
194
|
+
@twf.warn("XFS device check skipped: no mountpoints found", section_entry.lineno) if mounts.empty?
|
188
195
|
mounts.each do |mount|
|
189
196
|
xfsinfo = Tweek.xfsinfo(mount)
|
190
197
|
unless xfsinfo
|
191
|
-
twf.warn "No XFS filesystem at #{mount}"
|
192
|
-
each_entry (twf) { |entry| twf.generate :entry, entry }
|
198
|
+
@twf.warn "No XFS filesystem at #{mount}", section_entry.lineno
|
193
199
|
next
|
194
200
|
end
|
201
|
+
section_entry.list_element = mount
|
202
|
+
@twf.generate section_entry
|
195
203
|
data = Hash[xfsinfo.slice!(/^meta-data.*(?=^naming)/m).scan(/([^=\s]+)=([^=\s,]+)/)]
|
196
204
|
naming = Hash[xfsinfo.slice!(/^naming.*(?=^log)/m).scan(/([^=\s]+)=([^=\s,]+)/)]
|
197
205
|
log = Hash[xfsinfo.slice!(/^log.*(?=^realtime)/m).scan(/([^=\s]+)=([^=\s,]+)/)]
|
198
206
|
realtime = Hash[xfsinfo.slice!(/^realtime.*$/m).scan(/([^=\s]+)=([^=\s,]+)/)]
|
199
207
|
xfsparms = { 'data' => data, 'naming' => naming, 'log' => log, 'realtime' => realtime }
|
200
208
|
|
201
|
-
each_entry
|
209
|
+
each_entry do |entry|
|
202
210
|
parameter = entry.param.split('.',2)
|
203
211
|
entry.actual = xfsparms[parameter[0]][parameter[1]] rescue "<invalid name>"
|
204
|
-
twf.
|
205
|
-
twf.generate :entry, entry
|
212
|
+
@twf.generate entry
|
206
213
|
end
|
207
214
|
end
|
208
215
|
|
209
216
|
else
|
210
|
-
twf.error "
|
217
|
+
@twf.error "Unknown type #{section_entry.section_type}", section_entry.lineno
|
211
218
|
end
|
212
219
|
|
213
220
|
end
|
214
221
|
|
215
|
-
def handle_irqs netdev,
|
222
|
+
def handle_irqs netdev, entry
|
216
223
|
irqs = Tweek.irqs(netdev)
|
217
224
|
|
218
225
|
if irqs.empty?
|
219
|
-
twf.warn "#{netdev} IRQ check skipped: none found"
|
226
|
+
@twf.warn "#{netdev} IRQ check skipped: none found", entry.lineno
|
220
227
|
entry.actual = 'null'
|
221
|
-
twf.generate :entry, entry
|
222
228
|
return
|
223
229
|
end
|
224
230
|
|
@@ -240,7 +246,7 @@ class Tweek::Section < Array
|
|
240
246
|
expected_rps << "0000"
|
241
247
|
expected_rss << sprintf("%04x", 1<<ix)
|
242
248
|
else
|
243
|
-
twf.warn "#{netdev} IRQ strategy '#{entry.value}' can't handle #{ncores} cores"
|
249
|
+
@twf.warn "#{netdev} IRQ strategy '#{entry.value}' can't handle #{ncores} cores", entry.lineno
|
244
250
|
return
|
245
251
|
end
|
246
252
|
|
@@ -256,7 +262,7 @@ class Tweek::Section < Array
|
|
256
262
|
expected_rps << ("00000000," * 3) + "fffefe00"
|
257
263
|
expected_rss << ("00000000," * 3) + sprintf("%08x", 1<<(ix+1))
|
258
264
|
else
|
259
|
-
twf.warn "#{netdev} IRQ strategy '#{entry.value}' can't handle IRQ#{ix}"
|
265
|
+
@twf.warn "#{netdev} IRQ strategy '#{entry.value}' can't handle IRQ#{ix}", entry.lineno
|
260
266
|
return
|
261
267
|
end
|
262
268
|
|
@@ -269,29 +275,37 @@ class Tweek::Section < Array
|
|
269
275
|
expected_rps << ("00000000," * 2) + "ffff0000," + "ffe00000"
|
270
276
|
expected_rss << ("00000000," * 3) + sprintf("%08x", 1<<(ix + 9))
|
271
277
|
else
|
272
|
-
twf.warn "#{netdev} IRQ strategy '#{entry.value}' can't handle IRQ#{ix}"
|
278
|
+
@twf.warn "#{netdev} IRQ strategy '#{entry.value}' can't handle IRQ#{ix}", entry.lineno
|
273
279
|
return
|
274
280
|
end
|
275
281
|
|
276
282
|
else
|
277
|
-
twf.error "
|
283
|
+
@twf.error "Can't handle #{ncores} cores in IRQ strategy '#{entry.value}'", entry.lineno
|
278
284
|
return
|
279
285
|
end
|
280
286
|
|
281
287
|
else
|
282
|
-
twf.error "
|
288
|
+
@twf.error "Unrecognized #{netdev} IRQ strategy '#{entry.value}'", entry.lineno
|
283
289
|
return
|
284
290
|
end
|
285
291
|
end
|
292
|
+
rss = [] # sadly map.with_index doesn't work in 1.8.7, no other compatible way
|
286
293
|
irqs.each_with_index do |irq, ix|
|
287
|
-
|
288
|
-
|
294
|
+
rss << Tweek::Entry.new(self, :parameter, :param => "#{netdev}_irq_#{irq}_rss",
|
295
|
+
:actual => Tweek.net_rss(irq),
|
296
|
+
:value => expected_rss[ix])
|
289
297
|
end
|
298
|
+
rps = []
|
290
299
|
irqs.each_with_index do |irq, ix|
|
291
|
-
|
292
|
-
|
300
|
+
rps << Tweek::Entry.new(self, :parameter, :param => "#{netdev}_irq_rx-#{ix}_rps",
|
301
|
+
:actual => Tweek.net_rps(netdev, ix),
|
302
|
+
:value => expected_rps[ix])
|
293
303
|
end
|
294
|
-
|
304
|
+
mismatches = rss.reduce(0){|sum, r| sum += 1 if r.mismatch}
|
305
|
+
mismatches += rps.reduce(0){|sum, r| sum += 1 if r.mismatch}
|
306
|
+
entry.actual = 'explicit' if mismatches > 0
|
307
|
+
@twf.generate entry
|
308
|
+
(rss+rps).each {|e| @twf.generate e}
|
295
309
|
return
|
296
310
|
end
|
297
311
|
|
data/lib/tweek/version.rb
CHANGED
data/lib/tweek.rb
CHANGED
@@ -3,11 +3,13 @@
|
|
3
3
|
module Tweek
|
4
4
|
|
5
5
|
def self.bootline
|
6
|
-
@bootline ||= ::File.open("/proc/cmdline", &:read).
|
6
|
+
@bootline ||= ::File.open("/proc/cmdline", &:read).strip
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.balance_pid
|
10
|
-
@balance_pid ||= `pgrep irqbalance`.
|
10
|
+
@balance_pid ||= `pgrep irqbalance`.strip.to_i
|
11
|
+
raise "pgrep appears to be missing!" if $? == 127
|
12
|
+
return @balance_pid
|
11
13
|
end
|
12
14
|
|
13
15
|
# Determine the queue processing interrupts for the network device
|
@@ -20,15 +22,15 @@ module Tweek
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def self.ncores
|
23
|
-
`getconf _NPROCESSORS_ONLN`.
|
25
|
+
`getconf _NPROCESSORS_ONLN`.strip.to_i rescue 0
|
24
26
|
end
|
25
27
|
|
26
28
|
def self.clocksource param
|
27
|
-
::File.open("/sys/devices/system/clocksource/#{param}/current_clocksource",&:read).
|
29
|
+
::File.open("/sys/devices/system/clocksource/#{param}/current_clocksource",&:read).strip rescue $!.message
|
28
30
|
end
|
29
31
|
|
30
32
|
def self.sysctl name
|
31
|
-
::File.open("/proc/sys/#{name}", &:read).
|
33
|
+
::File.open("/proc/sys/#{name}", &:read).strip.gsub(/\s+/,' ') rescue $!.message
|
32
34
|
end
|
33
35
|
|
34
36
|
def self.set_sysctl name, value
|
@@ -40,26 +42,30 @@ module Tweek
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
45
|
+
def self.net_dev_exist? name
|
46
|
+
::File.readable?("/sys/class/net/#{name}")
|
47
|
+
end
|
48
|
+
|
43
49
|
def self.net_driver name
|
44
50
|
::File.basename(::File.readlink("/sys/class/net/#{name}/device/driver"))
|
45
51
|
end
|
46
52
|
|
47
53
|
def self.blk_param blkdev, name
|
48
|
-
::File.open("/sys/block/#{blkdev}/queue/#{name}", &:read).
|
54
|
+
::File.open("/sys/block/#{blkdev}/queue/#{name}", &:read).strip rescue $!.message
|
49
55
|
end
|
50
56
|
|
51
57
|
# Receive Side Scaling - hardware
|
52
58
|
def self.net_rss irq
|
53
|
-
::File.open("/proc/irq/#{irq}/smp_affinity",&:read).
|
59
|
+
::File.open("/proc/irq/#{irq}/smp_affinity",&:read).strip rescue $!.message
|
54
60
|
end
|
55
61
|
|
56
62
|
# Receive Packet Steering - software
|
57
63
|
def self.net_rps netdev, ix
|
58
|
-
::File.open("/sys/class/net/#{netdev}/queues/rx-#{ix}/rps_cpus", &:read).
|
64
|
+
::File.open("/sys/class/net/#{netdev}/queues/rx-#{ix}/rps_cpus", &:read).strip rescue $!.message
|
59
65
|
end
|
60
66
|
|
61
67
|
def self.net_param netdev, name
|
62
|
-
::File.open("/sys/class/net/#{netdev}/#{name}", &:read).
|
68
|
+
::File.open("/sys/class/net/#{netdev}/#{name}", &:read).strip.gsub(/\s+/,' ') rescue $!.message
|
63
69
|
end
|
64
70
|
|
65
71
|
def self.mounted
|
@@ -67,7 +73,7 @@ module Tweek
|
|
67
73
|
end
|
68
74
|
|
69
75
|
def self.xfsinfo mount
|
70
|
-
info = `xfs_info #{mount} 2>/dev/null`.
|
76
|
+
info = `xfs_info #{mount} 2>/dev/null`.strip
|
71
77
|
return $?.exitstatus > 0 ? nil : info
|
72
78
|
end
|
73
79
|
end
|
data/settings/sample.twk
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tweek
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Townsend
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.1
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|