machinery-tool 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +674 -0
  3. data/NEWS +143 -0
  4. data/bin/machinery +29 -0
  5. data/helpers/changed_managed_files.sh +32 -0
  6. data/helpers/filter-packages-for-build.yaml +6 -0
  7. data/html/assets/arrow_down.png +0 -0
  8. data/html/assets/arrow_up.png +0 -0
  9. data/html/assets/bootstrap-popover.js +113 -0
  10. data/html/assets/bootstrap-tooltip.js +457 -0
  11. data/html/assets/bootstrap.min.css +5 -0
  12. data/html/assets/collapse.js +174 -0
  13. data/html/assets/hogan-3.0.2.min.mustache.js +5 -0
  14. data/html/assets/jquery-2.1.1.min.js +4 -0
  15. data/html/assets/logo-changed-managed-files-small.png +0 -0
  16. data/html/assets/logo-changed-managed-files.png +0 -0
  17. data/html/assets/logo-config-files-small.png +0 -0
  18. data/html/assets/logo-config-files.png +0 -0
  19. data/html/assets/logo-groups-small.png +0 -0
  20. data/html/assets/logo-groups.png +0 -0
  21. data/html/assets/logo-os-small.png +0 -0
  22. data/html/assets/logo-os.png +0 -0
  23. data/html/assets/logo-packages-small.png +0 -0
  24. data/html/assets/logo-packages.png +0 -0
  25. data/html/assets/logo-patterns-small.png +0 -0
  26. data/html/assets/logo-patterns.png +0 -0
  27. data/html/assets/logo-repositories-small.png +0 -0
  28. data/html/assets/logo-repositories.png +0 -0
  29. data/html/assets/logo-services-small.png +0 -0
  30. data/html/assets/logo-services.png +0 -0
  31. data/html/assets/logo-unmanaged-files-small.png +0 -0
  32. data/html/assets/logo-unmanaged-files.png +0 -0
  33. data/html/assets/logo-users-small.png +0 -0
  34. data/html/assets/logo-users.png +0 -0
  35. data/html/assets/machinery-base.css +5767 -0
  36. data/html/assets/machinery.css +131 -0
  37. data/html/assets/machinery.js +148 -0
  38. data/html/assets/transition.js +59 -0
  39. data/html/assets/wheels_horizontal.png +0 -0
  40. data/html/index.html.haml +468 -0
  41. data/kiwi_helpers/kiwi_export_readme.md +22 -0
  42. data/kiwi_helpers/merge_users_and_groups.pl.erb +231 -0
  43. data/kiwi_helpers/unmanaged_files_build_excludes +5 -0
  44. data/lib/analyze_config_file_diffs_task.rb +130 -0
  45. data/lib/array.rb +98 -0
  46. data/lib/build_task.rb +124 -0
  47. data/lib/changed_rpm_files_helper.rb +96 -0
  48. data/lib/cli.rb +600 -0
  49. data/lib/compare_task.rb +68 -0
  50. data/lib/config.rb +33 -0
  51. data/lib/config_base.rb +117 -0
  52. data/lib/config_task.rb +56 -0
  53. data/lib/constants.rb +24 -0
  54. data/lib/copy_task.rb +22 -0
  55. data/lib/current_user.rb +23 -0
  56. data/lib/deploy_task.rb +89 -0
  57. data/lib/exceptions.rb +113 -0
  58. data/lib/generate_html_task.rb +22 -0
  59. data/lib/helper.rb +22 -0
  60. data/lib/hint.rb +39 -0
  61. data/lib/html.rb +103 -0
  62. data/lib/inspect_task.rb +93 -0
  63. data/lib/inspector.rb +65 -0
  64. data/lib/kiwi_config.rb +356 -0
  65. data/lib/kiwi_export_task.rb +36 -0
  66. data/lib/list_task.rb +64 -0
  67. data/lib/local_system.rb +127 -0
  68. data/lib/logged_cheetah.rb +25 -0
  69. data/lib/machinery.rb +85 -0
  70. data/lib/machinery_logger.rb +47 -0
  71. data/lib/migration.rb +128 -0
  72. data/lib/mountpoints.rb +72 -0
  73. data/lib/object.rb +138 -0
  74. data/lib/os.rb +78 -0
  75. data/lib/remote_system.rb +114 -0
  76. data/lib/remove_task.rb +43 -0
  77. data/lib/renderer.rb +243 -0
  78. data/lib/renderer_helper.rb +26 -0
  79. data/lib/rpm.rb +52 -0
  80. data/lib/scope_mixin.rb +38 -0
  81. data/lib/show_task.rb +65 -0
  82. data/lib/system.rb +81 -0
  83. data/lib/system_description.rb +228 -0
  84. data/lib/system_description_store.rb +167 -0
  85. data/lib/system_description_validator.rb +216 -0
  86. data/lib/tarball.rb +82 -0
  87. data/lib/ui.rb +74 -0
  88. data/lib/upgrade_format_task.rb +55 -0
  89. data/lib/validate_task.rb +23 -0
  90. data/lib/version.rb +22 -0
  91. data/lib/zypper.rb +70 -0
  92. data/man/generated/machinery.1.gz +0 -0
  93. data/man/generated/machinery.1.html +1056 -0
  94. data/plugins/docs/changed_managed_files.md +2 -0
  95. data/plugins/docs/config_files.md +5 -0
  96. data/plugins/docs/groups.md +2 -0
  97. data/plugins/docs/os.md +2 -0
  98. data/plugins/docs/packages.md +2 -0
  99. data/plugins/docs/patterns.md +5 -0
  100. data/plugins/docs/repositories.md +24 -0
  101. data/plugins/docs/services.md +6 -0
  102. data/plugins/docs/unmanaged_files.md +13 -0
  103. data/plugins/docs/users.md +3 -0
  104. data/plugins/inspect/changed_managed_files_inspector.rb +109 -0
  105. data/plugins/inspect/config_files_inspector.rb +117 -0
  106. data/plugins/inspect/groups_inspector.rb +46 -0
  107. data/plugins/inspect/os_inspector.rb +116 -0
  108. data/plugins/inspect/packages_inspector.rb +46 -0
  109. data/plugins/inspect/patterns_inspector.rb +67 -0
  110. data/plugins/inspect/repositories_inspector.rb +107 -0
  111. data/plugins/inspect/services_inspector.rb +88 -0
  112. data/plugins/inspect/unmanaged_files_inspector.rb +393 -0
  113. data/plugins/inspect/users_inspector.rb +87 -0
  114. data/plugins/model/changed_managed_files_model.rb +29 -0
  115. data/plugins/model/config_files_model.rb +29 -0
  116. data/plugins/model/groups_model.rb +26 -0
  117. data/plugins/model/os_model.rb +20 -0
  118. data/plugins/model/packages_model.rb +26 -0
  119. data/plugins/model/patterns_model.rb +26 -0
  120. data/plugins/model/repositories_model.rb +26 -0
  121. data/plugins/model/services_model.rb +48 -0
  122. data/plugins/model/unmanaged_files_model.rb +29 -0
  123. data/plugins/model/users_model.rb +26 -0
  124. data/plugins/schema/v1/system-description-changed-managed-files.schema.json +83 -0
  125. data/plugins/schema/v1/system-description-config-files.schema.json +83 -0
  126. data/plugins/schema/v1/system-description-groups.schema.json +30 -0
  127. data/plugins/schema/v1/system-description-os.schema.json +21 -0
  128. data/plugins/schema/v1/system-description-packages.schema.json +34 -0
  129. data/plugins/schema/v1/system-description-patterns.schema.json +24 -0
  130. data/plugins/schema/v1/system-description-repositories.schema.json +41 -0
  131. data/plugins/schema/v1/system-description-services.schema.json +30 -0
  132. data/plugins/schema/v1/system-description-unmanaged-files.schema.json +105 -0
  133. data/plugins/schema/v1/system-description-users.schema.json +61 -0
  134. data/plugins/schema/v2/system-description-changed-managed-files.schema.json +92 -0
  135. data/plugins/schema/v2/system-description-config-files.schema.json +92 -0
  136. data/plugins/schema/v2/system-description-groups.schema.json +30 -0
  137. data/plugins/schema/v2/system-description-os.schema.json +21 -0
  138. data/plugins/schema/v2/system-description-packages.schema.json +34 -0
  139. data/plugins/schema/v2/system-description-patterns.schema.json +24 -0
  140. data/plugins/schema/v2/system-description-repositories.schema.json +41 -0
  141. data/plugins/schema/v2/system-description-services.schema.json +30 -0
  142. data/plugins/schema/v2/system-description-unmanaged-files.schema.json +138 -0
  143. data/plugins/schema/v2/system-description-users.schema.json +61 -0
  144. data/plugins/show/changed_managed_files_renderer.rb +46 -0
  145. data/plugins/show/config_files_renderer.rb +62 -0
  146. data/plugins/show/groups_renderer.rb +36 -0
  147. data/plugins/show/os_renderer.rb +31 -0
  148. data/plugins/show/packages_renderer.rb +32 -0
  149. data/plugins/show/patterns_renderer.rb +32 -0
  150. data/plugins/show/repositories_renderer.rb +38 -0
  151. data/plugins/show/services_renderer.rb +32 -0
  152. data/plugins/show/unmanaged_files_renderer.rb +42 -0
  153. data/plugins/show/users_renderer.rb +35 -0
  154. data/schema/migrations/migrate1to2.rb +56 -0
  155. data/schema/v1/system-description-global.schema.json +31 -0
  156. data/schema/v2/system-description-global.schema.json +31 -0
  157. metadata +370 -0
