tweek 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|