• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yast / yast-bootloader / 11823138700

13 Nov 2024 06:04PM UTC coverage: 87.695% (+0.3%) from 87.378%
11823138700

Pull #707

github

schubi2
parent <a class=hub.com/yast/yast-bootloader/commit/f931f86756ce867e928721fb823ce7e869ea1fd9">f931f8675
author Stefan Schubert <schubi@suse.de> 1729782721 +0200
committer Stefan Schubert <schubi@suse.de> 1731520801 +0100

parent f931f86756ce867e928721fb823ce7e869ea1fd9
author Stefan Schubert <schubi@suse.de> 1729782721 +0200
committer Stefan Schubert <schubi@suse.de> 1731520720 +0100

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2-bls

grub2-bls

grub2-bls

grub2-bls

grub2-bls

grub2-bls

grub2-bls

grub2-bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

rubocop

rubocop

grub2bls

grub2bls

grub2bls

sync text about secure boot on s390 to be correct (bsc#1219989)

make rubocop happy

rubocop

rubocop

fixed test cases

fixed test cases

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

rubocop

rubocop

rubocop

rubocop

rubocop

docu

added yast-bootloader

added yast-bootloader

removed merge conflict

added grafic

added grafic

added grafic

merged with master

adapt more tests

make rubocop happy

merged with master

package
Pull Request #707: Supporting Grub2-BLS

309 of 352 new or added lines in 14 files covered. (87.78%)

138 existing lines in 10 files now uncovered.

3371 of 3844 relevant lines covered (87.7%)

13.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.8
/src/modules/Bootloader.rb
1
# frozen_string_literal: true
2

3
# File:
4
#      modules/Bootloader.rb
5
#
6
# Module:
7
#      Bootloader installation and configuration
8
#
9
# Summary:
10
#      Bootloader installation and configuration base module
11
#
12
# Authors:
13
#      Jiri Srain <jsrain@suse.cz>
14
#      Olaf Dabrunz <od@suse.de>
15
#
16
# $Id$
17
#
18
require "yast"
1✔
19
require "yast2/popup"
1✔
20
require "bootloader/exceptions"
1✔
21
require "bootloader/sysconfig"
1✔
22
require "bootloader/bootloader_factory"
1✔
23
require "bootloader/autoyast_converter"
1✔
24
require "bootloader/autoinst_profile/bootloader_section"
1✔
25
require "bootloader/systemdboot"
1✔
26
require "installation/autoinst_issues/invalid_value"
1✔
27
require "cfa/matcher"
1✔
28

29
Yast.import "Arch"
1✔
30
Yast.import "BootStorage"
1✔
31
Yast.import "Initrd"
1✔
32
Yast.import "Installation"
1✔
33
Yast.import "Mode"
1✔
34
Yast.import "Package"
1✔
35
Yast.import "Progress"
1✔
36
Yast.import "Report"
1✔
37
Yast.import "Stage"
1✔
38
Yast.import "UI"
1✔
39

40
module Yast
1✔
41
  class BootloaderClass < Module
1✔
42
    include Yast::Logger
1✔
43

44
    BOOLEAN_MAPPING = { true => :present, false => :missing }.freeze
1✔
45

46
    def main
1✔
47
      textdomain "bootloader"
1✔
48

49
      # installation proposal help variables
50

51
      # Configuration was changed during inst. proposal if true
52
      @proposed_cfg_changed = false
1✔
53

54
      # old vga value handling function
55

56
      # old value of vga parameter of default bootloader section
57
      @old_vga = nil
1✔
58

59
      # general functions
60

61
      @test_abort = nil
1✔
62
    end
63

64
    # Check whether abort was pressed
65
    # @return [Boolean] true if abort was pressed
66
    def testAbort
1✔
67
      return false if Mode.commandline
32✔
68

69
      UI.PollInput == :abort
32✔
70
    end
71

72
    # Export bootloader settings to a map
73
    # @return bootloader settings
74
    def Export
1✔
UNCOV
75
      config = ::Bootloader::BootloaderFactory.current
×
76
      config.read if !config.read? && !config.proposed?
×
77
      result = ::Bootloader::AutoyastConverter.export(config)
×
78

UNCOV
79
      log.info "autoyast map for bootloader: #{result.inspect}"
×
80

UNCOV
81
      result
×
82
    end
83

84
    # Import settings from a map
85
    # @param [Hash] data map of bootloader settings
86
    # @return [Boolean] true on success
87
    def Import(data)
1✔
88
      factory = ::Bootloader::BootloaderFactory
2✔
89
      bootloader_section = ::Bootloader::AutoinstProfile::BootloaderSection.new_from_hashes(data)
2✔
90

91
      imported_configuration = import_bootloader(bootloader_section)
2✔
92
      return false if imported_configuration.nil?
2✔
93

UNCOV
94
      factory.clear_cache
×
95

UNCOV
96
      proposed_configuration = factory.bootloader_by_name(imported_configuration.name)
×
97
      unless Mode.config # no AutoYaST configuration mode
×
98
        proposed_configuration.propose
×
99
        proposed_configuration.merge(imported_configuration)
×
100
      end
UNCOV
101
      factory.current = proposed_configuration
×
102

103
      # mark that it is not clear proposal (bsc#1081967)
UNCOV
104
      Yast::Bootloader.proposed_cfg_changed = true
×
105

UNCOV
106
      true
×
107
    end
108

109
    # Read settings from disk
110
    # @return [Boolean] true on success
111
    def Read
1✔
112
      log.info "Reading configuration"
16✔
113
      # run Progress bar
114
      stages = [
115
        # progress stage, text in dialog (short, infinitiv)
116
        _("Check boot loader"),
16✔
117
        # progress stage, text in dialog (short, infinitiv)
118
        _("Load boot loader settings")
119
      ]
120
      titles = [
121
        # progress step, text in dialog (short)
122
        _("Checking boot loader..."),
16✔
123
        # progress step, text in dialog (short)
124
        _("Reading partitioning..."),
125
        # progress step, text in dialog (short)
126
        _("Loading boot loader settings...")
127
      ]
128
      # dialog header
129
      Progress.New(
16✔
130
        _("Initializing Boot Loader Configuration"),
131
        " ",
132
        3,
133
        stages,
134
        titles,
135
        ""
136
      )
137

138
      Progress.NextStage
16✔
139
      return false if testAbort
16✔
140

141
      Progress.NextStage
16✔
142
      return false if testAbort
16✔
143

144
      begin
145
        ::Bootloader::BootloaderFactory.current.read
16✔
146
      rescue ::Bootloader::UnsupportedBootloader => e
UNCOV
147
        ret = Yast::Report.AnyQuestion(_("Unsupported Bootloader"),
×
148
          _("Unsupported bootloader '%s' detected. Use proposal of supported configuration instead?") %
149
            e.bootloader_name,
150
          _("Use"),
151
          _("Quit"),
152
          :yes) # focus proposing new one
UNCOV
153
        return false unless ret
×
154

UNCOV
155
        ::Bootloader::BootloaderFactory.current = ::Bootloader::BootloaderFactory.proposed
×
156
        ::Bootloader::BootloaderFactory.current.propose
×
157
      rescue ::Bootloader::BrokenConfiguration, ::Bootloader::UnsupportedOption => e
UNCOV
158
        msg = if e.is_a?(::Bootloader::BrokenConfiguration)
×
159
          # TRANSLATORS: %s stands for readon why yast cannot process it
UNCOV
160
          _("YaST cannot process current bootloader configuration (%s). " \
×
161
            "Propose new configuration from scratch?") % e.reason
162
        else
UNCOV
163
          e.message
×
164
        end
165

UNCOV
166
        ret = Yast::Report.AnyQuestion(_("Unsupported Configuration"),
×
167
          # TRANSLATORS: %s stands for readon why yast cannot process it
168
          msg,
169
          _("Propose"),
170
          _("Quit"),
171
          :yes) # focus proposing new one
UNCOV
172
        return false unless ret
×
173

UNCOV
174
        ::Bootloader::BootloaderFactory.current = ::Bootloader::BootloaderFactory.proposed
×
175
        ::Bootloader::BootloaderFactory.current.propose
×
176
      rescue Errno::EACCES
177
        # If the access to any needed file (e.g., grub.cfg when using GRUB bootloader) is not
178
        # allowed, just abort the execution. Using Yast::Confirm.MustBeRoot early in the
179
        # wizard/client is not enough since it allows continue.
180

UNCOV
181
        Yast2::Popup.show(
×
182
          # TRANSLATORS: pop-up message, beware the line breaks
183
          _("The module is running without enough privileges to perform all possible actions.\n\n" \
184
            "Cannot continue. Please, try again as root."),
185
          headline: :error
186
        )
187

UNCOV
188
        return false
×
189
      end
190

191
      Progress.Finish
16✔
192

193
      true
16✔
194
    end
195

196
    # Reset bootloader settings
197
    def Reset
1✔
UNCOV
198
      return if Mode.autoinst
×
199

UNCOV
200
      log.info "Resetting configuration"
×
201

UNCOV
202
      ::Bootloader::BootloaderFactory.clear_cache
×
203
      if Stage.initial
×
204
        config = ::Bootloader::BootloaderFactory.proposed
×
205
        config.propose
×
206
      else
UNCOV
207
        config = ::Bootloader::BootloaderFactory.system
×
208
        config.read
×
209
      end
UNCOV
210
      ::Bootloader::BootloaderFactory.current = config
×
211
      nil
212
    end
213

214
    # Propose bootloader settings
215
    def Propose
1✔
UNCOV
216
      log.info "Proposing configuration"
×
217
      ::Bootloader::BootloaderFactory.current.propose
×
218

UNCOV
219
      log.info "Proposed settings: #{Export()}"
×
220

221
      nil
222
    end
223

224
    # Display bootloader summary
225
    # @return a list of summary lines
226
    def Summary(simple_mode: false)
1✔
227
      # kokso: additional warning that root partition is nfs type -> bootloader will not be installed
UNCOV
228
      if BootStorage.boot_filesystem.is?(:nfs)
×
229
        log.info "Bootloader::Summary() -> Boot partition is nfs type, bootloader will not be installed."
×
230
        return [_("The boot partition is of type NFS. Bootloader cannot be installed.")]
×
231
      end
232

UNCOV
233
      ::Bootloader::BootloaderFactory.current.summary(simple_mode: simple_mode)
×
234
    end
235

236
    # Update the whole configuration
237
    # @return [Boolean] true on success
238
    def Update
1✔
UNCOV
239
      Write() # write also reads the configuration and updates it
×
240
    end
241

242
    # Write bootloader settings to disk
243
    # @return [Boolean] true on success
244
    def Write
1✔
245
      ReadOrProposeIfNeeded()
3✔
246

247
      mark_as_changed
3✔
248

249
      log.info "Writing bootloader configuration"
3✔
250

251
      stages = [
252
        _("Prepare system"),
3✔
253
        _("Create initrd"),
254
        _("Save boot loader configuration")
255
      ]
256
      titles = [
257
        _("Preparing system..."),
3✔
258
        _("Creating initrd..."),
259
        _("Saving boot loader configuration...")
260
      ]
261

262
      if Mode.normal
3✔
263
        Progress.New(_("Saving Boot Loader Configuration"), " ", stages.size, stages, titles, "")
3✔
264
        Progress.NextStage
3✔
265
      else
UNCOV
266
        Progress.Title(titles[0])
×
267
      end
268

269
      # Prepare system
270
      progress_state = Progress.set(false)
3✔
271
      if !::Bootloader::BootloaderFactory.current.prepare
3✔
272
        log.error("System could not be prepared successfully, required packages were not installed")
3✔
273
        Yast2::Popup.show(_("Cannot continue without install required packages"))
3✔
274
        return false
3✔
275
      end
UNCOV
276
      Progress.set(progress_state)
×
277

UNCOV
278
      transactional = Package.IsTransactionalSystem
×
279

280
      # Create initrd
UNCOV
281
      Progress.NextStage
×
282
      Progress.Title(titles[1]) unless Mode.normal
×
283

UNCOV
284
      write_initrd || log.error("Error occurred while creating initrd") if !transactional
×
285

286
      # Save boot loader configuration
UNCOV
287
      Progress.NextStage
×
288
      Progress.Title(titles[2]) unless Mode.normal
×
289
      ::Bootloader::BootloaderFactory.current.write(etc_only: transactional)
×
290
      if transactional
×
291
        # all writing to target is done in specific transactional command
UNCOV
292
        Yast::Execute.on_target!("transactional-update", "--continue", "bootloader")
×
293
      end
294

UNCOV
295
      true
×
296
    end
297

298
    # return default section label
299
    # @return [String] default section label
300
    def getDefaultSection
1✔
UNCOV
301
      ReadOrProposeIfNeeded()
×
302

UNCOV
303
      bootloader = Bootloader::BootloaderFactory.current
×
304
      return "" unless bootloader.respond_to?(:sections)
×
305

UNCOV
306
      bootloader.sections.default
×
307
    end
308

309
    FLAVOR_KERNEL_LINE_MAP = {
1✔
310
      :common    => "append",
311
      :xen_guest => "xen_append",
312
      :xen_host  => "xen_kernel_append"
313
    }.freeze
314

315
    # Gets value for given parameter in kernel parameters for given flavor.
316
    # @param [Symbol] flavor flavor of kernel, for possible values see #modify_kernel_param
317
    # @param [String] key of parameter on kernel command line
318
    # @return [String,:missing,:present] Returns string for parameters with value,
319
    #   `:missing` if key is not there and `:present` for parameters without value.
320
    #
321
    # @example get crashkernel parameter to common kernel
322
    #   Bootloader.kernel_param(:common, "crashkernel")
323
    #   => "256M@64B"
324
    #
325
    # @example get cio_ignore parameter for xen_host kernel when missing
326
    #   Bootloader.kernel_param(:xen_host, "cio_ignore")
327
    #   => :missing
328
    #
329
    # @example get verbose parameter for xen_guest which is there
330
    #   Bootloader.kernel_param(:xen_guest, "verbose")
331
    #   => :present
332
    #
333

334
    def kernel_param(flavor, key)
1✔
335
      if flavor == :recovery
4✔
UNCOV
336
        log.warn "Using deprecated recovery flavor"
×
337
        return :missing
×
338
      end
339

340
      current_bl = ::Bootloader::BootloaderFactory.current
4✔
341
      if current_bl.is_a?(::Bootloader::SystemdBoot)
4✔
342
        # systemd-boot
NEW
343
        kernel_params = current_bl.kernel_params
×
344
      elsif current_bl.respond_to?(:grub_default)
4✔
345
        # all grub bootloader types
346
        grub_default = current_bl.grub_default
4✔
347
        kernel_params = case flavor
4✔
348
        when :common then grub_default.kernel_params
4✔
NEW
349
        when :xen_guest then grub_default.xen_kernel_params
×
NEW
350
        when :xen_host then grub_default.xen_hypervisor_params
×
NEW
351
        else raise ArgumentError, "Unknown flavor #{flavor}"
×
352
        end
353
      else
NEW
354
        return :missing
×
355
      end
356

357
      ReadOrProposeIfNeeded() # ensure we have some data
4✔
358

359
      res = kernel_params.parameter(key)
4✔
360

361
      BOOLEAN_MAPPING[res] || res
4✔
362
    end
363

364
    # Modify kernel parameters for installed kernels according to values
365
    # @param [Array]  args parameters to modify. Last parameter is hash with keys
366
    #   and its values, keys are strings and values are `:present`, `:missing` or
367
    #   string value. Other parameters specify which kernel flavors are affected.
368
    #   Known values are:
369
    #     - `:common` for non-specific flavor
370
    #     - `:recovery` DEPRECATED: no longer use
371
    #     - `:xen_guest` for xen guest kernels
372
    #     - `:xen_host` for xen host kernels
373
    # @return [Boolean] true if params were modified; false otherwise.
374
    #
375
    # @example add crashkernel parameter to common kernel and xen guest
376
    #   Bootloader.modify_kernel_params(:common, :xen_guest, "crashkernel" => "256M@64M")
377
    #
378
    # @example same as before just with array passing
379
    #   targets = [:common, :xen_guest]
380
    #   Bootloader.modify_kernel_params(targets, "crashkernel" => "256M@64M")
381
    #
382
    # @example remove cio_ignore parameter for common kernel only
383
    #   Bootloader.modify_kernel_params("cio_ignore" => :missing)
384
    #
385
    # @example add cio_ignore parameter for xen host kernel
386
    #   Bootloader.modify_kernel_params(:xen_host, "cio_ignore" => :present)
387
    #
388
    def modify_kernel_params(*args)
1✔
389
      ReadOrProposeIfNeeded() # ensure we have data to modify
9✔
390
      current_bl = ::Bootloader::BootloaderFactory.current
9✔
391
      # currently only grub2 bootloader and systemd-boot supported
392
      if !current_bl.respond_to?(:grub_default) && !current_bl.is_a?(::Bootloader::SystemdBoot)
9✔
NEW
393
        return :missing
×
394
      end
395

396
      values = args.pop
9✔
397
      raise ArgumentError, "Missing parameters to modify #{args.inspect}" if !values.is_a? Hash
9✔
398

399
      args = [:common] if args.empty? # by default change common kernels only
8✔
400
      args = args.first if args.first.is_a? Array # support array like syntax
8✔
401

402
      if args.include?(:recovery)
8✔
UNCOV
403
        args.delete(:recovery)
×
UNCOV
404
        log.warn "recovery flavor is deprecated and not set"
×
405
      end
406

407
      remap_values = BOOLEAN_MAPPING.invert
8✔
408
      values.each_key do |key|
8✔
409
        values[key] = remap_values[values[key]] if remap_values.key?(values[key])
8✔
410
      end
411

412
      if current_bl.is_a?(::Bootloader::SystemdBoot)
8✔
NEW
413
        params = [current_bl.kernel_params]
×
414
      else
415
        grub_default = current_bl.grub_default
8✔
416
        params = args.map do |flavor|
8✔
417
          case flavor
10✔
418
          when :common then grub_default.kernel_params
4✔
419
          when :xen_guest then grub_default.xen_kernel_params
3✔
420
          when :xen_host then grub_default.xen_hypervisor_params
2✔
421
          else raise ArgumentError, "Unknown flavor #{flavor}"
1✔
422
          end
423
        end
424
      end
425

426
      changed = false
7✔
427
      values.each do |key, value|
7✔
428
        params.each do |param|
7✔
429
          old_val = param.parameter(key)
9✔
430
          next if old_val == value
9✔
431

432
          changed = true
9✔
433
          # at first clean old entries
434
          matcher = CFA::Matcher.new(key: key)
9✔
435
          param.remove_parameter(matcher)
9✔
436

437
          case value
9✔
438
          when false then next # already done
1✔
439
          when Array
440
            value.each { |val| param.add_parameter(key, val) }
3✔
441
          else
442
            param.add_parameter(key, value)
7✔
443
          end
444
        end
445
      end
446

447
      changed
7✔
448
    end
449

450
    # Get currently used bootloader, detect if not set yet
451
    # @return [String] botloader type
452
    def getLoaderType
1✔
UNCOV
453
      ::Bootloader::BootloaderFactory.current.name
×
454
    end
455

456
    # Check whether settings were read or proposed, if not, decide
457
    # what to do and read or propose settings
458
    def ReadOrProposeIfNeeded
1✔
459
      current_bl = ::Bootloader::BootloaderFactory.current
22✔
460
      return if current_bl.read? || current_bl.proposed?
22✔
461

462
      if Mode.config || (Stage.initial && !Mode.update)
20✔
463
        Propose()
2✔
464
      else
465
        progress_orig = Progress.set(false)
18✔
466
        if Stage.initial && Mode.update
18✔
467
          # SCR has been currently set to inst-sys. So we have
468
          # set the SCR to installed system in order to read
469
          # grub settings
470
          old_SCR = WFM.SCRGetDefault
1✔
471
          new_SCR = WFM.SCROpen("chroot=#{Yast::Installation.destdir}:scr",
1✔
472
            false)
473
          WFM.SCRSetDefault(new_SCR)
1✔
474
        end
475
        Read()
18✔
476
        if Stage.initial && Mode.update
18✔
477
          # settings have been read from the target system
478
          current_bl.read
1✔
479
          # reset target system to inst-sys
480
          WFM.SCRSetDefault(old_SCR)
1✔
481
          WFM.SCRClose(new_SCR)
1✔
482
        end
483
        Progress.set(progress_orig)
18✔
484
      end
485
    end
486

487
  private
1✔
488

489
    def mark_as_changed
1✔
490
      # always run mkinitrd at the end of S/390 installation (bsc#933177)
491
      # otherwise cio_ignore settings are not honored in initrd
492
      Initrd.changed = true if Arch.s390 && Stage.initial
3✔
493
    end
494

495
    NONSPLASH_VGA_VALUES = ["", "false", "ask"].freeze
1✔
496

497
    # regenerates initrd if needed
498
    # @return boolean true if succeed
499
    def write_initrd
1✔
UNCOV
500
      return true unless Initrd.changed
×
501

502
      # save initrd
UNCOV
503
      Initrd.Write
×
504
    end
505

506
    # @param section [AutoinstProfile::BootloaderSection] Bootloader section
507
    def import_bootloader(section)
1✔
508
      ::Bootloader::AutoyastConverter.import(section)
2✔
509
    rescue ::Bootloader::UnsupportedBootloader => e
510
      Yast.import "AutoInstall"
2✔
511

512
      possible_values = ::Bootloader::BootloaderFactory.supported_names +
2✔
513
        [::Bootloader::BootloaderFactory::DEFAULT_KEYWORD]
514
      Yast::AutoInstall.issues_list.add(
2✔
515
        ::Installation::AutoinstIssues::InvalidValue,
516
        section,
517
        "loader_type",
518
        e.bootloader_name,
519
        _("The selected bootloader is not supported on this architecture. Possible values: ") +
520
        possible_values.join(", "),
521
        :fatal
522
      )
523
      nil
2✔
524
    end
525

526
    publish :function => :Export, :type => "map ()"
1✔
527
    publish :function => :Import, :type => "boolean (map)"
1✔
528
    publish :function => :Propose, :type => "void ()"
1✔
529
    publish :function => :Read, :type => "boolean ()"
1✔
530
    publish :function => :Reset, :type => "void ()"
1✔
531
    publish :function => :Write, :type => "boolean ()"
1✔
532
    publish :function => :getDefaultSection, :type => "string ()"
1✔
533
    publish :function => :getLoaderType, :type => "string ()"
1✔
534
    publish :variable => :proposed_cfg_changed, :type => "boolean"
1✔
535
    publish :function => :blRead, :type => "boolean (boolean, boolean)"
1✔
536
    publish :function => :blSave, :type => "boolean (boolean, boolean, boolean)"
1✔
537
    publish :function => :blWidgetMaps, :type => "map <string, map <string, any>> ()"
1✔
538
    publish :function => :blDialogs, :type => "map <string, symbol ()> ()"
1✔
539
    publish :variable => :test_abort, :type => "boolean ()"
1✔
540
    publish :function => :Summary, :type => "list <string> ()"
1✔
541
    publish :function => :Update, :type => "boolean ()"
1✔
542
    publish :function => :WriteInstallation, :type => "boolean ()"
1✔
543
  end
544

545
  Bootloader = BootloaderClass.new
1✔
546
  Bootloader.main
1✔
547
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc