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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS +18 -0
  3. data/html/assets/machinery.css +20 -1
  4. data/html/assets/machinery.js +53 -26
  5. data/html/index.html.haml +69 -46
  6. data/lib/analyze_config_file_diffs_task.rb +1 -1
  7. data/lib/array.rb +36 -14
  8. data/lib/cli.rb +45 -50
  9. data/lib/compare_task.rb +1 -1
  10. data/lib/config.rb +8 -2
  11. data/lib/config_base.rb +14 -1
  12. data/lib/element_filter.rb +48 -11
  13. data/lib/exceptions.rb +5 -0
  14. data/lib/filter.rb +63 -14
  15. data/lib/filter_option_parser.rb +83 -0
  16. data/lib/generate_html_task.rb +3 -1
  17. data/lib/hint.rb +36 -18
  18. data/lib/html.rb +1 -0
  19. data/lib/inspect_task.rb +12 -21
  20. data/lib/inspector.rb +13 -6
  21. data/lib/kiwi_config.rb +17 -14
  22. data/lib/list_task.rb +5 -1
  23. data/lib/local_system.rb +3 -4
  24. data/lib/logged_cheetah.rb +3 -1
  25. data/lib/machinery.rb +1 -0
  26. data/lib/object.rb +24 -19
  27. data/lib/scope_file_store.rb +3 -1
  28. data/lib/show_task.rb +5 -7
  29. data/lib/system_description.rb +11 -12
  30. data/lib/system_description_store.rb +1 -1
  31. data/lib/ui.rb +44 -36
  32. data/lib/upgrade_format_task.rb +4 -1
  33. data/lib/version.rb +1 -1
  34. data/man/generated/machinery.1.gz +0 -0
  35. data/man/generated/machinery.1.html +7 -2
  36. data/plugins/inspect/changed_managed_files_inspector.rb +13 -6
  37. data/plugins/inspect/config_files_inspector.rb +27 -20
  38. data/plugins/inspect/groups_inspector.rb +12 -4
  39. data/plugins/inspect/os_inspector.rb +29 -22
  40. data/plugins/inspect/packages_inspector.rb +13 -5
  41. data/plugins/inspect/patterns_inspector.rb +24 -10
  42. data/plugins/inspect/repositories_inspector.rb +19 -15
  43. data/plugins/inspect/services_inspector.rb +28 -22
  44. data/plugins/inspect/unmanaged_files_inspector.rb +42 -33
  45. data/plugins/inspect/users_inspector.rb +13 -5
  46. data/plugins/model/os_model.rb +1 -1
  47. 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
@@ -17,6 +17,8 @@
17
17
 
18
18
  class GenerateHtmlTask
19
19
  def generate(description)
20
- Html.generate(description)
20
+ output = Html.generate(description)
21
+ Machinery::Ui.puts "The generated HTML file is stored in: \n" +
22
+ "#{output}"
21
23
  end
22
24
  end
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
- def self.get_started
20
- output "You can get started by inspecting a system. Run:\n#{$0} inspect HOSTNAME"
21
- end
19
+ class << self
20
+ def print(method, options = {})
21
+ return if !Machinery::Config.new.hints
22
22
 
23
- def self.show_data(options)
24
- output "To show the data of the system you just inspected run:\n#{$0} show #{options[:name]}"
25
- end
23
+ Machinery::Ui.puts to_string(method, options)
24
+ end
26
25
 
27
- def self.show_analyze_data(options)
28
- output "To show the config file diffs you just created run:\n" \
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
- def self.do_complete_inspection(options)
33
- output "To do a full inspection containing all scopes and to extract files run:\n" \
34
- "#{$0} inspect #{options[:host]} --name #{options[:name]} --extract-files"
35
- end
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
- private
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
- def self.output(text)
40
- if Machinery::Config.new.hints
41
- Machinery::Ui.puts "\nHint: #{text}\n"
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
@@ -44,6 +44,7 @@ class Html
44
44
  }
45
45
  EOT
46
46
  )
47
+ target
47
48
  end
48
49
 
49
50
  # Template helpers
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
- if description.filters["inspect"]
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.map { |s| Inspector.for(s) }.each do |inspector|
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
- summary = inspector.inspect(system, description, filter, options)
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
- description.set_filter("inspect", filter_in_metadata)
95
+ effective_filter.apply!(description)
96
+ description.set_filter_definitions("inspect", effective_filter.to_array)
107
97
  description.save
108
98
  end
109
- Machinery::Ui.puts " -> " + summary
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).new if Object.const_defined?(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.map(&:new)
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
- # Return the un-camelcased name of the inspector,
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
- when OsOpenSuse13_1
152
- boot = "vmxboot/suse-13.1"
153
- bootloader = "grub2"
154
- when OsSles12
155
- boot = "vmxboot/suse-SLES12"
156
- bootloader = "grub2"
157
- when OsSles11
158
- boot = "vmxboot/suse-SLES11"
159
- bootloader = "grub"
160
- else
161
- raise Machinery::Errors::ExportFailed.new(
162
- "Export is not possible because the operating system " \
163
- "'#{@system_description.os.display_name}' is not supported."
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.sort
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
- SystemDescriptionMemoryStore.new)
26
- inspector = OsInspector.new
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
@@ -20,6 +20,8 @@ class LoggedCheetah
20
20
  command = args.select{|e| e.is_a?(String)}.join(" ")
21
21
  Machinery.logger.info("Running '#{command}'")
22
22
 
23
- Cheetah.run(*args)
23
+ with_c_locale do
24
+ Cheetah.run(*args)
25
+ end
24
26
  end
25
27
  end
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 object_hash_from_json(json)
27
- return nil unless json
28
-
29
- entries = json.map do |key, value|
30
- value_converted = if @property_classes && @property_classes[key.to_s]
31
- @property_classes[key.to_s].from_json(value)
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
- case value
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
- [key, value_converted]
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(json)
50
- new(object_hash_from_json(json))
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
- @attributes[name.to_s[0..-2].to_s] = args.first
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?