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

yast / yast-autoinstallation / 13494013411

24 Feb 2025 08:44AM UTC coverage: 69.018% (+0.03%) from 68.993%
13494013411

push

github

web-flow
Merge pull request #881 from yast/merge_SLE-15-SP7

Added pervasive encryption fields (master)

6429 of 9315 relevant lines covered (69.02%)

10.35 hits per line

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

82.17
/src/modules/AutoinstConfig.rb
1
# File:  modules/AutoinstConfig.ycp
2
# Module:  Auto-Installation
3
# Summary:  This module handles the configuration for auto-installation
4
# Authors:  Anas Nashif <nashif@suse.de>
5
#
6
# $Id$
7
require "yast"
1✔
8
require "y2packager/product"
1✔
9

10
module Yast
1✔
11
  import "ServicesManagerTarget"
1✔
12

13
  class AutoinstConfigClass < Module
1✔
14
    attr_reader(:dont_edit)
1✔
15

16
    module Target
1✔
17
      include ServicesManagerTargetClass::BaseTargets
1✔
18
    end
19

20
    DEFAULT_PROFILE_NAME = "autoinst.xml".freeze
1✔
21

22
    include Yast::Logger
1✔
23

24
    def main
1✔
25
      Yast.import "UI"
48✔
26
      textdomain "autoinst"
48✔
27

28
      Yast.import "Misc"
48✔
29
      Yast.import "Mode"
48✔
30
      Yast.import "Installation"
48✔
31
      Yast.import "URL"
48✔
32
      Yast.import "SLP"
48✔
33
      Yast.import "Stage"
48✔
34
      Yast.import "Label"
48✔
35
      Yast.import "Report"
48✔
36

37
      Yast.include self, "autoinstall/xml.rb"
48✔
38

39
      @runModule = ""
48✔
40

41
      # Profile Repository
42
      @Repository = ""
48✔
43

44
      @ProfileEncrypted = false
48✔
45
      @ProfilePassword = ""
48✔
46

47
      # Package Repository
48
      @PackageRepository = ""
48✔
49

50
      # Classes
51
      @classDir = ""
48✔
52

53
      # Current file name
54
      @currentFile = ""
48✔
55

56
      #
57
      # Temporary directory for storing profile before installation starts
58
      #
59
      @tmpDir = Convert.to_string(SCR.Read(path(".target.tmpdir")))
48✔
60

61
      #
62
      # Main directory for data generated during installation
63
      #
64
      @var_dir = "/var/adm/autoinstall"
48✔
65

66
      #
67
      # Directory for the pre/post and chroot scripts
68
      #
69
      @scripts_dir = Ops.add(@var_dir, "/scripts")
48✔
70
      @initscripts_dir = Ops.add(@var_dir, "/init.d")
48✔
71

72
      #
73
      # Directory where log files of pre/post and chroot scripts are kept
74
      #
75
      @logs_dir = Ops.add(@var_dir, "/logs")
48✔
76

77
      #
78
      # Destination dir
79
      #
80
      @destdir = Installation.destdir
48✔
81

82
      #
83
      # Cache directory
84
      #
85
      @cache = Ops.add(@var_dir, "/cache")
48✔
86

87
      #
88
      # Temporary file name for retrieved system profile
89
      #
90
      @xml_tmpfile = Ops.add(@tmpDir, "/autoinst.xml")
48✔
91

92
      #
93
      # Final location for retrieved system profile
94
      #
95
      @xml_file = Ops.add(@cache, "/installedSystem.xml")
48✔
96

97
      #
98
      # Direcotry for runtime operation data
99
      #
100
      @runtime_dir = "/var/lib/autoinstall"
48✔
101

102
      #
103
      # Directory where complete configuration files are kept.
104
      #
105
      @files_dir = Ops.add(@var_dir, "/files")
48✔
106

107
      #
108
      # Directory to store profile for possible user manipulation.
109
      #
110
      @profile_dir = "/tmp/profile"
48✔
111

112
      #
113
      # The user  modified version of the Profile
114
      #
