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
data/lib/build_task.rb
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
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 BuildTask
|
|
19
|
+
def build(system_description, output_path, options = {})
|
|
20
|
+
LocalSystem.validate_build_compatibility(system_description)
|
|
21
|
+
LocalSystem.validate_existence_of_package("kiwi")
|
|
22
|
+
LocalSystem.validate_existence_of_package("kiwi-desc-vmxboot")
|
|
23
|
+
|
|
24
|
+
tmp_config_dir = Dir.mktmpdir("machinery-config", "/tmp")
|
|
25
|
+
tmp_image_dir = Dir.mktmpdir("machinery-image", "/tmp")
|
|
26
|
+
img_extension = "qcow2"
|
|
27
|
+
|
|
28
|
+
config = KiwiConfig.new(system_description, options)
|
|
29
|
+
config.write(tmp_config_dir)
|
|
30
|
+
|
|
31
|
+
if system_description["unmanaged_files"]
|
|
32
|
+
filters = File.read(
|
|
33
|
+
File.join(Machinery::ROOT, "kiwi_helpers/unmanaged_files_build_excludes")
|
|
34
|
+
)
|
|
35
|
+
Machinery::Ui.puts "\nUnmanaged files following these patterns are not added " \
|
|
36
|
+
"to the built image:"
|
|
37
|
+
Machinery::Ui.puts filters
|
|
38
|
+
Machinery::Ui.puts "\n"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
FileUtils.mkdir_p(output_path)
|
|
42
|
+
if tmp_image_dir.start_with?("/tmp/") && tmp_config_dir.start_with?("/tmp/")
|
|
43
|
+
tmp_script = write_kiwi_wrapper(tmp_config_dir, tmp_image_dir,
|
|
44
|
+
output_path, img_extension)
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
LoggedCheetah.run("sudo", tmp_script.path, :stdout => $stdout,
|
|
48
|
+
:stderr => $stderr)
|
|
49
|
+
rescue SignalException => e
|
|
50
|
+
# Handle SIGHUP(1), SIGINT(2) and SIGTERM(15) gracefully
|
|
51
|
+
if [1, 2, 15].include?(e.signo)
|
|
52
|
+
Machinery::Ui.warn "Warning: Interrupted by user. Waiting for build process to abort..."
|
|
53
|
+
|
|
54
|
+
# When we got a SIGHUP or a SIGTERM we send a SIGINT to all processes
|
|
55
|
+
# in our progress group (forked by Cheetah).
|
|
56
|
+
# For SIGINT that's not needed because it is propagated automatically.
|
|
57
|
+
#
|
|
58
|
+
# The reason for killing the child processes with SIGINT (vs SIGTERM)
|
|
59
|
+
# is that with SIGTERM the bash wrapper script around kiwi returns
|
|
60
|
+
# while the unmounting of /proc is still in progress. That would break
|
|
61
|
+
# the cleanup of the temporary kiwi directories below.
|
|
62
|
+
if [1, 15].include?(e.signo)
|
|
63
|
+
trap("INT") {}
|
|
64
|
+
`sudo kill -INT -#{Process.getpgrp}`
|
|
65
|
+
end
|
|
66
|
+
Process.waitall
|
|
67
|
+
|
|
68
|
+
Machinery::Ui.warn "Cleaning up temporary files..."
|
|
69
|
+
[tmp_config_dir, tmp_image_dir].each do |path|
|
|
70
|
+
LoggedCheetah.run("sudo", "rm", "-r", path) if Dir.exists?(path)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
raise
|
|
74
|
+
ensure
|
|
75
|
+
tmp_script.delete
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
raise RuntimeError.new(
|
|
79
|
+
"The Kiwi temporary build directories are not in /tmp. This should " \
|
|
80
|
+
"never happen, so nothing is deleted."
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
image_file = Dir.glob(File.join(output_path, "*.#{img_extension}")).first
|
|
85
|
+
|
|
86
|
+
if !image_file
|
|
87
|
+
raise(Machinery::Errors::BuildFailed, "The image build process failed. Check " \
|
|
88
|
+
"build log '#{tmp_image_dir}/kiwi-terminal-output.log' for more " \
|
|
89
|
+
"details."
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
meta_data = {
|
|
94
|
+
description: system_description.name,
|
|
95
|
+
image_file: File.basename(image_file)
|
|
96
|
+
}
|
|
97
|
+
File.write(File.join(output_path, Machinery::IMAGE_META_DATA_FILE),
|
|
98
|
+
meta_data.to_yaml
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def kiwi_wrapper(tmp_config_dir, tmp_image_dir, output_path, image_extension)
|
|
103
|
+
script = "#!/bin/bash\n"
|
|
104
|
+
script << "/usr/sbin/kiwi --build '#{tmp_config_dir}' --destdir '#{tmp_image_dir}' --logfile '#{tmp_image_dir}/kiwi-terminal-output.log'\n"
|
|
105
|
+
script << "if [ $? -eq 0 ]; then\n"
|
|
106
|
+
script << " mv '#{tmp_image_dir}/'*.#{image_extension} '#{output_path}'\n"
|
|
107
|
+
script << " rm -rf '#{tmp_image_dir}'\n"
|
|
108
|
+
script << "else\n"
|
|
109
|
+
script << " echo -e 'Building the Image with Kiwi failed.\nThe Kiwi build directory #{tmp_image_dir} was not removed.'\n"
|
|
110
|
+
script << "fi\n"
|
|
111
|
+
script << "rm -rf '#{tmp_config_dir}'\n"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def write_kiwi_wrapper(tmp_config_dir, tmp_image_dir, output_path, image_extension)
|
|
115
|
+
begin
|
|
116
|
+
script = Tempfile.new('machinery-kiwi-wrapper-script')
|
|
117
|
+
script << kiwi_wrapper(tmp_config_dir, tmp_image_dir, output_path, image_extension)
|
|
118
|
+
ensure
|
|
119
|
+
script.close unless script == nil
|
|
120
|
+
end
|
|
121
|
+
File.chmod(0755, script.path)
|
|
122
|
+
script
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
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 ChangedRpmFilesHelper
|
|
19
|
+
def parse_rpm_changes_line(line)
|
|
20
|
+
# rpm provides lines per config file where first 9 characters indicate which
|
|
21
|
+
# properties of the file are modified
|
|
22
|
+
rpm_changes, *fields = line.split(" ")
|
|
23
|
+
|
|
24
|
+
# For config or documentation files there's an additional field which
|
|
25
|
+
# contains "c" or "d"
|
|
26
|
+
type = fields[0].start_with?("/") ? "" : fields.shift
|
|
27
|
+
path = fields.join(" ")
|
|
28
|
+
|
|
29
|
+
changes = []
|
|
30
|
+
if rpm_changes == "missing"
|
|
31
|
+
changes << "deleted"
|
|
32
|
+
elsif rpm_changes == "........." && path.end_with?(" (replaced)")
|
|
33
|
+
changes << "replaced"
|
|
34
|
+
path.slice!(/ \(replaced\)$/)
|
|
35
|
+
else
|
|
36
|
+
changes << "mode" if rpm_changes[1] == "M"
|
|
37
|
+
changes << "md5" if rpm_changes[2] == "5"
|
|
38
|
+
changes << "user" if rpm_changes[5] == "U"
|
|
39
|
+
changes << "group" if rpm_changes[6] == "G"
|
|
40
|
+
end
|
|
41
|
+
[path, changes, type]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def parse_stat_line(line)
|
|
45
|
+
mode, user, group, uid, gid, *path = line.split(":")
|
|
46
|
+
|
|
47
|
+
user = uid if user == "UNKNOWN"
|
|
48
|
+
group = gid if group == "UNKNOWN"
|
|
49
|
+
|
|
50
|
+
[path.join(":").chomp,
|
|
51
|
+
{
|
|
52
|
+
:mode => mode,
|
|
53
|
+
:user => user,
|
|
54
|
+
:group => group
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# get path data for list of files
|
|
60
|
+
# cur_files is guaranteed to not exceed max command line length
|
|
61
|
+
def get_file_properties(system, cur_files)
|
|
62
|
+
ret = {}
|
|
63
|
+
out = system.run_command(
|
|
64
|
+
"stat", "--printf", "%a:%U:%G:%u:%g:%n\\n",
|
|
65
|
+
*cur_files,
|
|
66
|
+
:stdout => :capture
|
|
67
|
+
)
|
|
68
|
+
out.each_line do |l|
|
|
69
|
+
path, values = parse_stat_line(l)
|
|
70
|
+
ret[path] = values
|
|
71
|
+
end
|
|
72
|
+
ret
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def get_path_data(system, paths)
|
|
76
|
+
ret = {}
|
|
77
|
+
path_index = 0
|
|
78
|
+
# arbitrary number for maximum command line length that should always work
|
|
79
|
+
max_len = 50000
|
|
80
|
+
cur_files = []
|
|
81
|
+
cur_len = 0
|
|
82
|
+
while path_index < paths.size
|
|
83
|
+
if cur_files.empty? || paths[path_index].size + cur_len + 1 < max_len
|
|
84
|
+
cur_files << paths[path_index]
|
|
85
|
+
cur_len += paths[path_index].size + 1
|
|
86
|
+
path_index += 1
|
|
87
|
+
else
|
|
88
|
+
ret.merge!(get_file_properties(system, cur_files))
|
|
89
|
+
cur_files.clear
|
|
90
|
+
cur_len = 0
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
ret.merge!(get_file_properties(system, cur_files)) unless cur_files.empty?
|
|
94
|
+
ret
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/cli.rb
ADDED
|
@@ -0,0 +1,600 @@
|
|
|
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 Cli
|
|
19
|
+
extend GLI::App
|
|
20
|
+
|
|
21
|
+
program_desc 'A systems management toolkit for Linux'
|
|
22
|
+
preserve_argv(true)
|
|
23
|
+
@version = Machinery::VERSION + " (system description format version " +
|
|
24
|
+
"#{SystemDescription::CURRENT_FORMAT_VERSION})"
|
|
25
|
+
switch :version, :negatable => false, :desc => "Show version"
|
|
26
|
+
switch :debug, :negatable => false, :desc => "Enable debug mode"
|
|
27
|
+
switch [:help, :h], :negatable => false, :desc => "Show help"
|
|
28
|
+
|
|
29
|
+
sort_help :manually
|
|
30
|
+
pre do |global_options,command,options,args|
|
|
31
|
+
if global_options[:debug]
|
|
32
|
+
Machinery.logger.level = Logger::DEBUG
|
|
33
|
+
else
|
|
34
|
+
Machinery.logger.level = Logger::INFO
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
post do |global_options,command,options,args|
|
|
39
|
+
if command.is_a?(GLI::Commands::Help) && !global_options[:version]
|
|
40
|
+
|
|
41
|
+
Machinery::Ui.puts "\nMachinery can show hints which guide through a typical workflow."
|
|
42
|
+
if Machinery::Config.new.hints
|
|
43
|
+
Machinery::Ui.puts "These hints can be switched off by '#{$0} config hints off'."
|
|
44
|
+
else
|
|
45
|
+
Machinery::Ui.puts "These hints can be switched on by '#{$0} config hints on'."
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Hint.get_started
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
GLI::Commands::Help.skips_post = false
|
|
53
|
+
|
|
54
|
+
def self.handle_error(e)
|
|
55
|
+
case e
|
|
56
|
+
when GLI::UnknownCommandArgument, GLI::UnknownGlobalArgument,
|
|
57
|
+
GLI::UnknownCommand, GLI::BadCommandLine, OptionParser::MissingArgument
|
|
58
|
+
Machinery::Ui.error e.to_s + "\n\n"
|
|
59
|
+
command = ARGV & @commands.keys.map(&:to_s)
|
|
60
|
+
run(command << "--help")
|
|
61
|
+
exit 1
|
|
62
|
+
when Machinery::Errors::MachineryError
|
|
63
|
+
Machinery.logger.error(e.message)
|
|
64
|
+
Machinery::Ui.error e.message
|
|
65
|
+
exit 1
|
|
66
|
+
when SystemExit
|
|
67
|
+
raise
|
|
68
|
+
when SignalException
|
|
69
|
+
Machinery.logger.info "Machinery was aborted with signal #{e.signo}."
|
|
70
|
+
exit 1
|
|
71
|
+
else
|
|
72
|
+
Machinery::Ui.error "Machinery experienced an unexpected error. Please file a " \
|
|
73
|
+
"bug report at https://github.com/SUSE/machinery/issues/new.\n"
|
|
74
|
+
if e.is_a?(Cheetah::ExecutionFailed)
|
|
75
|
+
result = ""
|
|
76
|
+
result << "#{e.message}\n"
|
|
77
|
+
result << "\n"
|
|
78
|
+
|
|
79
|
+
if e.stderr && !e.stderr.empty?
|
|
80
|
+
result << "Error output:\n"
|
|
81
|
+
result << "#{e.stderr}\n"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if e.stdout && !e.stdout.empty?
|
|
85
|
+
result << "Standard output:\n"
|
|
86
|
+
result << "#{e.stdout}\n\n"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if e.backtrace && !e.backtrace.empty?
|
|
90
|
+
result << "Backtrace:\n"
|
|
91
|
+
result << "#{e.backtrace.join("\n")}\n\n"
|
|
92
|
+
end
|
|
93
|
+
Machinery.logger.error(result)
|
|
94
|
+
Machinery::Ui.error result
|
|
95
|
+
exit 1
|
|
96
|
+
else
|
|
97
|
+
Machinery.logger.error("Machinery experienced an unexpected error:")
|
|
98
|
+
Machinery.logger.error(e.message)
|
|
99
|
+
Machinery.logger.error(e.backtrace.join("\n"))
|
|
100
|
+
raise
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
on_error do |e|
|
|
108
|
+
Cli.handle_error(e)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def self.shift_arg(args, name)
|
|
112
|
+
if !res = args.shift
|
|
113
|
+
raise GLI::BadCommandLine.new("Machinery was called with missing argument #{name}.")
|
|
114
|
+
end
|
|
115
|
+
res
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.process_scope_option(scopes, exclude_scopes)
|
|
119
|
+
if scopes
|
|
120
|
+
if exclude_scopes
|
|
121
|
+
# scope and exclude-scope
|
|
122
|
+
raise Machinery::Errors::InvalidCommandLine.new( "You cannot provide the --scope and --exclude-scope option at the same time.")
|
|
123
|
+
else
|
|
124
|
+
# scope only
|
|
125
|
+
scope_list = parse_scopes(scopes)
|
|
126
|
+
end
|
|
127
|
+
else
|
|
128
|
+
if exclude_scopes
|
|
129
|
+
# exclude-scope only
|
|
130
|
+
scope_list = Inspector.all_scopes - parse_scopes(exclude_scopes)
|
|
131
|
+
else
|
|
132
|
+
# neither scope nor exclude-scope
|
|
133
|
+
scope_list = Inspector.all_scopes
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
if scope_list.empty?
|
|
137
|
+
raise Machinery::Errors::InvalidCommandLine.new( "No scopes to process. Nothing to do.")
|
|
138
|
+
end
|
|
139
|
+
scope_list
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def self.parse_scopes(scope_string)
|
|
143
|
+
unknown_scopes = []
|
|
144
|
+
invalid_scopes = []
|
|
145
|
+
scopes = []
|
|
146
|
+
|
|
147
|
+
scope_string.split(",").each do |scope|
|
|
148
|
+
if !(scope =~ /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/)
|
|
149
|
+
invalid_scopes << scope
|
|
150
|
+
next
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# convert cli scope naming to internal one
|
|
154
|
+
scope.tr!("-", "_")
|
|
155
|
+
|
|
156
|
+
if Inspector.all_scopes.include?(scope) && Renderer.for(scope)
|
|
157
|
+
scopes << scope
|
|
158
|
+
else
|
|
159
|
+
unknown_scopes << scope
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
if invalid_scopes.length > 0
|
|
164
|
+
form = invalid_scopes.length > 1 ? "scopes are" : "scope is"
|
|
165
|
+
raise Machinery::Errors::UnknownScope.new(
|
|
166
|
+
"The following #{form} not valid:" \
|
|
167
|
+
" \"#{invalid_scopes.join("\", \"")}\"." \
|
|
168
|
+
" Scope names must start with a letter and contain only lowercase" \
|
|
169
|
+
" letters and digits separated by dashes (\"-\")."
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if unknown_scopes.length > 0
|
|
174
|
+
form = unknown_scopes.length > 1 ? "scopes are" : "scope is"
|
|
175
|
+
raise Machinery::Errors::UnknownScope.new(
|
|
176
|
+
"The following #{form} not supported: " \
|
|
177
|
+
"#{Machinery::Ui.internal_scope_list_to_string(unknown_scopes)}. " \
|
|
178
|
+
"Valid scopes are: #{AVAILABLE_SCOPE_LIST}."
|
|
179
|
+
)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
scopes
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
AVAILABLE_SCOPE_LIST = Machinery::Ui.internal_scope_list_to_string(
|
|
186
|
+
Inspector.all_scopes
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
desc "Analyze system description"
|
|
190
|
+
long_desc <<-LONGDESC
|
|
191
|
+
Analyze stored system description.
|
|
192
|
+
|
|
193
|
+
The supported operations are:
|
|
194
|
+
|
|
195
|
+
- config-file-diffs: Generate diffs against the original version from
|
|
196
|
+
the package for the modified config files
|
|
197
|
+
LONGDESC
|
|
198
|
+
arg "NAME"
|
|
199
|
+
command :analyze do |c|
|
|
200
|
+
c.flag [:operation, :o], :type => String, :required => true,
|
|
201
|
+
:desc => "The analyze operation to perform", :arg_name => "OPERATION"
|
|
202
|
+
|
|
203
|
+
c.action do |global_options,options,args|
|
|
204
|
+
name = shift_arg(args, "NAME")
|
|
205
|
+
store = SystemDescriptionStore.new
|
|
206
|
+
description = store.load(name)
|
|
207
|
+
|
|
208
|
+
case options[:operation]
|
|
209
|
+
when "config-file-diffs"
|
|
210
|
+
task = AnalyzeConfigFileDiffsTask.new
|
|
211
|
+
task.analyze(description)
|
|
212
|
+
else
|
|
213
|
+
raise Machinery::Errors::InvalidCommandLine.new(
|
|
214
|
+
"The operation '#{options[:operation]}' is not supported. " \
|
|
215
|
+
"Valid operations are: config-file-diffs."
|
|
216
|
+
)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
desc "Build image from system description"
|
|
224
|
+
long_desc <<-LONGDESC
|
|
225
|
+
Build image from a given system description and store it to the given
|
|
226
|
+
location.
|
|
227
|
+
LONGDESC
|
|
228
|
+
arg "NAME"
|
|
229
|
+
command :build do |c|
|
|
230
|
+
c.flag ["image-dir", :i], :type => String, :required => true,
|
|
231
|
+
:desc => "Store the image under the specified path", :arg_name => "DIRECTORY"
|
|
232
|
+
c.switch ["enable-dhcp", :d], :required => false, :negatable => false,
|
|
233
|
+
:desc => "Enable DCHP client on first network card of built image"
|
|
234
|
+
c.switch ["enable-ssh", :s], :required => false, :negatable => false,
|
|
235
|
+
:desc => "Enable SSH service in built image"
|
|
236
|
+
|
|
237
|
+
c.action do |global_options,options,args|
|
|
238
|
+
name = shift_arg(args, "NAME")
|
|
239
|
+
store = SystemDescriptionStore.new
|
|
240
|
+
description = store.load(name)
|
|
241
|
+
|
|
242
|
+
task = BuildTask.new
|
|
243
|
+
task.build(description, File.expand_path(options["image-dir"]), {:enable_dhcp => options["enable-dhcp"], :enable_ssh => options["enable-ssh"]})
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
desc "Compare system descriptions"
|
|
250
|
+
long_desc <<-LONGDESC
|
|
251
|
+
Compare system descriptions stored under specified names.
|
|
252
|
+
|
|
253
|
+
Multiple scopes can be passed as comma-separated list. If no specific scopes
|
|
254
|
+
are given, all scopes are compared.
|
|
255
|
+
|
|
256
|
+
Available scopes: #{AVAILABLE_SCOPE_LIST}
|
|
257
|
+
LONGDESC
|
|
258
|
+
arg "NAME1"
|
|
259
|
+
arg "NAME2"
|
|
260
|
+
command :compare do |c|
|
|
261
|
+
c.flag [:scope, :s], :type => String, :required => false,
|
|
262
|
+
:desc => "Compare specified scopes", :arg_name => "SCOPE_LIST"
|
|
263
|
+
c.flag ["exclude-scope", :e], :type => String, :required => false,
|
|
264
|
+
:desc => "Exclude specified scopes", :arg_name => "SCOPE_LIST"
|
|
265
|
+
c.switch "show-all", :required => false, :negatable => false,
|
|
266
|
+
:desc => "Show also common properties"
|
|
267
|
+
c.switch "pager", :required => false, :default_value => true,
|
|
268
|
+
:desc => "Pipe output into a pager"
|
|
269
|
+
|
|
270
|
+
c.action do |global_options,options,args|
|
|
271
|
+
name1 = shift_arg(args, "NAME1")
|
|
272
|
+
name2 = shift_arg(args, "NAME2")
|
|
273
|
+
store = SystemDescriptionStore.new
|
|
274
|
+
description1 = store.load(name1)
|
|
275
|
+
description2 = store.load(name2)
|
|
276
|
+
scope_list = process_scope_option(options[:scope], options["exclude-scope"])
|
|
277
|
+
|
|
278
|
+
task = CompareTask.new
|
|
279
|
+
opts = {
|
|
280
|
+
show_all: options["show-all"],
|
|
281
|
+
no_pager: !options["pager"]
|
|
282
|
+
}
|
|
283
|
+
task.compare(description1, description2, scope_list, opts)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
desc "Copy system description"
|
|
290
|
+
long_desc <<-LONGDESC
|
|
291
|
+
Copy a system description.
|
|
292
|
+
|
|
293
|
+
The system description is copied and stored under the provided name.
|
|
294
|
+
LONGDESC
|
|
295
|
+
arg_name "FROM_NAME TO_NAME"
|
|
296
|
+
command :copy do |c|
|
|
297
|
+
c.action do |global_options,options,args|
|
|
298
|
+
from = shift_arg(args, "FROM_NAME")
|
|
299
|
+
to = shift_arg(args, "TO_NAME")
|
|
300
|
+
store = SystemDescriptionStore.new
|
|
301
|
+
task = CopyTask.new
|
|
302
|
+
task.copy(store, from, to)
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
desc "Deploy image to OpenStack cloud"
|
|
309
|
+
long_desc <<-LONGDESC
|
|
310
|
+
Deploy system description as image to OpenStack cloud.
|
|
311
|
+
|
|
312
|
+
The image will be deployed to the OpenStack cloud. If no --image-dir is
|
|
313
|
+
specified an image will be built from the description before deployment.
|
|
314
|
+
LONGDESC
|
|
315
|
+
arg "NAME"
|
|
316
|
+
command :deploy do |c|
|
|
317
|
+
c.flag ["cloud-config", :c], :type => String, :required => true, :arg_name => "FILE",
|
|
318
|
+
:desc => "Path to file where the cloud config (openrc.sh) is located"
|
|
319
|
+
c.flag ["image-dir", :i], :type => String, :required => false,
|
|
320
|
+
:desc => "Directory where the image is located", :arg_name => "DIRECTORY"
|
|
321
|
+
c.switch [:insecure, :s], :required => false, :negatable => false,
|
|
322
|
+
:desc => "Explicitly allow glanceclient to perform 'insecure SSL' (https) requests."
|
|
323
|
+
c.flag ["cloud-image-name", :n], :type => String, :required => false,
|
|
324
|
+
:desc => "Name of the image in the cloud", :arg_name => "NAME"
|
|
325
|
+
|
|
326
|
+
c.action do |global_options,options,args|
|
|
327
|
+
name = shift_arg(args, "NAME")
|
|
328
|
+
store = SystemDescriptionStore.new
|
|
329
|
+
description = store.load(name)
|
|
330
|
+
|
|
331
|
+
task = DeployTask.new
|
|
332
|
+
opts = {
|
|
333
|
+
image_name: options[:cloud_image_name],
|
|
334
|
+
insecure: options[:insecure]
|
|
335
|
+
}
|
|
336
|
+
opts[:image_dir] = File.expand_path(options["image-dir"]) if options["image-dir"]
|
|
337
|
+
task.deploy(description, File.expand_path(options["cloud-config"]), opts)
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
desc "Export system description as KIWI image description"
|
|
344
|
+
long_desc <<-LONGDESC
|
|
345
|
+
Export system description as KIWI image description.
|
|
346
|
+
|
|
347
|
+
The description will be placed in the given location. The image format in the
|
|
348
|
+
description is 'vmx'.
|
|
349
|
+
LONGDESC
|
|
350
|
+
arg "NAME"
|
|
351
|
+
command "export-kiwi" do |c|
|
|
352
|
+
c.flag ["kiwi-dir", :k], :type => String, :required => true,
|
|
353
|
+
:desc => "Location where the description will be stored", :arg_name => "DIRECTORY"
|
|
354
|
+
c.switch :force, :default_value => false, :required => false, :negatable => false,
|
|
355
|
+
:desc => "Overwrite existing description"
|
|
356
|
+
|
|
357
|
+
c.action do |global_options,options,args|
|
|
358
|
+
name = shift_arg(args, "NAME")
|
|
359
|
+
store = SystemDescriptionStore.new
|
|
360
|
+
description = store.load(name)
|
|
361
|
+
|
|
362
|
+
task = KiwiExportTask.new
|
|
363
|
+
task.export(description, File.expand_path(options["kiwi-dir"]), force: options[:force])
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
desc "Inspect running system"
|
|
370
|
+
long_desc <<-LONGDESC
|
|
371
|
+
Inspect running system and generate system descripton from inspected data.
|
|
372
|
+
|
|
373
|
+
Multiple scopes can be passed as comma-separated list. If no specific scopes
|
|
374
|
+
are given, all scopes are inspected.
|
|
375
|
+
|
|
376
|
+
Available scopes: #{AVAILABLE_SCOPE_LIST}
|
|
377
|
+
LONGDESC
|
|
378
|
+
arg "HOSTNAME"
|
|
379
|
+
command :inspect do |c|
|
|
380
|
+
c.flag [:name, :n], :type => String, :required => false, :arg_name => "NAME",
|
|
381
|
+
:desc => "Store system description under the specified name"
|
|
382
|
+
c.flag [:scope, :s], :type => String, :required => false,
|
|
383
|
+
:desc => "Show specified scopes", :arg_name => "SCOPE_LIST"
|
|
384
|
+
c.flag ["exclude-scope", :e], :type => String, :required => false,
|
|
385
|
+
:desc => "Exclude specified scopes", :arg_name => "SCOPE_LIST"
|
|
386
|
+
c.switch ["extract-files", :x], :required => false, :negatable => false,
|
|
387
|
+
:desc => "Extract changed configuration files and unmanaged files from inspected system"
|
|
388
|
+
c.switch "extract-changed-config-files", :required => false, :negatable => false,
|
|
389
|
+
:desc => "Extract changed configuration files from inspected system"
|
|
390
|
+
c.switch "extract-unmanaged-files", :required => false, :negatable => false,
|
|
391
|
+
:desc => "Extract unmanaged files from inspected system"
|
|
392
|
+
c.switch "extract-changed-managed-files", :required => false, :negatable => false,
|
|
393
|
+
:desc => "Extract changed managed files from inspected system"
|
|
394
|
+
c.switch :show, :required => false, :negatable => false,
|
|
395
|
+
:desc => "Print inspection result"
|
|
396
|
+
|
|
397
|
+
c.action do |global_options,options,args|
|
|
398
|
+
host = shift_arg(args, "HOSTNAME")
|
|
399
|
+
store = SystemDescriptionStore.new
|
|
400
|
+
inspector_task = InspectTask.new
|
|
401
|
+
scope_list = process_scope_option(options[:scope], options["exclude-scope"])
|
|
402
|
+
name = options[:name] || host
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
if !scope_list.empty?
|
|
406
|
+
inspected_scopes = " for #{Machinery::Ui.internal_scope_list_to_string(scope_list)}"
|
|
407
|
+
end
|
|
408
|
+
Machinery::Ui.puts "Inspecting #{host}#{inspected_scopes}..."
|
|
409
|
+
|
|
410
|
+
inspect_options = {}
|
|
411
|
+
if options["show"]
|
|
412
|
+
inspect_options[:show] = true
|
|
413
|
+
end
|
|
414
|
+
if options["extract-files"] || options["extract-changed-config-files"]
|
|
415
|
+
inspect_options[:extract_changed_config_files] = true
|
|
416
|
+
end
|
|
417
|
+
if options["extract-files"] || options["extract-changed-managed-files"]
|
|
418
|
+
inspect_options[:extract_changed_managed_files] = true
|
|
419
|
+
end
|
|
420
|
+
if options["extract-files"] || options["extract-unmanaged-files"]
|
|
421
|
+
inspect_options[:extract_unmanaged_files] = true
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
inspector_task.inspect_system(
|
|
425
|
+
store, host, name, CurrentUser.new, scope_list, inspect_options
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
Hint.show_data(:name => name)
|
|
429
|
+
|
|
430
|
+
if !options["extract-files"] || Inspector.all_scopes.count != scope_list.count
|
|
431
|
+
Hint.do_complete_inspection(:name => name, :host => host)
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
desc "List system descriptions"
|
|
439
|
+
long_desc <<-LONGDESC
|
|
440
|
+
List system descriptions and their stored scopes.
|
|
441
|
+
|
|
442
|
+
The date of modification for each scope can be shown with the verbose
|
|
443
|
+
option.
|
|
444
|
+
LONGDESC
|
|
445
|
+
command :list do |c|
|
|
446
|
+
c.switch :verbose, :required => false, :negatable => false,
|
|
447
|
+
:desc => "Display additional information about origin of scopes"
|
|
448
|
+
|
|
449
|
+
c.action do |global_options,options,args|
|
|
450
|
+
store = SystemDescriptionStore.new
|
|
451
|
+
task = ListTask.new
|
|
452
|
+
task.list(store, options)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
desc "Remove system description"
|
|
459
|
+
long_desc <<-LONGDESC
|
|
460
|
+
Remove system description stored under the specified name.
|
|
461
|
+
|
|
462
|
+
The success of a removal can be shown with the verbose option.
|
|
463
|
+
LONGDESC
|
|
464
|
+
arg "NAME"
|
|
465
|
+
command :remove do |c|
|
|
466
|
+
c.switch :all, :negatable => false,
|
|
467
|
+
:desc => "Remove all system descriptions"
|
|
468
|
+
c.switch :verbose, :required => false, :negatable => false,
|
|
469
|
+
:desc => "Explain what is being done"
|
|
470
|
+
|
|
471
|
+
c.action do |global_options,options,args|
|
|
472
|
+
name = shift_arg(args, "NAME") if !options[:all]
|
|
473
|
+
|
|
474
|
+
store = SystemDescriptionStore.new
|
|
475
|
+
task = RemoveTask.new
|
|
476
|
+
task.remove(store, name, :verbose => options[:verbose], :all => options[:all])
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
desc "Show system description"
|
|
483
|
+
long_desc <<-LONGDESC
|
|
484
|
+
Show system description stored under the specified name.
|
|
485
|
+
|
|
486
|
+
Multiple scopes can be passed as comma-separated list. If no specific scopes
|
|
487
|
+
are given, all scopes are shown.
|
|
488
|
+
|
|
489
|
+
Available scopes: #{AVAILABLE_SCOPE_LIST}
|
|
490
|
+
LONGDESC
|
|
491
|
+
arg "NAME"
|
|
492
|
+
command :show do |c|
|
|
493
|
+
c.flag [:scope, :s], :type => String, :required => false,
|
|
494
|
+
:desc => "Show specified scopes", :arg_name => "SCOPE_LIST"
|
|
495
|
+
c.flag ["exclude-scope", :e], :type => String, :required => false,
|
|
496
|
+
:desc => "Exclude specified scopes", :arg_name => "SCOPE_LIST"
|
|
497
|
+
c.switch "pager", :required => false, :default_value => true,
|
|
498
|
+
:desc => "Pipe output into a pager"
|
|
499
|
+
c.switch "show-diffs", :required => false, :negatable => false,
|
|
500
|
+
:desc => "Show diffs of configuration files changes."
|
|
501
|
+
c.switch "html", :required => false, :negatable => false,
|
|
502
|
+
:desc => "Open system description in HTML format in your web browser."
|
|
503
|
+
|
|
504
|
+
c.action do |global_options,options,args|
|
|
505
|
+
name = shift_arg(args, "NAME")
|
|
506
|
+
if name == "localhost" && !CurrentUser.new.is_root?
|
|
507
|
+
Machinery::Ui.puts "You need root rights to access the system description of your locally inspected system."
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
store = SystemDescriptionStore.new
|
|
511
|
+
description = store.load(name)
|
|
512
|
+
scope_list = process_scope_option(options[:scope], options["exclude-scope"])
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
task = ShowTask.new
|
|
516
|
+
opts = {
|
|
517
|
+
no_pager: !options["pager"],
|
|
518
|
+
show_diffs: options["show-diffs"],
|
|
519
|
+
show_html: options["html"]
|
|
520
|
+
}
|
|
521
|
+
task.show(description, scope_list, opts)
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
desc "Validate system description"
|
|
527
|
+
long_desc <<-LONGDESC
|
|
528
|
+
Validate system description stored under the specified name.
|
|
529
|
+
LONGDESC
|
|
530
|
+
arg "NAME"
|
|
531
|
+
command :validate do |c|
|
|
532
|
+
c.action do |global_options,options,args|
|
|
533
|
+
name = shift_arg(args, "NAME")
|
|
534
|
+
if name == "localhost" && !CurrentUser.new.is_root?
|
|
535
|
+
Machinery::Ui.puts "You need root rights to access the system description of your locally inspected system."
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
store = SystemDescriptionStore.new
|
|
539
|
+
task = ValidateTask.new
|
|
540
|
+
task.validate(store, name)
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
desc "Upgrade format of system description"
|
|
545
|
+
long_desc <<-LONGDESC
|
|
546
|
+
Upgrade the format of one or all system descriptions.
|
|
547
|
+
LONGDESC
|
|
548
|
+
arg "NAME"
|
|
549
|
+
command "upgrade-format" do |c|
|
|
550
|
+
c.switch :all, :negatable => false,
|
|
551
|
+
:desc => "Upgrade all system descriptions"
|
|
552
|
+
|
|
553
|
+
c.action do |global_options,options,args|
|
|
554
|
+
name = shift_arg(args, "NAME") if !options[:all]
|
|
555
|
+
|
|
556
|
+
store = SystemDescriptionStore.new
|
|
557
|
+
task = UpgradeFormatTask.new
|
|
558
|
+
task.upgrade(store, name, :all => options[:all])
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
desc "Generate an HTML view for a system description"
|
|
563
|
+
long_desc <<-LONGDESC
|
|
564
|
+
Generates an HTML view for a system description.
|
|
565
|
+
LONGDESC
|
|
566
|
+
arg "NAME"
|
|
567
|
+
command "generate-html" do |c|
|
|
568
|
+
c.action do |global_options,options,args|
|
|
569
|
+
name = shift_arg(args, "NAME")
|
|
570
|
+
|
|
571
|
+
store = SystemDescriptionStore.new
|
|
572
|
+
description = store.load(name)
|
|
573
|
+
task = GenerateHtmlTask.new
|
|
574
|
+
task.generate(description)
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
desc "Show or change machinery's configuration"
|
|
579
|
+
long_desc <<-LONGDESC
|
|
580
|
+
Show or change machinery's configuration.
|
|
581
|
+
|
|
582
|
+
The value of a key is shown when no value argument is passed.
|
|
583
|
+
If neither the key argument nor the value argument are specified a list of all keys and their values are shown.
|
|
584
|
+
LONGDESC
|
|
585
|
+
arg "KEY", :optional
|
|
586
|
+
arg "VALUE", :optional
|
|
587
|
+
command "config" do |c|
|
|
588
|
+
c.action do |global_options,options,args|
|
|
589
|
+
key = args[0]
|
|
590
|
+
value = args[1]
|
|
591
|
+
|
|
592
|
+
task = ConfigTask.new
|
|
593
|
+
task.config(key, value)
|
|
594
|
+
|
|
595
|
+
if key == "hints" && !Machinery::Config.new.hints
|
|
596
|
+
Machinery::Ui.puts "Hints can be switched on again by '#{$0} config hints on'."
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
end
|