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
@@ -0,0 +1,83 @@
|
|
1
|
+
# Copyright (c) 2013-2015 SUSE LLC
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
5
|
+
# published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
+
# GNU General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU General Public License
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
14
|
+
#
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
16
|
+
# you may find current contact information at www.suse.com
|
17
|
+
|
18
|
+
# This class takes care of transforming the user-provided filter options into
|
19
|
+
# actual Filter objects.
|
20
|
+
class FilterOptionParser
|
21
|
+
class <<self
|
22
|
+
def parse(command, options, global_options)
|
23
|
+
filter = Filter.from_default_definition(command)
|
24
|
+
|
25
|
+
definitions = skip_files_definitions(options.delete("skip-files"))
|
26
|
+
definitions += exclude_definitions(global_options["exclude"])
|
27
|
+
|
28
|
+
definitions.map! { |definition| definition.gsub("\\@", "@") } # Unescape escaped @s
|
29
|
+
definitions.each do |definition|
|
30
|
+
filter.add_element_filter_from_definition(definition)
|
31
|
+
end
|
32
|
+
|
33
|
+
filter
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def exclude_definitions(exclude)
|
39
|
+
return [] if !exclude
|
40
|
+
|
41
|
+
filters = exclude.scan(/(@[^,]+)|\"([^,]+?=[^\"]+)\"|([^,]+=[^=]+)$|([^,]+=[^,]+)/).
|
42
|
+
map(&:compact).flat_map do |filter_definition|
|
43
|
+
if filter_definition[0].start_with?("@")
|
44
|
+
expand_filter_file(filter_definition[0])
|
45
|
+
else
|
46
|
+
filter_definition
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
filters.reject!(&:empty?) # Ignore empty filters
|
51
|
+
filters
|
52
|
+
end
|
53
|
+
|
54
|
+
def skip_files_definitions(skip_files)
|
55
|
+
return [] if !skip_files
|
56
|
+
|
57
|
+
files = skip_files.split(/(?<!\\),/) # Do not split on escaped commas
|
58
|
+
files = files.flat_map do |file|
|
59
|
+
if file.start_with?("@")
|
60
|
+
expand_filter_file(file)
|
61
|
+
else
|
62
|
+
file
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
files.reject!(&:empty?) # Ignore empty filters
|
67
|
+
files.map! { |file| file.chomp("/") } # List directories without the trailing /, in order to
|
68
|
+
# not confuse the unmanaged files inspector
|
69
|
+
files.map { |file| "/unmanaged_files/files/name=#{file}" }
|
70
|
+
end
|
71
|
+
|
72
|
+
def expand_filter_file(path)
|
73
|
+
filename = File.expand_path(path[1..-1])
|
74
|
+
|
75
|
+
if !File.exists?(filename)
|
76
|
+
raise Machinery::Errors::MachineryError.new(
|
77
|
+
"The filter file '#{filename}' does not exist."
|
78
|
+
)
|
79
|
+
end
|
80
|
+
File.read(filename).lines.map(&:strip)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/generate_html_task.rb
CHANGED
data/lib/hint.rb
CHANGED
@@ -16,29 +16,47 @@
|
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
18
|
class Hint
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
class << self
|
20
|
+
def print(method, options = {})
|
21
|
+
return if !Machinery::Config.new.hints
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
23
|
+
Machinery::Ui.puts to_string(method, options)
|
24
|
+
end
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
"#{$0} show --scope config-files --show-diffs #{options[:name]}"
|
30
|
-
end
|
26
|
+
def to_string(method, options = {})
|
27
|
+
return "" if !Machinery::Config.new.hints
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
29
|
+
"\nHint: #{send(method, options)}\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def get_started(_options)
|
35
|
+
"You can get started by inspecting a system. Run:\n#{$0} inspect HOSTNAME"
|
36
|
+
end
|
37
|
+
|
38
|
+
def upgrade_format_force(options)
|
39
|
+
"To force an upgrade of system descriptions run:\n" \
|
40
|
+
"#{$0} upgrade-format --force #{options[:name]}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def show_data(options)
|
44
|
+
"To show the data of the system you just inspected run:\n#{$0} show #{options[:name]}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def show_analyze_data(options)
|
48
|
+
"To show the config file diffs you just created run:\n" \
|
49
|
+
"#{$0} show --scope config-files --show-diffs #{options[:name]}"
|
50
|
+
end
|
36
51
|
|
37
|
-
|
52
|
+
def do_complete_inspection(options)
|
53
|
+
"To do a full inspection containing all scopes and to extract files run:\n" \
|
54
|
+
"#{$0} inspect #{options[:host]} --name #{options[:name]} --extract-files"
|
55
|
+
end
|
38
56
|
|
39
|
-
|
40
|
-
|
41
|
-
|
57
|
+
def upgrade_system_description(_options)
|
58
|
+
"To upgrade all system descriptions run:\n" \
|
59
|
+
"#{$0} upgrade-format --all"
|
42
60
|
end
|
43
61
|
end
|
44
62
|
end
|
data/lib/html.rb
CHANGED
data/lib/inspect_task.rb
CHANGED
@@ -58,16 +58,6 @@ class InspectTask
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def adapt_filter_in_metadata(filter_in_metadata, scope, filter)
|
62
|
-
filter_in_metadata.element_filters.
|
63
|
-
reject! { |path, _filter| path.start_with?("/#{scope}") }
|
64
|
-
filter.element_filters.
|
65
|
-
select { |path, _filter| path.start_with?("/#{scope}") }.
|
66
|
-
each do |_path, element_filter|
|
67
|
-
filter_in_metadata.add_element_filter(element_filter)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
61
|
def build_description(store, name, system, scopes, filter, options)
|
72
62
|
begin
|
73
63
|
description = SystemDescription.load(name, store)
|
@@ -83,16 +73,17 @@ class InspectTask
|
|
83
73
|
|
84
74
|
failed_inspections = {}
|
85
75
|
|
86
|
-
|
87
|
-
filter_in_metadata = description.filters["inspect"]
|
88
|
-
else
|
89
|
-
filter_in_metadata = Filter.new
|
90
|
-
end
|
76
|
+
effective_filter = Filter.new(description.filter_definitions("inspect"))
|
91
77
|
|
92
|
-
scopes.
|
78
|
+
scopes.each do |scope|
|
79
|
+
inspector = Inspector.for(scope).new(system, description)
|
93
80
|
Machinery::Ui.puts "Inspecting #{Machinery::Ui.internal_scope_list_to_string(inspector.scope)}..."
|
81
|
+
|
82
|
+
element_filters = filter.element_filters_for_scope(scope)
|
83
|
+
effective_filter.set_element_filters_for_scope(scope, element_filters)
|
84
|
+
|
94
85
|
begin
|
95
|
-
|
86
|
+
inspector.inspect(effective_filter, options)
|
96
87
|
rescue Machinery::Errors::MachineryError => e
|
97
88
|
Machinery::Ui.puts " -> Inspection failed!"
|
98
89
|
failed_inspections[inspector.scope] = e
|
@@ -100,13 +91,13 @@ class InspectTask
|
|
100
91
|
end
|
101
92
|
description[inspector.scope].set_metadata(timestring, host)
|
102
93
|
|
103
|
-
adapt_filter_in_metadata(filter_in_metadata, inspector.scope, filter)
|
104
|
-
|
105
94
|
if !description.attributes.empty?
|
106
|
-
|
95
|
+
effective_filter.apply!(description)
|
96
|
+
description.set_filter_definitions("inspect", effective_filter.to_array)
|
107
97
|
description.save
|
108
98
|
end
|
109
|
-
|
99
|
+
|
100
|
+
Machinery::Ui.puts " -> " + inspector.summary
|
110
101
|
end
|
111
102
|
|
112
103
|
return description, failed_inspections
|
data/lib/inspector.rb
CHANGED
@@ -33,6 +33,7 @@
|
|
33
33
|
# The description object can also be used to store files in the description.
|
34
34
|
class Inspector
|
35
35
|
abstract_method :inspect
|
36
|
+
abstract_method :summary
|
36
37
|
|
37
38
|
@inspectors = []
|
38
39
|
|
@@ -44,22 +45,28 @@ class Inspector
|
|
44
45
|
def for(scope)
|
45
46
|
class_name = "#{scope.split("_").map(&:capitalize).join}Inspector"
|
46
47
|
|
47
|
-
Object.const_get(class_name)
|
48
|
+
Object.const_get(class_name) if Object.const_defined?(class_name)
|
48
49
|
end
|
49
50
|
|
50
51
|
def all
|
51
|
-
@inspectors
|
52
|
+
@inspectors
|
52
53
|
end
|
53
54
|
|
54
55
|
def all_scopes
|
55
56
|
all.map(&:scope)
|
56
57
|
end
|
58
|
+
|
59
|
+
def scope
|
60
|
+
# Return the un-camelcased name of the inspector,
|
61
|
+
# e.g. "foo_bar" for "FooBarInspector"
|
62
|
+
scope = self.name.match(/^(.*)Inspector$/)[1]
|
63
|
+
scope.gsub(/([^A-Z])([A-Z])/, "\\1_\\2").downcase
|
64
|
+
end
|
57
65
|
end
|
58
66
|
|
67
|
+
attr_accessor :system, :description
|
68
|
+
|
59
69
|
def scope
|
60
|
-
|
61
|
-
# e.g. "foo_bar" for "FooBarInspector"
|
62
|
-
scope = self.class.name.match(/^(.*)Inspector$/)[1]
|
63
|
-
scope.gsub(/([^A-Z])([A-Z])/, "\\1_\\2").downcase
|
70
|
+
self.class.scope
|
64
71
|
end
|
65
72
|
end
|
data/lib/kiwi_config.rb
CHANGED
@@ -148,20 +148,23 @@ suseImportBuildKey
|
|
148
148
|
suseConfig
|
149
149
|
EOF
|
150
150
|
case @system_description.os
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
151
|
+
when OsOpenSuse13_2
|
152
|
+
boot = "vmxboot/suse-13.2"
|
153
|
+
bootloader = "grub2"
|
154
|
+
when OsOpenSuse13_1
|
155
|
+
boot = "vmxboot/suse-13.1"
|
156
|
+
bootloader = "grub2"
|
157
|
+
when OsSles12
|
158
|
+
boot = "vmxboot/suse-SLES12"
|
159
|
+
bootloader = "grub2"
|
160
|
+
when OsSles11
|
161
|
+
boot = "vmxboot/suse-SLES11"
|
162
|
+
bootloader = "grub"
|
163
|
+
else
|
164
|
+
raise Machinery::Errors::ExportFailed.new(
|
165
|
+
"Export is not possible because the operating system " \
|
166
|
+
"'#{@system_description.os.display_name}' is not supported."
|
167
|
+
)
|
165
168
|
end
|
166
169
|
|
167
170
|
builder = Nokogiri::XML::Builder.new do |xml|
|
data/lib/list_task.rb
CHANGED
@@ -17,7 +17,8 @@
|
|
17
17
|
|
18
18
|
class ListTask
|
19
19
|
def list(store, options = {})
|
20
|
-
descriptions = store.list
|
20
|
+
descriptions = store.list
|
21
|
+
has_incompatible_version = false
|
21
22
|
|
22
23
|
descriptions.each do |name|
|
23
24
|
begin
|
@@ -28,6 +29,7 @@ class ListTask
|
|
28
29
|
elsif e.format_version < SystemDescription::CURRENT_FORMAT_VERSION
|
29
30
|
show_error("#{name}: format version #{e.format_version}, " \
|
30
31
|
"needs to be upgraded.\n", options)
|
32
|
+
has_incompatible_version = true
|
31
33
|
else
|
32
34
|
show_error("#{name}: format version #{e.format_version}. " \
|
33
35
|
"Please upgrade Machinery to the latest version.\n", options)
|
@@ -77,6 +79,8 @@ class ListTask
|
|
77
79
|
Machinery::Ui.puts " #{name}:\n * " + scopes .join("\n * ") + "\n\n"
|
78
80
|
end
|
79
81
|
end
|
82
|
+
|
83
|
+
Hint.print(:upgrade_system_description) if has_incompatible_version
|
80
84
|
end
|
81
85
|
|
82
86
|
private
|
data/lib/local_system.rb
CHANGED
@@ -21,10 +21,9 @@ class LocalSystem < System
|
|
21
21
|
class << self
|
22
22
|
def os
|
23
23
|
if !@@os
|
24
|
-
description = SystemDescription.new("localhost",
|
25
|
-
|
26
|
-
inspector
|
27
|
-
inspector.inspect(System.for("localhost"), description, nil)
|
24
|
+
description = SystemDescription.new("localhost", SystemDescriptionMemoryStore.new)
|
25
|
+
inspector = OsInspector.new(System.for("localhost"), description)
|
26
|
+
inspector.inspect(nil)
|
28
27
|
@@os = description.os
|
29
28
|
end
|
30
29
|
@@os
|
data/lib/logged_cheetah.rb
CHANGED
data/lib/machinery.rb
CHANGED
@@ -89,6 +89,7 @@ require_relative "json_validation_error_cleaner"
|
|
89
89
|
require_relative "file_validator"
|
90
90
|
require_relative "element_filter"
|
91
91
|
require_relative "filter"
|
92
|
+
require_relative "filter_option_parser"
|
92
93
|
|
93
94
|
Dir[File.join(Machinery::ROOT, "plugins", "**", "*.rb")].each { |f| require(f) }
|
94
95
|
|
data/lib/object.rb
CHANGED
@@ -23,31 +23,34 @@ module Machinery
|
|
23
23
|
@property_classes[name.to_s] = options[:class]
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
def convert_element(key, value)
|
27
|
+
property_class = @property_classes[key.to_s] if @property_classes
|
28
|
+
if property_class
|
29
|
+
value.is_a?(property_class) ? value : property_class.from_json(value)
|
30
|
+
else
|
31
|
+
case value
|
32
|
+
when ::Array
|
33
|
+
Machinery::Array.from_json(value)
|
34
|
+
when Hash
|
35
|
+
Machinery::Object.from_json(value)
|
32
36
|
else
|
33
|
-
|
34
|
-
when ::Array
|
35
|
-
Machinery::Array.from_json(value)
|
36
|
-
when Hash
|
37
|
-
Machinery::Object.from_json(value)
|
38
|
-
else
|
39
|
-
value
|
40
|
-
end
|
37
|
+
value
|
41
38
|
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def convert_raw_hash(hash)
|
43
|
+
return nil unless hash
|
42
44
|
|
43
|
-
|
45
|
+
entries = hash.map do |key, value|
|
46
|
+
[key, convert_element(key, value)]
|
44
47
|
end
|
45
48
|
|
46
49
|
Hash[entries]
|
47
50
|
end
|
48
51
|
|
49
|
-
def from_json(
|
50
|
-
new(
|
52
|
+
def from_json(json_object)
|
53
|
+
new(json_object)
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
@@ -58,6 +61,7 @@ module Machinery
|
|
58
61
|
end
|
59
62
|
|
60
63
|
def set_attributes(attrs)
|
64
|
+
attrs = self.class.convert_raw_hash(attrs) if attrs.is_a?(Hash)
|
61
65
|
@attributes = attrs.inject({}) do |attributes, (key, value)|
|
62
66
|
key = key.to_s if key.is_a?(Symbol)
|
63
67
|
|
@@ -84,7 +88,7 @@ module Machinery
|
|
84
88
|
end
|
85
89
|
|
86
90
|
def []=(key, value)
|
87
|
-
@attributes[key.to_s] = value
|
91
|
+
@attributes[key.to_s] = self.class.convert_element(key, value)
|
88
92
|
end
|
89
93
|
|
90
94
|
def empty?
|
@@ -96,7 +100,8 @@ module Machinery
|
|
96
100
|
if args.size != 1
|
97
101
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)"
|
98
102
|
end
|
99
|
-
|
103
|
+
key = name.to_s[0..-2]
|
104
|
+
@attributes[key] = self.class.convert_element(key, args.first)
|
100
105
|
else
|
101
106
|
if @attributes.has_key?(name.to_s)
|
102
107
|
if !args.empty?
|