115
      @modified_profile = Ops.add(@profile_dir, "/modified.xml")
48✔
116

117
      @autoconf_file = Ops.add(@runtime_dir, "/autoconf/autoconf.xml")
48✔
118

119
      #
120
      # Parsed data from XML control in YCP format
121
      #
122
      @parsedControlFile = Ops.add(@cache, "/autoinst.ycp")
48✔
123

124
      @remote_rules_location = "rules/rules.xml"
48✔
125
      @local_rules_location = Ops.add(@tmpDir, "/rules")
48✔
126
      @local_rules_file = Ops.add(@local_rules_location, "/rules.xml")
48✔
127

128
      # Data from command line
129
      @urltok = {}
48✔
130

131
      @scheme = ""
48✔
132
      @host = ""
48✔
133
      @filepath = ""
48✔
134
      @directory = ""
48✔
135
      @port = ""
48✔
136
      @user = ""
48✔
137
      @pass = ""
48✔
138

139
      #
140
      # Default systemd target
141
      #
142
      @default_target = Target::GRAPHICAL
48✔
143

144
      #
145
      # Confirm installation
146
      #
147
      @Confirm = true
48✔
148

149
      #
150
      # S390
151
      #
152
      @cio_ignore = true
48✔
153

154
      # Running autoyast second_stage
155
      @second_stage = true
48✔
156

157
      @OriginalURI = ""
48✔
158

159
      @message = ""
48✔
160

161
      # Class merging.
162
      # lists not to be merged, instead they will be "added"
163
      #
164
      @dontmerge = []
48✔
165

166
      # the "writo setting now" button is disabled for there modules
167
      #
168
      #
169
      @noWriteNow = []
48✔
170

171
      # Edit button is disabled for these modules
172
      @dont_edit = []
48✔
173

174
      #
175
      # Halt after initial phase
176
      #
177
      @Halt = false
48✔
178

179
      #
180
      # Dont Hard Reboot
181
      #
182
      @ForceBoot = false
48✔
183

184
      #
185
      # Show Reboot Message
186
      #
187
      @RebootMsg = false
48✔
188

189
      #
190
      # AutoYaST profile is stored in the root partition (for upgrade)
191
      #
192
      @ProfileInRootPart = false
48✔
193

194
      #
195
      # remote profile (invented for pre-probing of s390)
196
      # in case of a remote profile, the profile can be fetched
197
      # before the probing stage DASD module can has run
198
      #
199
      @remoteProfile = true
48✔
200
      @Proposals = []
48✔
201

202
      Yast.include self, "autoinstall/io.rb"
48✔
203
      AutoinstConfig()
48✔
204
    end
205

206
    def getProposalList
1✔
207
      deep_copy(@Proposals)
×
208
    end
209

210
    def setProposalList(l)
1✔
211
      l = deep_copy(l)
19✔
212
      @Proposals = deep_copy(l)
19✔
213

214
      nil
19✔
215
    end
216

217
    # Searches for 'autoyast' via SLP and returns the full URL of
218
    # the profile. If more providers are found, user is asked to
219
    # select one.
220
    #
221
    # FIXME: This function has been intentionally left (almost) intact
222
    # and needs refactoring
223
    #
224
    # @return [String] profile location or 'nil' if nothing is found
225
    def find_slp_autoyast
1✔
226
      profile_location = nil
3✔
227

228
      slpData = SLP.FindSrvs("autoyast", "")
3✔
229

230
      # SLP data returned by SLP server contain the service ID, colon
231
      # and then the URL of that service
232
      url_starts_at = "service.autoyast:".size
3✔
233

234
      # More providers to choose from
235
      if Ops.greater_than(Builtins.size(slpData), 1)
3✔
236
        dummy = []
1✔
237
        comment2url = {}
1✔
238
        Builtins.foreach(slpData) do |m|
1✔
239
          attrList = SLP.FindAttrs(Ops.get_string(m, "srvurl", ""))
2✔
240

241
          url = Builtins.substring(Ops.get_string(m, "srvurl", ""), url_starts_at)