@@ -0,0 +1,22 @@
1
+ # Copyright (c) 2013-2014 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
+ class GenerateHtmlTask
19
+ def generate(description)
20
+ Html.generate(description)
21
+ end
22
+ end
data/lib/helper.rb ADDED
@@ -0,0 +1,22 @@
1
+ # Copyright (c) 2013-2014 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
+ module Machinery
19
+ def self.is_int?(string)
20
+ (string =~ /^\d+$/) != nil
21
+ end
22
+ end
data/lib/hint.rb ADDED
@@ -0,0 +1,39 @@
1
+ # Copyright (c) 2013-2014 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
+ class Hint
19
+ def self.get_started
20
+ output "You can get started by inspecting a system. Run:\n#{$0} inspect HOSTNAME"
21
+ end
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
26
+
27
+ def self.do_complete_inspection(options)
28
+ output "To do a full inspection containing all scopes and to extract files run:\n" \
29
+ "#{$0} inspect #{options[:host]} --name #{options[:name]} --extract-files"
30
+ end
31
+
32
+ private
33
+
34
+ def self.output(text)
35
+ if Machinery::Config.new.hints
36
+ Machinery::Ui.puts "\nHint: #{text}\n"
37
+ end
38
+ end
39
+ end
data/lib/html.rb ADDED
@@ -0,0 +1,103 @@
1
+ # Copyright (c) 2013-2014 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
+ class Html
19
+ def self.generate(description)
20
+ template = Haml::Engine.new(
21
+ File.read(File.join(Machinery::ROOT, "html", "index.html.haml"))
22
+ )
23
+ target = description.store.description_path(description.name)
24
+
25
+ diffs_dir = description.store.file_store(description.name, "config-file-diffs")
26
+ if description.config_files && diffs_dir
27
+ # Enrich description with the config file diffs
28
+ description.config_files.files.each do |file|
29
+ path = File.join(diffs_dir, file.name + ".diff")
30
+ file.diff = diff_to_object(File.read(path)) if File.exists?(path)
31
+ end
32
+ end
33
+
34
+ FileUtils.cp_r(File.join(Machinery::ROOT, "html", "assets"), target)
35
+ File.write(File.join(target, "index.html"), template.render(binding))
36
+ File.write(File.join(target, "assets/description.js"),<<-EOT
37
+ function getDescription() {
38
+ return JSON.parse('#{description.to_hash.to_json}')
39
+ }
40
+ EOT
41
+ )
42
+ end
43
+
44
+ # Template helpers
45
+
46
+ def self.scope_help(scope)
47
+ text = File.read(File.join(Machinery::ROOT, "plugins", "docs", "#{scope}.md"))
48
+ Kramdown::Document.new(text).to_html
49
+ end
50
+
51
+ def self.diff_to_object(diff)
52
+ lines = diff.lines[2..-1]
53
+ diff_object = {
54
+ file: diff[/--- a(.*)/, 1],
55
+ additions: lines.select { |l| l.start_with?("+") }.length,
56
+ deletions: lines.select { |l| l.start_with?("-") }.length
57
+ }
58
+
59
+ original_line_number = 0
60
+ new_line_number = 0
61
+ diff_object[:lines] = lines.map do |line|
62
+ line = ERB::Util.html_escape(line.chomp).
63
+ gsub("\\", "&#92;").
64
+ gsub("\t", "&nbsp;"*8)
65
+ case line
66
+ when /^@.*/
67
+ entry = {
68
+ type: "header",
69
+ content: line
70
+ }
71
+ original_line_number = line[/-(\d+)/, 1].to_i
72
+ new_line_number = line[/\+(\d+)/, 1].to_i
73
+ when /^ .*/, ""
74
+ entry = {
75
+ type: "common",
76
+ new_line_number: new_line_number,
77
+ original_line_number: original_line_number,
78
+ content: line[1..-1]
79
+ }
80
+ new_line_number += 1
81
+ original_line_number += 1
82
+ when /^\+.*/
83
+ entry = {
84
+ type: "addition",
85
+ new_line_number: new_line_number,
86
+ content: line[1..-1]
87
+ }
88
+ new_line_number += 1
89
+ when /^\-.*/
90
+ entry = {
91
+ type: "deletion",
92
+ original_line_number: original_line_number,
93
+ content: line[1..-1]
94
+ }
95
+ original_line_number += 1
96
+ end
97
+
98
+ entry
99
+ end
100
+
101
+ diff_object
102
+ end
103
+ end
@@ -0,0 +1,93 @@
1
+ # Copyright (c) 2013-2014 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
+ class InspectTask
19
+ def inspect_system(store, host, name, current_user, scopes, options = {})
20
+ system = System.for(host)
21
+ check_root(system, current_user)
22
+
23
+ description, failed_inspections = build_description(store, name, system, scopes, options)
24
+
25
+ if !description.attributes.empty?
26
+ description.name = name
27
+ store.save(description)
28
+ print_description(description, scopes) if options[:show]
29
+ end
30
+
31
+ if !failed_inspections.empty?
32
+ Machinery::Ui.puts "\n"
33
+ message = failed_inspections.map { |scope, msg|
34
+ "Errors while inspecting " \
35
+ "#{Machinery::Ui.internal_scope_list_to_string(scope)}:\n#{msg}" }.join("\n\n")
36
+ raise Machinery::Errors::InspectionFailed.new(message)
37
+ end
38
+ description
39
+ end
40
+
41
+ private
42
+
43
+ def check_root(system, current_user)
44
+ if system.requires_root? && !current_user.is_root?
45
+ raise Machinery::Errors::MissingRequirement,
46
+ "Need to be root to inspect local system."
47
+ end
48
+ end
49
+
50
+ def print_description(description, scopes)
51
+ return unless scopes
52
+
53
+ scopes.each do |scope|
54
+ renderer = Renderer.for(scope)
55
+ next unless renderer
56
+
57
+ output = renderer.render(description)
58
+ Machinery::Ui.puts output if output
59
+ end
60
+ end
61
+
62
+ def build_description(store, name, system, scopes, options)
63
+ begin
64
+ description = store.load(name)
65
+ rescue Machinery::Errors::SystemDescriptionNotFound
66
+ description = SystemDescription.new(name, {}, store)
67
+ end
68
+ timestring = Time.now.utc.iso8601
69
+ if system.class == LocalSystem
70
+ host = "localhost"
71
+ else
72
+ host = system.host
73
+ end
74
+
75
+ failed_inspections = {}
76
+
77
+ scopes.map { |s| Inspector.for(s) }.each do |inspector|
78
+ Machinery::Ui.puts "Inspecting #{Machinery::Ui.internal_scope_list_to_string(inspector.scope)}..."
79
+ begin
80
+ summary = inspector.inspect(system, description, options)
81
+ rescue Machinery::Errors::MachineryError => e
82
+ Machinery::Ui.puts "Inspection of scope " \
83
+ "#{Machinery::Ui.internal_scope_list_to_string(inspector.scope)} failed!"
84
+ failed_inspections[inspector.scope] = e
85
+ next
86
+ end
87
+ description[inspector.scope].set_metadata(timestring, host)
88
+ Machinery::Ui.puts " -> " + summary
89
+ end
90
+
91
+ return description, failed_inspections
92
+ end
93
+ end
data/lib/inspector.rb ADDED
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2013-2014 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 is the base class for all the "inspect" plugin classes. It keeps
19
+ # track of the loaded subclasses and allows for querying plugins for a
20
+ # given scope or all available ones.
21
+ #
22
+ # The names of the subclasses are 1:1 mappings of the inspection areas, e.g.
23
+ # the PackagesInspector class would be used for inspection when the user
24
+ # specifies "--scope=packages".
25
+ #
26
+ # All subclasses have to implement the
27
+ #
28
+ # inspect(system, description)
29
+ #
30
+ # method which inspects the given system and stores the gathered information in
31
+ # the description. It returns a brief summary string of the inspection.
32
+ #
33
+ # The description object can also be used to store files in the description.
34
+ class Inspector
35
+ abstract_method :inspect
36
+
37
+ @inspectors = []
38
+
39
+ class << self
40
+ def inherited(klass)
41
+ @inspectors << klass
42
+ end
43
+
44
+ def for(scope)
45
+ class_name = "#{scope.split("_").map(&:capitalize).join}Inspector"
46
+
47
+ Object.const_get(class_name).new if Object.const_defined?(class_name)
48
+ end
49
+
50
+ def all
51
+ @inspectors.map(&:new)
52
+ end
53
+
54
+ def all_scopes
55
+ all.map(&:scope)
56
+ end
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.class.name.match(/^(.*)Inspector$/)[1]
63
+ scope.gsub(/([^A-Z])([A-Z])/, "\\1_\\2").downcase
64
+ end
65
+ end
@@ -0,0 +1,356 @@
1
+ # Copyright (c) 2013-2014 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
+ class KiwiConfig
19
+ attr_accessor :xml, :sh
20
+
21
+ def initialize(system_description, options = {})
22
+ @system_description = system_description
23
+ @name = system_description.name
24
+ @options = options
25
+
26
+ @system_description.assert_scopes(
27
+ "repositories",
28
+ "packages",
29
+ "os"
30
+ )
31
+ check_existance_of_extraced_files
32
+
33
+ generate_config
34
+ end
35
+
36
+ def write(output_location)
37
+ inject_users_and_groups(output_location)
38
+ inject_extracted_files(output_location)
39
+
40
+ @sh << "baseCleanMount\n"
41
+ @sh << "exit 0\n"
42
+ File.write(File.join(output_location, "config.xml") , @xml.to_xml)
43
+ File.write(File.join(output_location, "config.sh") , @sh)
44
+ FileUtils.cp(
45
+ File.join(Machinery::ROOT, "kiwi_helpers/kiwi_export_readme.md"),
46
+ File.join(output_location, "README.md")
47
+ )
48
+
49
+ post_process_config(output_location)
50
+ end
51
+
52
+ private
53
+
54
+ def pre_process_config
55
+ enable_ssh if @options[:enable_ssh]
56
+ end
57
+
58
+ def post_process_config(output_location)
59
+ enable_dhcp(output_location) if @options[:enable_dhcp]
60
+ end
61
+
62
+ def inject_users_and_groups(output_location)
63
+ return if !@system_description.users || !@system_description.groups
64
+
65
+ merge_script_name = "merge_users_and_groups.pl"
66
+
67
+ template = ERB.new(
68
+ File.read(File.join(Machinery::ROOT, "kiwi_helpers", "#{merge_script_name}.erb"))
69
+ )
70
+
71
+ passwd_entries = @system_description.users.map do |u|
72
+ passwd = [u.name, u.password, u.uid, u.gid, u.comment, u.home, u.shell].join(":")
73
+
74
+ # The shadow file contains an eigth reserved field at the end, so we have
75
+ # to manually add it, too.
76
+ shadow = [u.name, u.encrypted_password, u.last_changed_date, u.min_days,
77
+ u.max_days, u.warn_days, u.disable_days, u.disabled_date, ""].join(":")
78
+ "['#{passwd}', '#{shadow}']"
79
+ end.join(",\n")
80
+ group_entries = @system_description.groups.map do |g|
81
+ "'#{g.name}:#{g.password}:#{g.gid}:#{g.users.join(",")}'"
82
+ end.join(",\n")
83
+
84
+ FileUtils.mkdir_p(File.join(output_location, "root", "tmp"), mode: 01777)
85
+ script_path = File.join(output_location, "root", "tmp", merge_script_name)
86
+ File.write(script_path, template.result(binding))
87
+
88
+ @sh << "perl /tmp/#{merge_script_name} /etc/passwd /etc/shadow /etc/group\n"
89
+ @sh << "rm /tmp/#{merge_script_name}\n"
90
+ end
91
+
92
+ def inject_extracted_files(output_location)
93
+ ["config_files", "changed_managed_files"].each do |dir|
94
+ path = @system_description.file_store(dir)
95
+ if path
96
+ output_root_path = File.join(output_location, "root")
97
+ FileUtils.mkdir_p(output_root_path)
98
+ FileUtils.cp_r(Dir.glob("#{path}/*"), output_root_path)
99
+ end
100
+ end
101
+
102
+ unmanaged_files_path = @system_description.file_store("unmanaged_files")
103
+ if unmanaged_files_path
104
+ filter = "unmanaged_files_build_excludes"
105
+ destination = File.join(output_location, "root", "tmp")
106
+ FileUtils.mkdir_p(destination, mode: 01777)
107
+ FileUtils.cp_r(unmanaged_files_path, destination)
108
+ FileUtils.cp(
109
+ File.join(Machinery::ROOT, "kiwi_helpers/#{filter}"),
110
+ destination
111
+ )
112
+
113
+ @sh << "# Apply the extracted unmanaged files\n"
114
+ @sh << "find /tmp/unmanaged_files -name *.tgz -exec " \
115
+ "tar -C / -X '/tmp/#{filter}' -xf {} \\;\n"
116
+ @sh << "rm -rf '/tmp/unmanaged_files' '/tmp/#{filter}'\n"
117
+ end
118
+ end
119
+
120
+ def check_existance_of_extraced_files
121
+ missing_scopes = []
122
+ ["config_files", "changed_managed_files", "unmanaged_files"].each do |scope|
123
+
124
+ if @system_description[scope] && !@system_description.file_store(scope)
125
+ missing_scopes << scope
126
+ end
127
+ end
128
+
129
+ if !missing_scopes.empty?
130
+ raise Machinery::Errors::MissingExtractedFiles.new(@system_description, missing_scopes)
131
+ end
132
+ end
133
+
134
+ def generate_config
135
+ @sh = <<EOF
136
+ test -f /.kconfig && . /.kconfig
137
+ test -f /.profile && . /.profile
138
+ baseMount
139
+ suseSetupProduct
140
+ suseImportBuildKey
141
+ suseConfig
142
+ EOF
143
+ case @system_description.os_object
144
+ when OsOpenSuse13_1
145
+ boot = "vmxboot/suse-13.1"
146
+ bootloader = "grub2"
147
+ when OsSles12
148
+ boot = "vmxboot/suse-SLES12"
149
+ bootloader = "grub2"
150
+ when OsSles11
151
+ boot = "vmxboot/suse-SLES11"
152
+ bootloader = "grub"
153
+ else
154
+ raise Machinery::Errors::KiwiExportFailed.new(
155
+ "Building is not possible because the operating system " \
156
+ "'#{@system_description.os_object.name}' is not supported."
157
+ )
158
+ end
159
+
160
+ builder = Nokogiri::XML::Builder.new do |xml|
161
+ xml.image(schemaversion: "5.8", name: @system_description.name) do
162
+ xml.description(type: "system") do
163
+ xml.author "Machinery"
164
+ xml.contact ""
165
+ xml.specification "Description of system '#{@system_description.name}' exported by Machinery"
166
+ end
167
+
168
+ xml.preferences do
169
+ xml.packagemanager "zypper"
170
+ xml.version "0.0.1"
171
+ xml.type_(image: "vmx", filesystem: "ext3", installiso: "true",
172
+ boot: boot, format: "qcow2", bootloader: bootloader)
173
+ end
174
+
175
+ xml.users(group: "root") do
176
+ xml.user(password: "$1$wYJUgpM5$RXMMeASDc035eX.NbYWFl0",
177
+ home: "/root", name: "root")
178
+ end
179
+
180
+
181
+ apply_repositories(xml)
182
+ apply_packages(xml)
183
+ apply_extracted_files_attributes
184
+ apply_services
185
+ end
186
+ end
187
+
188
+ pre_process_config
189
+
190
+ @xml = builder.doc
191
+ end
192
+
193
+ def apply_packages(xml)
194
+ build_filter = YAML.load_file(File.join(
195
+ Machinery::ROOT, "helpers", "filter-packages-for-build.yaml")
196
+ )
197
+ filter = build_filter[@system_description.os_object.name] || []
198
+
199
+ xml.packages(type: "bootstrap") do
200
+ if @system_description.packages
201
+ @system_description.packages.each do |package|
202
+ next if filter.include?(package.name)
203
+ xml.package(name: "#{package.name}")
204
+ end
205
+ end
206
+ pattern_array = Array.new
207
+ if @system_description.patterns
208
+ @system_description.patterns.each do |pattern|
209
+ xml.namedCollection(name: "#{pattern.name}")
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ def apply_repositories(xml)
216
+ if @system_description.repositories
217
+ @system_description.repositories.each do |repo|
218
+ # only use accessible repositories as source for kiwi build
219
+ parameters = { alias: repo.alias, type: repo.type, priority: repo.priority }
220
+ if repo.username && repo.password
221
+ parameters[:username] = repo.username
222
+ parameters[:password] = repo.password
223
+ end
224
+ is_external_medium = repo.url.start_with?("cd://") ||
225
+ repo.url.start_with?("dvd://")
226
+ if repo.enabled && !repo.type.nil? && !is_external_medium
227
+ xml.repository(parameters) do
228
+ xml.source(path: repo.url)
229
+ end
230
+ end
231
+ if !repo.url.match(/^https:\/\/nu.novell.com|^https:\/\/update.suse.com/)
232
+ @sh << "zypper -n ar --name='#{repo.name}' "
233
+ @sh << "--type='#{repo.type}' " if repo.type
234
+ @sh << "--refresh " if repo.autorefresh
235
+ @sh << "--disable " unless repo.enabled
236
+ @sh << "'#{repo.url}' '#{repo.alias}'\n"
237
+ @sh << "zypper -n mr --priority=#{repo.priority} '#{repo.name}'\n"
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ def apply_extracted_files_attributes
244
+ ["config_files", "changed_managed_files"].each do |scope|
245
+ if @system_description[scope]
246
+ deleted, files = @system_description[scope].files.partition do |f|
247
+ f.changes == Machinery::Array.new(["deleted"])
248
+ end
249
+
250
+ files.each do |file|
251
+ @sh << "chmod #{file.mode} '#{file.name}'\n"
252
+ @sh << "chown #{file.user}:#{file.group} '#{file.name}'\n"
253
+ end
254
+
255
+ deleted.each do |file|
256
+ @sh << "rm -rf '#{file.name}'\n"
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ def apply_services
263
+ if @system_description["services"]
264
+ init_system = @system_description["services"].init_system
265
+
266
+ case init_system
267
+ when "sysvinit"
268
+ @system_description["services"].services.each do |service|
269
+ if service.state == "on"
270
+ @sh << "chkconfig #{service.name} on\n"
271
+ else
272
+ @sh << "chkconfig #{service.name} off\n"
273
+ end
274
+ end
275
+
276
+ when "systemd"
277
+ # possible systemd service states:
278
+ # http://www.freedesktop.org/software/systemd/man/systemctl.html#Unit%20File%20Commands
279
+ @system_description["services"].services.each do |service|
280
+ case service.state
281
+ when "enabled"
282
+ @sh << "systemctl enable #{service.name}\n"
283
+ when "disabled"
284
+ @sh << "systemctl disable #{service.name}\n"
285
+ when "masked"
286
+ @sh << "systemctl mask #{service.name}\n"
287
+ when "static"
288
+ # Don't do anything because the unit is not meant to be
289
+ # enabled/disabled manually.
290
+ when "linked"
291
+ # Don't do anything because linking doesn't mean enabling
292
+ # nor disabling.
293
+ when "enabled-runtime"
294
+ when "linked-runtime"
295
+ when "masked-runtime"
296
+ # Don't do anything because these states are not supposed
297
+ # to be permanent.
298
+ else
299
+ raise Machinery::Errors::KiwiExportFailed.new(
300
+ "The systemd unit state #{service.state} is unknown."
301
+ )
302
+ end
303
+ end
304
+
305
+ else
306
+ raise "Unsupported init system: #{init_system.inspect}."
307
+ end
308
+ end
309
+ end
310
+
311
+ def enable_dhcp(output_location)
312
+ case @system_description.os_object
313
+ when OsSles11
314
+ write_dhcp_network_config(output_location, "eth0")
315
+ when OsSles12
316
+ write_dhcp_network_config(output_location, "lan0")
317
+ write_persistent_net_rules(output_location)
318
+ when OsOpenSuse13_1
319
+ write_dhcp_network_config(output_location, "lan0")
320
+ write_persistent_net_rules(output_location)
321
+ end
322
+ puts "DHCP in built image will be enabled for the first device"
323
+ end
324
+
325
+ def write_dhcp_network_config(output_location, device)
326
+ network_location = File.join(output_location, "root/etc/sysconfig/network")
327
+ FileUtils.mkdir_p(network_location)
328
+ File.write(File.join(network_location, "ifcfg-#{device}"),
329
+ "BOOTPROTO='dhcp'\nSTARTMODE='onboot'"
330
+ )
331
+ end
332
+
333
+ def write_persistent_net_rules(output_location)
334
+ udev_location = File.join(output_location, "root/etc/udev/rules.d")
335
+ persistent_net_rule = [
336
+ 'SUBSYSTEM=="net"',
337
+ 'ACTION=="add"',
338
+ 'DRIVERS=="?*"',
339
+ 'ATTR{address}=="?*"',
340
+ 'ATTR{dev_id}=="0x0"',
341
+ 'ATTR{type}=="1"',
342
+ 'KERNEL=="?*"',
343
+ 'NAME="lan0"'
344
+ ]
345
+
346
+ FileUtils.mkdir_p(udev_location)
347
+ File.write(
348
+ File.join(udev_location, "70-persistent-net.rules"),
349
+ persistent_net_rule.join(", ")
350
+ )
351
+ end
352
+
353
+ def enable_ssh
354
+ @sh << "suseInsertService sshd\n"
355
+ end
356
+ end