machinery-tool 1.22.1 → 1.22.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.git_revision +1 -1
- data/NEWS +10 -0
- data/bin/machinery +1 -1
- data/lib/analyze_changed_config_files_diffs_task.rb +6 -6
- data/lib/autoyast.rb +2 -2
- data/lib/build_task.rb +10 -7
- data/lib/cli.rb +1005 -801
- data/lib/compare_task.rb +11 -7
- data/lib/comparison.rb +2 -2
- data/lib/config_base.rb +1 -1
- data/lib/config_task.rb +1 -1
- data/lib/containerize_task.rb +3 -3
- data/lib/containerized_app.rb +1 -1
- data/lib/copy_task.rb +1 -1
- data/lib/current_user.rb +1 -1
- data/lib/deploy_task.rb +6 -4
- data/lib/diff_widget.rb +67 -63
- data/lib/docker_system.rb +12 -8
- data/lib/dpkg_database.rb +1 -1
- data/lib/element_filter.rb +7 -4
- data/lib/exceptions.rb +23 -5
- data/lib/export_task.rb +1 -1
- data/lib/exporter.rb +1 -1
- data/lib/file_diff.rb +1 -1
- data/lib/file_scope.rb +1 -1
- data/lib/file_validator.rb +7 -4
- data/lib/filter.rb +97 -93
- data/lib/filter_option_parser.rb +2 -2
- data/lib/hint.rb +64 -59
- data/lib/html.rb +1 -1
- data/lib/inspect_task.rb +12 -12
- data/lib/inspector.rb +3 -3
- data/lib/json_validation_error_cleaner.rb +1 -1
- data/lib/json_validator.rb +4 -4
- data/lib/kiwi_config.rb +8 -4
- data/lib/list_task.rb +10 -9
- data/lib/local_system.rb +11 -5
- data/lib/logged_cheetah.rb +1 -1
- data/lib/man_task.rb +10 -6
- data/lib/managed_files_database.rb +1 -1
- data/lib/manifest.rb +5 -5
- data/lib/migration.rb +16 -10
- data/lib/mountpoints.rb +1 -1
- data/lib/move_task.rb +1 -1
- data/lib/remote_system.rb +7 -7
- data/lib/remove_task.rb +1 -1
- data/lib/renderer.rb +177 -172
- data/lib/rpm.rb +4 -4
- data/lib/rpm_database.rb +1 -1
- data/lib/scope.rb +2 -2
- data/lib/scope_file_access_archive.rb +1 -1
- data/lib/scope_file_access_flat.rb +1 -1
- data/lib/scope_file_store.rb +1 -1
- data/lib/serve_html_task.rb +6 -2
- data/lib/server.rb +19 -12
- data/lib/show_task.rb +10 -6
- data/lib/static_html.rb +1 -1
- data/lib/system.rb +10 -10
- data/lib/system_description.rb +14 -13
- data/lib/system_description_memory_store.rb +1 -1
- data/lib/system_description_store.rb +9 -9
- data/lib/tarball.rb +8 -2
- data/lib/upgrade_format_task.rb +11 -6
- data/lib/validate_task.rb +2 -2
- data/lib/version.rb +1 -1
- data/lib/workload_mapper.rb +2 -2
- data/lib/workload_mapper_dsl.rb +1 -1
- data/lib/zypper.rb +40 -17
- data/machinery-helper/machinery_helper.go +35 -16
- data/machinery-helper/version.go +1 -1
- data/man/generated/machinery.1.gz +0 -0
- data/manual/site/sitemap.xml +24 -24
- data/plugins/changed_config_files/changed_config_files_inspector.rb +59 -56
- data/plugins/changed_config_files/changed_config_files_model.rb +23 -21
- data/plugins/changed_config_files/changed_config_files_renderer.rb +56 -52
- data/plugins/changed_managed_files/changed_managed_files_inspector.rb +52 -50
- data/plugins/changed_managed_files/changed_managed_files_model.rb +23 -21
- data/plugins/changed_managed_files/changed_managed_files_renderer.rb +43 -39
- data/plugins/environment/environment_inspector.rb +25 -23
- data/plugins/environment/environment_model.rb +5 -3
- data/plugins/groups/groups_inspector.rb +30 -28
- data/plugins/groups/groups_model.rb +18 -17
- data/plugins/groups/groups_renderer.rb +29 -25
- data/plugins/os/os_inspector.rb +120 -118
- data/plugins/os/os_model.rb +139 -134
- data/plugins/os/os_renderer.rb +13 -9
- data/plugins/packages/packages_inspector.rb +99 -86
- data/plugins/packages/packages_model.rb +35 -34
- data/plugins/packages/packages_renderer.rb +47 -39
- data/plugins/patterns/patterns_inspector.rb +70 -68
- data/plugins/patterns/patterns_model.rb +19 -18
- data/plugins/patterns/patterns_renderer.rb +36 -32
- data/plugins/repositories/repositories_inspector.rb +162 -156
- data/plugins/repositories/repositories_model.rb +50 -49
- data/plugins/repositories/repositories_renderer.rb +48 -44
- data/plugins/repositories/schema/system-description-repositories.schema-v10.json +0 -1
- data/plugins/services/services_inspector.rb +187 -176
- data/plugins/services/services_model.rb +37 -36
- data/plugins/services/services_renderer.rb +28 -24
- data/plugins/unmanaged_files/unmanaged_files_inspector.rb +102 -99
- data/plugins/unmanaged_files/unmanaged_files_model.rb +64 -56
- data/plugins/unmanaged_files/unmanaged_files_renderer.rb +44 -40
- data/plugins/users/users_inspector.rb +67 -65
- data/plugins/users/users_model.rb +37 -36
- data/plugins/users/users_renderer.rb +31 -27
- data/schema/migrations/migrate1to2.rb +1 -1
- data/schema/migrations/migrate2to3.rb +1 -1
- data/schema/migrations/migrate3to4.rb +1 -1
- data/schema/migrations/migrate4to5.rb +1 -1
- data/schema/migrations/migrate5to6.rb +1 -1
- data/schema/migrations/migrate6to7.rb +1 -1
- data/schema/migrations/migrate7to8.rb +1 -1
- data/schema/migrations/migrate8to9.rb +1 -1
- data/schema/migrations/migrate9to10.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da0041ff3a8d3c178063228340914aca550a308f
|
4
|
+
data.tar.gz: e333966071f57a102961d8de5eb57f47d0cea5a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99fa10a1ff34c692d35638a5aaef0b411456e3f183ee822b4d803a1366b65ad7cc52b1be636e6df78ce4914bfbca17201efa7954f09efe0ab1f13dfbcff1a846
|
7
|
+
data.tar.gz: f612d4f45396600d41b6b579077bd85bce57768386b109a2bf549f25c8818e9ec48c8d8138ac1dec35ecfd31027464e4179ac0319129d7e74c43e622c674680f
|
data/.git_revision
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
5544d3469a45c0bf15d3263bc1bbbd32afb7f274
|
data/NEWS
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
# Machinery Release Notes
|
2
2
|
|
3
3
|
|
4
|
+
## Version 1.22.2 - Wed Nov 16 16:44:00 CET 2016 - thardeck@suse.de
|
5
|
+
|
6
|
+
* Prevent machinery-helper from crashing when files are inaccessible during
|
7
|
+
inspection (bnc#1009774)
|
8
|
+
* Fix analyze of changed-config-files when NFS or SMB repositories are
|
9
|
+
used (gh#SUSE/machinery#2132)
|
10
|
+
* Do not add repositories which require registration to built images (bnc#1004697)
|
11
|
+
* Fix package inspection on older Debian systems
|
12
|
+
* Fix extraction of information for Debian packages containing a dash in their name
|
13
|
+
|
4
14
|
## Version 1.22.1 - Fri Oct 14 16:13:50 CEST 2016 - thardeck@suse.de
|
5
15
|
|
6
16
|
* Only use sudo for reading files when necessary (gh#SUSE/machinery#2077)
|
data/bin/machinery
CHANGED
@@ -38,7 +38,7 @@ begin
|
|
38
38
|
command_log = "Executing (Version #{Machinery::VERSION}) '#{$PROGRAM_NAME} #{ARGV.join(" ")}'"
|
39
39
|
command_log += " (store: #{ENV["MACHINERY_DIR"]})" if ENV["MACHINERY_DIR"]
|
40
40
|
Machinery.logger.info command_log
|
41
|
-
Cli.run(ARGV)
|
41
|
+
Machinery::Cli.run(ARGV)
|
42
42
|
rescue Machinery::Errors::IncompatibleHost => e
|
43
43
|
puts e
|
44
44
|
end
|
@@ -15,11 +15,11 @@
|
|
15
15
|
# To contact SUSE about this file by physical or electronic mail,
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
|
-
class AnalyzeConfigFileDiffsTask
|
18
|
+
class Machinery::AnalyzeConfigFileDiffsTask
|
19
19
|
def analyze(description)
|
20
20
|
description.assert_scopes("os")
|
21
21
|
check_os(description)
|
22
|
-
LocalSystem.validate_existence_of_packages(["zypper"])
|
22
|
+
Machinery::LocalSystem.validate_existence_of_packages(["zypper"])
|
23
23
|
description.validate_analysis_compatibility
|
24
24
|
description.assert_scopes(
|
25
25
|
"repositories",
|
@@ -55,7 +55,7 @@ class AnalyzeConfigFileDiffsTask
|
|
55
55
|
end
|
56
56
|
|
57
57
|
package.files.each do |file|
|
58
|
-
diff = Rpm.new(path).diff(file, File.join(extracted_files_path, file))
|
58
|
+
diff = Machinery::Rpm.new(path).diff(file, File.join(extracted_files_path, file))
|
59
59
|
|
60
60
|
if !diff || diff.empty?
|
61
61
|
Machinery::Ui.warn "Warning: Could not generate diff for #{file}."
|
@@ -76,7 +76,7 @@ class AnalyzeConfigFileDiffsTask
|
|
76
76
|
private
|
77
77
|
|
78
78
|
def check_os(description)
|
79
|
-
unless description.os.is_a?(OsSuse)
|
79
|
+
unless description.os.is_a?(Machinery::OsSuse)
|
80
80
|
raise Machinery::Errors::AnalysisFailed.new(
|
81
81
|
"Can not analyze the system description because its operating system" \
|
82
82
|
" '#{description.os.display_name}' is not supported."
|
@@ -100,7 +100,7 @@ class AnalyzeConfigFileDiffsTask
|
|
100
100
|
|
101
101
|
files.inject({}) do |result, file|
|
102
102
|
key = "#{file.package_name}-#{file.package_version}"
|
103
|
-
result[key] ||= Package.new(
|
103
|
+
result[key] ||= Machinery::Package.new(
|
104
104
|
"name" => file["package_name"],
|
105
105
|
"version" => file["package_version"],
|
106
106
|
"files" => []
|
@@ -114,7 +114,7 @@ class AnalyzeConfigFileDiffsTask
|
|
114
114
|
def with_repositories(description, &block)
|
115
115
|
Machinery::Ui.puts "Setting up repository access..."
|
116
116
|
arch = description.os.architecture
|
117
|
-
Zypper.isolated(arch: arch) do |zypper|
|
117
|
+
Machinery::Zypper.isolated(arch: arch) do |zypper|
|
118
118
|
begin
|
119
119
|
remote_repos = description.repositories.reject do |repo|
|
120
120
|
repo.url.start_with?("cd://") || repo.url.start_with?("dvd://")
|
data/lib/autoyast.rb
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
# To contact SUSE about this file by physical or electronic mail,
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
|
-
class Autoyast < Exporter
|
18
|
+
class Machinery::Autoyast < Machinery::Exporter
|
19
19
|
attr_accessor :name
|
20
20
|
|
21
21
|
def initialize(description)
|
@@ -110,7 +110,7 @@ class Autoyast < Exporter
|
|
110
110
|
private
|
111
111
|
|
112
112
|
def check_exported_os
|
113
|
-
unless @system_description.os.is_a?(OsSuse)
|
113
|
+
unless @system_description.os.is_a?(Machinery::OsSuse)
|
114
114
|
raise Machinery::Errors::ExportFailed.new(
|
115
115
|
"Export is not possible because the operating system " \
|
116
116
|
"'#{@system_description.os.display_name}' is not supported."
|
data/lib/build_task.rb
CHANGED
@@ -15,23 +15,26 @@
|
|
15
15
|
# To contact SUSE about this file by physical or electronic mail,
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
|
-
class BuildTask
|
18
|
+
class Machinery::BuildTask
|
19
19
|
def build(system_description, output_path, options = {})
|
20
|
-
LocalSystem.validate_architecture("x86_64")
|
21
|
-
LocalSystem.validate_existence_of_packages(["kiwi", "kiwi-desc-vmxboot"])
|
20
|
+
Machinery::LocalSystem.validate_architecture("x86_64")
|
21
|
+
Machinery::LocalSystem.validate_existence_of_packages(["kiwi", "kiwi-desc-vmxboot"])
|
22
22
|
system_description.validate_build_compatibility
|
23
23
|
|
24
24
|
tmp_config_dir = Dir.mktmpdir("machinery-config", "/tmp")
|
25
25
|
tmp_image_dir = Dir.mktmpdir("machinery-image", "/tmp")
|
26
26
|
img_extension = "qcow2"
|
27
27
|
|
28
|
-
config = KiwiConfig.new(system_description, options)
|
28
|
+
config = Machinery::KiwiConfig.new(system_description, options)
|
29
29
|
config.write(tmp_config_dir)
|
30
30
|
|
31
31
|
begin
|
32
32
|
FileUtils.mkdir_p(output_path)
|
33
33
|
rescue Errno::EACCES
|
34
|
-
raise Machinery::Errors::BuildDirectoryCreateError.new(
|
34
|
+
raise Machinery::Errors::BuildDirectoryCreateError.new(
|
35
|
+
output_path,
|
36
|
+
Machinery::CurrentUser.new.username
|
37
|
+
)
|
35
38
|
end
|
36
39
|
|
37
40
|
if tmp_image_dir.start_with?("/tmp/") && tmp_config_dir.start_with?("/tmp/")
|
@@ -40,7 +43,7 @@ class BuildTask
|
|
40
43
|
|
41
44
|
begin
|
42
45
|
with_c_locale do
|
43
|
-
LoggedCheetah.run(
|
46
|
+
Machinery::LoggedCheetah.run(
|
44
47
|
"sudo",
|
45
48
|
tmp_script.path,
|
46
49
|
stdout: $stdout,
|
@@ -68,7 +71,7 @@ class BuildTask
|
|
68
71
|
|
69
72
|
Machinery::Ui.warn "Cleaning up temporary files..."
|
70
73
|
[tmp_config_dir, tmp_image_dir].each do |path|
|
71
|
-
LoggedCheetah.run("sudo", "rm", "-r", path) if Dir.exist?(path)
|
74
|
+
Machinery::LoggedCheetah.run("sudo", "rm", "-r", path) if Dir.exist?(path)
|
72
75
|
end
|
73
76
|
end
|
74
77
|
raise
|
data/lib/cli.rb
CHANGED
@@ -15,672 +15,756 @@
|
|
15
15
|
# To contact SUSE about this file by physical or electronic mail,
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
module Machinery
|
19
|
+
class Cli
|
20
|
+
extend GLI::App
|
21
|
+
|
22
|
+
program_desc "A systems management toolkit for Linux"
|
23
|
+
preserve_argv(true)
|
24
|
+
@version = "#{Machinery::VERSION} (system description format version "\
|
25
|
+
"#{SystemDescription::CURRENT_FORMAT_VERSION})"
|
26
|
+
@config = Machinery::Config.new
|
27
|
+
switch :version, negatable: false, desc: "Show version"
|
28
|
+
switch :debug, negatable: false, desc: "Enable debug mode"
|
29
|
+
switch [:help, :h], negatable: false, desc: "Show help"
|
30
|
+
|
31
|
+
sort_help :manually
|
32
|
+
pre do |global_options, command, _options, args|
|
33
|
+
Machinery.logger.level = if global_options[:debug]
|
34
|
+
Logger::DEBUG
|
35
|
+
else
|
36
|
+
Logger::INFO
|
37
|
+
end
|
20
38
|
|
21
|
-
|
22
|
-
preserve_argv(true)
|
23
|
-
@version = Machinery::VERSION + " (system description format version " +
|
24
|
-
"#{SystemDescription::CURRENT_FORMAT_VERSION})"
|
25
|
-
@config = Machinery::Config.new
|
26
|
-
switch :version, negatable: false, desc: "Show version"
|
27
|
-
switch :debug, negatable: false, desc: "Enable debug mode"
|
28
|
-
switch [:help, :h], negatable: false, desc: "Show help"
|
39
|
+
validate_command_line(command.arguments, args)
|
29
40
|
|
30
|
-
|
31
|
-
pre do |global_options,command,options,args|
|
32
|
-
if global_options[:debug]
|
33
|
-
Machinery.logger.level = Logger::DEBUG
|
34
|
-
else
|
35
|
-
Machinery.logger.level = Logger::INFO
|
41
|
+
true
|
36
42
|
end
|
37
43
|
|
38
|
-
|
44
|
+
post do |global_options, command, _options, _args|
|
45
|
+
if (command.is_a?(GLI::Commands::Help) &&
|
46
|
+
!global_options[:version]) || ARGV == ["help"]
|
39
47
|
|
40
|
-
|
41
|
-
|
48
|
+
Machinery::Ui.puts "\nFor more detailed information, open the "\
|
49
|
+
"documentation by typing 'machinery man --html'.\nIf you are unable "\
|
50
|
+
"to find a solution within the man page visit our wiki page\nat "\
|
51
|
+
"https://github.com/SUSE/machinery/wiki"
|
42
52
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
53
|
+
Machinery::Ui.puts "\nMachinery can show hints which guide through a "\
|
54
|
+
"typical workflow."
|
55
|
+
if @config.hints
|
56
|
+
Machinery::Ui.puts "These hints can be switched off by " \
|
57
|
+
"'#{Ui::Hint.program_name} config hints=off'."
|
58
|
+
else
|
59
|
+
Machinery::Ui.puts "These hints can be switched on by " \
|
60
|
+
"'#{Ui::Hint.program_name} config hints=on'."
|
61
|
+
end
|
49
62
|
|
50
|
-
|
51
|
-
if @config.hints
|
52
|
-
Machinery::Ui.puts "These hints can be switched off by " \
|
53
|
-
"'#{Hint.program_name} config hints=off'."
|
54
|
-
else
|
55
|
-
Machinery::Ui.puts "These hints can be switched on by " \
|
56
|
-
"'#{Hint.program_name} config hints=on'."
|
63
|
+
Ui::Hint.print(:get_started)
|
57
64
|
end
|
58
|
-
|
59
|
-
Hint.print(:get_started)
|
60
|
-
end
|
61
|
-
Machinery::Ui.close_pager
|
62
|
-
end
|
63
|
-
|
64
|
-
GLI::Commands::Help.skips_post = false
|
65
|
-
|
66
|
-
def self.validate_command_line(defined, parsed)
|
67
|
-
if defined.any?(&:multiple?) && !defined.any?(&:optional?) && parsed.empty?
|
68
|
-
message = "No arguments given. Nothing to do."
|
69
|
-
raise GLI::BadCommandLine.new(message)
|
70
|
-
elsif !defined.any?(&:multiple?) && parsed.size > defined.size
|
71
|
-
parsed_arguments = "#{parsed.size} #{Machinery.pluralize(parsed.size, "argument")}"
|
72
|
-
defined_arguments = defined.empty? ? "none" : "only: #{defined.map(&:name).join(", ")}"
|
73
|
-
message = "Too many arguments: got #{parsed_arguments}, expected #{defined_arguments}"
|
74
|
-
raise GLI::BadCommandLine.new(message)
|
65
|
+
Machinery::Ui.close_pager
|
75
66
|
end
|
76
|
-
end
|
77
67
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
Machinery.logger.error(e.message)
|
94
|
-
Machinery::Ui.error e.message
|
95
|
-
exit 1
|
96
|
-
when SystemExit
|
97
|
-
raise
|
98
|
-
when SignalException
|
99
|
-
Machinery.logger.info "Machinery was aborted with signal #{e.signo}."
|
100
|
-
exit 1
|
101
|
-
when Errno::ENOSPC
|
102
|
-
Machinery::Ui.error("Error: " + e.message)
|
103
|
-
exit 1
|
104
|
-
else
|
105
|
-
if LocalSystem.os.canonical_name.include? ("SUSE Linux Enterprise")
|
106
|
-
Machinery::Ui.error "Machinery experienced an unexpected error.\n" \
|
107
|
-
"If this impacts your business please file a service request at " \
|
108
|
-
"https://www.suse.com/mysupport\n" \
|
109
|
-
"so that we can assist you on this issue. An active support contract is required.\n"
|
110
|
-
else
|
111
|
-
Machinery::Ui.error "Machinery experienced an unexpected error. Please file a " \
|
112
|
-
"bug report at: https://github.com/SUSE/machinery/issues/new\n"
|
113
|
-
end
|
114
|
-
|
115
|
-
if e.is_a?(Cheetah::ExecutionFailed)
|
116
|
-
result = ""
|
117
|
-
result << "#{e.message}\n"
|
118
|
-
result << "\n"
|
119
|
-
|
120
|
-
if e.stderr && !e.stderr.empty?
|
121
|
-
result << "Error output:\n"
|
122
|
-
result << "#{e.stderr}\n"
|
68
|
+
GLI::Commands::Help.skips_post = false
|
69
|
+
|
70
|
+
def self.validate_command_line(defined, parsed)
|
71
|
+
if defined.any?(&:multiple?) &&
|
72
|
+
!defined.any?(&:optional?) &&
|
73
|
+
parsed.empty?
|
74
|
+
message = "No arguments given. Nothing to do."
|
75
|
+
raise GLI::BadCommandLine.new(message)
|
76
|
+
elsif !defined.any?(&:multiple?) && parsed.size > defined.size
|
77
|
+
parsed_arguments = "#{parsed.size} "\
|
78
|
+
"#{Machinery.pluralize(parsed.size, "argument")}"
|
79
|
+
defined_arguments = if defined.empty?
|
80
|
+
"none"
|
81
|
+
else
|
82
|
+
"only: #{defined.map(&:name).join(", ")}"
|
123
83
|
end
|
84
|
+
message = "Too many arguments: got #{parsed_arguments}, "\
|
85
|
+
"expected #{defined_arguments}"
|
86
|
+
raise GLI::BadCommandLine.new(message)
|
87
|
+
end
|
88
|
+
end
|
124
89
|
|
125
|
-
|
126
|
-
|
127
|
-
result << "#{e.stdout}\n\n"
|
128
|
-
end
|
90
|
+
def self.handle_error(e)
|
91
|
+
Machinery::Ui.kill_pager
|
129
92
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
134
|
-
Machinery.logger.error(result)
|
135
|
-
Machinery::Ui.error result
|
93
|
+
case e
|
94
|
+
when GLI::MissingRequiredArgumentsException
|
95
|
+
Machinery::Ui.error("Option --" + e.message)
|
136
96
|
exit 1
|
137
|
-
|
138
|
-
|
97
|
+
when GLI::UnknownCommandArgument, GLI::UnknownGlobalArgument,
|
98
|
+
GLI::UnknownCommand, GLI::BadCommandLine,
|
99
|
+
OptionParser::MissingArgument, OptionParser::AmbiguousOption
|
100
|
+
Machinery::Ui.error e.to_s + "\n\n"
|
101
|
+
command = ARGV & @commands.keys.map(&:to_s)
|
102
|
+
Machinery::Ui.error "Run '#{Ui::Hint.program_name} #{command.first} "\
|
103
|
+
"--help' for more information."
|
104
|
+
exit 1
|
105
|
+
when Machinery::Errors::MachineryError
|
139
106
|
Machinery.logger.error(e.message)
|
140
|
-
Machinery.
|
107
|
+
Machinery::Ui.error e.message
|
108
|
+
exit 1
|
109
|
+
when SystemExit
|
141
110
|
raise
|
111
|
+
when SignalException
|
112
|
+
Machinery.logger.info "Machinery was aborted with signal #{e.signo}."
|
113
|
+
exit 1
|
114
|
+
when Errno::ENOSPC
|
115
|
+
Machinery::Ui.error("Error: " + e.message)
|
116
|
+
exit 1
|
117
|
+
else
|
118
|
+
if LocalSystem.os.canonical_name.include? "SUSE Linux Enterprise"
|
119
|
+
Machinery::Ui.error "Machinery experienced an unexpected error.\n" \
|
120
|
+
"If this impacts your business please file a service request at " \
|
121
|
+
"https://www.suse.com/mysupport\n" \
|
122
|
+
"so that we can assist you on this issue. An active support "\
|
123
|
+
"contract is required.\n"
|
124
|
+
else
|
125
|
+
Machinery::Ui.error "Machinery experienced an unexpected error. "\
|
126
|
+
"Please file a bug report at: "\
|
127
|
+
"https://github.com/SUSE/machinery/issues/new\n"
|
128
|
+
end
|
129
|
+
|
130
|
+
if e.is_a?(Cheetah::ExecutionFailed)
|
131
|
+
result = ""
|
132
|
+
result << "#{e.message}\n"
|
133
|
+
result << "\n"
|
134
|
+
|
135
|
+
if e.stderr && !e.stderr.empty?
|
136
|
+
result << "Error output:\n"
|
137
|
+
result << "#{e.stderr}\n"
|
138
|
+
end
|
139
|
+
|
140
|
+
if e.stdout && !e.stdout.empty?
|
141
|
+
result << "Standard output:\n"
|
142
|
+
result << "#{e.stdout}\n\n"
|
143
|
+
end
|
144
|
+
|
145
|
+
if e.backtrace && !e.backtrace.empty?
|
146
|
+
result << "Backtrace:\n"
|
147
|
+
result << "#{e.backtrace.join("\n")}\n\n"
|
148
|
+
end
|
149
|
+
Machinery.logger.error(result)
|
150
|
+
Machinery::Ui.error result
|
151
|
+
exit 1
|
152
|
+
else
|
153
|
+
Machinery.logger.error("Machinery experienced an unexpected error:")
|
154
|
+
Machinery.logger.error(e.message)
|
155
|
+
Machinery.logger.error(e.backtrace.join("\n"))
|
156
|
+
raise
|
157
|
+
end
|
142
158
|
end
|
159
|
+
true
|
143
160
|
end
|
144
|
-
true
|
145
|
-
end
|
146
161
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
" uses the image name as description name if the
|
152
|
-
" provided.\nIf the image name contains
|
153
|
-
"
|
154
|
-
"
|
155
|
-
|
162
|
+
def self.check_container_name!(image, name)
|
163
|
+
if image == name && !SystemDescription.valid_name?(name)
|
164
|
+
raise Machinery::Errors::InvalidCommandLine,
|
165
|
+
"Error: System description name '#{name}' is invalid. By default "\
|
166
|
+
"Machinery uses the image name as description name if the "\
|
167
|
+
"parameter `--name` is not provided.\nIf the image name contains "\
|
168
|
+
"a slash the `--name=NAME` parameter is mandatory. "\
|
169
|
+
"Valid characters are 'a-zA-Z0-9_:.-'.\n\nFor example run:\n" \
|
170
|
+
"#{Ui::Hint.program_name} #{ARGV.join(" ")} "\
|
171
|
+
"--name='#{image.tr("/", "_")}'"
|
172
|
+
end
|
156
173
|
end
|
157
|
-
end
|
158
174
|
|
159
|
-
|
160
|
-
|
161
|
-
|
175
|
+
on_error do |e|
|
176
|
+
Cli.handle_error(e)
|
177
|
+
end
|
162
178
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
179
|
+
def self.show_filter_note(scopes, filter)
|
180
|
+
if scopes.any? { |scope| !filter.element_filters_for_scope(scope).empty? }
|
181
|
+
Machinery::Ui.puts "\nNote: There are filters being applied during "\
|
182
|
+
"inspection. (Use `--verbose` option to show the filters)\n\n"
|
183
|
+
end
|
167
184
|
end
|
168
|
-
end
|
169
185
|
|
170
|
-
|
171
|
-
|
172
|
-
|
186
|
+
def self.shift_arg(args, name)
|
187
|
+
unless res = args.shift
|
188
|
+
raise GLI::BadCommandLine,
|
189
|
+
"You need to provide the required argument #{name}."
|
190
|
+
end
|
191
|
+
res
|
173
192
|
end
|
174
|
-
res
|
175
|
-
end
|
176
193
|
|
177
|
-
|
178
|
-
|
179
|
-
if exclude_scopes
|
194
|
+
def self.process_scope_option(scopes, exclude_scopes)
|
195
|
+
if scopes && exclude_scopes
|
180
196
|
# scope and ignore-scope
|
181
|
-
raise Machinery::Errors::InvalidCommandLine
|
182
|
-
"
|
183
|
-
|
197
|
+
raise Machinery::Errors::InvalidCommandLine,
|
198
|
+
"You cannot provide the --scope and "\
|
199
|
+
"--ignore-scope option at the same time."
|
200
|
+
elsif scopes && !exclude_scopes
|
184
201
|
# scope only
|
185
202
|
scope_list = parse_scopes(scopes)
|
186
|
-
|
187
|
-
else
|
188
|
-
if exclude_scopes
|
203
|
+
elsif !scopes && exclude_scopes
|
189
204
|
# ignore-scope only
|
190
205
|
scope_list = Inspector.all_scopes - parse_scopes(exclude_scopes)
|
191
|
-
|
206
|
+
elsif !scopes && !exclude_scopes
|
192
207
|
# neither scope nor ignore-scope
|
193
208
|
scope_list = Inspector.all_scopes
|
194
209
|
end
|
210
|
+
if scope_list.empty?
|
211
|
+
raise Machinery::Errors::InvalidCommandLine,
|
212
|
+
"No scopes to process. Nothing to do."
|
213
|
+
end
|
214
|
+
scope_list
|
195
215
|
end
|
196
|
-
if scope_list.empty?
|
197
|
-
raise Machinery::Errors::InvalidCommandLine.new( "No scopes to process. Nothing to do.")
|
198
|
-
end
|
199
|
-
scope_list
|
200
|
-
end
|
201
216
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
217
|
+
def self.parse_scopes(scope_string)
|
218
|
+
unknown_scopes = []
|
219
|
+
invalid_scopes = []
|
220
|
+
scopes = []
|
221
|
+
|
222
|
+
scope_string.split(",").each do |scope|
|
223
|
+
unless scope =~ /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/
|
224
|
+
invalid_scopes << scope
|
225
|
+
next
|
226
|
+
end
|
227
|
+
|
228
|
+
if scope == "config-files"
|
229
|
+
Machinery::Ui.warn(
|
230
|
+
"The scope name `config-files` is deprecated. "\
|
231
|
+
"The new name is `changed-config-files`."
|
232
|
+
)
|
233
|
+
scope = "changed-config-files"
|
234
|
+
end
|
235
|
+
|
236
|
+
# convert cli scope naming to internal one
|
237
|
+
scope.tr!("-", "_")
|
206
238
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
239
|
+
if Inspector.all_scopes.include?(scope) && Ui::Renderer.for(scope)
|
240
|
+
scopes << scope
|
241
|
+
else
|
242
|
+
unknown_scopes << scope
|
243
|
+
end
|
211
244
|
end
|
212
245
|
|
213
|
-
|
214
|
-
|
215
|
-
|
246
|
+
unless invalid_scopes.empty?
|
247
|
+
form = invalid_scopes.length > 1 ? "scopes are" : "scope is"
|
248
|
+
raise Machinery::Errors::UnknownScope.new(
|
249
|
+
"The following #{form} not valid:" \
|
250
|
+
" '#{invalid_scopes.join("', '")}'." \
|
251
|
+
" Scope names must start with a letter and contain only lowercase" \
|
252
|
+
" letters and digits separated by dashes ('-')."
|
216
253
|
)
|
217
|
-
scope = "changed-config-files"
|
218
254
|
end
|
219
255
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
256
|
+
unless unknown_scopes.empty?
|
257
|
+
form = unknown_scopes.length > 1 ? "scopes are" : "scope is"
|
258
|
+
raise Machinery::Errors::UnknownScope.new(
|
259
|
+
"The following #{form} not supported: " \
|
260
|
+
"#{Machinery::Ui.internal_scope_list_to_string(unknown_scopes)}. " \
|
261
|
+
"Valid scopes are: #{AVAILABLE_SCOPE_LIST}."
|
262
|
+
)
|
227
263
|
end
|
264
|
+
|
265
|
+
Inspector.sort_scopes(scopes.uniq)
|
228
266
|
end
|
229
267
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
"
|
235
|
-
|
236
|
-
" letters and digits separated by dashes ('-')."
|
237
|
-
)
|
268
|
+
def self.supports_filtering(command)
|
269
|
+
if @config.experimental_features
|
270
|
+
command.flag :exclude,
|
271
|
+
negatable: false,
|
272
|
+
desc: "Exclude elements matching the filter criteria"
|
273
|
+
end
|
238
274
|
end
|
239
275
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
"
|
245
|
-
|
246
|
-
|
276
|
+
def self.check_port_validity(port)
|
277
|
+
if port < 2 || port > 65_535
|
278
|
+
raise Machinery::Errors::ServerPortError,
|
279
|
+
"The specified port '#{port}' is not valid. " \
|
280
|
+
"A valid port can be in a range between 2 and 65535."
|
281
|
+
elsif port >= 2 && port <= 1_023 && !CurrentUser.new.is_root?
|
282
|
+
raise Machinery::Errors::ServerPortError,
|
283
|
+
"The specified port '#{port}' needs root privileges. "\
|
284
|
+
"Otherwise, the server cannot be started. All ports " \
|
285
|
+
"in a range of 2-1023 need root privileges."
|
286
|
+
end
|
247
287
|
end
|
248
288
|
|
249
|
-
|
250
|
-
|
289
|
+
AVAILABLE_SCOPE_LIST = Machinery::Ui.internal_scope_list_to_string(
|
290
|
+
Inspector.all_scopes
|
291
|
+
)
|
251
292
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
end
|
256
|
-
end
|
293
|
+
desc "Analyze system description"
|
294
|
+
long_desc <<-LONGDESC
|
295
|
+
Analyze stored system description.
|
257
296
|
|
258
|
-
|
259
|
-
if port < 2 || port > 65535
|
260
|
-
raise Machinery::Errors::ServerPortError.new("The specified port '#{port}' is not " \
|
261
|
-
"valid. A valid port can be in a range between 2 and 65535.")
|
262
|
-
else
|
263
|
-
if port >= 2 && port <= 1023 && !CurrentUser.new.is_root?
|
264
|
-
raise Machinery::Errors::ServerPortError.new("The specified port '#{port}' needs " \
|
265
|
-
"root privileges. Otherwise, the server cannot be started. All ports in a range " \
|
266
|
-
"of 2-1023 need root privileges.")
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|
297
|
+
The supported operations are:
|
270
298
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
Hint.print(:show_analyze_data, name: name)
|
298
|
-
else
|
299
|
-
raise Machinery::Errors::InvalidCommandLine.new(
|
300
|
-
"The operation '#{options[:operation]}' is not supported. " \
|
301
|
-
"Valid operations are: changed-config-files-diffs."
|
302
|
-
)
|
299
|
+
- changed-config-files-diffs: Generate diffs against the original
|
300
|
+
version from the package for the changed configuration files
|
301
|
+
LONGDESC
|
302
|
+
arg "NAME"
|
303
|
+
command "analyze" do |c|
|
304
|
+
c.flag [:operation, :o],
|
305
|
+
type: String,
|
306
|
+
required: false,
|
307
|
+
desc: "The analyze operation to perform",
|
308
|
+
arg_name: "OPERATION",
|
309
|
+
default_value: "changed-config-files-diffs"
|
310
|
+
|
311
|
+
c.action do |_global_options, options, args|
|
312
|
+
name = shift_arg(args, "NAME")
|
313
|
+
description = SystemDescription.load(name, system_description_store)
|
314
|
+
|
315
|
+
case options[:operation]
|
316
|
+
when "changed-config-files-diffs"
|
317
|
+
task = AnalyzeConfigFileDiffsTask.new
|
318
|
+
task.analyze(description)
|
319
|
+
Ui::Hint.print(:show_analyze_data, name: name)
|
320
|
+
else
|
321
|
+
raise Machinery::Errors::InvalidCommandLine,
|
322
|
+
"The operation '#{options[:operation]}' is not supported. " \
|
323
|
+
"Valid operations are: changed-config-files-diffs."
|
324
|
+
end
|
303
325
|
end
|
304
326
|
end
|
305
|
-
end
|
306
327
|
|
307
328
|
|
308
329
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
description, File.expand_path(options["image-dir"]),
|
330
|
-
enable_dhcp: options["enable-dhcp"], enable_ssh: options["enable-ssh"]
|
331
|
-
)
|
332
|
-
end
|
333
|
-
end
|
330
|
+
desc "Build image from system description"
|
331
|
+
long_desc <<-LONGDESC
|
332
|
+
Build image from a given system description and store it to the given
|
333
|
+
location.
|
334
|
+
LONGDESC
|
335
|
+
arg "NAME"
|
336
|
+
command "build" do |c|
|
337
|
+
c.flag ["image-dir", :i],
|
338
|
+
type: String,
|
339
|
+
required: true,
|
340
|
+
desc: "Store the image under the specified path",
|
341
|
+
arg_name: "DIRECTORY"
|
342
|
+
c.switch ["enable-dhcp", :d],
|
343
|
+
required: false,
|
344
|
+
negatable: false,
|
345
|
+
desc: "Enable DCHP client on first network card of built image"
|
346
|
+
c.switch ["enable-ssh", :s],
|
347
|
+
required: false,
|
348
|
+
negatable: false,
|
349
|
+
desc: "Enable SSH service in built image"
|
334
350
|
|
351
|
+
c.action do |_global_options, options, args|
|
352
|
+
name = shift_arg(args, "NAME")
|
353
|
+
description = SystemDescription.load(name, system_description_store)
|
335
354
|
|
355
|
+
task = BuildTask.new
|
356
|
+
task.build(
|
357
|
+
description,
|
358
|
+
File.expand_path(options["image-dir"]),
|
359
|
+
enable_dhcp: options["enable-dhcp"],
|
360
|
+
enable_ssh: options["enable-ssh"]
|
361
|
+
)
|
362
|
+
end
|
363
|
+
end
|
336
364
|
|
337
|
-
desc "Compare system descriptions"
|
338
|
-
long_desc <<-LONGDESC
|
339
|
-
Compare system descriptions stored under specified names.
|
340
365
|
|
341
|
-
Multiple scopes can be passed as comma-separated list. If no specific scopes
|
342
|
-
are given, all scopes are compared.
|
343
366
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
arg "NAME2"
|
348
|
-
command "compare" do |c|
|
349
|
-
c.flag [:scope, :s], type: String, required: false,
|
350
|
-
desc: "Compare specified scopes", arg_name: "SCOPE_LIST"
|
351
|
-
c.flag ["ignore-scope", :e], type: String, required: false,
|
352
|
-
desc: "Exclude specified scopes", arg_name: "SCOPE_LIST"
|
353
|
-
c.switch "show-all", required: false, negatable: false,
|
354
|
-
desc: "Show also common properties"
|
355
|
-
c.switch "html", required: false, negatable: false,
|
356
|
-
desc: "Open comparison in HTML format in your web browser."
|
357
|
-
c.switch "pager", required: false, default_value: true,
|
358
|
-
desc: "Pipe output into a pager"
|
367
|
+
desc "Compare system descriptions"
|
368
|
+
long_desc <<-LONGDESC
|
369
|
+
Compare system descriptions stored under specified names.
|
359
370
|
|
360
|
-
|
361
|
-
|
371
|
+
Multiple scopes can be passed as comma-separated list. If no
|
372
|
+
specific scopes are given, all scopes are compared.
|
362
373
|
|
363
|
-
|
364
|
-
|
374
|
+
Available scopes: #{AVAILABLE_SCOPE_LIST}
|
375
|
+
LONGDESC
|
376
|
+
arg "NAME1"
|
377
|
+
arg "NAME2"
|
378
|
+
command "compare" do |c|
|
379
|
+
c.flag [:scope, :s],
|
380
|
+
type: String,
|
381
|
+
required: false,
|
382
|
+
desc: "Compare specified scopes",
|
383
|
+
arg_name: "SCOPE_LIST"
|
384
|
+
c.flag ["ignore-scope", :e],
|
385
|
+
type: String,
|
386
|
+
required: false,
|
387
|
+
desc: "Exclude specified scopes",
|
388
|
+
arg_name: "SCOPE_LIST"
|
389
|
+
c.switch "show-all",
|
390
|
+
required: false,
|
391
|
+
negatable: false,
|
392
|
+
desc: "Show also common properties"
|
393
|
+
c.switch "html",
|
394
|
+
required: false,
|
395
|
+
negatable: false,
|
396
|
+
desc: "Open comparison in HTML format in your web browser."
|
397
|
+
c.switch "pager",
|
398
|
+
required: false,
|
399
|
+
default_value: true,
|
400
|
+
desc: "Pipe output into a pager"
|
365
401
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
402
|
+
c.action do |_global_options, options, args|
|
403
|
+
Machinery::Ui.use_pager = options[:pager]
|
404
|
+
|
405
|
+
name1 = shift_arg(args, "NAME1")
|
406
|
+
name2 = shift_arg(args, "NAME2")
|
407
|
+
|
408
|
+
if options[:html]
|
409
|
+
begin
|
410
|
+
check_port_validity(@config.http_server_port)
|
411
|
+
rescue Machinery::Errors::ServerPortError => e
|
412
|
+
raise Machinery::Errors::InvalidCommandLine,
|
413
|
+
"#{e.message} The port can be specified in the " \
|
414
|
+
"'http_server_port' section of the configuration file."
|
415
|
+
end
|
372
416
|
end
|
373
|
-
end
|
374
417
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
418
|
+
store = system_description_store
|
419
|
+
description1 = SystemDescription.load(name1, store)
|
420
|
+
description2 = SystemDescription.load(name2, store)
|
421
|
+
scope_list =
|
422
|
+
process_scope_option(options[:scope], options["ignore-scope"])
|
423
|
+
|
424
|
+
task = CompareTask.new
|
425
|
+
opts = {
|
426
|
+
show_html: options["html"],
|
427
|
+
show_all: options["show-all"],
|
428
|
+
ip: "127.0.0.1",
|
429
|
+
port: @config.http_server_port
|
430
|
+
}
|
431
|
+
task.compare(description1, description2, scope_list, opts)
|
432
|
+
end
|
388
433
|
end
|
389
|
-
end
|
390
434
|
|
391
435
|
|
392
436
|
|
393
|
-
|
394
|
-
|
395
|
-
|
437
|
+
desc "Copy system description"
|
438
|
+
long_desc <<-LONGDESC
|
439
|
+
Copy a system description.
|
396
440
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
441
|
+
The system description is copied and stored under the provided name.
|
442
|
+
LONGDESC
|
443
|
+
arg "FROM_NAME"
|
444
|
+
arg "TO_NAME"
|
445
|
+
command "copy" do |c|
|
446
|
+
c.action do |_global_options, _options, args|
|
447
|
+
from = shift_arg(args, "FROM_NAME")
|
448
|
+
to = shift_arg(args, "TO_NAME")
|
449
|
+
task = CopyTask.new
|
450
|
+
task.copy(system_description_store, from, to)
|
451
|
+
end
|
407
452
|
end
|
408
|
-
end
|
409
453
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
454
|
+
desc "Move system description"
|
455
|
+
long_desc <<-LONGDESC
|
456
|
+
Move a system description.
|
457
|
+
|
458
|
+
The system description name is changed to the provided name.
|
459
|
+
LONGDESC
|
460
|
+
arg "FROM_NAME"
|
461
|
+
arg "TO_NAME"
|
462
|
+
command "move" do |c|
|
463
|
+
c.action do |_global_options, _options, args|
|
464
|
+
from = shift_arg(args, "FROM_NAME")
|
465
|
+
to = shift_arg(args, "TO_NAME")
|
466
|
+
task = MoveTask.new
|
467
|
+
task.move(system_description_store, from, to)
|
468
|
+
end
|
424
469
|
end
|
425
|
-
end
|
426
470
|
|
427
471
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
472
|
+
desc "Deploy image to OpenStack cloud"
|
473
|
+
long_desc <<-LONGDESC
|
474
|
+
Deploy system description as image to OpenStack cloud.
|
475
|
+
|
476
|
+
The image will be deployed to the OpenStack cloud. If no --image-dir is
|
477
|
+
specified an image will be built from the description before deployment.
|
478
|
+
LONGDESC
|
479
|
+
arg "NAME"
|
480
|
+
command "deploy" do |c|
|
481
|
+
c.flag ["cloud-config", :c],
|
482
|
+
type: String,
|
483
|
+
required: true,
|
484
|
+
arg_name: "FILE",
|
485
|
+
desc: "Path to file where the cloud config (openrc.sh) is located"
|
486
|
+
c.flag ["image-dir", :i],
|
487
|
+
type: String,
|
488
|
+
required: false,
|
489
|
+
desc: "Directory where the image is located",
|
490
|
+
arg_name: "DIRECTORY"
|
491
|
+
c.switch [:insecure, :s],
|
492
|
+
required: false,
|
493
|
+
negatable: false,
|
494
|
+
desc: "Explicitly allow glanceclient to perform "\
|
495
|
+
"'insecure SSL' (https) requests."
|
496
|
+
c.flag ["cloud-image-name", :n],
|
497
|
+
type: String,
|
498
|
+
required: false,
|
499
|
+
desc: "Name of the image in the cloud",
|
500
|
+
arg_name: "NAME"
|
501
|
+
|
502
|
+
c.action do |_global_options, options, args|
|
503
|
+
name = shift_arg(args, "NAME")
|
504
|
+
description = SystemDescription.load(name, system_description_store)
|
505
|
+
|
506
|
+
task = DeployTask.new
|
507
|
+
opts = {
|
452
508
|
image_name: options[:cloud_image_name],
|
453
|
-
insecure:
|
454
|
-
|
455
|
-
|
456
|
-
|
509
|
+
insecure: options[:insecure]
|
510
|
+
}
|
511
|
+
if options["image-dir"]
|
512
|
+
opts[:image_dir] = File.expand_path(options["image-dir"])
|
513
|
+
end
|
514
|
+
task.deploy(
|
515
|
+
description,
|
516
|
+
File.expand_path(options["cloud-config"]),
|
517
|
+
opts
|
518
|
+
)
|
519
|
+
end
|
457
520
|
end
|
458
|
-
end
|
459
521
|
|
460
522
|
|
461
523
|
|
462
|
-
|
463
|
-
|
464
|
-
|
524
|
+
desc "Export system description as KIWI image description"
|
525
|
+
long_desc <<-LONGDESC
|
526
|
+
Export system description as KIWI image description.
|
465
527
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
528
|
+
The description will be placed in a subdirectory at the given location.
|
529
|
+
The image format in the description is 'vmx'.
|
530
|
+
LONGDESC
|
531
|
+
arg "NAME"
|
532
|
+
command "export-kiwi" do |c|
|
533
|
+
c.flag ["kiwi-dir", :k],
|
534
|
+
type: String,
|
535
|
+
required: true,
|
536
|
+
desc: "Location where the description will be stored",
|
537
|
+
arg_name: "DIRECTORY"
|
538
|
+
c.switch :force,
|
539
|
+
default_value: false,
|
540
|
+
required: false,
|
541
|
+
negatable: false,
|
542
|
+
desc: "Overwrite existing directory"
|
475
543
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
544
|
+
c.action do |_global_options, options, args|
|
545
|
+
name = shift_arg(args, "NAME")
|
546
|
+
description = SystemDescription.load(name, system_description_store)
|
547
|
+
exporter = KiwiConfig.new(description)
|
480
548
|
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
549
|
+
task = ExportTask.new(exporter)
|
550
|
+
task.export(
|
551
|
+
File.expand_path(options["kiwi-dir"]),
|
552
|
+
force: options[:force]
|
553
|
+
)
|
554
|
+
end
|
486
555
|
end
|
487
|
-
end
|
488
556
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
The profile will be placed in a subdirectory at the given location by the 'autoyast-dir'
|
494
|
-
option.
|
495
|
-
LONGDESC
|
496
|
-
arg "NAME"
|
497
|
-
command "export-autoyast" do |c|
|
498
|
-
c.flag ["autoyast-dir", :a], type: String, required: true,
|
499
|
-
desc: "Location where the autoyast profile will be stored", arg_name: "DIRECTORY"
|
500
|
-
c.switch :force, default_value: false, required: false, negatable: false,
|
501
|
-
desc: "Overwrite existing directory"
|
502
|
-
|
503
|
-
c.action do |_global_options, options, args|
|
504
|
-
name = shift_arg(args, "NAME")
|
505
|
-
description = SystemDescription.load(name, system_description_store)
|
506
|
-
exporter = Autoyast.new(description)
|
507
|
-
|
508
|
-
task = ExportTask.new(exporter)
|
509
|
-
task.export(
|
510
|
-
File.expand_path(options["autoyast-dir"]),
|
511
|
-
force: options[:force]
|
512
|
-
)
|
513
|
-
end
|
514
|
-
end
|
557
|
+
desc "Export system description as AutoYaST profile"
|
558
|
+
long_desc <<-LONGDESC
|
559
|
+
Export system description as AutoYaST profile
|
515
560
|
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
name = shift_arg(args, "NAME")
|
532
|
-
description = SystemDescription.load(name, system_description_store)
|
533
|
-
exporter = StaticHtml.new(description, options["html-dir"])
|
534
|
-
exporter.create_directory(options[:force])
|
535
|
-
exporter.write
|
536
|
-
end
|
537
|
-
end
|
561
|
+
The profile will be placed in a subdirectory at the given location by
|
562
|
+
the 'autoyast-dir' option.
|
563
|
+
LONGDESC
|
564
|
+
arg "NAME"
|
565
|
+
command "export-autoyast" do |c|
|
566
|
+
c.flag ["autoyast-dir", :a],
|
567
|
+
type: String,
|
568
|
+
required: true,
|
569
|
+
desc: "Location where the autoyast profile will be stored",
|
570
|
+
arg_name: "DIRECTORY"
|
571
|
+
c.switch :force,
|
572
|
+
default_value: false,
|
573
|
+
required: false,
|
574
|
+
negatable: false,
|
575
|
+
desc: "Overwrite existing directory"
|
538
576
|
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
desc: "Inspect specified scopes", arg_name: "SCOPE_LIST"
|
544
|
-
c.flag ["ignore-scope", :e], type: String, required: false,
|
545
|
-
desc: "Exclude specified scopes", arg_name: "SCOPE_LIST"
|
546
|
-
c.flag "skip-files", required: false, negatable: false,
|
547
|
-
desc: "Do not consider given files or directories during inspection. " \
|
548
|
-
"Either provide one file or directory name or a list of names separated by commas."
|
549
|
-
c.switch ["extract-files", :x], required: false, negatable: false,
|
550
|
-
desc: "Extract changed configuration files and unmanaged files from inspected system"
|
551
|
-
if @config.experimental_features
|
552
|
-
c.switch ["extract-metadata", :m], required: false, negatable: false,
|
553
|
-
desc: "Extract unmanaged files metadata without extracting the files."
|
554
|
-
end
|
555
|
-
c.switch "extract-changed-config-files", required: false, negatable: false,
|
556
|
-
desc: "Extract changed configuration files from inspected system"
|
557
|
-
c.switch "extract-unmanaged-files", required: false, negatable: false,
|
558
|
-
desc: "Extract unmanaged files from inspected system"
|
559
|
-
c.switch "extract-changed-managed-files", required: false, negatable: false,
|
560
|
-
desc: "Extract changed managed files from inspected system"
|
561
|
-
c.switch :show, required: false, negatable: false,
|
562
|
-
desc: "Print inspection result"
|
563
|
-
c.switch :verbose, required: false, negatable: false,
|
564
|
-
desc: "Display the filters which are used during inspection"
|
565
|
-
end
|
577
|
+
c.action do |_global_options, options, args|
|
578
|
+
name = shift_arg(args, "NAME")
|
579
|
+
description = SystemDescription.load(name, system_description_store)
|
580
|
+
exporter = Autoyast.new(description)
|
566
581
|
|
567
|
-
|
568
|
-
|
569
|
-
|
582
|
+
task = ExportTask.new(exporter)
|
583
|
+
task.export(
|
584
|
+
File.expand_path(options["autoyast-dir"]),
|
585
|
+
force: options[:force]
|
586
|
+
)
|
587
|
+
end
|
588
|
+
end
|
570
589
|
|
590
|
+
desc "Export static HTML view of a system description"
|
591
|
+
long_desc <<-LONGDESC
|
592
|
+
Export system description as HTML
|
571
593
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
594
|
+
The HTML view will be placed in a subdirectory at the given location by
|
595
|
+
the 'html-dir' option.
|
596
|
+
LONGDESC
|
597
|
+
arg "NAME"
|
598
|
+
command "export-html" do |c|
|
599
|
+
c.flag ["html-dir", :d],
|
600
|
+
type: String,
|
601
|
+
required: true,
|
602
|
+
desc: "Location where the HTML view will be stored",
|
603
|
+
arg_name: "DIRECTORY"
|
604
|
+
c.switch :force,
|
605
|
+
default_value: false,
|
606
|
+
required: false,
|
607
|
+
negatable: false,
|
608
|
+
desc: "Overwrite existing directory"
|
576
609
|
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
if options["extract-files"] || options["extract-changed-config-files"]
|
585
|
-
inspect_options[:extract_changed_changed_config_files] = true
|
586
|
-
end
|
587
|
-
if options["extract-files"] || options["extract-changed-managed-files"]
|
588
|
-
inspect_options[:extract_changed_managed_files] = true
|
589
|
-
end
|
590
|
-
if options["extract-files"] || options["extract-unmanaged-files"]
|
591
|
-
inspect_options[:extract_unmanaged_files] = true
|
610
|
+
c.action do |_global_options, options, args|
|
611
|
+
name = shift_arg(args, "NAME")
|
612
|
+
description = SystemDescription.load(name, system_description_store)
|
613
|
+
exporter = StaticHtml.new(description, options["html-dir"])
|
614
|
+
exporter.create_directory(options[:force])
|
615
|
+
exporter.write
|
616
|
+
end
|
592
617
|
end
|
593
|
-
|
594
|
-
|
618
|
+
|
619
|
+
def self.define_inspect_command_options(c)
|
620
|
+
c.flag [:name, :n],
|
621
|
+
type: String,
|
622
|
+
required: false,
|
623
|
+
arg_name: "NAME",
|
624
|
+
desc: "Store system description under the specified name"
|
625
|
+
c.flag [:scope, :s],
|
626
|
+
type: String,
|
627
|
+
required: false,
|
628
|
+
desc: "Inspect specified scopes",
|
629
|
+
arg_name: "SCOPE_LIST"
|
630
|
+
c.flag ["ignore-scope", :e],
|
631
|
+
type: String,
|
632
|
+
required: false,
|
633
|
+
desc: "Exclude specified scopes",
|
634
|
+
arg_name: "SCOPE_LIST"
|
635
|
+
c.flag "skip-files",
|
636
|
+
required: false,
|
637
|
+
negatable: false,
|
638
|
+
desc: "Do not consider given files or directories during " \
|
639
|
+
"inspection. Either provide one file or directory name "\
|
640
|
+
"or a list of names separated by commas."
|
641
|
+
c.switch ["extract-files", :x],
|
642
|
+
required: false,
|
643
|
+
negatable: false,
|
644
|
+
desc: "Extract changed configuration files and unmanaged "\
|
645
|
+
"files from inspected system"
|
646
|
+
if @config.experimental_features
|
647
|
+
c.switch ["extract-metadata", :m],
|
648
|
+
required: false,
|
649
|
+
negatable: false,
|
650
|
+
desc: "Extract unmanaged files metadata without "\
|
651
|
+
"extracting the files."
|
652
|
+
end
|
653
|
+
c.switch "extract-changed-config-files",
|
654
|
+
required: false,
|
655
|
+
negatable: false,
|
656
|
+
desc: "Extract changed configuration files from inspected system"
|
657
|
+
c.switch "extract-unmanaged-files",
|
658
|
+
required: false,
|
659
|
+
negatable: false,
|
660
|
+
desc: "Extract unmanaged files from inspected system"
|
661
|
+
c.switch "extract-changed-managed-files",
|
662
|
+
required: false,
|
663
|
+
negatable: false,
|
664
|
+
desc: "Extract changed managed files from inspected system"
|
665
|
+
c.switch :show,
|
666
|
+
required: false,
|
667
|
+
negatable: false,
|
668
|
+
desc: "Print inspection result"
|
669
|
+
c.switch :verbose,
|
670
|
+
required: false,
|
671
|
+
negatable: false,
|
672
|
+
desc: "Display the filters which are used during inspection"
|
595
673
|
end
|
596
674
|
|
597
|
-
|
675
|
+
def self.parse_inspect_command_options(host, options)
|
676
|
+
scope_list = process_scope_option(
|
677
|
+
options[:scope],
|
678
|
+
options["ignore-scope"]
|
679
|
+
)
|
680
|
+
name = options[:name] || host
|
598
681
|
|
599
|
-
if options["verbose"] && !filter.empty?
|
600
|
-
Machinery::Ui.puts "\nThe following filters are applied during inspection:"
|
601
|
-
Machinery::Ui.puts filter.to_array.join("\n") + "\n\n"
|
602
|
-
else
|
603
|
-
show_filter_note(scope_list, filter)
|
604
|
-
end
|
605
682
|
|
606
|
-
|
607
|
-
|
683
|
+
unless scope_list.empty?
|
684
|
+
inspected_scopes =
|
685
|
+
" for #{Machinery::Ui.internal_scope_list_to_string(scope_list)}"
|
686
|
+
end
|
687
|
+
Machinery::Ui.puts "Inspecting #{host}#{inspected_scopes}..."
|
608
688
|
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
c.flag ["ssh-identity-file", :i], type: String, required: false,
|
629
|
-
desc: "The private SSH key location what can be used to authenticate with the remote system.",
|
630
|
-
arg_name: "SSH-KEY"
|
631
|
-
|
632
|
-
c.action do |_global_options, options, args|
|
633
|
-
host = shift_arg(args, "HOSTNAME")
|
634
|
-
system = System.for(host, remote_user: options["remote-user"], ssh_port: options["ssh-port"],
|
635
|
-
ssh_identity_file: options["ssh-identity-file"])
|
636
|
-
inspector_task = InspectTask.new
|
637
|
-
|
638
|
-
name, scope_list, inspect_options, filter = parse_inspect_command_options(host, options)
|
639
|
-
|
640
|
-
inspector_task.inspect_system(
|
641
|
-
system_description_store,
|
642
|
-
system,
|
643
|
-
name,
|
644
|
-
CurrentUser.new,
|
645
|
-
scope_list,
|
646
|
-
filter,
|
647
|
-
inspect_options
|
648
|
-
)
|
689
|
+
inspect_options = {}
|
690
|
+
if options["show"]
|
691
|
+
inspect_options[:show] = true
|
692
|
+
end
|
693
|
+
if options["verbose"]
|
694
|
+
inspect_options[:verbose] = true
|
695
|
+
end
|
696
|
+
if options["extract-files"] || options["extract-changed-config-files"]
|
697
|
+
inspect_options[:extract_changed_changed_config_files] = true
|
698
|
+
end
|
699
|
+
if options["extract-files"] || options["extract-changed-managed-files"]
|
700
|
+
inspect_options[:extract_changed_managed_files] = true
|
701
|
+
end
|
702
|
+
if options["extract-files"] || options["extract-unmanaged-files"]
|
703
|
+
inspect_options[:extract_unmanaged_files] = true
|
704
|
+
end
|
705
|
+
if options["extract-metadata"]
|
706
|
+
inspect_options[:extract_metadata] = true
|
707
|
+
end
|
649
708
|
|
650
|
-
|
709
|
+
filter = FilterOptionParser.parse("inspect", options)
|
651
710
|
|
652
|
-
if
|
653
|
-
|
711
|
+
if options["verbose"] && !filter.empty?
|
712
|
+
Machinery::Ui.puts "\nThe following filters are applied "\
|
713
|
+
"during inspection:"
|
714
|
+
Machinery::Ui.puts filter.to_array.join("\n") + "\n\n"
|
715
|
+
else
|
716
|
+
show_filter_note(scope_list, filter)
|
654
717
|
end
|
655
|
-
end
|
656
|
-
end
|
657
718
|
|
658
|
-
|
659
|
-
|
660
|
-
Inspect container image and generate system descripton from inspected data.
|
661
|
-
Right now we only support docker images.
|
719
|
+
[name, scope_list, inspect_options, filter]
|
720
|
+
end
|
662
721
|
|
663
|
-
|
664
|
-
|
722
|
+
desc "Inspect running system"
|
723
|
+
long_desc <<-LONGDESC
|
724
|
+
Inspect running system and generate system descripton from inspected data.
|
665
725
|
|
666
|
-
|
667
|
-
|
668
|
-
arg "(IMAGENAME|IMAGEID)"
|
669
|
-
command "inspect-container" do |c|
|
670
|
-
supports_filtering(c)
|
671
|
-
define_inspect_command_options(c)
|
726
|
+
Multiple scopes can be passed as comma-separated list. If no specific
|
727
|
+
scopes are given, all scopes are inspected.
|
672
728
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
729
|
+
Available scopes: #{AVAILABLE_SCOPE_LIST}
|
730
|
+
LONGDESC
|
731
|
+
arg "HOSTNAME"
|
732
|
+
command "inspect" do |c|
|
733
|
+
supports_filtering(c)
|
734
|
+
define_inspect_command_options(c)
|
735
|
+
c.flag ["remote-user", :r],
|
736
|
+
type: String,
|
737
|
+
required: false,
|
738
|
+
default_value: @config.remote_user,
|
739
|
+
desc: "Defines the user which is used to access the "\
|
740
|
+
"inspected system via SSH. This user needs sudo "\
|
741
|
+
"access on the remote machine or be root.",
|
742
|
+
arg_name: "USER"
|
743
|
+
c.flag ["ssh-port", :p],
|
744
|
+
type: Integer,
|
745
|
+
required: false,
|
746
|
+
desc: "The SSH port of the remote server.",
|
747
|
+
arg_name: "SSH-PORT"
|
748
|
+
c.flag ["ssh-identity-file", :i],
|
749
|
+
type: String,
|
750
|
+
required: false,
|
751
|
+
desc: "The private SSH key location what can be used to "\
|
752
|
+
"authenticate with the remote system.",
|
753
|
+
arg_name: "SSH-KEY"
|
677
754
|
|
678
|
-
|
755
|
+
c.action do |_global_options, options, args|
|
756
|
+
host = shift_arg(args, "HOSTNAME")
|
757
|
+
system = System.for(
|
758
|
+
host,
|
759
|
+
remote_user: options["remote-user"],
|
760
|
+
ssh_port: options["ssh-port"],
|
761
|
+
ssh_identity_file: options["ssh-identity-file"]
|
762
|
+
)
|
763
|
+
inspector_task = InspectTask.new
|
679
764
|
|
680
|
-
|
765
|
+
name, scope_list, inspect_options, filter =
|
766
|
+
parse_inspect_command_options(host, options)
|
681
767
|
|
682
|
-
begin
|
683
|
-
system.start
|
684
768
|
inspector_task.inspect_system(
|
685
769
|
system_description_store,
|
686
770
|
system,
|
@@ -690,317 +774,437 @@ class Cli
|
|
690
774
|
filter,
|
691
775
|
inspect_options
|
692
776
|
)
|
693
|
-
ensure
|
694
|
-
system.stop
|
695
|
-
end
|
696
777
|
|
697
|
-
|
778
|
+
Ui::Hint.print(:show_data, name: name) if options[:show] == false
|
698
779
|
|
699
|
-
|
700
|
-
|
780
|
+
if !options["extract-files"] ||
|
781
|
+
Inspector.all_scopes.count != scope_list.count
|
782
|
+
Ui::Hint.print(:do_complete_inspection, name: name, host: host)
|
783
|
+
end
|
701
784
|
end
|
702
785
|
end
|
703
|
-
end
|
704
786
|
|
705
|
-
|
706
|
-
|
707
|
-
|
787
|
+
desc "Inspect container image"
|
788
|
+
long_desc <<-LONGDESC
|
789
|
+
Inspect container image and generate system descripton from inspected data.
|
790
|
+
Right now we only support docker images.
|
791
|
+
|
792
|
+
Multiple scopes can be passed as comma-separated list. If no specific scopes
|
793
|
+
are given, all scopes are inspected.
|
708
794
|
|
709
|
-
|
795
|
+
Available scopes: #{AVAILABLE_SCOPE_LIST}
|
796
|
+
LONGDESC
|
797
|
+
arg "(IMAGENAME|IMAGEID)"
|
798
|
+
command "inspect-container" do |c|
|
799
|
+
supports_filtering(c)
|
800
|
+
define_inspect_command_options(c)
|
801
|
+
|
802
|
+
c.action do |_global_options, options, args|
|
803
|
+
image = shift_arg(args, "(IMAGENAME|IMAGEID)")
|
804
|
+
system = DockerSystem.new(image)
|
805
|
+
inspector_task = InspectTask.new
|
710
806
|
|
711
|
-
|
712
|
-
|
713
|
-
LONGDESC
|
714
|
-
arg "NAME", [:multiple, :optional]
|
807
|
+
name, scope_list, inspect_options, filter =
|
808
|
+
parse_inspect_command_options(image, options)
|
715
809
|
|
716
|
-
|
717
|
-
c.switch :verbose, required: false, negatable: false,
|
718
|
-
desc: "Display additional information about origin of scopes"
|
719
|
-
c.switch :short, required: false, negatable: false,
|
720
|
-
desc: "List only description names"
|
721
|
-
c.switch :html, required: false, negatable: false,
|
722
|
-
desc: "Open overview of all system descriptions in HTML format in your web browser"
|
810
|
+
check_container_name!(image, name)
|
723
811
|
|
724
|
-
c.action do |global_options,options,args|
|
725
|
-
if options[:html]
|
726
812
|
begin
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
813
|
+
system.start
|
814
|
+
inspector_task.inspect_system(
|
815
|
+
system_description_store,
|
816
|
+
system,
|
817
|
+
name,
|
818
|
+
CurrentUser.new,
|
819
|
+
scope_list,
|
820
|
+
filter,
|
821
|
+
inspect_options
|
822
|
+
)
|
823
|
+
ensure
|
824
|
+
system.stop
|
731
825
|
end
|
732
|
-
end
|
733
826
|
|
734
|
-
|
735
|
-
ip: "127.0.0.1",
|
736
|
-
port: @config.http_server_port
|
737
|
-
}.merge(options)
|
827
|
+
Ui::Hint.print(:show_data, name: name) unless options[:show]
|
738
828
|
|
739
|
-
|
740
|
-
|
829
|
+
if !options["extract-files"] ||
|
830
|
+
Inspector.all_scopes.count != scope_list.count
|
831
|
+
Ui::Hint.print(
|
832
|
+
:do_complete_inspection,
|
833
|
+
name: name,
|
834
|
+
docker_container: image
|
835
|
+
)
|
836
|
+
end
|
837
|
+
end
|
741
838
|
end
|
742
|
-
end
|
743
839
|
|
744
|
-
|
745
|
-
|
746
|
-
|
840
|
+
desc "List system descriptions"
|
841
|
+
long_desc <<-LONGDESC
|
842
|
+
List all system descriptions and their stored scopes, when no NAME
|
843
|
+
parameter is specified.
|
747
844
|
|
748
|
-
|
749
|
-
|
750
|
-
c.switch "html", required: false, negatable: false,
|
751
|
-
desc: "Open documentation in HTML format in your web browser."
|
845
|
+
List only the specified system descriptions and its stored scopes, when
|
846
|
+
NAME parameter is given.
|
752
847
|
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
848
|
+
The date of modification for each scope can be shown with the verbose
|
849
|
+
option.
|
850
|
+
LONGDESC
|
851
|
+
arg "NAME", [:multiple, :optional]
|
852
|
+
|
853
|
+
command "list" do |c|
|
854
|
+
c.switch :verbose,
|
855
|
+
required: false,
|
856
|
+
negatable: false,
|
857
|
+
desc: "Display additional information about origin of scopes"
|
858
|
+
c.switch :short,
|
859
|
+
required: false,
|
860
|
+
negatable: false,
|
861
|
+
desc: "List only description names"
|
862
|
+
c.switch :html,
|
863
|
+
required: false,
|
864
|
+
negatable: false,
|
865
|
+
desc: "Open overview of all system descriptions in "\
|
866
|
+
"HTML format in your web browser"
|
758
867
|
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
868
|
+
c.action do |_global_options, options, args|
|
869
|
+
if options[:html]
|
870
|
+
begin
|
871
|
+
check_port_validity(@config.http_server_port)
|
872
|
+
rescue Machinery::Errors::ServerPortError => e
|
873
|
+
raise Machinery::Errors::InvalidCommandLine,
|
874
|
+
"#{e.message} The port can be specified in the "\
|
875
|
+
"'http_server_port' section of the configuration file."
|
876
|
+
end
|
877
|
+
end
|
763
878
|
|
879
|
+
opts = {
|
880
|
+
ip: "127.0.0.1",
|
881
|
+
port: @config.http_server_port
|
882
|
+
}.merge(options)
|
764
883
|
|
884
|
+
task = ListTask.new
|
885
|
+
task.list(system_description_store, args, opts)
|
886
|
+
end
|
887
|
+
end
|
765
888
|
|
766
|
-
|
767
|
-
|
768
|
-
|
889
|
+
desc "Shows man page"
|
890
|
+
long_desc <<-LONGDESC
|
891
|
+
Shows the man page of the machinery tool.
|
769
892
|
|
770
|
-
|
771
|
-
|
772
|
-
|
893
|
+
LONGDESC
|
894
|
+
command "man" do |c|
|
895
|
+
c.switch "html",
|
896
|
+
required: false,
|
897
|
+
negatable: false,
|
898
|
+
desc: "Open documentation in HTML format in your web browser."
|
899
|
+
|
900
|
+
c.action do |_global_options, options, _args|
|
901
|
+
options = {
|
902
|
+
ip: "127.0.0.1",
|
903
|
+
port: @config.http_server_port
|
904
|
+
}.merge(options)
|
905
|
+
|
906
|
+
task = ManTask.new
|
907
|
+
task.man(options)
|
908
|
+
end
|
909
|
+
end
|
773
910
|
|
774
|
-
command "remove" do |c|
|
775
|
-
c.switch :all, negatable: false,
|
776
|
-
desc: "Remove all system descriptions"
|
777
|
-
c.switch :verbose, required: false, negatable: false,
|
778
|
-
desc: "Explain what is being done"
|
779
911
|
|
780
|
-
c.action do |global_options,options,args|
|
781
|
-
if !options[:all] && args.empty?
|
782
|
-
raise GLI::BadCommandLine.new, "You need to either specify `--all` or a list of system descriptions"
|
783
|
-
end
|
784
912
|
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
end
|
913
|
+
desc "Remove system descriptions"
|
914
|
+
long_desc <<-LONGDESC
|
915
|
+
Removes all specified descriptions stored under the specified names.
|
789
916
|
|
917
|
+
The success of a removal can be shown with the verbose option.
|
918
|
+
LONGDESC
|
919
|
+
arg "NAME", [:multiple, :optional]
|
790
920
|
|
921
|
+
command "remove" do |c|
|
922
|
+
c.switch :all,
|
923
|
+
negatable: false,
|
924
|
+
desc: "Remove all system descriptions"
|
925
|
+
c.switch :verbose,
|
926
|
+
required: false,
|
927
|
+
negatable: false,
|
928
|
+
desc: "Explain what is being done"
|
791
929
|
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
are given, all scopes are shown.
|
798
|
-
|
799
|
-
Available scopes: #{AVAILABLE_SCOPE_LIST}
|
800
|
-
LONGDESC
|
801
|
-
arg "NAME"
|
802
|
-
command "show" do |c|
|
803
|
-
supports_filtering(c)
|
804
|
-
c.flag [:scope, :s], type: String, required: false,
|
805
|
-
desc: "Show specified scopes", arg_name: "SCOPE_LIST"
|
806
|
-
c.flag ["ignore-scope", :e], type: String, required: false,
|
807
|
-
desc: "Exclude specified scopes", arg_name: "SCOPE_LIST"
|
808
|
-
c.switch "pager", required: false, default_value: true,
|
809
|
-
desc: "Pipe output into a pager"
|
810
|
-
c.switch "show-diffs", required: false, negatable: false,
|
811
|
-
desc: "Show diffs of changed configuration files changes."
|
812
|
-
c.switch "html", required: false, negatable: false,
|
813
|
-
desc: "Open system description in HTML format in your web browser."
|
814
|
-
c.switch "verbose", required: false, negatable: false,
|
815
|
-
desc: "Show the filters that were applied before showing the description."
|
816
|
-
|
817
|
-
c.action do |global_options,options,args|
|
818
|
-
Machinery::Ui.use_pager = options["pager"]
|
819
|
-
|
820
|
-
if options[:html]
|
821
|
-
begin
|
822
|
-
check_port_validity(@config.http_server_port)
|
823
|
-
rescue Machinery::Errors::ServerPortError => e
|
824
|
-
raise Machinery::Errors::InvalidCommandLine.new(e.message + " The port can be " \
|
825
|
-
"specified in the 'http_server_port' section of the configuration file.")
|
930
|
+
c.action do |_global_options, options, args|
|
931
|
+
if !options[:all] && args.empty?
|
932
|
+
raise GLI::BadCommandLine,
|
933
|
+
"You need to either specify `--all` or a list of "\
|
934
|
+
"system descriptions"
|
826
935
|
end
|
827
|
-
end
|
828
936
|
|
829
|
-
|
830
|
-
|
831
|
-
|
937
|
+
task = RemoveTask.new
|
938
|
+
task.remove(
|
939
|
+
system_description_store,
|
940
|
+
args,
|
941
|
+
verbose: options[:verbose],
|
942
|
+
all: options[:all]
|
943
|
+
)
|
832
944
|
end
|
945
|
+
end
|
833
946
|
|
834
|
-
description = SystemDescription.load(name, system_description_store)
|
835
|
-
scope_list = process_scope_option(options[:scope], options["ignore-scope"])
|
836
947
|
|
837
|
-
filter = FilterOptionParser.parse("show", options)
|
838
948
|
|
839
|
-
|
949
|
+
desc "Show system description"
|
950
|
+
long_desc <<-LONGDESC
|
951
|
+
Show system description stored under the specified name.
|
840
952
|
|
841
|
-
|
842
|
-
|
953
|
+
Multiple scopes can be passed as comma-separated list. If no specific
|
954
|
+
scopes are given, all scopes are shown.
|
843
955
|
|
844
|
-
|
845
|
-
|
846
|
-
|
956
|
+
Available scopes: #{AVAILABLE_SCOPE_LIST}
|
957
|
+
LONGDESC
|
958
|
+
arg "NAME"
|
959
|
+
command "show" do |c|
|
960
|
+
supports_filtering(c)
|
961
|
+
c.flag [:scope, :s],
|
962
|
+
type: String,
|
963
|
+
required: false,
|
964
|
+
desc: "Show specified scopes",
|
965
|
+
arg_name: "SCOPE_LIST"
|
966
|
+
c.flag ["ignore-scope", :e],
|
967
|
+
type: String,
|
968
|
+
required: false,
|
969
|
+
desc: "Exclude specified scopes",
|
970
|
+
arg_name: "SCOPE_LIST"
|
971
|
+
c.switch "pager",
|
972
|
+
required: false,
|
973
|
+
default_value: true,
|
974
|
+
desc: "Pipe output into a pager"
|
975
|
+
c.switch "show-diffs",
|
976
|
+
required: false,
|
977
|
+
negatable: false,
|
978
|
+
desc: "Show diffs of changed configuration files changes."
|
979
|
+
c.switch "html",
|
980
|
+
required: false,
|
981
|
+
negatable: false,
|
982
|
+
desc: "Open system description in HTML format in your web browser."
|
983
|
+
c.switch "verbose",
|
984
|
+
required: false,
|
985
|
+
negatable: false,
|
986
|
+
desc: "Show the filters that were applied before "\
|
987
|
+
"showing the description."
|
847
988
|
|
848
|
-
|
849
|
-
|
850
|
-
|
989
|
+
c.action do |_global_options, options, args|
|
990
|
+
Machinery::Ui.use_pager = options["pager"]
|
991
|
+
|
992
|
+
if options[:html]
|
993
|
+
begin
|
994
|
+
check_port_validity(@config.http_server_port)
|
995
|
+
rescue Machinery::Errors::ServerPortError => e
|
996
|
+
raise Machinery::Errors::InvalidCommandLine,
|
997
|
+
"#{e.message} The port can be specified in the "\
|
998
|
+
"'http_server_port' section of the configuration file."
|
999
|
+
end
|
851
1000
|
end
|
852
1001
|
|
853
|
-
|
854
|
-
|
855
|
-
|
1002
|
+
name = shift_arg(args, "NAME")
|
1003
|
+
if name == "localhost" && !CurrentUser.new.is_root?
|
1004
|
+
Machinery::Ui.puts "You need root rights to access the system "\
|
1005
|
+
"description of your locally inspected system."
|
856
1006
|
end
|
857
1007
|
|
858
|
-
|
1008
|
+
description = SystemDescription.load(name, system_description_store)
|
1009
|
+
scope_list = process_scope_option(
|
1010
|
+
options[:scope],
|
1011
|
+
options["ignore-scope"]
|
1012
|
+
)
|
1013
|
+
|
1014
|
+
filter = FilterOptionParser.parse("show", options)
|
1015
|
+
|
1016
|
+
inspected_filters = description.filter_definitions("inspect")
|
1017
|
+
|
1018
|
+
if options[:verbose]
|
1019
|
+
details = ""
|
1020
|
+
|
1021
|
+
if description[:environment].system_type == "docker"
|
1022
|
+
details += "\n Type of inspected container: "\
|
1023
|
+
"#{description[:environment].system_type}\n"
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
unless inspected_filters.empty?
|
1027
|
+
details += "\n The following filters were applied during "\
|
1028
|
+
"inspection:\n * #{inspected_filters.join("\n * ")}\n\n"
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
unless filter.empty?
|
1032
|
+
details += "\n The following filters were applied before "\
|
1033
|
+
"showing the description:"
|
1034
|
+
details += "\n * " + filter.to_array.join("\n * ") + "\n\n"
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
unless details.empty?
|
1038
|
+
Machinery::Ui.puts "# Inspection details\n#{details}"
|
1039
|
+
end
|
1040
|
+
end
|
1041
|
+
task = ShowTask.new
|
1042
|
+
opts = {
|
1043
|
+
show_diffs: options["show-diffs"],
|
1044
|
+
show_html: options["html"],
|
1045
|
+
ip: "127.0.0.1",
|
1046
|
+
port: @config.http_server_port
|
1047
|
+
}
|
1048
|
+
task.show(description, scope_list, filter, opts)
|
859
1049
|
end
|
860
|
-
task = ShowTask.new
|
861
|
-
opts = {
|
862
|
-
show_diffs: options["show-diffs"],
|
863
|
-
show_html: options["html"],
|
864
|
-
ip: "127.0.0.1",
|
865
|
-
port: @config.http_server_port
|
866
|
-
}
|
867
|
-
task.show(description, scope_list, filter, opts)
|
868
1050
|
end
|
869
|
-
end
|
870
1051
|
|
871
1052
|
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
1053
|
+
desc "Validate system description"
|
1054
|
+
long_desc <<-LONGDESC
|
1055
|
+
Validate system description stored under the specified name.
|
1056
|
+
LONGDESC
|
1057
|
+
arg "NAME"
|
1058
|
+
command "validate" do |c|
|
1059
|
+
c.action do |_global_options, _options, args|
|
1060
|
+
name = shift_arg(args, "NAME")
|
1061
|
+
if name == "localhost" && !CurrentUser.new.is_root?
|
1062
|
+
Machinery::Ui.puts "You need root rights to access the system "\
|
1063
|
+
"description of your locally inspected system."
|
1064
|
+
end
|
883
1065
|
|
884
|
-
|
885
|
-
|
1066
|
+
task = ValidateTask.new
|
1067
|
+
task.validate(system_description_store, name)
|
1068
|
+
end
|
886
1069
|
end
|
887
|
-
end
|
888
1070
|
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
name,
|
907
|
-
|
908
|
-
|
909
|
-
|
1071
|
+
desc "Upgrade format of system description"
|
1072
|
+
long_desc <<-LONGDESC
|
1073
|
+
Upgrade the format of one or all system descriptions.
|
1074
|
+
LONGDESC
|
1075
|
+
arg "NAME"
|
1076
|
+
command "upgrade-format" do |c|
|
1077
|
+
c.switch :all,
|
1078
|
+
negatable: false,
|
1079
|
+
desc: "Upgrade all system descriptions"
|
1080
|
+
c.switch :force,
|
1081
|
+
default_value: false,
|
1082
|
+
required: false,
|
1083
|
+
negatable: false,
|
1084
|
+
desc: "Keep backup after migration and ignore "\
|
1085
|
+
"validation errors"
|
1086
|
+
|
1087
|
+
c.action do |_global_options, options, args|
|
1088
|
+
name = shift_arg(args, "NAME") unless options[:all]
|
1089
|
+
|
1090
|
+
task = UpgradeFormatTask.new
|
1091
|
+
task.upgrade(
|
1092
|
+
system_description_store,
|
1093
|
+
name,
|
1094
|
+
all: options[:all],
|
1095
|
+
force: options[:force]
|
1096
|
+
)
|
1097
|
+
end
|
910
1098
|
end
|
911
|
-
end
|
912
1099
|
|
913
|
-
|
914
|
-
|
915
|
-
|
1100
|
+
desc "Show or change machinery's configuration"
|
1101
|
+
long_desc <<-LONGDESC
|
1102
|
+
Show or change machinery's configuration.
|
916
1103
|
|
917
|
-
|
918
|
-
|
1104
|
+
The value of a key is shown when no value argument is passed.
|
1105
|
+
If neither the key argument nor the value argument are specified a list
|
1106
|
+
of all keys and their values are shown.
|
919
1107
|
|
920
|
-
|
1108
|
+
ALTERNATIVE SYNOPSIS:
|
921
1109
|
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
1110
|
+
machinery [global options] config [KEY][=VALUE]
|
1111
|
+
LONGDESC
|
1112
|
+
arg "KEY", :optional
|
1113
|
+
arg "VALUE", :optional
|
1114
|
+
command "config" do |c|
|
1115
|
+
c.action do |_global_options, _options, args|
|
1116
|
+
if args[0] && args[0].include?("=")
|
1117
|
+
if args[1]
|
1118
|
+
raise GLI::BadCommandLine,
|
1119
|
+
"Too many arguments: got 2 arguments, expected only: KEY=VALUE"
|
1120
|
+
else
|
1121
|
+
key, value = args[0].split("=")
|
1122
|
+
end
|
931
1123
|
else
|
932
|
-
key
|
1124
|
+
key = args[0]
|
1125
|
+
value = args[1]
|
933
1126
|
end
|
934
|
-
else
|
935
|
-
key = args[0]
|
936
|
-
value = args[1]
|
937
|
-
end
|
938
1127
|
|
939
|
-
|
940
|
-
|
1128
|
+
task = ConfigTask.new(@config)
|
1129
|
+
task.config(key, value)
|
941
1130
|
|
942
|
-
|
943
|
-
|
944
|
-
|
1131
|
+
if key == "hints" && (value == "false" || value == "off")
|
1132
|
+
Machinery::Ui.puts "Hints can be switched on again by " \
|
1133
|
+
"'#{Ui::Hint.program_name} config hints=on'."
|
1134
|
+
end
|
945
1135
|
end
|
946
1136
|
end
|
947
|
-
end
|
948
|
-
|
949
|
-
desc "Start a web server for viewing system descriptions"
|
950
|
-
long_desc <<-LONGDESC
|
951
|
-
Starts a web server which serves an HTML view of all system descriptions and
|
952
|
-
an overview page listing all descriptions.
|
953
|
-
LONGDESC
|
954
|
-
command "serve" do |c|
|
955
|
-
c.flag [:port, :p], type: Integer, required: false,
|
956
|
-
default_value: @config.http_server_port,
|
957
|
-
desc: "Listen on port PORT. Ports can be selected in a range between 2-65535. Ports between
|
958
|
-
2 and 1023 can only be chosen when `machinery` will be executed as `root` user.",
|
959
|
-
arg_name: "PORT"
|
960
|
-
c.switch "public", required: false, negatable: false,
|
961
|
-
desc: "Makes the server reachable from all IP addresses."
|
962
|
-
|
963
|
-
c.action do |_global_options, options, args|
|
964
|
-
begin
|
965
|
-
check_port_validity(options[:port])
|
966
|
-
rescue Machinery::Errors::ServerPortError => e
|
967
|
-
raise Machinery::Errors::InvalidCommandLine.new(e.message + " The port can be " \
|
968
|
-
"either specified in the 'http_server_port' section of the configuration file " \
|
969
|
-
"or via the --port option.")
|
970
|
-
end
|
971
|
-
|
972
|
-
task = ServeHtmlTask.new
|
973
|
-
task.serve(
|
974
|
-
system_description_store, port: options[:port], public: options[:public]
|
975
|
-
)
|
976
|
-
end
|
977
|
-
end
|
978
1137
|
|
979
|
-
|
980
|
-
desc "Containerize a system description"
|
1138
|
+
desc "Start a web server for viewing system descriptions"
|
981
1139
|
long_desc <<-LONGDESC
|
982
|
-
|
983
|
-
|
1140
|
+
Starts a web server which serves an HTML view of all system descriptions
|
1141
|
+
and an overview page listing all descriptions.
|
984
1142
|
LONGDESC
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
1143
|
+
command "serve" do |c|
|
1144
|
+
c.flag [:port, :p],
|
1145
|
+
type: Integer,
|
1146
|
+
required: false,
|
1147
|
+
default_value: @config.http_server_port,
|
1148
|
+
desc: "Listen on port PORT. Ports can be selected in a range "\
|
1149
|
+
"between 2-65535. Ports between 2 and 1023 can only be "\
|
1150
|
+
"chosen when `machinery` will be executed as `root` "\
|
1151
|
+
"user.",
|
1152
|
+
arg_name: "PORT"
|
1153
|
+
c.switch "public",
|
1154
|
+
required: false,
|
1155
|
+
negatable: false,
|
1156
|
+
desc: "Makes the server reachable from all IP addresses."
|
989
1157
|
|
990
1158
|
c.action do |_global_options, options, args|
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
1159
|
+
begin
|
1160
|
+
check_port_validity(options[:port])
|
1161
|
+
rescue Machinery::Errors::ServerPortError => e
|
1162
|
+
raise Machinery::Errors::InvalidCommandLine,
|
1163
|
+
"#{e.message} The port can be either specified in the "\
|
1164
|
+
"'http_server_port' section of the configuration file " \
|
1165
|
+
"or via the --port option."
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
task = ServeHtmlTask.new
|
1169
|
+
task.serve(
|
1170
|
+
system_description_store,
|
1171
|
+
port: options[:port],
|
1172
|
+
public: options[:public]
|
1173
|
+
)
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
if @config.experimental_features
|
1178
|
+
desc "Containerize a system description"
|
1179
|
+
long_desc <<-LONGDESC
|
1180
|
+
Detects workloads from a system description and creates a recommendation
|
1181
|
+
for a corresponding container setup
|
1182
|
+
LONGDESC
|
1183
|
+
arg "NAME"
|
1184
|
+
command "containerize" do |c|
|
1185
|
+
c.flag ["output-dir", :o],
|
1186
|
+
type: String,
|
1187
|
+
required: true,
|
1188
|
+
desc: "Location where the container files will be stored",
|
1189
|
+
arg_name: "DIRECTORY"
|
1190
|
+
|
1191
|
+
c.action do |_global_options, options, args|
|
1192
|
+
name = shift_arg(args, "NAME")
|
1193
|
+
description = SystemDescription.load(name, system_description_store)
|
1194
|
+
task = ContainerizeTask.new
|
1195
|
+
task.containerize(
|
1196
|
+
description, File.expand_path(options["output-dir"])
|
1197
|
+
)
|
1198
|
+
end
|
995
1199
|
end
|
996
1200
|
end
|
997
|
-
end
|
998
1201
|
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1202
|
+
def self.system_description_store
|
1203
|
+
if ENV.key?("MACHINERY_DIR")
|
1204
|
+
SystemDescriptionStore.new(ENV["MACHINERY_DIR"])
|
1205
|
+
else
|
1206
|
+
SystemDescriptionStore.new
|
1207
|
+
end
|
1004
1208
|
end
|
1005
1209
|
end
|
1006
1210
|
end
|