2✔
242
          if Ops.greater_than(Builtins.size(attrList), 0)
2✔
243
            # FIXME: that's really lazy coding here but I allow only one attribute currently anyway
244
            #        so it's lazy but okay. No reason to be too strict here with the checks
245
            #        As soon as more than one attr is possible, I need to iterate over the attr list
246
            #
247
            comment = Ops.get(attrList, 0, "")
×
248
            # The line above needs to be fixed when we have more attributes
249

250
            # comment will look like this: "(description=BLA BLA)"
251
            startComment = Builtins.findfirstof(comment, "=")
×
252
            endComment = Builtins.findlastof(comment, ")")
×
253

254
            comment = if !startComment.nil? && !endComment.nil? &&
×
255
                Ops.greater_than(
256
                  Ops.subtract(Ops.subtract(endComment, startComment), 1),
257
                  0
258
                )
259
              Builtins.substring(
×
260
                comment,
261
                Ops.add(startComment, 1),
262
                Ops.subtract(Ops.subtract(endComment, startComment), 1)
263
              )
264
            else
265
              ""
×
266
            end
267

268
            if Ops.less_than(Builtins.size(comment), 1)
×
269
              comment = Builtins.sformat(
×
270
                "bad description in SLP for %1",
271
                url
272
              )
273
            end
274

275
            dummy = Builtins.add(dummy, Item(comment, false))
×
276
            Ops.set(comment2url, comment, url)
×
277
          else
278
            dummy = Builtins.add(dummy, Item(url, false))
2✔
279
            Ops.set(comment2url, url, url)
2✔
280
          end
281
        end
282

283
        dlg = Left(ComboBox(Id(:choose), _("Choose Profile"), dummy))
1✔
284

285
        UI.OpenDialog(VBox(dlg, PushButton(Id(:ok), Label.OKButton)))
1✔
286
        UI.UserInput
1✔
287

288
        profile_location = Ops.get(
1✔
289
          comment2url,
290
          Convert.to_string(UI.QueryWidget(Id(:choose), :Value)),
291
          ""
292
        )
293

294
        UI.CloseDialog
1✔
295

296
      # just one provider
297
      elsif Builtins.size(slpData) == 1
2✔
298
        profile_location = Builtins.substring(
1✔
299
          Ops.get_string(slpData, [0, "srvurl"], ""),
300
          17
301
        )
302

303
      # Nothing returned by SLP query
304
      else
305
        log.error "slp query for 'autoyast' failed"
1✔
306
        Report.Error(_("No 'autoyast' provider has been found via SLP."))
1✔
307
      end
308

309
      profile_location
3✔
310
    end
311

312
    # Updates or extends the profile location according to defaults
313
    # @param profile_location [String] AutoYast profile location as defined on commandline
314
    # @return [String] updated profile location
315
    def update_profile_location(profile_location)
1✔
316
      if profile_location.nil? || profile_location == ""
29✔
317
        # FIXME: reevaluate this statement
318
        #
319
        # Autoinstall mode was not activated from command line.
320
        # There must be a floppy with an 'autoinst.xml' in order
321
        # to be able to reach this point, so we set floppy with
322
        # autoinst.xml as the control file.
323
        "floppy:///#{DEFAULT_PROFILE_NAME}"
1✔
324
      elsif profile_location == "default"
28✔
325
        "file:///#{DEFAULT_PROFILE_NAME}"
1✔
326
      # bsc#987858: autoyast=usb checks for the default profile
327
      elsif profile_location == "usb"
27✔
328
        "usb:///#{DEFAULT_PROFILE_NAME}"
1✔
329
      elsif profile_location == "slp"
26✔
330
        find_slp_autoyast
1✔
331
      else
332
        profile_location
25✔
333
      end
334
    end
335

336
    # Processes location of the profile given as a parameter.
337
    # @param profile_location [String] AutoYast profile location as defined on commandline
338
    # @example autoyast=http://www.server.com/profiles/
339
    # Fills internal variables
340
    def ParseCmdLine(profile_location)
1✔
341
      log.info "AutoYast profile location #{profile_location}"
