machinery-tool 1.5.0 → 1.6.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/NEWS +18 -0
- data/html/assets/machinery.css +20 -1
- data/html/assets/machinery.js +53 -26
- data/html/index.html.haml +69 -46
- data/lib/analyze_config_file_diffs_task.rb +1 -1
- data/lib/array.rb +36 -14
- data/lib/cli.rb +45 -50
- data/lib/compare_task.rb +1 -1
- data/lib/config.rb +8 -2
- data/lib/config_base.rb +14 -1
- data/lib/element_filter.rb +48 -11
- data/lib/exceptions.rb +5 -0
- data/lib/filter.rb +63 -14
- data/lib/filter_option_parser.rb +83 -0
- data/lib/generate_html_task.rb +3 -1
- data/lib/hint.rb +36 -18
- data/lib/html.rb +1 -0
- data/lib/inspect_task.rb +12 -21
- data/lib/inspector.rb +13 -6
- data/lib/kiwi_config.rb +17 -14
- data/lib/list_task.rb +5 -1
- data/lib/local_system.rb +3 -4
- data/lib/logged_cheetah.rb +3 -1
- data/lib/machinery.rb +1 -0
- data/lib/object.rb +24 -19
- data/lib/scope_file_store.rb +3 -1
- data/lib/show_task.rb +5 -7
- data/lib/system_description.rb +11 -12
- data/lib/system_description_store.rb +1 -1
- data/lib/ui.rb +44 -36
- data/lib/upgrade_format_task.rb +4 -1
- data/lib/version.rb +1 -1
- data/man/generated/machinery.1.gz +0 -0
- data/man/generated/machinery.1.html +7 -2
- data/plugins/inspect/changed_managed_files_inspector.rb +13 -6
- data/plugins/inspect/config_files_inspector.rb +27 -20
- data/plugins/inspect/groups_inspector.rb +12 -4
- data/plugins/inspect/os_inspector.rb +29 -22
- data/plugins/inspect/packages_inspector.rb +13 -5
- data/plugins/inspect/patterns_inspector.rb +24 -10
- data/plugins/inspect/repositories_inspector.rb +19 -15
- data/plugins/inspect/services_inspector.rb +28 -22
- data/plugins/inspect/unmanaged_files_inspector.rb +42 -33
- data/plugins/inspect/users_inspector.rb +13 -5
- data/plugins/model/os_model.rb +1 -1
- metadata +3 -2
data/lib/scope_file_store.rb
CHANGED
@@ -51,7 +51,9 @@ class ScopeFileStore
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def list_content
|
54
|
-
files = Dir.
|
54
|
+
files = Dir.
|
55
|
+
glob(File.join(path, "**/*"), File::FNM_DOTMATCH).
|
56
|
+
reject { |path| [".", ".."].include?(File.basename(path)) }
|
55
57
|
# filter parent directories because they should not be listed separately
|
56
58
|
files.reject { |f| files.index { |e| e =~ /^#{f}\/.+/ } }
|
57
59
|
end
|
data/lib/show_task.rb
CHANGED
@@ -16,10 +16,11 @@
|
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
18
|
class ShowTask
|
19
|
-
def show(description, scopes, options = {})
|
19
|
+
def show(description, scopes, filter, options = {})
|
20
20
|
if options[:show_html]
|
21
21
|
show_html(description)
|
22
22
|
else
|
23
|
+
filter.apply!(description)
|
23
24
|
show_console(description, scopes, options )
|
24
25
|
end
|
25
26
|
end
|
@@ -42,24 +43,21 @@ class ShowTask
|
|
42
43
|
|
43
44
|
def show_console(description, scopes, options)
|
44
45
|
missing_scopes = []
|
45
|
-
output = ""
|
46
46
|
|
47
47
|
scopes.map { |s| Renderer.for(s) }.each do |renderer|
|
48
48
|
section = renderer.render(description, options)
|
49
49
|
unless section.empty?
|
50
|
-
|
50
|
+
Machinery::Ui.puts section
|
51
51
|
else
|
52
52
|
missing_scopes << renderer.scope
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
56
|
if missing_scopes.length > 0
|
57
|
-
|
57
|
+
Machinery::Ui.puts "# The following requested scopes were not inspected\n\n"
|
58
58
|
missing_scopes.each do |scope|
|
59
|
-
|
59
|
+
Machinery::Ui.puts " * #{Machinery::Ui.internal_scope_list_to_string(scope)}\n"
|
60
60
|
end
|
61
61
|
end
|
62
|
-
|
63
|
-
Machinery::Ui.print_output(output, :no_pager => options[:no_pager])
|
64
62
|
end
|
65
63
|
end
|
data/lib/system_description.rb
CHANGED
@@ -36,7 +36,7 @@ class SystemDescription < Machinery::Object
|
|
36
36
|
attr_accessor :name
|
37
37
|
attr_accessor :store
|
38
38
|
attr_accessor :format_version
|
39
|
-
attr_accessor :
|
39
|
+
attr_accessor :filter_definitions
|
40
40
|
|
41
41
|
class << self
|
42
42
|
# Load the system description with the given name
|
@@ -103,12 +103,7 @@ class SystemDescription < Machinery::Object
|
|
103
103
|
description.format_version = json_format_version
|
104
104
|
|
105
105
|
if hash["meta"] && hash["meta"]["filters"]
|
106
|
-
hash["meta"]["filters"]
|
107
|
-
description.filters[command] = Filter.new
|
108
|
-
filter_definitions.each do |definition|
|
109
|
-
description.filters[command].add_element_filter_from_definition(definition)
|
110
|
-
end
|
111
|
-
end
|
106
|
+
description.filter_definitions = hash["meta"]["filters"]
|
112
107
|
end
|
113
108
|
|
114
109
|
description
|
@@ -139,7 +134,7 @@ class SystemDescription < Machinery::Object
|
|
139
134
|
@name = name
|
140
135
|
@store = store
|
141
136
|
@format_version = CURRENT_FORMAT_VERSION
|
142
|
-
@
|
137
|
+
@filter_definitions = {}
|
143
138
|
|
144
139
|
super(hash)
|
145
140
|
end
|
@@ -176,9 +171,9 @@ class SystemDescription < Machinery::Object
|
|
176
171
|
attributes.keys.each do |key|
|
177
172
|
meta[key] = self[key].meta.as_json if self[key].meta
|
178
173
|
end
|
179
|
-
@
|
174
|
+
@filter_definitions.each do |command, filter|
|
180
175
|
meta["filters"] ||= {}
|
181
|
-
meta["filters"][command] = filter
|
176
|
+
meta["filters"][command] = filter
|
182
177
|
end
|
183
178
|
|
184
179
|
hash = as_json
|
@@ -199,14 +194,18 @@ class SystemDescription < Machinery::Object
|
|
199
194
|
File.chmod(0600, path) if created
|
200
195
|
end
|
201
196
|
|
202
|
-
def
|
197
|
+
def filter_definitions(command)
|
198
|
+
@filter_definitions[command] || []
|
199
|
+
end
|
200
|
+
|
201
|
+
def set_filter_definitions(command, filter)
|
203
202
|
if !["inspect"].include?(command)
|
204
203
|
raise Machinery::Errors::MachineryError.new(
|
205
204
|
"Storing the filter for command '#{command}' is not supported."
|
206
205
|
)
|
207
206
|
end
|
208
207
|
|
209
|
-
@
|
208
|
+
@filter_definitions[command] = filter
|
210
209
|
end
|
211
210
|
|
212
211
|
def scopes
|
data/lib/ui.rb
CHANGED
@@ -17,58 +17,66 @@
|
|
17
17
|
|
18
18
|
module Machinery
|
19
19
|
class Ui
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
class <<self
|
21
|
+
attr_accessor :use_pager
|
22
|
+
|
23
|
+
def internal_scope_list_to_string(scopes)
|
24
|
+
list = Array(scopes)
|
25
|
+
list.map { |e| e.tr("_", "-") }.join(", ")
|
26
|
+
end
|
27
|
+
|
28
|
+
def write_output_to_pager(output)
|
29
|
+
@pager ||= IO.popen("$PAGER", "w")
|
24
30
|
|
25
|
-
def self.write_output_to_pager(output)
|
26
|
-
IO.popen("$PAGER", "w") do |f|
|
27
31
|
begin
|
28
|
-
|
32
|
+
@pager.puts output
|
29
33
|
rescue Errno::EPIPE
|
30
34
|
# We just ignore broken pipes.
|
31
35
|
end
|
32
36
|
end
|
33
|
-
end
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
ENV['LESS'] = 'FSRX'
|
38
|
+
def close_pager
|
39
|
+
@pager.close if @pager
|
40
|
+
end
|
41
|
+
|
42
|
+
def puts(output)
|
43
|
+
if !use_pager || !$stdout.tty?
|
42
44
|
begin
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
Machinery::Ui.puts output
|
45
|
+
STDOUT.puts output
|
46
|
+
rescue Errno::EPIPE
|
47
|
+
# We just ignore broken pipes.
|
47
48
|
end
|
48
49
|
else
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
if !ENV['PAGER'] || ENV['PAGER'] == ''
|
51
|
+
ENV['PAGER'] = 'less'
|
52
|
+
ENV['LESS'] = 'FSRX'
|
53
|
+
begin
|
54
|
+
LocalSystem.validate_existence_of_package("less")
|
55
|
+
write_output_to_pager(output)
|
56
|
+
rescue Machinery::Errors::MissingRequirement
|
57
|
+
STDOUT.puts output
|
58
|
+
end
|
52
59
|
else
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
IO.popen("$PAGER &>/dev/null", "w") { |f| f.close }
|
61
|
+
if $?.success?
|
62
|
+
write_output_to_pager(output)
|
63
|
+
else
|
64
|
+
raise(Machinery::Errors::InvalidPager.new("'#{ENV['PAGER']}' could not " \
|
65
|
+
"be executed. Use the --no-pager option or modify your $PAGER " \
|
66
|
+
"bash environment variable to display output.")
|
67
|
+
)
|
68
|
+
end
|
57
69
|
end
|
58
70
|
end
|
59
71
|
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.puts(s)
|
63
|
-
STDOUT.puts s
|
64
|
-
end
|
65
72
|
|
66
|
-
|
67
|
-
|
68
|
-
|
73
|
+
def warn(s)
|
74
|
+
STDERR.puts s
|
75
|
+
end
|
69
76
|
|
70
|
-
|
71
|
-
|
77
|
+
def error(s)
|
78
|
+
STDERR.puts s
|
79
|
+
end
|
72
80
|
end
|
73
81
|
end
|
74
82
|
end
|
data/lib/upgrade_format_task.rb
CHANGED
@@ -35,6 +35,7 @@ class UpgradeFormatTask
|
|
35
35
|
descriptions.each do |description|
|
36
36
|
begin
|
37
37
|
Machinery.logger.info "Upgrading description \"#{description}\""
|
38
|
+
Machinery::Ui.puts "Upgrading description \"#{description}\""
|
38
39
|
migrated = Migration.migrate_description(store, description, force: options[:force])
|
39
40
|
migrations_done += 1 if migrated
|
40
41
|
rescue StandardError => e
|
@@ -44,7 +45,9 @@ class UpgradeFormatTask
|
|
44
45
|
|
45
46
|
if !errors.empty?
|
46
47
|
Machinery.logger.error errors.join("\n")
|
47
|
-
|
48
|
+
exception = Machinery::Errors::UpgradeFailed.new(errors.join("\n") +
|
49
|
+
Hint.to_string(:upgrade_format_force, name: name || "--all"))
|
50
|
+
raise exception
|
48
51
|
end
|
49
52
|
|
50
53
|
if options[:all]
|
data/lib/version.rb
CHANGED
Binary file
|
@@ -865,7 +865,11 @@ by adding an '@' before the path, e.g.</p>
|
|
865
865
|
|
866
866
|
<p>If a filename contains a comma it needs to be escaped, e.g.</p>
|
867
867
|
|
868
|
-
<p> $ <code>machinery</code> inspect --skip-files=/file\,with_comma myhost</p
|
868
|
+
<p> $ <code>machinery</code> inspect --skip-files=/file\,with_comma myhost</p>
|
869
|
+
|
870
|
+
<p><strong>Note</strong>: File or directory names are not expanded, e.g. '../path' is taken
|
871
|
+
literally and not expanded.</p></dd>
|
872
|
+
<dt><code>--verbose</code> (optional)</dt><dd><p>Display the filters which are used during inspection.</p></dd>
|
869
873
|
</dl>
|
870
874
|
|
871
875
|
|
@@ -1027,6 +1031,7 @@ See the <a href="#Scopes" data-bare-link="true">Scope section</a> for more infor
|
|
1027
1031
|
<dt><code>--show-diffs</code> (optional)</dt><dd><p>Include the generated diffs in the output if available (see <code>machinery help analyze</code>
|
1028
1032
|
for more information).</p></dd>
|
1029
1033
|
<dt><code>--html</code> (optional)</dt><dd><p>Open the system description in HTML format in your web browser using the <code>xdg-open</code> command.</p></dd>
|
1034
|
+
<dt><code>--verbose</code> (optional)</dt><dd><p>Display the filters which were applied before showing the system description.</p></dd>
|
1030
1035
|
</dl>
|
1031
1036
|
|
1032
1037
|
|
@@ -1162,7 +1167,7 @@ manually editing it.</p>
|
|
1162
1167
|
|
1163
1168
|
<ol class='man-decor man-foot man foot'>
|
1164
1169
|
<li class='tl'></li>
|
1165
|
-
<li class='tc'>
|
1170
|
+
<li class='tc'>April 2015</li>
|
1166
1171
|
<li class='tr'>machinery(1)</li>
|
1167
1172
|
</ol>
|
1168
1173
|
|
@@ -18,11 +18,16 @@
|
|
18
18
|
class ChangedManagedFilesInspector < Inspector
|
19
19
|
include ChangedRpmFilesHelper
|
20
20
|
|
21
|
-
def
|
21
|
+
def initialize(system, description)
|
22
|
+
@system = system
|
23
|
+
@description = description
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect(_filter, options = {})
|
22
27
|
system.check_requirement("rsync", "--version") if options[:extract_changed_managed_files]
|
23
28
|
|
24
29
|
@system = system
|
25
|
-
file_store = description.scope_file_store("changed_managed_files")
|
30
|
+
file_store = @description.scope_file_store("changed_managed_files")
|
26
31
|
|
27
32
|
result = changed_files
|
28
33
|
|
@@ -39,13 +44,15 @@ class ChangedManagedFilesInspector < Inspector
|
|
39
44
|
system.retrieve_files(existing_files.map(&:name), file_store.path)
|
40
45
|
end
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
description["changed_managed_files"] = ChangedManagedFilesScope.new(
|
47
|
+
@description["changed_managed_files"] = ChangedManagedFilesScope.new(
|
45
48
|
extracted: !!options[:extract_changed_managed_files],
|
46
49
|
files: ChangedManagedFileList.new(result.sort_by(&:name))
|
47
50
|
)
|
48
|
-
|
51
|
+
end
|
52
|
+
|
53
|
+
def summary
|
54
|
+
"#{@description.changed_managed_files.extracted ? "Extracted" : "Found"} " +
|
55
|
+
"#{@description.changed_managed_files.files.count} changed files."
|
49
56
|
end
|
50
57
|
|
51
58
|
private
|
@@ -19,14 +19,14 @@ class ConfigFilesInspector < Inspector
|
|
19
19
|
include ChangedRpmFilesHelper
|
20
20
|
|
21
21
|
# checks if all required binaries are present
|
22
|
-
def check_requirements(
|
23
|
-
system.check_requirement("rpm", "--version")
|
24
|
-
system.check_requirement("stat", "--version")
|
25
|
-
system.check_requirement("rsync", "--version") if check_rsync
|
22
|
+
def check_requirements(check_rsync)
|
23
|
+
@system.check_requirement("rpm", "--version")
|
24
|
+
@system.check_requirement("stat", "--version")
|
25
|
+
@system.check_requirement("rsync", "--version") if check_rsync
|
26
26
|
end
|
27
27
|
|
28
28
|
# returns list of packages containing configfiles
|
29
|
-
def packages_with_config_files
|
29
|
+
def packages_with_config_files
|
30
30
|
# first determine packages that have config files at all
|
31
31
|
# rpm command provides lines with package names and subsequent
|
32
32
|
# lines with pathes of config files for that package
|
@@ -35,7 +35,7 @@ class ConfigFilesInspector < Inspector
|
|
35
35
|
# /etc/apache2/charset.conv
|
36
36
|
# /etc/apache2/default-server.conf
|
37
37
|
#
|
38
|
-
output = system.run_command(
|
38
|
+
output = @system.run_command(
|
39
39
|
"rpm", "-qa", "--configfiles", "--queryformat",
|
40
40
|
"%{NAME}-%{VERSION}\n",
|
41
41
|
:stdout => :capture
|
@@ -47,9 +47,9 @@ class ConfigFilesInspector < Inspector
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# returns a hash with entries for changed config files
|
50
|
-
def config_file_changes(
|
50
|
+
def config_file_changes(pkg)
|
51
51
|
begin
|
52
|
-
out = system.run_command(
|
52
|
+
out = @system.run_command(
|
53
53
|
"rpm", "-V",
|
54
54
|
"--nodeps", "--nodigest", "--nosignature", "--nomtime", "--nolinkto",
|
55
55
|
pkg,
|
@@ -82,16 +82,21 @@ class ConfigFilesInspector < Inspector
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
def
|
85
|
+
def initialize(system, description)
|
86
|
+
@system = system
|
87
|
+
@description = description
|
88
|
+
end
|
89
|
+
|
90
|
+
def inspect(_filter, options = {})
|
86
91
|
do_extract = options[:extract_changed_config_files]
|
87
|
-
check_requirements(
|
92
|
+
check_requirements(do_extract)
|
88
93
|
|
89
|
-
result = packages_with_config_files
|
90
|
-
config_file_changes(
|
94
|
+
result = packages_with_config_files.flat_map do |package|
|
95
|
+
config_file_changes(package)
|
91
96
|
end
|
92
97
|
|
93
|
-
paths = result.reject { |f| f.changes == ["deleted"] }.map(&:name)
|
94
|
-
path_data = get_path_data(system, paths)
|
98
|
+
paths = result.reject { |f| f.changes == Machinery::Array.new(["deleted"]) }.map(&:name)
|
99
|
+
path_data = get_path_data(@system, paths)
|
95
100
|
key_list = [ :user, :group, :mode ]
|
96
101
|
result.each do |pkg|
|
97
102
|
pname = pkg.name
|
@@ -100,19 +105,21 @@ class ConfigFilesInspector < Inspector
|
|
100
105
|
end
|
101
106
|
end
|
102
107
|
|
103
|
-
file_store = description.scope_file_store("config_files")
|
108
|
+
file_store = @description.scope_file_store("config_files")
|
104
109
|
file_store.remove
|
105
110
|
if do_extract
|
106
111
|
file_store.create
|
107
|
-
system.retrieve_files(paths, file_store.path)
|
112
|
+
@system.retrieve_files(paths, file_store.path)
|
108
113
|
end
|
109
114
|
|
110
|
-
|
111
|
-
|
112
|
-
description["config_files"] = ConfigFilesScope.new(
|
115
|
+
@description["config_files"] = ConfigFilesScope.new(
|
113
116
|
extracted: !!do_extract,
|
114
117
|
files: ConfigFileList.new(result.sort_by(&:name))
|
115
118
|
)
|
116
|
-
|
119
|
+
end
|
120
|
+
|
121
|
+
def summary
|
122
|
+
"#{@description.config_files.extracted ? "Extracted" : "Found"} " +
|
123
|
+
"#{@description.config_files.files.count} changed configuration files."
|
117
124
|
end
|
118
125
|
end
|
@@ -16,13 +16,21 @@
|
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
18
|
class GroupsInspector < Inspector
|
19
|
-
def
|
20
|
-
|
19
|
+
def initialize(system, description)
|
20
|
+
@system = system
|
21
|
+
@description = description
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect(_filter, _options = {})
|
25
|
+
group_content = @system.read_file("/etc/group")
|
21
26
|
|
22
27
|
groups = group_content ? parse_groups(group_content) : []
|
23
28
|
|
24
|
-
description.groups = GroupsScope.new(groups.sort_by(&:name))
|
25
|
-
|
29
|
+
@description.groups = GroupsScope.new(groups.sort_by(&:name))
|
30
|
+
end
|
31
|
+
|
32
|
+
def summary
|
33
|
+
"Found #{@description.groups.size} groups."
|
26
34
|
end
|
27
35
|
|
28
36
|
private
|