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

yast / yast-kdump / 5390716816

pending completion
5390716816

push

github

web-flow
Merge pull request #135 from jiribohac/fixed

jiribohac: Adapt yast2-kdump for new version of kdump  (bsc#1212646)

23 of 23 new or added lines in 3 files covered. (100.0%)

726 of 1810 relevant lines covered (40.11%)

4.04 hits per line

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

75.41
/src/modules/Kdump.rb
1
# encoding: utf-8
2

3
# ------------------------------------------------------------------------------
4
# Copyright (c) 2006 Novell, Inc. All Rights Reserved.
5
#
6
#
7
# This program is free software; you can redistribute it and/or modify it under
8
# the terms of version 2 of the GNU General Public License as published by the
9
# 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 FITNESS
13
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License along with
16
# this program; if not, contact Novell, Inc.
17
#
18
# To contact Novell about this file by physical or electronic mail, you may find
19
# current contact information at www.novell.com.
20
# ------------------------------------------------------------------------------
21

22
# File:        modules/Kdump.ycp
23
# Package:        Configuration of kdump
24
# Summary:        Kdump settings, input and output functions
25
# Authors:        Jozef Uhliarik <juhliarik@suse.com>
26
#
27
# $Id: Kdump.ycp 27914 2006-02-13 14:32:08Z locilka $
28
#
29
# Representation of the configuration of kdump.
30
# Input and output routines.
31
require "yast"
1✔
32
require "kdump/kdump_system"
1✔
33
require "kdump/kdump_calibrator"
1✔
34

35
require "shellwords"
1✔
36

37
module Yast
1✔
38
  class KdumpClass < Module
1✔
39
    include Yast::Logger
1✔
40

41
    FADUMP_KEY = "KDUMP_FADUMP".freeze
1✔
42
    KDUMP_SERVICE_NAME = "kdump".freeze
1✔
43
    KDUMP_PACKAGES = ["kexec-tools", "kdump"].freeze
1✔
44
    TEMPORARY_CONFIG_FILE = "/var/lib/YaST2/kdump.sysconfig".freeze
1✔
45
    TEMPORARY_CONFIG_PATH = Path.new(".temporary.sysconfig.kdump")
1✔
46

47
    # Space on disk reserved for dump additionally to memory size in bytes
48
    # @see FATE #317488
49
    RESERVED_DISK_SPACE_BUFFER_B = 4 * 1024**3
1✔
50

51
    def main
1✔
52
      textdomain "kdump"
1✔
53

54
      Yast.import "Arch"
1✔
55
      Yast.import "Bootloader"
1✔
56
      Yast.import "Directory"
1✔
57
      Yast.import "FileUtils"
1✔
58
      Yast.import "Map"
1✔
59
      Yast.import "Message"
1✔
60
      Yast.import "Mode"
1✔
61
      Yast.import "Package"
1✔
62
      Yast.import "PackagesProposal"
1✔
63
      Yast.import "Popup"
1✔
64
      Yast.import "ProductControl"
1✔
65
      Yast.import "ProductFeatures"
1✔
66
      Yast.import "Progress"
1✔
67
      Yast.import "Report"
1✔
68
      Yast.import "Service"
1✔
69
      Yast.import "SpaceCalculation"
1✔
70
      Yast.import "String"
1✔
71
      Yast.import "Summary"
1✔
72

73
      reset
1✔
74
    end
75

76
    def reset
1✔
77
      # Data was modified?
78
      @modified = false
91✔
79

80
      # kdump config file
81

82
      @kdump_file = "/etc/sysconfig/kdump"
91✔
83

84
      @proposal_valid = false
91✔
85

86
      # true if propose was called
87
      @propose_called = false
91✔
88

89
      # Boolean option indicates that "crashkernel" includes
90
      # several values for the same kind of memory (low, high)
91
      # or several ranges in one of the values
92
      #
93
      # boolean true if there are several ranges (>1) or overriden values
94
      @crashkernel_list_ranges = false
91✔
95

96
      #  list of packages for installation
97
      @kdump_packages = []
91✔
98

99
      # Boolean option indicates kernel parameter
100
      # "crashkernel"
101
      #
102
      # boolean true if kernel parameter is set
103
      @crashkernel_param = false
91✔
104

105
      # Array (or String) with the values of the kernel parameter
106
      # "crashkernel"
107
      # It can also contain :missing or :present.
108
      # See Yast::Bootloader.kernel_param for details about those special values
109
      #
110
      # array values of kernel parameter
111
      @crashkernel_param_values = []
91✔
112

113
      # array values of kernel parameter for Xen hypervisor
114
      # see @crashkernel_param_values for details
115
      @crashkernel_xen_param_values = []
91✔
116

117
      # Boolean option indicates add kernel param
118
      # "crashkernel"
119
      #
120
      # boolean true if kernel parameter will be add
121
      @add_crashkernel_param = false
91✔
122

123
      # Set of values (high and low) for allocation of memory for boot param
124
      # "crashkernel"
125
      #
126
      # hash with up to two keys (:low and :high) and string values
127
      @allocated_memory = {}
91✔
128

129
      # Boolean option indicates that Import()
130
      # was called and data was proposed
131
      #
132
      # boolean true if import was called with data
133

134
      @import_called = false
91✔
135

136
      # Write only, used during autoinstallation/autoupgrade.
137
      # Don't run services and SuSEconfig, it's all done at one place.
138
      @write_only = false
91✔
139

140
      # Abort function
141
      # return boolean return true if abort
142
      @AbortFunction = nil
91✔
143

144
      # map of deafult values for options in UI
145
      #
146
      # global map <string, string >
147

148
      @DEFAULT_CONFIG = {
149
        "KDUMP_KERNELVER"          => "",
91✔
150
        "KDUMP_CPUS"               => "1",
151
        "KDUMP_COMMANDLINE"        => "",
152
        "KDUMP_COMMANDLINE_APPEND" => "",
153
        "KDUMP_AUTO_RESIZE"        => "false",
154
        "KEXEC_OPTIONS"            => "",
155
        "KDUMP_IMMEDIATE_REBOOT"   => "true",
156
        "KDUMP_TRANSFER"           => "",
157
        "KDUMP_SAVEDIR"            => "file:///var/crash",
158
        "KDUMP_KEEP_OLD_DUMPS"     => "0",
159
        "KDUMP_FREE_DISK_SIZE"     => "64",
160
        "KDUMP_VERBOSE"            => "0",
161
        "KDUMP_DUMPLEVEL"          => "31",
162
        "KDUMP_DUMPFORMAT"         => "compressed",
163
        "KDUMP_CONTINUE_ON_ERROR"  => "true",
164
        "KDUMP_REQUIRED_PROGRAMS"  => "",
165
        "KDUMP_PRESCRIPT"          => "",
166
        "KDUMP_POSTSCRIPT"         => "",
167
        "KDUMP_NETCONFIG"          => "auto",
168
        "KDUMP_NET_TIMEOUT"        => "30",
169
        "KDUMP_SMTP_SERVER"        => "",
170
        "KDUMP_SMTP_USER"          => "",
171
        "KDUMP_SMTP_PASSWORD"      => "",
172
        "KDUMP_NOTIFICATION_TO"    => "",
173
        "KDUMP_NOTIFICATION_CC"    => "",
174
        "KDUMP_HOST_KEY"           => ""
175
      }
176

177
      # map <string, string > of kdump settings
178
      #
179
      @KDUMP_SETTINGS = {}
91✔
180

181
      # initial kdump settings replaced in Read function
182
      @initial_kdump_settings = deep_copy(@KDUMP_SETTINGS)
91✔
183
    end
184

185
    # Abort function
186
    # @return [Boolean] return true if abort
187
    def Abort
1✔
188
      return @AbortFunction.call == true if !@AbortFunction.nil?
×
189
      false
×
190
    end
191

192
    # Data was modified?
193
    # @return true if modified
194
    def GetModified
1✔
195
      Builtins.y2debug("modified=%1", @modified)
1✔
196
      @modified
1✔
197
    end
198

199
    # Set data was modified
200
    def SetModified
1✔
201
      @modified = true
1✔
202
      Builtins.y2debug("modified=%1", @modified)
1✔
203

204
      nil
205
    end
206

207
    # Function set permission for file.
208
    #
209
    # @return        [Boolean] true on success
210
    # @param        string file name
211
    # @param [String] permissions
212
    #
213
    # @example
214
    #        FileUtils::Chmod ("/etc/sysconfig/kdump", "600") -> true
215
    #        FileUtils::Chmod ("/tmp/doesnt_exist", "644") -> false
216
    def Chmod(target, permissions)
1✔
217
      if !FileUtils.Exists(target)
×
218
        Builtins.y2error("Target %1 doesn't exist", target)
×
219
        return false
×
220
      end
221

222
      if !FileUtils.Exists("/bin/chmod")
×
223
        Builtins.y2error("tool: /bin/chmod not found")
×
224
        return false
×
225
      end
226

227
      cmd = Builtins.sformat("/bin/chmod %1 %2", permissions.shellescape, target.shellescape)
×
228
      cmd_out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
229

230
      if Ops.get_integer(cmd_out, "exit", -1) != 0
×
231
        Builtins.y2error("Command >%1< returned %2", cmd, cmd_out)
×
232
        return false
×
233
      end
234
      Builtins.y2milestone("Command: %1 finish successful.", cmd)
×
235
      true
×
236
    end
237

238
    # Function check if KDUMP_SAVEDIR or
239
    # KDUMP_SMTP_PASSWORD include password
240
    #
241
    # @return [Boolean] true if inlude password
242

243
    def checkPassword
1✔
244
      return true if Ops.get(@KDUMP_SETTINGS, "KDUMP_SMTP_PASSWORD", "") != ""
×
245

246
      if Ops.get(@KDUMP_SETTINGS, "KDUMP_SAVEDIR", "").include?("file") ||
×
247
          Ops.get(@KDUMP_SETTINGS, "KDUMP_SAVEDIR", "").include?("nfs") ||
248
          Ops.get(@KDUMP_SETTINGS, "KDUMP_SAVEDIR", "") == ""
249
        return false
×
250
      end
251

252
      if !Ops.get(@KDUMP_SETTINGS, "KDUMP_SAVEDIR", "").include?("@")
×
253
        return false
×
254
      end
255

256
      temp = Builtins.splitstring(
×
257
        Ops.get(@KDUMP_SETTINGS, "KDUMP_SAVEDIR", ""),
258
        "@"
259
      )
260
      temp_1 = Ops.get(temp, 0, "")
×
261
      position = Builtins.findlastof(temp_1, ":")
×
262
      return false if position.nil?
×
263

264
      # if there is 2 times ":" -> it means that password is defined
265
      # for example cifs://login:password@server....
266
      position > 6
×
267
    end
268

269
    # Read current kdump configuration
270
    #
271
    # read kernel parameter "crashkernel"
272
    #  @return [Boolean] successfull
273
    def ReadKdumpKernelParam
1✔
274
      result = Bootloader.kernel_param(:common, "crashkernel")
51✔
275
      xen_result = Bootloader.kernel_param(:xen_host, "crashkernel")
51✔
276
      # result could be [String,Array,:missing,:present]
277
      # String   - the value of the only occurrence
278
      # Array    - the values of the multiple occurrences
279
      # :missing - crashkernel is missed
280
      # :present - crashkernel is defined but no value is available
281

282
      if result == :missing
51✔
283
        @crashkernel_param = false
5✔
284
        @add_crashkernel_param = false
5✔
285
      else
286
        @crashkernel_param = true
46✔
287
        @add_crashkernel_param = true
46✔
288
      end
289

290
      if result == :missing || result == :present
51✔
291
        @crashkernel_param_values = result
8✔
292
      else
293
        # Let's make sure it's an array
294
        # filtering nils and empty entries bnc#991140
295
        @crashkernel_param_values = Array(result).compact.reject(&:empty?)
43✔
296
        # Read the current value only if crashkernel parameter is set.
297
        # (bnc#887901)
298
        @allocated_memory = get_allocated_memory(@crashkernel_param_values)
43✔
299
      end
300

301
      if xen_result == :missing || xen_result == :present
51✔
302
        @crashkernel_xen_param_values = xen_result
8✔
303
      else
304
        # Let's make sure it's an array
305
        # filtering nils and empty entries bnc#991140
306
        @crashkernel_xen_param_values = Array(xen_result).compact.reject(&:empty?)
43✔
307
      end
308

309
      true
51✔
310
    end
311

312
    # Returns the KdumpSystem instance
313
    def system
1✔
314
      @system ||= Yast::KdumpSystem.new
×
315
    end
316

317
    def write_temporary_config_file
1✔
318
      SCR.RegisterAgent(TEMPORARY_CONFIG_PATH,
×
319
        term(:ag_ini,
320
          term(:SysConfigFile, TEMPORARY_CONFIG_FILE)))
321
      WriteKdumpSettingsTo(TEMPORARY_CONFIG_PATH, TEMPORARY_CONFIG_FILE)
×
322
      SCR.UnregisterAgent(TEMPORARY_CONFIG_PATH)
×
323
    end
324

325
    # Return the Kdump calibrator instance
326
    #
327
    # @return [Yast::KdumpCalibrator] Calibrator instance
328
    def calibrator
1✔
329
      return @calibrator unless @calibrator.nil?
82✔
330
      if Mode.normal
1✔
331
        @calibrator = Yast::KdumpCalibrator.new
1✔
332
      else
333
        write_temporary_config_file
×
334
        @calibrator = Yast::KdumpCalibrator.new(TEMPORARY_CONFIG_FILE)
×
335
      end
336
    end
337

338
    # Returns the Kdump memory limits
339
    #
340
    # It relies on the calibrator but it adjust the low memory limits when using firmware-assisted
341
    # dumps. The reason is that those limits might contradict the recommended value. See
342
    # jsc#SLE-21644 for more information.
343
    #
344
    # @return [Hash] The hash contains the following keys: :min_low, :max_low,
345
    #   :default_low, :min_high, :max_high, :default_high, :min_fadump,
346
    #    :max_fadump, :default_fadump
347
    def memory_limits
1✔
348
      calibrator.memory_limits
1✔
349
    end
350

351
    # Propose reserved/allocated memory
352
    # Store the result as a hash to @allocated_memory
353
    # @return [Boolean] true, always successful
354
    def ProposeAllocatedMemory
1✔
355
      # only propose once
356
      return true unless @allocated_memory.empty?
10✔
357

358
      @allocated_memory = { low: calibrator.default_low.to_s, high: calibrator.default_high.to_s }
8✔
359
      Builtins.y2milestone(
8✔
360
        "[kdump] allocated memory if not set in \"crashkernel\" param: %1",
361
        @allocated_memory
362
      )
363
      true
8✔
364
    end
365

366
    # Returns total size of physical memory in MiB
367
    def total_memory
1✔
368
      calibrator.total_memory
1✔
369
    end
370

371
    def log_settings_censoring_passwords(message)
1✔
372
      debug_KDUMP_SETTINGS = deep_copy(@KDUMP_SETTINGS)
2✔
373
      debug_KDUMP_SETTINGS["KDUMP_SAVEDIR"]       = "********"
2✔
374
      debug_KDUMP_SETTINGS["KDUMP_SMTP_PASSWORD"] = "********"
2✔
375

376
      log.info "-------------KDUMP_SETTINGS-------------------"
2✔
377
      log.info "#{message}; here with censored passwords: #{debug_KDUMP_SETTINGS}"
2✔
378
      log.info "---------------------------------------------"
2✔
379
    end
380

381
    # Read current kdump configuration
382
    #
383
    #  @return [Boolean] successful
384
    def ReadKdumpSettings
1✔
385
      @KDUMP_SETTINGS = deep_copy(@DEFAULT_CONFIG)
2✔
386
      SCR.Dir(path(".sysconfig.kdump")).each do |key|
2✔
387
        val = Convert.to_string(
×
388
          SCR.Read(path(".sysconfig.kdump") + key)
389
        )
390
        @KDUMP_SETTINGS[key] = val
×
391
      end
392

393
      log_settings_censoring_passwords("kdump configuration has been read")
2✔
394

395
      @initial_kdump_settings = deep_copy(@KDUMP_SETTINGS)
2✔
396

397
      true
2✔
398
    end
399

400
    # Updates initrd and reports whether it was successful.
401
    # Failed update is reported using Report library.
402
    #
403
    # @return [Boolean] whether successful
404
    def update_initrd
1✔
405
      # when /boot is ro, we need to use transactional update to be able to
406
      # rebuild initrd. In the end tu script below is used, but needs sauce
407
      # around
408
      if Package.IsTransactionalSystem
1✔
409
        return update_initrd_with("transactional-update --continue kdump")
1✔
410
      else
411
        return update_initrd_with("mkdumprd")
×
412
      end
413
    end
414

415
    # @param update_command [String] a command for .target.bash
416
    # @return [Boolean] whether successful
417
    def update_initrd_with(update_command)
1✔
418
      update_logfile = File.join(Directory.logdir, "y2logmkinitrd")
×
419

420
      run_command = update_command + " >> #{update_logfile.shellescape} 2>&1"
×
421
      y2milestone("Running command: #{run_command}")
×
422
      ret = SCR.Execute(path(".target.bash"), run_command)
×
423

424
      if ret != 0
×
425
        y2error("Error updating initrd, see #{update_logfile} or call #{update_command} manually")
×
426
        Report.Error(_(
×
427
          "Error updating initrd while calling '%{cmd}'.\n" \
428
          "See %{log} for details."
429
        ) % { :cmd => update_command, :log => update_logfile })
430
        return false
×
431
      end
432

433
      true
×
434
    end
435

436
    # Writes a file in the /etc/sysconfig/kdump format
437
    def WriteKdumpSettingsTo(scr_path, file_name)
1✔
438
      log_settings_censoring_passwords("kdump configuration for writing")
×
439

440
      @KDUMP_SETTINGS.each do |option_key, option_val|
×
441
        SCR.Write(scr_path + option_key, option_val)
×
442
      end
443
      SCR.Write(scr_path, nil)
×
444

445
      if checkPassword
×
446
        Chmod(file_name, "600")
×
447
      else
448
        Chmod(file_name, "644")
×
449
      end
450
    end
451

452
    # Write current kdump configuration
453
    #
454
    #  @return [Boolean] successful
455
    def WriteKdumpSettings
1✔
456
      WriteKdumpSettingsTo(path(".sysconfig.kdump"), @kdump_file)
×
457

458
      update_initrd
×
459
    end
460

461
    # Write kdump boot arguments - crashkernel and fadump
462
    # set kdump start at boot
463
    #
464
    #  @return [Boolean] successfull
465
    def WriteKdumpBootParameter
1✔
466
      reboot_needed = using_fadump_changed?
23✔
467

468
      # First, write or remove the fadump param if needed
469
      write_fadump_boot_param
23✔
470

471
      # Then, do the same for the crashkernel param
472
      #
473
      # If we need to add crashkernel param
474
      if @add_crashkernel_param
23✔
475
        if Mode.autoinst || Mode.autoupgrade
19✔
476
          # Use the value(s) read by import
477
          crash_values = @crashkernel_param_values
12✔
478
          crash_xen_values = @crashkernel_xen_param_values
12✔
479
          # Always write the value
480
          skip_crash_values = false
12✔
481
        else
482
          # Calculate the param values based on @allocated_memory
483
          crash_values = crash_kernel_values
7✔
484
          crash_xen_values = crash_xen_kernel_values
7✔
485
          remove_offsets!(crash_values) if Mode.update
7✔
486
          remove_offsets!(crash_xen_values) if Mode.update
7✔
487
          # Skip writing of param if it's already set to the desired values
488
          skip_crash_values = @crashkernel_param && @crashkernel_param_values == crash_values
7✔
489
          skip_crash_values &&= @crashkernel_xen_param_values && @crashkernel_xen_param_values == crash_xen_values
7✔
490
        end
491

492
        if skip_crash_values
19✔
493
          # start kdump at boot
494
          Service.Enable(KDUMP_SERVICE_NAME)
2✔
495
          Service.Restart(KDUMP_SERVICE_NAME) if Service.active?(KDUMP_SERVICE_NAME)
2✔
496
        else
497
          Bootloader.modify_kernel_params(:common, :recovery, "crashkernel" => crash_values)
17✔
498
          Bootloader.modify_kernel_params(:xen_host, "crashkernel" => crash_xen_values)
17✔
499
          # do mass write in installation to speed up, so skip this one
500
          if !Stage.initial
17✔
501
            old_progress = Progress.set(false)
17✔
502
            Bootloader.Write
17✔
503
            Progress.set(old_progress)
17✔
504
          end
505
          Builtins.y2milestone(
17✔
506
            "[kdump] (WriteKdumpBootParameter) adding crashkernel options with values: %1",
507
            crash_values
508
          )
509
          Builtins.y2milestone(
17✔
510
            "[kdump] (WriteKdumpBootParameter) adding xen crashkernel options with values: %1",
511
            crash_xen_values
512
          )
513
          reboot_needed = true
17✔
514
          Service.Enable(KDUMP_SERVICE_NAME)
17✔
515
        end
516
      else
517
        # If we don't need the param but it is there
518
        if @crashkernel_param
4✔
519
          # delete crashkernel parameter from bootloader
520
          Bootloader.modify_kernel_params(:common, :xen_guest, :recovery, :xen_host, "crashkernel" => :missing)
1✔
521
          if !Stage.initial
1✔
522
            old_progress = Progress.set(false)
1✔
523
            Bootloader.Write
1✔
524
            Progress.set(old_progress)
1✔
525
          end
526
          reboot_needed = true
1✔
527
        end
528
        Service.Disable(KDUMP_SERVICE_NAME)
4✔
529
        Service.Stop(KDUMP_SERVICE_NAME) if Service.active?(KDUMP_SERVICE_NAME)
4✔
530
      end
531

532
      if reboot_needed && Mode.normal && !Mode.commandline
23✔
533
        Popup.Message(_("To apply changes a reboot is necessary."))
18✔
534
      end
535

536
      true
23✔
537
    end
538

539
    # Read all kdump settings
540
    # @return true on success
541
    def Read
1✔
542
      # Kdump read dialog caption
543
      caption = _("Initializing kdump Configuration")
×
544
      steps = 4
×
545

546
      Progress.New(
×
547
        caption,
548
        " ",
549
        steps,
550
        [
551
          # Progress stage 1/4
552
          _("Reading the config file..."),
553
          # Progress stage 3/4
554
          _("Reading kernel boot options..."),
555
          # Progress stage 4/4
556
          _("Calculating memory limits...")
557
        ],
558
        [
559
          # Progress step 1/4
560
          _("Reading the config file..."),
561
          # Progress step 2/4
562
          _("Reading partitions of disks..."),
563
          # Progress finished 3/4
564
          _("Reading available memory and calibrating usage..."),
565
          # Progress finished 4/4
566
          Message.Finished
567
        ],
568
        ""
569
      )
570

571
      # read database
572
      return false if Abort()
×
573
      Progress.NextStage
×
574
      # Error message
575
      if !ReadKdumpSettings()
×
576
        Report.Error(_("Cannot read config file /etc/sysconfig/kdump"))
×
577
      end
578

579
      # read another database
580
      return false if Abort()
×
581
      Progress.NextStep
×
582
      # Error message
583
      if !ReadKdumpKernelParam()
×
584
        Report.Error(_("Cannot read kernel boot options."))
×
585
      end
586

587
      # read another database
588
      return false if Abort()
×
589
      Progress.NextStep
×
590
      ProposeAllocatedMemory()
×
591
      # Error message
592
      Report.Error(_("Cannot read available memory.")) if total_memory.zero?
×
593

594
      return false if Abort()
×
595
      # Progress finished
596
      Progress.NextStage
×
597

598
      return false if Abort()
×
599
      @modified = false
×
600
      true
×
601
    end
602

603
    # Update crashkernel argument during update of OS
604
    # @return true on success
605

606
    def Update
1✔
607
      Builtins.y2milestone("Update kdump settings")
2✔
608
      ReadKdumpKernelParam() unless Mode.autoupgrade
2✔
609
      WriteKdumpBootParameter()
2✔
610
      true
2✔
611
    end
612

613
    # Write all kdump settings
614
    # @return true on success
615
    def Write
1✔
616
      # Kdump read dialog caption
617
      caption = _("Saving kdump Configuration")
×
618

619
      # number of stages
620
      steps = 2
×
621
      if (Mode.installation || Mode.autoinst) && !@add_crashkernel_param
×
622
        Builtins.y2milestone(
×
623
          "Skip writing of configuration for kdump during installation"
624
        )
625
        return true
×
626
      end
627

628
      # We do not set help text here, because it was set outside
629
      Progress.New(
×
630
        caption,
631
        " ",
632
        steps,
633
        [
634
          # Progress stage 1/2
635
          _("Write the settings"),
636
          # Progress stage 2/2
637
          _("Update boot options")
638
        ],
639
        [
640
          # Progress step 1/2
641
          _("Writing the settings..."),
642
          # Progress step 2/2
643
          _("Updating boot options..."),
644
          # Progress finished
645
          _("Finished")
646
        ],
647
        ""
648
      )
649

650
      # write settings
651
      return false if Abort()
×
652
      Progress.NextStage
×
653
      # Error message
654
      if !WriteKdumpSettings()
×
655
        Report.Error(_("Cannot write settings."))
×
656
        return false
×
657
      end
658

659
      # write/delete bootloader options for kernel - "crashkernel" and "fadump"
660
      return false if Abort()
×
661
      Progress.NextStage
×
662
      # Error message
663
      if !WriteKdumpBootParameter()
×
664
        Report.Error(_("Adding crashkernel parameter to bootloader fault."))
×
665
      end
666

667
      return false if Abort()
×
668
      # Progress finished
669
      Progress.NextStage
×
670

671
      return false if Abort()
×
672
      true
×
673
    end
674

675
    # Adding necessary packages for installation
676
    #
677

678
    def AddPackages
1✔
679
      return unless Mode.installation
1✔
680

681
      @kdump_packages.concat KDUMP_PACKAGES
×
682
    end
683

684
    # Proposes default state of kdump (enabled/disabled)
685
    #
686
    # @return [Boolean] the default proposed state
687

688
    def ProposeCrashkernelParam
1✔
689
      # proposing disabled kdump if product wants it (bsc#1071242)
690
      if !ProductFeatures.GetBooleanFeature("globals", "enable_kdump")
5✔
691
        log.info "Kdump disabled in control file"
1✔
692
        false
1✔
693
      # proposing disabled kdump if PC has less than 1024MB RAM
694
      elsif total_memory < 1024
4✔
695
        log.info "not enough memory - kdump proposed as disabled"
1✔
696
        false
1✔
697
      # proposing disabled kdump on aarch64 (bsc#989321) - kdump not implemented
698
      elsif Arch.aarch64
3✔
699
        log.info "aarch64 - kdump proposed as disabled"
1✔
700
        false
1✔
701
      else
702
        true
2✔
703
      end
704
    end
705

706
    # Propose global variables once...
707
    # after that remember user settings
708

709
    def ProposeGlobalVars
1✔
710
      # Settings have not been imported by AutoYaST and have not already
711
      # been suggested by proposal. (bnc#930950, bnc#995750, bnc#890719).
712
      if !@propose_called && !@import_called
1✔
713
        # added default settings
714
        @KDUMP_SETTINGS = deep_copy(@DEFAULT_CONFIG)
×
715
        @add_crashkernel_param = ProposeCrashkernelParam()
×
716
        @crashkernel_param = false
×
717
      end
718
      @propose_called = true
1✔
719

720
      nil
721
    end
722

723
    # Check if user enabled kdump
724
    # if no deselect packages for installing
725
    # if yes add necessary packages for installation
726
    def CheckPackages
1✔
727
      # remove duplicates
728
      @kdump_packages.uniq!
1✔
729
      if !@add_crashkernel_param
1✔
730
        Builtins.y2milestone(
×
731
          "deselect packages for installation: %1",
732
          @kdump_packages
733
        )
734
        @kdump_packages.each do |p|
×
735
          PackagesProposal.RemoveResolvables("yast2-kdump", :package, [p])
×
736
        end
737
        if !@kdump_packages.empty?
×
738
          Builtins.y2milestone(
×
739
            "Deselected kdump packages for installation: %1",
740
            @kdump_packages
741
          )
742
        end
743
      else
744
        Builtins.y2milestone(
1✔
745
          "select packages for installation: %1",
746
          @kdump_packages
747
        )
748
        @kdump_packages.each do |p|
1✔
749
          PackagesProposal.AddResolvables("yast2-kdump", :package, [p])
×
750
        end
751
        if !@kdump_packages.empty?
1✔
752
          Builtins.y2milestone(
×
753
            "Selected kdump packages for installation: %1",
754
            @kdump_packages
755
          )
756
        end
757
      end
758

759
      nil
760
    end
761

762
    # Propose all kdump settings
763
    #
764
    def Propose
1✔
765
      Builtins.y2milestone("Proposing new settings of kdump")
1✔
766
      # set default values for global variables
767
      ProposeGlobalVars()
1✔
768
      # check available memory and execute the calibrator
769
      ProposeAllocatedMemory()
1✔
770
      # add packages for installation
771
      AddPackages()
1✔
772
      # select packages for installation
773
      CheckPackages()
1✔
774

775
      nil
776
    end
777

778
    # Create a textual summary
779
    # @return summary of the current configuration
780
    def Summary
1✔
781
      result = []
1✔
782
      result = Builtins.add(
1✔
783
        result,
784
        Builtins.sformat(
785
          _("Kdump status: %1"),
786
          @add_crashkernel_param ? _("enabled") : _("disabled")
1✔
787
        )
788
      )
789
      if @add_crashkernel_param
1✔
790
        result = Builtins.add(
×
791
          result,
792
          Builtins.sformat(
793
            _("Value(s) of crashkernel option: %1"),
794
            crash_kernel_values.join(" ")
795
          )
796
        )
797
        result = Builtins.add(
×
798
          result,
799
          Builtins.sformat(
800
            _("Dump format: %1"),
801
            Ops.get(@KDUMP_SETTINGS, "KDUMP_DUMPFORMAT", "")
802
          )
803
        )
804
        result = Builtins.add(
×
805
          result,
806
          Builtins.sformat(
807
            _("Target of dumps: %1"),
808
            Ops.get(@KDUMP_SETTINGS, "KDUMP_SAVEDIR", "")
809
          )
810
        )
811
        result = Builtins.add(
×
812
          result,
813
          Builtins.sformat(
814
            _("Number of dumps: %1"),
815
            Ops.get(@KDUMP_SETTINGS, "KDUMP_KEEP_OLD_DUMPS", "")
816
          )
817
        )
818
      end
819
      deep_copy(result)
1✔
820
    end
821

822
    # Returns available space (in bytes) for Kernel dump according to KDUMP_SAVEDIR option
823
    # only local space is evaluated (starts with file://)
824
    #
825
    # @return [Integer] free space in bytes or nil if filesystem is not local or no
826
    #                   packages proposal is made yet
827
    def free_space_for_dump_b
1✔
828
      kdump_savedir = @KDUMP_SETTINGS.fetch("KDUMP_SAVEDIR", "file:///var/log/dump")
8✔
829
      log.info "Using savedir #{kdump_savedir}"
8✔
830

831
      if kdump_savedir.start_with?("/")
8✔
832
        log.warn "Using old format"
1✔
833
      elsif kdump_savedir.start_with?("file://")
7✔
834
        kdump_savedir.sub!(/file:\/\//, "")
5✔
835
      else
836
        log.info "KDUMP_SAVEDIR #{kdump_savedir.inspect} is not local"
2✔
837
        return nil
2✔
838
      end
839

840
      # unified format of directory
841
      kdump_savedir = format_dirname(kdump_savedir)
6✔
842

843
      partitions_info = SpaceCalculation.GetPartitionInfo()
6✔
844
      if partitions_info.empty?
6✔
845
        log.warn "No partitions info available"
1✔
846
        return nil
1✔
847
      end
848

849
      log.info "Disk usage: #{partitions_info}"
5✔
850
      # Create a hash of partitions and their free space { partition => free_space, ... }
851
      # "name" usually does not start with "/", but does so for root filesystem
852
      # File.join ensures that paths do not contain dulplicit "/" characters
853
      partitions_info = partitions_info.map do |partition|
5✔
854
        { format_dirname(partition["name"]) => partition["free"] }
15✔
855
      end.inject(:merge)
856

857
      # All partitions matching KDUMP_SAVEDIR
858
      matching_partitions = partitions_info.select do |partition, _space|
5✔
859
        kdump_savedir.start_with?(partition)
15✔
860
      end
861

862
      # The longest match
863
      partition = matching_partitions.keys.sort_by { |partiton| partiton.length }.last
13✔
864
      free_space = matching_partitions[partition]
5✔
865

866
      if free_space.nil? || !free_space.is_a?(::Integer)
5✔
867
        log.warn "Available space for partition #{partition} not provided (#{free_space.inspect})"
2✔
868
        return nil
2✔
869
      end
870

871
      # packager counts in kB, we need bytes
872
      free_space *= 1024
3✔
873
      log.info "Available space for dump: #{free_space} bytes in #{partition} directory"
3✔
874

875
      free_space
3✔
876
    end
877

878
    # Returns disk space in bytes requested for kernel dump (as defined in FATE#317488)
879
    #
880
    # @return [Integer] bytes
881
    def space_requested_for_dump_b
1✔
882
      # Total memory is in MB, converting to bytes
883
      total_memory * 1024**2 + RESERVED_DISK_SPACE_BUFFER_B
1✔
884
    end
885

886
    # Returns installation proposal warning as part of the MakeProposal map result
887
    # includes 'warning' and 'warning_level' keys
888
    #
889
    # @param returns [Hash] with warnings
890
    def proposal_warning
1✔
891
      return {} unless @add_crashkernel_param
3✔
892

893
      free_space = free_space_for_dump_b
2✔
894
      requested_space = space_requested_for_dump_b
2✔
895

896
      log.info "Free: #{free_space}, requested: #{requested_space}"
2✔
897
      return {} if free_space.nil? || requested_space.nil?
2✔
898

899
      warning = {}
2✔
900

901
      if free_space < requested_space
2✔
902
        warning = {
903
          "warning_level" => :warning,
1✔
904
          # TRANSLATORS: warning message in installation proposal. Do not translate %{requested} and
905
          # %{available} - they are replaced with actual sizes later.
906
          "warning"       => "<ul><li>" + _(
907
            "Warning! There might not be enough free space to have kdump enabled. " \
908
            "%{required} required for saving a kernel dump, but only %{available} are available."
909
          ) % {
910
            required:  String.FormatSizeWithPrecision(requested_space, 2, true),
911
            available: String.FormatSizeWithPrecision(free_space, 2, true)
912
          } + "</li></ul>"
913
        }
914
      end
915

916
      log.warn warning["warning"] if warning["warning"]
2✔
917
      warning
2✔
918
    end
919

920
    # bnc# 480466 - fix problem with validation autoyast profil
921
    # Function filters keys for autoyast profil
922
    #
923
    # @param map <string, string > KDUMP_SETTINGS
924
    # @return [Hash{String => String}] filtered KDUMP_SETTINGS by DEFAULT_CONFIG
925

926
    def filterExport(settings)
1✔
927
      settings = deep_copy(settings)
1✔
928
      keys = Map.Keys(@DEFAULT_CONFIG)
1✔
929
      Builtins.filter(settings) do |key, _value|
1✔
930
        Builtins.contains(keys, key)
26✔
931
      end
932
    end
933

934
    # Export kdump settings to a map
935
    # @return kdump settings
936
    def Export
1✔
937
      if @add_crashkernel_param
3✔
938
        crash_kernel = crash_kernel_values
1✔
939
        crash_kernel = crash_kernel[0] if crash_kernel.size == 1
1✔
940
        crash_xen_kernel = crash_xen_kernel_values
1✔
941
        crash_xen_kernel = crash_xen_kernel[0] if crash_xen_kernel.size == 1
1✔
942
        out = {
943
          "crash_kernel"     => crash_kernel,
1✔
944
          "crash_xen_kernel" => crash_xen_kernel,
945
          "add_crash_kernel" => true,
946
          "general"          => filterExport(@KDUMP_SETTINGS)
947
        }
948
      else
949
        out = { "add_crash_kernel" => false }
2✔
950
      end
951

952
      Builtins.y2milestone("Kdump exporting settings: %1", out)
3✔
953
      deep_copy(out)
3✔
954
    end
955

956
    # Import settings from a map
957
    # @param [Hash, nil] settings map of kdump settings
958
    # @return [Boolean] true on success
959
    def Import(settings)
1✔
960
      settings ||= {}
16✔
961
      Builtins.y2milestone("Importing settings for kdump #{settings.inspect}")
16✔
962

963
      my_import_map = Ops.get_map(settings, "general", {})
16✔
964
      @DEFAULT_CONFIG.each_pair do |key, def_value|
16✔
965
        value = my_import_map[key]
416✔
966
        @KDUMP_SETTINGS[key] = value.nil? ? def_value : value
416✔
967
      end
968

969
      if settings.key?("crash_kernel")
16✔
970
        # Make sure it's an array
971
        @crashkernel_param_values = Array(settings.fetch("crash_kernel", ""))
9✔
972
        # In order not to overwrite the values by the proposal we will have to set
973
        # according allocated memory too. (bnc#995750)
974
        @allocated_memory = get_allocated_memory(@crashkernel_param_values)
9✔
975
      else
976
        # Taking proposed values (bnc#997448)
977
        ProposeAllocatedMemory()
7✔
978
        # Make sure it's an array
979
        @crashkernel_param_values = Array(crash_kernel_values)
7✔
980
      end
981

982
      if settings.key?("crash_xen_kernel")
16✔
983
        # Make sure it's an array
984
        @crashkernel_xen_param_values = Array(settings.fetch("crash_xen_kernel", ""))
6✔
985
      else
986
        @crashkernel_xen_param_values = Array(crash_xen_kernel_values)
10✔
987
      end
988

989
      @add_crashkernel_param = if settings.key?("add_crash_kernel")
16✔
990
        settings["add_crash_kernel"]
15✔
991
      else
992
        ProposeCrashkernelParam()
1✔
993
      end
994

995
      if settings.key?("crash_kernel") || settings.key?("add_crash_kernel") ||
16✔
996
          !my_import_map.empty?
997
        @import_called = true
15✔
998
      end
999

1000
      true
16✔
1001
    end
1002

1003
    # Sets whether to use FADump (Firmware assisted dump)
1004
    #
1005
    # @param [Boolean] new state
1006
    # @return [Boolean] whether successfully set
1007
    def use_fadump(new_value)
1✔
1008
      # Trying to use fadump on unsupported hardware
1009
      if !fadump_supported? && new_value
7✔
1010
        Builtins.y2milestone("FADump is not supported on this hardware")
1✔
1011
        Report.Error(_("Cannot use Firmware-assisted dump.\nIt is not supported on this hardware."))
1✔
1012
        return false
1✔
1013
      end
1014

1015
      @KDUMP_SETTINGS[FADUMP_KEY] = (new_value ? "true" : "false")
6✔
1016
      true
6✔
1017
    end
1018

1019
    # Returns whether FADump (Firmware assisted dump) is currently in use
1020
    #
1021
    # @return [Boolean] currently in use
1022
    def using_fadump?
1✔
1023
      ["yes", "true", "1"].include?(@KDUMP_SETTINGS[FADUMP_KEY])
3✔
1024
    end
1025

1026
    # Has the using_fadump? been changed?
1027
    #
1028
    # @return [Boolean] whether changed
1029
    def using_fadump_changed?
1✔
1030
      @initial_kdump_settings[FADUMP_KEY] != @KDUMP_SETTINGS[FADUMP_KEY]
25✔
1031
    end
1032

1033
    # Returns whether usage of high memory in the crashkernel bootloader param
1034
    # is supported by the current system
1035
    #
1036
    # @return [Boolean] is supported
1037
    def high_memory_supported?
1✔
1038
      calibrator.high_memory_supported?
×
1039
    end
1040

1041
    # Returns whether usage of fadump is supported by the current system
1042
    #
1043
    # @return [Boolean] is supported
1044
    def fadump_supported?
1✔
1045
      calibrator.fadump_supported?
5✔
1046
    end
1047

1048
    publish :function => :GetModified, :type => "boolean ()"
1✔
1049
    publish :function => :SetModified, :type => "void ()"
1✔
1050
    publish :variable => :modified, :type => "boolean"
1✔
1051
    publish :variable => :proposal_valid, :type => "boolean"
1✔
1052
    publish :variable => :propose_called, :type => "boolean"
1✔
1053
    publish :function => :total_memory, :type => "integer ()"
1✔
1054
    publish :variable => :crashkernel_list_ranges, :type => "boolean"
1✔
1055
    publish :variable => :kdump_packages, :type => "list <string>"
1✔
1056
    publish :variable => :crashkernel_param, :type => "boolean"
1✔
1057
    publish :variable => :add_crashkernel_param, :type => "boolean"
1✔
1058
    publish :variable => :allocated_memory, :type => "map"
1✔
1059
    publish :function => :memory_limits, :type => "map ()"
1✔
1060
    publish :variable => :import_called, :type => "boolean"
1✔
1061
    publish :variable => :write_only, :type => "boolean"
1✔
1062
    publish :variable => :AbortFunction, :type => "boolean ()"
1✔
1063
    publish :variable => :DEFAULT_CONFIG, :type => "map <string, string>"
1✔
1064
    publish :variable => :KDUMP_SETTINGS, :type => "map <string, string>"
1✔
1065
    publish :function => :Abort, :type => "boolean ()"
1✔
1066
    publish :function => :Read, :type => "boolean ()"
1✔
1067
    publish :function => :Update, :type => "boolean ()"
1✔
1068
    publish :function => :Write, :type => "boolean ()"
1✔
1069
    publish :function => :CheckPackages, :type => "void ()"
1✔
1070
    publish :function => :Propose, :type => "void ()"
1✔
1071
    publish :function => :Summary, :type => "list <string> ()"
1✔
1072
    publish :function => :Export, :type => "map ()"
1✔
1073
    publish :function => :Import, :type => "boolean (map)"
1✔
1074

1075
    # Offer this to ensure backward compatibility
1076
    def allocated_memory=(memory)
1✔
1077
      @allocated_memory = if memory.is_a?(::String)
10✔
1078
        if memory.empty?
3✔
1079
          {}
1✔
1080
        else
1081
          { low: memory }
2✔
1082
        end
1083
      else
1084
        memory
7✔
1085
      end
1086
    end
1087

1088
  private
1✔
1089

1090
    # Returns unified directory name with leading and ending "/"
1091
    # for exact matching
1092
    def format_dirname(dirname)
1✔
1093
      "/#{dirname}/".gsub(/\/+/, "/")
21✔
1094
    end
1095

1096
    # get allocated memory from the set of values of the crashkernel option
1097
    #
1098
    # each value can be a set of ranges (first range will be taken) or a
1099
    # concrete value for high or low memory
1100
    # syntax for ranges: 64M@16M or 128M-:64M@16M [(reserved_memory*2)-:reserved_memory]
1101
    # syntax for concrete value: 64M or 64M,high or 64M,low
1102
    #
1103
    #  @param crash_values [Array<string>] list of values
1104
    #  @return [Hash] values of allocated memory ({low: "64", high: "16"})
1105
    def get_allocated_memory(crash_values)
1✔
1106
      result = {}
52✔
1107
      crash_values.each do |crash_value|
52✔
1108
        pieces = crash_value.split(",")
62✔
1109

1110
        if pieces.last =~ /^(low|high)$/i
62✔
1111
          key = pieces.last.downcase.to_sym
28✔
1112
          @crashkernel_list_ranges ||= (pieces.size > 2)
28✔
1113
        else
1114
          key = :low
34✔
1115
          @crashkernel_list_ranges ||= (pieces.size > 1)
34✔
1116
        end
1117
        # Skip everything but the first occurrence
1118
        if result[key]
62✔
1119
          @crashkernel_list_ranges = true
4✔
1120
          next
4✔
1121
        end
1122

1123
        range = pieces.first
58✔
1124
        Builtins.y2milestone("The 1st range from crashkernel is %1", range)
58✔
1125
        value = range.split(":").last.split("M").first
58✔
1126
        result[key] = value
58✔
1127
      end
1128
      Builtins.y2milestone("Allocated memory is %1", result)
52✔
1129
      result
52✔
1130
    end
1131

1132
    # Build crashkernel values from allocated memory
1133
    #
1134
    # @return [Array<String>] list of values of crashkernel
1135
    def crash_kernel_values
1✔
1136
      # If the current values include "nasty" things and the user has not
1137
      # overriden the value of @crashkernel_list_ranges to autorize the
1138
      # modification.
1139
      # The old value (ensuring the Array format) will be returned.
1140
      if @crashkernel_list_ranges
15✔
1141
        return Array(@crashkernel_param_values.to_s) if @crashkernel_param_values.is_a?(Symbol)
2✔
1142

1143
        return Array(@crashkernel_param_values.dup)
2✔
1144
      end
1145

1146
      result = []
13✔
1147
      if ["yes", "true", "1"].include?(@KDUMP_SETTINGS["KDUMP_AUTO_RESIZE"])
13✔
1148
        maxsize = total_memory / 2
×
1149
        if high_memory_supported?
×
1150
          low = memory_limits[:default_low]
×
1151
          high = memory_limits[:max_high]
×
1152
          high = (maxsize - low.to_i).to_s if high.to_i > maxsize
×
1153
        else
1154
          high = memory_limits[:min_high]
×
1155
          low = memory_limits[:max_low]
×
1156
          low = maxsize.to_s if low.to_i > maxsize
×
1157
        end
1158
      else
1159
        high = @allocated_memory[:high]
13✔
1160
        low = @allocated_memory[:low]
13✔
1161
      end
1162
      result << "#{high}M,high" if high && high.to_i != 0
13✔
1163
      # Add the ',low' suffix only there is a ',high' one
1164
      result << (result.empty? ? "#{low}M" : "#{low}M,low") if low && low.to_i != 0
13✔
1165

1166
      log.info "built crashkernel values are #{result}"
13✔
1167

1168
      result
13✔
1169
    end
1170

1171
    def crash_xen_kernel_values
1✔
1172
      # If the current values include "nasty" things and the user has not
1173
      # overriden the value of @crashkernel_list_ranges to autorize the
1174
      # modification.
1175
      # The old value (ensuring the Array format) will be returned.
1176
      if @crashkernel_list_ranges
18✔
1177
        if @crashkernel_xen_param_values.is_a?(Symbol)
2✔
1178
          return Array(@crashkernel_xen_param_values.to_s)
×
1179
        else
1180
          return Array(@crashkernel_xen_param_values.dup)
2✔
1181
        end
1182
      end
1183

1184
      result = []
16✔
1185
      if ["yes", "true", "1"].include?(@KDUMP_SETTINGS["KDUMP_AUTO_RESIZE"])
16✔
1186
        high = memory_limits[:default_high]
×
1187
        low = memory_limits[:default_low]
×
1188
      else
1189
        high = @allocated_memory[:high]
16✔
1190
        low = @allocated_memory[:low]
16✔
1191
      end
1192
      sum = 0
16✔
1193
      sum += low.to_i if low
16✔
1194
      sum += high.to_i if high
16✔
1195

1196
      result << "#{sum}M\\<4G" if sum != 0
16✔
1197

1198
      log.info "built xen crashkernel values are #{result}"
16✔
1199

1200
      result
16✔
1201
    end
1202

1203
    # Removes offsets from all the crashkernel values
1204
    #
1205
    # Beware: not functional, it modifies the passed argument
1206
    #
1207
    # @param values [Array,Symbol] list of values or one of the special values
1208
    #       returned by Bootloader.kernel_param
1209
    def remove_offsets!(values)
1✔
1210
      # It could also be :missing or :present
1211
      if values.is_a?(Array)
4✔
1212
        values.map! do |value|
4✔
1213
          pieces = value.split("@")
4✔
1214
          if pieces.size > 1
4✔
1215
            Builtins.y2milestone("Delete offset crashkernel value: %1", value)
2✔
1216
          end
1217
          pieces.first
4✔
1218
        end
1219
      end
1220
    end
1221

1222
    def write_fadump_boot_param
1✔
1223
      if fadump_supported?
23✔
1224
        # If fdump is selected and we want to enable kdump
1225
        value = "on" if using_fadump? && @add_crashkernel_param
×
1226
        Bootloader.modify_kernel_params(:common, :recovery, "fadump" => value)
×
1227
        Bootloader.Write unless Yast::Stage.initial # do mass write in installation to speed up
×
1228
      end
1229
    end
1230
  end
1231

1232
  Kdump = KdumpClass.new
1✔
1233
  Kdump.main
1✔
1234
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