25✔
342

343
      profile_location = update_profile_location(profile_location)
25✔
344
      # There is no profile defined/found anywhere
345
      return false if profile_location.nil?
25✔
346

347
      parsed_url = URL.Parse(profile_location)
25✔
348

349
      if parsed_url["scheme"].nil? || parsed_url["scheme"] == ""
25✔
350
        Report.Error(format(_("Invalid AutoYaST profile URL\n%{url}"), url: profile_location))
1✔
351
        return false
1✔
352
      end
353

354
      @OriginalURI = profile_location
24✔
355
      @urltok = deep_copy(parsed_url)
24✔
356

357
      @scheme   = parsed_url["scheme"] || "default"
24✔
358
      @host     = parsed_url["host"]   || ""
24✔
359
      @filepath = parsed_url["path"]   || ""
24✔
360
      @port     = parsed_url["port"]   || ""
24✔
361
      @user     = parsed_url["user"]   || ""
24✔
362
      @pass     = parsed_url["pass"]   || ""
24✔
363

364
      if @scheme == "relurl" || @scheme == "repo" || @scheme == "file"
24✔
365
        # "relurl": No host information has been given here. So a part of the path or the
366
        # complete path has been stored in the host variable while parsing it.
367
        # This will be reverted.
368
        #
369
        # "file": Normally the file is defined with 3 slashes like file:///autoinst.xml
370
        # in order to define an empty host entry. But that will be often overseen
371
        # by the user. So we will support file://autoinst.xml too:
372
        log.info "correcting #{@scheme}://#{@host}/#{@filepath} to empty host entry"
18✔
373
        if !@host.empty? && !@filepath.empty?
18✔
374
          @filepath = File.join(@host, @filepath)
2✔
375
        else
376
          @filepath = @host unless @host.empty?
16✔
377
        end
378
        @host = ""
18✔
379
        # keep in sync
380
        @urltok["path"] = @filepath
18✔
381
        @urltok["host"] = @host
18✔
382
      end
383

384
      @remoteProfile = !["default", "file", "floppy", "usb", "device"].include?(@scheme)
24✔
385

386
      log.info "urltok = #{URL.HidePassword(profile_location)}"
24✔
387
      true
24✔
388
    end
389

390
    # SetProtocolMessage ()
391
    # @return [void]
392

393
    def SetProtocolMessage
1✔
394
      @message = case @scheme
×
395
      when "floppy"
396
        _("Retrieving control file from floppy.")
×
397
      when "tftp"
398
        Builtins.sformat(
×
399
          _("Retrieving control file (%1) from TFTP server: %2."),
400
          @filepath,
401
          @host
402
        )
403
      when "nfs"
404
        Builtins.sformat(
×
405
          _("Retrieving control file (%1) from NFS server: %2."),
406
          @filepath,
407
          @host
408
        )
409
      when "http"
410
        Builtins.sformat(
×
411
          _("Retrieving control file (%1) from HTTP server: %2."),
412
          @filepath,
413
          @host
414
        )
415
      when "ftp"
416
        Builtins.sformat(
×
417
          _("Retrieving control file (%1) from FTP server: %2."),
418
          @filepath,
419
          @host
420
        )
421
      when "file"
422
        Builtins.sformat(
×
423
          _("Copying control file from file: %1."),
424
          @filepath
425
        )
426
      when "device"
427
        Builtins.sformat(
×
428
          _("Copying control file from device: /dev/%1."),
429
          @filepath
430
        )
431
      when "default"
432
        _("Copying control file from default location.")
×
433
      else
434
        _("Source unknown.")
×
435
      end
436
      nil
×
437
    end
438

439
    # Save Configuration global settings
440
    # @return  [void]
441
    def Save
1✔
442
      # Write sysconfig variables.
443
      Builtins.y2milestone("Saving configuration data")
×
444

445
      SCR.Write(path(".sysconfig.autoinstall.REPOSITORY"), @Repository)
×
446
      SCR.Write(path(".sysconfig.autoinstall.CLASS_DIR"), @classDir)
