machinery-tool 1.0.2
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 +7 -0
- data/COPYING +674 -0
- data/NEWS +143 -0
- data/bin/machinery +29 -0
- data/helpers/changed_managed_files.sh +32 -0
- data/helpers/filter-packages-for-build.yaml +6 -0
- data/html/assets/arrow_down.png +0 -0
- data/html/assets/arrow_up.png +0 -0
- data/html/assets/bootstrap-popover.js +113 -0
- data/html/assets/bootstrap-tooltip.js +457 -0
- data/html/assets/bootstrap.min.css +5 -0
- data/html/assets/collapse.js +174 -0
- data/html/assets/hogan-3.0.2.min.mustache.js +5 -0
- data/html/assets/jquery-2.1.1.min.js +4 -0
- data/html/assets/logo-changed-managed-files-small.png +0 -0
- data/html/assets/logo-changed-managed-files.png +0 -0
- data/html/assets/logo-config-files-small.png +0 -0
- data/html/assets/logo-config-files.png +0 -0
- data/html/assets/logo-groups-small.png +0 -0
- data/html/assets/logo-groups.png +0 -0
- data/html/assets/logo-os-small.png +0 -0
- data/html/assets/logo-os.png +0 -0
- data/html/assets/logo-packages-small.png +0 -0
- data/html/assets/logo-packages.png +0 -0
- data/html/assets/logo-patterns-small.png +0 -0
- data/html/assets/logo-patterns.png +0 -0
- data/html/assets/logo-repositories-small.png +0 -0
- data/html/assets/logo-repositories.png +0 -0
- data/html/assets/logo-services-small.png +0 -0
- data/html/assets/logo-services.png +0 -0
- data/html/assets/logo-unmanaged-files-small.png +0 -0
- data/html/assets/logo-unmanaged-files.png +0 -0
- data/html/assets/logo-users-small.png +0 -0
- data/html/assets/logo-users.png +0 -0
- data/html/assets/machinery-base.css +5767 -0
- data/html/assets/machinery.css +131 -0
- data/html/assets/machinery.js +148 -0
- data/html/assets/transition.js +59 -0
- data/html/assets/wheels_horizontal.png +0 -0
- data/html/index.html.haml +468 -0
- data/kiwi_helpers/kiwi_export_readme.md +22 -0
- data/kiwi_helpers/merge_users_and_groups.pl.erb +231 -0
- data/kiwi_helpers/unmanaged_files_build_excludes +5 -0
- data/lib/analyze_config_file_diffs_task.rb +130 -0
- data/lib/array.rb +98 -0
- data/lib/build_task.rb +124 -0
- data/lib/changed_rpm_files_helper.rb +96 -0
- data/lib/cli.rb +600 -0
- data/lib/compare_task.rb +68 -0
- data/lib/config.rb +33 -0
- data/lib/config_base.rb +117 -0
- data/lib/config_task.rb +56 -0
- data/lib/constants.rb +24 -0
- data/lib/copy_task.rb +22 -0
- data/lib/current_user.rb +23 -0
- data/lib/deploy_task.rb +89 -0
- data/lib/exceptions.rb +113 -0
- data/lib/generate_html_task.rb +22 -0
- data/lib/helper.rb +22 -0
- data/lib/hint.rb +39 -0
- data/lib/html.rb +103 -0
- data/lib/inspect_task.rb +93 -0
- data/lib/inspector.rb +65 -0
- data/lib/kiwi_config.rb +356 -0
- data/lib/kiwi_export_task.rb +36 -0
- data/lib/list_task.rb +64 -0
- data/lib/local_system.rb +127 -0
- data/lib/logged_cheetah.rb +25 -0
- data/lib/machinery.rb +85 -0
- data/lib/machinery_logger.rb +47 -0
- data/lib/migration.rb +128 -0
- data/lib/mountpoints.rb +72 -0
- data/lib/object.rb +138 -0
- data/lib/os.rb +78 -0
- data/lib/remote_system.rb +114 -0
- data/lib/remove_task.rb +43 -0
- data/lib/renderer.rb +243 -0
- data/lib/renderer_helper.rb +26 -0
- data/lib/rpm.rb +52 -0
- data/lib/scope_mixin.rb +38 -0
- data/lib/show_task.rb +65 -0
- data/lib/system.rb +81 -0
- data/lib/system_description.rb +228 -0
- data/lib/system_description_store.rb +167 -0
- data/lib/system_description_validator.rb +216 -0
- data/lib/tarball.rb +82 -0
- data/lib/ui.rb +74 -0
- data/lib/upgrade_format_task.rb +55 -0
- data/lib/validate_task.rb +23 -0
- data/lib/version.rb +22 -0
- data/lib/zypper.rb +70 -0
- data/man/generated/machinery.1.gz +0 -0
- data/man/generated/machinery.1.html +1056 -0
- data/plugins/docs/changed_managed_files.md +2 -0
- data/plugins/docs/config_files.md +5 -0
- data/plugins/docs/groups.md +2 -0
- data/plugins/docs/os.md +2 -0
- data/plugins/docs/packages.md +2 -0
- data/plugins/docs/patterns.md +5 -0
- data/plugins/docs/repositories.md +24 -0
- data/plugins/docs/services.md +6 -0
- data/plugins/docs/unmanaged_files.md +13 -0
- data/plugins/docs/users.md +3 -0
- data/plugins/inspect/changed_managed_files_inspector.rb +109 -0
- data/plugins/inspect/config_files_inspector.rb +117 -0
- data/plugins/inspect/groups_inspector.rb +46 -0
- data/plugins/inspect/os_inspector.rb +116 -0
- data/plugins/inspect/packages_inspector.rb +46 -0
- data/plugins/inspect/patterns_inspector.rb +67 -0
- data/plugins/inspect/repositories_inspector.rb +107 -0
- data/plugins/inspect/services_inspector.rb +88 -0
- data/plugins/inspect/unmanaged_files_inspector.rb +393 -0
- data/plugins/inspect/users_inspector.rb +87 -0
- data/plugins/model/changed_managed_files_model.rb +29 -0
- data/plugins/model/config_files_model.rb +29 -0
- data/plugins/model/groups_model.rb +26 -0
- data/plugins/model/os_model.rb +20 -0
- data/plugins/model/packages_model.rb +26 -0
- data/plugins/model/patterns_model.rb +26 -0
- data/plugins/model/repositories_model.rb +26 -0
- data/plugins/model/services_model.rb +48 -0
- data/plugins/model/unmanaged_files_model.rb +29 -0
- data/plugins/model/users_model.rb +26 -0
- data/plugins/schema/v1/system-description-changed-managed-files.schema.json +83 -0
- data/plugins/schema/v1/system-description-config-files.schema.json +83 -0
- data/plugins/schema/v1/system-description-groups.schema.json +30 -0
- data/plugins/schema/v1/system-description-os.schema.json +21 -0
- data/plugins/schema/v1/system-description-packages.schema.json +34 -0
- data/plugins/schema/v1/system-description-patterns.schema.json +24 -0
- data/plugins/schema/v1/system-description-repositories.schema.json +41 -0
- data/plugins/schema/v1/system-description-services.schema.json +30 -0
- data/plugins/schema/v1/system-description-unmanaged-files.schema.json +105 -0
- data/plugins/schema/v1/system-description-users.schema.json +61 -0
- data/plugins/schema/v2/system-description-changed-managed-files.schema.json +92 -0
- data/plugins/schema/v2/system-description-config-files.schema.json +92 -0
- data/plugins/schema/v2/system-description-groups.schema.json +30 -0
- data/plugins/schema/v2/system-description-os.schema.json +21 -0
- data/plugins/schema/v2/system-description-packages.schema.json +34 -0
- data/plugins/schema/v2/system-description-patterns.schema.json +24 -0
- data/plugins/schema/v2/system-description-repositories.schema.json +41 -0
- data/plugins/schema/v2/system-description-services.schema.json +30 -0
- data/plugins/schema/v2/system-description-unmanaged-files.schema.json +138 -0
- data/plugins/schema/v2/system-description-users.schema.json +61 -0
- data/plugins/show/changed_managed_files_renderer.rb +46 -0
- data/plugins/show/config_files_renderer.rb +62 -0
- data/plugins/show/groups_renderer.rb +36 -0
- data/plugins/show/os_renderer.rb +31 -0
- data/plugins/show/packages_renderer.rb +32 -0
- data/plugins/show/patterns_renderer.rb +32 -0
- data/plugins/show/repositories_renderer.rb +38 -0
- data/plugins/show/services_renderer.rb +32 -0
- data/plugins/show/unmanaged_files_renderer.rb +42 -0
- data/plugins/show/users_renderer.rb +35 -0
- data/schema/migrations/migrate1to2.rb +56 -0
- data/schema/v1/system-description-global.schema.json +31 -0
- data/schema/v2/system-description-global.schema.json +31 -0
- 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("\\", "\").
|
|
64
|
+
gsub("\t", " "*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
|
data/lib/inspect_task.rb
ADDED
|
@@ -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
|
data/lib/kiwi_config.rb
ADDED
|
@@ -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
|