machinery-tool 1.5.0 → 1.6.0

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