×
447

448
      nil
×
449
    end
450

451
    # escape a string so it can be passed to a shell
452
    # @return escaped string string
453
    def ShellEscape(s)
1✔
454
      i = 0
×
455
      res = ""
×
456

457
      while Ops.less_than(i, Builtins.size(s))
×
458
        c = Builtins.substring(s, i, 1)
×
459
        c = Ops.add("\\", c) if c == "\"" || c == "$" || c == "\\" || c == "`"
×
460
        res = Ops.add(res, c)
×
461
        i = Ops.add(i, 1)
×
462
      end
463
      res
×
464
    end
465

466
    # Constructor
467
    # @return [void]
468
    def AutoinstConfig
1✔
469
      if (Mode.autoinst || Mode.autoupgrade) && Stage.initial
48✔
470
        autoinstall = SCR.Read(path(".etc.install_inf.AutoYaST"))
33✔
471
        if !autoinstall.nil? && Ops.is_string?(autoinstall)
33✔
472
          ParseCmdLine(Convert.to_string(autoinstall))
×
473
          Builtins.y2milestone("cmd line=%1", autoinstall)
×
474
          SetProtocolMessage()
×
475
        end
476
      elsif Mode.config
15✔
477
        # Load configuration data from /etc/sysconfig/autoinstall
478
        @Repository = sysconfig_autoinstall("REPOSITORY", "/var/lib/autoinstall/repository/")
×
479
        @classDir = sysconfig_autoinstall("CLASS_DIR", @Repository + "/classes")
×
480
        tmp_dontmerge = sysconfig_autoinstall("XSLT_DONTMERGE", "addon,conf")
×
481
        tmp_no_writenow = sysconfig_autoinstall("FORBID_WRITENOW",
×
482
          "add-on,suse_register,partitioning,bootloader,general,report")
483

484
        @dontmerge = Builtins.splitstring(tmp_dontmerge, ",")
×
485
        @noWriteNow = Builtins.splitstring(tmp_no_writenow, ",")
×
486
        @dont_edit = sysconfig_autoinstall("FORBID_EDIT").split(",")
×
487

488
        # Set the defaults, just in case.
489
        @Repository = "/var/lib/autoinstall/repository" if @Repository == "" || @Repository.nil?
×
490
      end
491
      # This probably gets never executed and it only breaks the commandline iface
492
      # by Mode::test() call which instantiates UI
493
      # else if (Mode::test () && Mode::normal ())
494
      # {
495
      #     local_rules_file = (string)WFM::Args(1);
496
      # }
497
      nil
48✔
498
    end
499

500
    def MainHelp
1✔
501
      _(
4✔
502
        "<h3>AutoYaST Configuration Management System</h3>\n" \
503
        "<p>Almost all resources of the control file can be\n" \
504
        "configured using the configuration management system.</p>\n"
505
      ) +
506
        _(
507
          "<p>Most of the modules used to create the configuration are identical " \
508
          "to those available\n" \
509
          "through the YaST Control Center. Instead of configuring this system, the data\n" \
510
          "entered is collected and exported to the control file that can be used to\n" \
511
          "install another system using AutoYaST.\n" \
512
          "</p>\n"
513
        ) +
514
        _(
515
          "<p>In addition to the existing and familiar modules,\n" \
516
          "new interfaces were created for special and complex configurations, including\n" \
517
          "partitioning, general options, and software.</p>\n"
518
        )
519
    end
520

521
    # Profile path during installation
522
    #
523
    # @return [String] Path
524
    def profile_path
1✔
525
      File.join(profile_dir, DEFAULT_PROFILE_NAME)
14✔
526
    end
527

528
    # Profile backup path during installation
529
    #
530
    # @return [String] Path
531
    def profile_backup_path
1✔
532
      File.join(profile_dir, "pre-autoinst.xml")
6✔
533
    end
534

535
    publish variable: :runModule, type: "string"
1✔
536
    publish variable: :Repository, type: "string"
1✔
537
    publish variable: :ProfileEncrypted, type: "boolean"
