d-installer 0.4.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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +29 -0
  3. data/Gemfile.lock +75 -0
  4. data/bin/d-installer +74 -0
  5. data/etc/d-installer.yaml +284 -0
  6. data/lib/dinstaller/can_ask_question.rb +48 -0
  7. data/lib/dinstaller/cmdline_args.rb +80 -0
  8. data/lib/dinstaller/cockpit_manager.rb +176 -0
  9. data/lib/dinstaller/config.rb +128 -0
  10. data/lib/dinstaller/config_reader.rb +164 -0
  11. data/lib/dinstaller/dbus/base_object.rb +58 -0
  12. data/lib/dinstaller/dbus/clients/base.rb +71 -0
  13. data/lib/dinstaller/dbus/clients/language.rb +86 -0
  14. data/lib/dinstaller/dbus/clients/manager.rb +76 -0
  15. data/lib/dinstaller/dbus/clients/software.rb +185 -0
  16. data/lib/dinstaller/dbus/clients/users.rb +112 -0
  17. data/lib/dinstaller/dbus/clients/with_progress.rb +56 -0
  18. data/lib/dinstaller/dbus/clients/with_service_status.rb +75 -0
  19. data/lib/dinstaller/dbus/clients.rb +34 -0
  20. data/lib/dinstaller/dbus/interfaces/progress.rb +113 -0
  21. data/lib/dinstaller/dbus/interfaces/service_status.rb +89 -0
  22. data/lib/dinstaller/dbus/language.rb +93 -0
  23. data/lib/dinstaller/dbus/language_service.rb +92 -0
  24. data/lib/dinstaller/dbus/manager.rb +147 -0
  25. data/lib/dinstaller/dbus/manager_service.rb +132 -0
  26. data/lib/dinstaller/dbus/question.rb +176 -0
  27. data/lib/dinstaller/dbus/questions.rb +124 -0
  28. data/lib/dinstaller/dbus/service_runner.rb +97 -0
  29. data/lib/dinstaller/dbus/service_status.rb +87 -0
  30. data/lib/dinstaller/dbus/software/manager.rb +131 -0
  31. data/lib/dinstaller/dbus/software/proposal.rb +82 -0
  32. data/lib/dinstaller/dbus/software.rb +31 -0
  33. data/lib/dinstaller/dbus/software_service.rb +86 -0
  34. data/lib/dinstaller/dbus/storage/proposal.rb +170 -0
  35. data/lib/dinstaller/dbus/storage.rb +30 -0
  36. data/lib/dinstaller/dbus/users.rb +132 -0
  37. data/lib/dinstaller/dbus/users_service.rb +92 -0
  38. data/lib/dinstaller/dbus/with_service_status.rb +48 -0
  39. data/lib/dinstaller/dbus/y2dir/manager/modules/Package.rb +51 -0
  40. data/lib/dinstaller/dbus/y2dir/manager/modules/PackagesProposal.rb +62 -0
  41. data/lib/dinstaller/dbus/y2dir/modules/Autologin.rb +214 -0
  42. data/lib/dinstaller/dbus/y2dir/software/modules/SpaceCalculation.rb +44 -0
  43. data/lib/dinstaller/dbus.rb +11 -0
  44. data/lib/dinstaller/errors.rb +28 -0
  45. data/lib/dinstaller/installation_phase.rb +106 -0
  46. data/lib/dinstaller/language.rb +73 -0
  47. data/lib/dinstaller/luks_activation_question.rb +92 -0
  48. data/lib/dinstaller/manager.rb +261 -0
  49. data/lib/dinstaller/network.rb +53 -0
  50. data/lib/dinstaller/package_callbacks.rb +69 -0
  51. data/lib/dinstaller/progress.rb +149 -0
  52. data/lib/dinstaller/question.rb +103 -0
  53. data/lib/dinstaller/questions_manager.rb +145 -0
  54. data/lib/dinstaller/security.rb +96 -0
  55. data/lib/dinstaller/service_status_recorder.rb +58 -0
  56. data/lib/dinstaller/software.rb +232 -0
  57. data/lib/dinstaller/storage/actions.rb +90 -0
  58. data/lib/dinstaller/storage/callbacks/activate.rb +93 -0
  59. data/lib/dinstaller/storage/callbacks/activate_luks.rb +93 -0
  60. data/lib/dinstaller/storage/callbacks/activate_multipath.rb +77 -0
  61. data/lib/dinstaller/storage/callbacks.rb +30 -0
  62. data/lib/dinstaller/storage/manager.rb +104 -0
  63. data/lib/dinstaller/storage/proposal.rb +197 -0
  64. data/lib/dinstaller/storage.rb +30 -0
  65. data/lib/dinstaller/users.rb +156 -0
  66. data/lib/dinstaller/with_progress.rb +63 -0
  67. data/lib/dinstaller.rb +5 -0
  68. data/share/dbus.conf +38 -0
  69. data/share/dbus.service +5 -0
  70. data/share/systemd.service +14 -0
  71. metadata +295 -0
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) [2022] SUSE LLC
4
+ #
5
+ # All Rights Reserved.
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify it
8
+ # under the terms of version 2 of the GNU General Public License as published
9
+ # by the Free Software Foundation.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
+ # more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, contact SUSE LLC.
18
+ #
19
+ # To contact SUSE LLC about this file by physical or electronic mail, you may
20
+ # find current contact information at www.suse.com.
21
+
22
+ module DInstaller
23
+ # Manager for questions
24
+ #
25
+ # Allows to configure callbacks with the actions to perform when adding, deleting or waiting for
26
+ # questions.
27
+ class QuestionsManager
28
+ # @return [Array<Question>]
29
+ attr_reader :questions
30
+
31
+ # Constructor
32
+ #
33
+ # @params logger [Logger]
34
+ def initialize(logger)
35
+ @logger = logger
36
+ @questions = []
37
+ @on_add_callbacks = []
38
+ @on_delete_callbacks = []
39
+ @on_wait_callbacks = []
40
+ end
41
+
42
+ # Adds a question
43
+ #
44
+ # Callbacks are called after adding the question, see {#on_add}.
45
+ #
46
+ # @yieldparam question [Question] added question
47
+ #
48
+ # @param question [Question]
49
+ # @return [Boolean] whether the question was added
50
+ def add(question)
51
+ return false if include?(question)
52
+
53
+ questions << question
54
+ on_add_callbacks.each { |c| c.call(question) }
55
+
56
+ true
57
+ end
58
+
59
+ # Deletes the given question
60
+ #
61
+ # Callbacks are called after deleting the question, see {#on_delete}.
62
+ #
63
+ # @yieldparam question [Question] deleted question
64
+ #
65
+ # @param question [Question]
66
+ # @return [Boolean] whether the question was deleted
67
+ def delete(question)
68
+ return false unless include?(question)
69
+
70
+ questions.delete(question)
71
+ on_delete_callbacks.each { |c| c.call(question) }
72
+
73
+ true
74
+ end
75
+
76
+ # Waits until all questions are answered
77
+ #
78
+ # Callbacks are periodically called while waiting, see {#on_wait}.
79
+ def wait
80
+ logger.info "Waiting for questions to be answered"
81
+
82
+ loop do
83
+ on_wait_callbacks.each(&:call)
84
+ sleep(0.1)
85
+ break if questions_answered?
86
+ end
87
+ end
88
+
89
+ # Registers a callback to be called when a new question is added
90
+ #
91
+ # @param block [Proc]
92
+ def on_add(&block)
93
+ on_add_callbacks << block
94
+ end
95
+
96
+ # Registers a callback to be called when a question is deleted
97
+ #
98
+ # @param block [Proc]
99
+ def on_delete(&block)
100
+ on_delete_callbacks << block
101
+ end
102
+
103
+ # Registers a callback to be called while waiting for questions be answered
104
+ #
105
+ # @param block [Proc]
106
+ def on_wait(&block)
107
+ on_wait_callbacks << block
108
+ end
109
+
110
+ private
111
+
112
+ # @return [Logger]
113
+ attr_reader :logger
114
+
115
+ # Callbacks to be called when the a new question is added
116
+ #
117
+ # @return [Array<Proc>]
118
+ attr_reader :on_add_callbacks
119
+
120
+ # Callbacks to be called when the a question is deleted
121
+ #
122
+ # @return [Array<Proc>]
123
+ attr_reader :on_delete_callbacks
124
+
125
+ # Callbacks to be called when waiting for answers
126
+ #
127
+ # @return [Array<Proc>]
128
+ attr_reader :on_wait_callbacks
129
+
130
+ # Whether a question with the same id as the given question is already in the list of questions
131
+ #
132
+ # @param question [Question]
133
+ # @return [Boolean]
134
+ def include?(question)
135
+ questions.any? { |q| q.id == question.id }
136
+ end
137
+
138
+ # Whether all questions are already answered
139
+ #
140
+ # @return [Boolean]
141
+ def questions_answered?
142
+ questions.all?(&:answered?)
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) [2022] SUSE LLC
4
+ #
5
+ # All Rights Reserved.
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify it
8
+ # under the terms of version 2 of the GNU General Public License as published
9
+ # by the Free Software Foundation.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
+ # more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, contact SUSE LLC.
18
+ #
19
+ # To contact SUSE LLC about this file by physical or electronic mail, you may
20
+ # find current contact information at www.suse.com.
21
+
22
+ require "yast"
23
+ require "y2security/lsm"
24
+ require "yast2/execute"
25
+
26
+ require "dinstaller/config"
27
+
28
+ # FIXME: monkey patching of security config to not read control.xml and
29
+ # instead use DIinstaller::Config
30
+ # TODO: add ability to set product features in LSM::Base
31
+ module Y2Security
32
+ module LSM
33
+ # modified LSM Base class to use dinstaller config
34
+ class Base
35
+ def product_feature_settings
36
+ return @product_feature_settings unless @product_feature_settings.nil?
37
+
38
+ value = ::DInstaller::Config.current.data["security"]["available_lsms"][id.to_s]
39
+ res = if value
40
+ {
41
+ selectable: true,
42
+ configurable: true,
43
+ patterns: (value["patterns"] || []).join(" "),
44
+ mode: value["policy"]
45
+ }
46
+ else
47
+ {
48
+ selectable: false,
49
+ configurable: false,
50
+ patterns: "",
51
+ mode: nil
52
+ }
53
+ end
54
+ @product_feature_settings = res
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ module DInstaller
61
+ # Backend class between dbus service and yast code
62
+ class Security
63
+ # @return [Logger]
64
+ attr_reader :logger
65
+
66
+ def initialize(logger, config)
67
+ @config = config
68
+ @logger = logger
69
+ end
70
+
71
+ def write
72
+ lsm_config.save
73
+ end
74
+
75
+ def probe
76
+ selected_lsm = config.data["security"]["lsm"]
77
+ lsm_config.select(selected_lsm)
78
+
79
+ patterns = if selected_lsm.nil?
80
+ []
81
+ else
82
+ lsm_data = config.data["security"]["available_lsms"][selected_lsm]
83
+ lsm_data["patterns"]
84
+ end
85
+ Yast::PackagesProposal.SetResolvables("LSM", :pattern, patterns)
86
+ end
87
+
88
+ private
89
+
90
+ attr_reader :config
91
+
92
+ def lsm_config
93
+ Y2Security::LSM::Config.instance
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) [2022] SUSE LLC
4
+ #
5
+ # All Rights Reserved.
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify it
8
+ # under the terms of version 2 of the GNU General Public License as published
9
+ # by the Free Software Foundation.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
+ # more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, contact SUSE LLC.
18
+ #
19
+ # To contact SUSE LLC about this file by physical or electronic mail, you may
20
+ # find current contact information at www.suse.com.
21
+
22
+ require "dinstaller/dbus/service_status"
23
+
24
+ module DInstaller
25
+ # Allows to record the status of services and to register callbacks to be called when a service
26
+ # status changes its value
27
+ class ServiceStatusRecorder
28
+ def initialize
29
+ @statuses = {}
30
+ @on_service_status_change_callbacks = []
31
+ end
32
+
33
+ # Saves the status of the given service and runs the callbacks if the status has changed
34
+ #
35
+ # @param service_name [String]
36
+ # @param status [String] see {ServiceStatus}
37
+ def save(service_name, status)
38
+ return if @statuses[service_name] == status
39
+
40
+ @statuses[service_name] = status
41
+ @on_service_status_change_callbacks.each(&:call)
42
+ end
43
+
44
+ # Name of services with busy status
45
+ #
46
+ # @return [Array<String>]
47
+ def busy_services
48
+ @statuses.select { |_service, status| status == DBus::ServiceStatus::BUSY }.keys
49
+ end
50
+
51
+ # Registers callbacks to be called when saving a new status
52
+ #
53
+ # @param block [Proc]
54
+ def on_service_status_change(&block)
55
+ @on_service_status_change_callbacks << block
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) [2021] SUSE LLC
4
+ #
5
+ # All Rights Reserved.
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify it
8
+ # under the terms of version 2 of the GNU General Public License as published
9
+ # by the Free Software Foundation.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
+ # more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, contact SUSE LLC.
18
+ #
19
+ # To contact SUSE LLC about this file by physical or electronic mail, you may
20
+ # find current contact information at www.suse.com.
21
+
22
+ require "yast"
23
+ require "fileutils"
24
+ require "dinstaller/package_callbacks"
25
+ require "dinstaller/config"
26
+ require "dinstaller/with_progress"
27
+ require "y2packager/product"
28
+
29
+ Yast.import "PackageInstallation"
30
+ Yast.import "Pkg"
31
+ Yast.import "Stage"
32
+
33
+ # YaST specific code lives under this namespace
34
+ module DInstaller
35
+ # This class is responsible for software handling
36
+ class Software
37
+ include WithProgress
38
+
39
+ GPG_KEYS_GLOB = "/usr/lib/rpm/gnupg/keys/gpg-*"
40
+ private_constant :GPG_KEYS_GLOB
41
+
42
+ attr_reader :product
43
+
44
+ DEFAULT_LANGUAGES = ["en_US"].freeze
45
+ private_constant :DEFAULT_LANGUAGES
46
+
47
+ attr_accessor :languages
48
+
49
+ # FIXME: what about defining a Product class?
50
+ # @return [Array<Array<String,Hash>>] An array containing the product ID and
51
+ # additional information in a hash
52
+ attr_reader :products
53
+
54
+ def initialize(config, logger)
55
+ @config = config
56
+ @logger = logger
57
+ @languages = DEFAULT_LANGUAGES
58
+ @products = @config.data["products"]
59
+ if @config.multi_product?
60
+ @product = nil
61
+ else
62
+ @product = @products.keys.first # use the available product as default
63
+ @config.pick_product(@product)
64
+ end
65
+ end
66
+
67
+ def select_product(name)
68
+ return if name == @product
69
+ raise ArgumentError unless @products[name]
70
+
71
+ @config.pick_product(name)
72
+ @product = name
73
+ end
74
+
75
+ def probe
76
+ logger.info "Probing software"
77
+
78
+ store_original_repos
79
+ Yast::Pkg.SetSolverFlags("ignoreAlreadyRecommended" => false, "onlyRequires" => true)
80
+
81
+ # as we use liveDVD with normal like ENV, lets temporary switch to normal to use its repos
82
+ Yast::Stage.Set("normal")
83
+
84
+ start_progress(3)
85
+ progress.step("Initialize target repositories") { initialize_target_repos }
86
+ progress.step("Initialize sources") { add_base_repo }
87
+ progress.step("Making the initial proposal") do
88
+ proposal = Yast::Packages.Proposal(force_reset = true, reinit = false, _simple = true)
89
+ logger.info "proposal #{proposal["raw_proposal"]}"
90
+ end
91
+
92
+ Yast::Stage.Set("initial")
93
+ end
94
+
95
+ def initialize_target_repos
96
+ Yast::Pkg.TargetInitialize("/")
97
+ import_gpg_keys
98
+ end
99
+
100
+ def propose
101
+ Yast::Pkg.TargetFinish # ensure that previous target is closed
102
+ Yast::Pkg.TargetInitialize(Yast::Installation.destdir)
103
+ Yast::Pkg.TargetLoad
104
+ Yast::Pkg.SetAdditionalLocales(languages)
105
+ select_base_product(@config.data["software"]["base_product"])
106
+
107
+ add_resolvables
108
+ proposal = Yast::Packages.Proposal(force_reset = false, reinit = false, _simple = true)
109
+ logger.info "proposal #{proposal["raw_proposal"]}"
110
+
111
+ solve_dependencies
112
+
113
+ # do not return proposal hash, so intentional nil here
114
+ nil
115
+ end
116
+
117
+ def install
118
+ start_progress(count_packages)
119
+ PackageCallbacks.setup(count_packages, progress)
120
+
121
+ # TODO: error handling
122
+ commit_result = Yast::PackageInstallation.Commit({})
123
+
124
+ if commit_result.nil? || commit_result.empty?
125
+ logger.error("Commit failed")
126
+ raise Yast::Pkg.LastError
127
+ end
128
+
129
+ logger.info "Commit result #{commit_result}"
130
+ end
131
+
132
+ # Writes the repositories information to the installed system
133
+ def finish
134
+ start_progress(2)
135
+ progress.step("Writing repositories to the target system") do
136
+ Yast::Pkg.SourceSaveAll
137
+ Yast::Pkg.TargetFinish
138
+ Yast::Pkg.SourceCacheCopyTo(Yast::Installation.destdir)
139
+ end
140
+ progress.step("Restoring original repositories") { restore_original_repos }
141
+ end
142
+
143
+ # Determine whether the given tag is provided by the selected packages
144
+ #
145
+ # @param tag [String] Tag to search for (package names, requires/provides, or file
146
+ # names)
147
+ # @return [Boolean] true if it is provided; false otherwise
148
+ def provision_selected?(tag)
149
+ Yast::Pkg.IsSelected(tag) || Yast::Pkg.IsProvided(tag)
150
+ end
151
+
152
+ private
153
+
154
+ # adds resolvables from yaml config for given product
155
+ def add_resolvables
156
+ mandatory_patterns = @config.data["software"]["mandatory_patterns"] || []
157
+ Yast::PackagesProposal.SetResolvables("d-installer", :pattern, mandatory_patterns)
158
+
159
+ optional_patterns = @config.data["software"]["optional_patterns"] || []
160
+ Yast::PackagesProposal.SetResolvables("d-installer", :pattern, optional_patterns,
161
+ optional: true)
162
+ end
163
+
164
+ # call solver to satisfy dependency or log error
165
+ def solve_dependencies
166
+ res = Yast::Pkg.PkgSolve(unused = true)
167
+ logger.info "solver run #{res.inspect}"
168
+
169
+ return if res
170
+
171
+ logger.error "Solver failed: #{Yast::Pkg.LastError}"
172
+ logger.error "Details: #{Yast::Pkg.LastErrorDetails}"
173
+ logger.error "Solving issues: #{Yast::Pkg.PkgSolveErrors}"
174
+ end
175
+
176
+ # @return [Logger]
177
+ attr_reader :logger
178
+
179
+ def count_packages
180
+ Yast::Pkg.PkgMediaCount.reduce(0) { |sum, res| sum + res.reduce(0, :+) }
181
+ end
182
+
183
+ def import_gpg_keys
184
+ gpg_keys = Dir.glob(GPG_KEYS_GLOB).map(&:to_s)
185
+ logger.info "Importing GPG keys: #{gpg_keys}"
186
+ gpg_keys.each do |path|
187
+ Yast::Pkg.ImportGPGKey(path, true)
188
+ end
189
+ end
190
+
191
+ def add_base_repo
192
+ @config.data["software"]["installation_repositories"].each do |repo|
193
+ Yast::Pkg.SourceCreate(repo, "/") # TODO: having that dir also in config?
194
+ end
195
+
196
+ Yast::Pkg.SourceSaveAll
197
+ end
198
+
199
+ def select_base_product(name)
200
+ base_product = Y2Packager::Product.available_base_products.find do |product|
201
+ product.name == name
202
+ end
203
+ logger.info "Base product to select: #{base_product&.name}"
204
+ base_product&.select
205
+ end
206
+
207
+ REPOS_BACKUP = "/etc/zypp/repos.d.dinstaller.backup"
208
+ private_constant :REPOS_BACKUP
209
+
210
+ REPOS_DIR = "/etc/zypp/repos.d"
211
+ private_constant :REPOS_DIR
212
+
213
+ # ensure that repos backup is there and repos.d is empty
214
+ def store_original_repos
215
+ # Backup was already created, so just remove all repos
216
+ if File.directory?(REPOS_BACKUP)
217
+ logger.info "removing #{REPOS_DIR}"
218
+ FileUtils.rm_rf(REPOS_DIR)
219
+ else # move repos to backup
220
+ logger.info "moving #{REPOS_DIR} to #{REPOS_BACKUP}"
221
+ FileUtils.mv(REPOS_DIR, REPOS_BACKUP)
222
+ end
223
+ end
224
+
225
+ def restore_original_repos
226
+ logger.info "removing #{REPOS_DIR}"
227
+ FileUtils.rm_rf(REPOS_DIR)
228
+ logger.info "moving #{REPOS_BACKUP} to #{REPOS_DIR}"
229
+ FileUtils.mv(REPOS_BACKUP, REPOS_DIR)
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) [2022] SUSE LLC
4
+ #
5
+ # All Rights Reserved.
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify it
8
+ # under the terms of version 2 of the GNU General Public License as published
9
+ # by the Free Software Foundation.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
+ # more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program; if not, contact SUSE LLC.
18
+ #
19
+ # To contact SUSE LLC about this file by physical or electronic mail, you may
20
+ # find current contact information at www.suse.com.
21
+
22
+ require "y2storage/storage_manager"
23
+
24
+ module DInstaller
25
+ module Storage
26
+ # Backend class to get the list of actions over the storage devices
27
+ class Actions
28
+ # @param logger [Logger]
29
+ def initialize(logger)
30
+ @logger = logger
31
+ end
32
+
33
+ # All actions properly sorted
34
+ #
35
+ # @return [Array<Y2Storage::CompoundAction>]
36
+ def all
37
+ main_actions + subvolume_actions
38
+ end
39
+
40
+ private
41
+
42
+ # @param [Logger]
43
+ attr_reader :logger
44
+
45
+ # Sorted main actions (everything except subvolume actions)
46
+ #
47
+ # @return [Array<Y2Storage::CompoundAction>]
48
+ def main_actions
49
+ actions = self.actions.reject { |a| subvol_action?(a) }
50
+ sort_actions(actions)
51
+ end
52
+
53
+ # Sorted subvolume actions
54
+ #
55
+ # @return [Array<Y2Storage::CompoundAction>]
56
+ def subvolume_actions
57
+ actions = self.actions.select { |a| subvol_action?(a) }
58
+ sort_actions(actions)
59
+ end
60
+
61
+ # All actions, without sorting
62
+ #
63
+ # @return [Array<Y2Storage::CompoundAction>]
64
+ def actions
65
+ actiongraph = Y2Storage::StorageManager.instance.staging.actiongraph
66
+
67
+ return [] unless actiongraph
68
+
69
+ actiongraph.compound_actions
70
+ end
71
+
72
+ # Sorts actions, placing destructive actions at the end
73
+ #
74
+ # @param actions [Array<Y2Storage::CompoundAction>]
75
+ # @return [Array<Y2Storage::CompoundAction>]
76
+ def sort_actions(actions)
77
+ delete, other = actions.partition(&:delete?)
78
+ delete.concat(other)
79
+ end
80
+
81
+ # Whether the action acts over a Btrfs subvolume
82
+ #
83
+ # @param action [Y2Storage::CompoundAction]
84
+ # @return [Boolean]
85
+ def subvol_action?(action)
86
+ action.device_is?(:btrfs_subvolume)
87
+ end
88
+ end
89
+ end
90
+ end