1✔
538
    publish variable: :ProfilePassword, type: "string"
1✔
539
    publish variable: :PackageRepository, type: "string"
1✔
540
    publish variable: :classDir, type: "string"
1✔
541
    publish variable: :currentFile, type: "string"
1✔
542
    publish variable: :tmpDir, type: "string"
1✔
543
    publish variable: :var_dir, type: "string"
1✔
544
    publish variable: :scripts_dir, type: "string"
1✔
545
    publish variable: :initscripts_dir, type: "string"
1✔
546
    publish variable: :logs_dir, type: "string"
1✔
547
    publish variable: :destdir, type: "string"
1✔
548
    publish variable: :cache, type: "string"
1✔
549
    publish variable: :xml_tmpfile, type: "string"
1✔
550
    publish variable: :xml_file, type: "string"
1✔
551
    publish variable: :runtime_dir, type: "string"
1✔
552
    publish variable: :files_dir, type: "string"
1✔
553
    publish variable: :profile_dir, type: "string"
1✔
554
    publish variable: :modified_profile, type: "string"
1✔
555
    publish variable: :autoconf_file, type: "string"
1✔
556
    publish variable: :parsedControlFile, type: "string"
1✔
557
    publish variable: :remote_rules_location, type: "string"
1✔
558
    publish variable: :local_rules_location, type: "string"
1✔
559
    publish variable: :local_rules_file, type: "string"
1✔
560
    publish variable: :urltok, type: "map"
1✔
561
    publish variable: :scheme, type: "string"
1✔
562
    publish variable: :host, type: "string"
1✔
563
    publish variable: :filepath, type: "string"
1✔
564
    publish variable: :directory, type: "string"
1✔
565
    publish variable: :port, type: "string"
1✔
566
    publish variable: :user, type: "string"
1✔
567
    publish variable: :pass, type: "string"
1✔
568
    publish variable: :default_target, type: "string"
1✔
569
    publish variable: :Confirm, type: "boolean"
1✔
570
    publish variable: :cio_ignore, type: "boolean"
1✔
571
    publish variable: :second_stage, type: "boolean"
1✔
572
    publish variable: :OriginalURI, type: "string"
1✔
573
    publish variable: :message, type: "string"
1✔
574
    publish variable: :dontmerge, type: "list <string>"
1✔
575
    publish variable: :noWriteNow, type: "list <string>"
1✔
576
    publish variable: :Halt, type: "boolean"
1✔
577
    publish variable: :ForceBoot, type: "boolean"
1✔
578
    publish variable: :RebootMsg, type: "boolean"
1✔
579
    publish variable: :ProfileInRootPart, type: "boolean"
1✔
580
    publish variable: :remoteProfile, type: "boolean"
1✔
581
    publish variable: :Proposals, type: "list <string>"
1✔
582
    publish function: :getProposalList, type: "list <string> ()"
1✔
583
    publish function: :setProposalList, type: "void (list <string>)"
1✔
584
    publish function: :ParseCmdLine, type: "boolean (string)"
1✔
585
    publish function: :SetProtocolMessage, type: "void ()"
1✔
586
    publish function: :Save, type: "void ()"
1✔
587
    publish function: :ShellEscape, type: "string (string)"
1✔
588
    publish function: :AutoinstConfig, type: "void ()"
1✔
589
    publish function: :MainHelp, type: "string ()"
1✔
590
    publish function: :check_second_stage_environment, type: "string ()"
1✔
591

592
  private
1✔
593

594
    # Reads configuration from /etc/sysconfig/autoinstall
595
    #
596
    # @param [String] option an option name string as can be found in /etc/sysconfig/autoinstall
597
    # @param [String] default a default value for the option
598
    # @return [String] option value or default
599
    def sysconfig_autoinstall(option, default = "")
1✔
600
      Misc.SysconfigRead(
×
601
        path(".sysconfig.autoinstall.#{option}"),
602
        default
603
      )
604
    end
605
  end
606

607
  AutoinstConfig = AutoinstConfigClass.new
1✔
608
  AutoinstConfig.main
1✔
609
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