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

yast / yast-installation / 8705582428

16 Apr 2024 12:27PM UTC coverage: 40.913% (-0.2%) from 41.105%
8705582428

Pull #1114

github

shundhammer
Version bump and change log
Pull Request #1114: WIP: Handle autoinst AND autoupgrade (bsc#1222153)

0 of 1 new or added line in 1 file covered. (0.0%)

95 existing lines in 13 files now uncovered.

4489 of 10972 relevant lines covered (40.91%)

6.24 hits per line

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

35.28
/src/modules/ImageInstallation.rb
1
# ------------------------------------------------------------------------------
2
# Copyright (c) 2006-2012 Novell, Inc. All Rights Reserved.
3
#
4
#
5
# This program is free software; you can redistribute it and/or modify it under
6
# the terms of version 2 of the GNU General Public License as published by the
7
# Free Software Foundation.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License along with
14
# this program; if not, contact Novell, Inc.
15
#
16
# To contact Novell about this file by physical or electronic mail, you may find
17
# current contact information at www.novell.com.
18
# ------------------------------------------------------------------------------
19

20
# File:
21
#  ImageInstallation.rb
22
#
23
# Module:
24
#  ImageInstallation
25
#
26
# Summary:
27
#  Support functions for installation via images
28
#
29
# Authors:
30
#  Jiri Srain <jsrain@suse.cz>
31
#  Lukas Ocilka <locilka@suse.cz>
32
#
33
require "yast"
1✔
34
require "y2packager/resolvable"
1✔
35

36
module Yast
1✔
37
  class ImageInstallationClass < Module
1✔
38
    include Yast::Logger
1✔
39

40
    IMAGE_COMPRESS_RATIO = 3.6
1✔
41
    MEGABYTE = 2**20
1✔
42

43
    def main
1✔
44
      Yast.import "UI"
1✔
45
      Yast.import "Pkg"
1✔
46

47
      Yast.import "Installation"
1✔
48
      Yast.import "XML"
1✔
49
      Yast.import "Progress"
1✔
50
      Yast.import "Report"
1✔
51
      Yast.import "String"
1✔
52
      Yast.import "Arch"
1✔
53
      Yast.import "PackageCallbacks"
1✔
54
      Yast.import "Popup"
1✔
55
      Yast.import "SlideShow"
1✔
56
      Yast.import "ProductControl"
1✔
57
      Yast.import "ProductFeatures"
1✔
58
      Yast.import "Packages"
1✔
59
      Yast.import "PackagesUI"
1✔
60

61
      textdomain "installation"
1✔
62

63
      # Repository holding all images
64
      @_repo = nil
1✔
65

66
      # Description of all available images
67
      @_images = {}
1✔
68

69
      # Order of images
70
      @_image_order = []
1✔
71

72
      # Image with software management metadata
73
      @_metadata_image = ""
1✔
74

75
      # Template for the path for an image on the media
76
      @_image_path = "/images"
1✔
77

78
      # List of already mounted images
79
      @_mounted_images = []
1✔
80

81
      #
82
      # **Structure:**
83
      #
84
      #     $[
85
      #        "image_filaname" : $[
86
      #          // size of an unpacked image in bytes
87
      #          "size" : integer,
88
      #          // number of files and directories in an image
89
      #          "files" : integer,
90
      #        ]
91
      #      ]
92
      @images_details = {}
1✔
93

94
      # Image currently being deployed
95
      @_current_image = {}
1✔
96

97
      # display progress messages every NUMBERth record
98
      @_checkpoint = 400
1✔
99

100
      # NUMBER of bytes per record, multiple of 512
101
      @_record_size = 10_240
1✔
102

103
      @last_patterns_selected = []
1✔
104

105
      @changed_by_user = false
1✔
106

107
      # Defines whether some installation images are available
108
      @image_installation_available = nil
1✔
109

110
      @tar_image_progress = nil
1✔
111

112
      @download_image_progress = nil
1✔
113

114
      @start_download_handler = nil
1✔
115

116
      @generic_set_progress = nil
1✔
117

118
      @_current_image_from_imageset = -1
1✔
119

120
      # --> Storing and restoring states
121

122
      # List of all handled types.
123
      # list <symbol> all_supported_types = [`product, `pattern, `language, `package, `patch];
124
      # Zypp currently counts [ `product, `pattern, `language ]
125
      @all_supported_types = [:package, :patch]
1✔
126

127
      # Map that stores all the requested states of all handled/supported types.
128
      @objects_state = {}
1✔
129

130
      @progress_layout = {
1✔
131
        "storing_user_prefs"   => {
132
          "steps_start_at" => 0,
133
          "steps_reserved" => 6
134
        },
135
        "deploying_images"     => {
136
          "steps_start_at" => 6,
137
          "steps_reserved" => 84
138
        },
139
        "restoring_user_prefs" => {
140
          "steps_start_at" => 90,
141
          "steps_reserved" => 10
142
        }
143
      }
144

145
      # Images selected by FindImageSet()
146
      @selected_images = {}
1✔
147
    end
148

149
    # Set the repository to get images from
150
    # @param [Fixnum] repo integer the repository identification
151
    def SetRepo(repo)
1✔
152
      @_repo = repo
1✔
153
      Builtins.y2milestone("New images repo: %1", @_repo)
1✔
154

155
      nil
156
    end
157

158
    # Adjusts the repository for images
159
    def InitRepo
1✔
160
      return if !@_repo.nil?
24✔
161

162
      SetRepo(Ops.get(Packages.theSources, 0, 0))
1✔
163

164
      nil
165
    end
166

167
    # Order of images to be deployed
168
    # @return a list of images definint the order
169
    def ImageOrder
1✔
170
      deep_copy(@_image_order)
×
171
    end
172

173
    # Returns list of currently selected images.
174
    #
175
    # @return [Hash <String,Hash{String => Object>}] images
176
    # @see #AddImage
177
    #
178
    #
179
    # **Structure:**
180
    #
181
    #     $[
182
    #        "image_id":$[
183
    #          "file":filename,
184
    #          "type":type
185
    #        ], ...
186
    #      ]
187
    def GetCurrentImages
1✔
188
      deep_copy(@_images)
×
189
    end
190

191
    # Add information about new image
192
    # @param [String] name string the name/id of the image
193
    # @param [String] file string the file name of the image
194
    # @param [String] type string the type of the image, one of "tar" and "fs"
195
    def AddImage(name, file, type)
1✔
196
      Ops.set(
51✔
197
        @_images,
198
        file,
199
        "file" => file, "type" => type, "name" => name
200
      )
201

202
      nil
203
    end
204

205
    # Removes the downloaded image. If the file is writable, releases
206
    # all sources because only libzypp knows which files are copies
207
    # and which are just symlinks to sources (e.g., nfs://, smb://).
208
    def RemoveTemporaryImage(image)
1✔
209
      out = Convert.to_map(
×
210
        SCR.Execute(
211
          path(".target.bash_output"),
212
          Builtins.sformat(
213
            "test -w '%1' && echo -n writable",
214
            String.Quote(image)
215
          )
216
        )
217
      )
218

219
      # Command has either failed or file is writable (non-empty stdout)
220
      if Ops.get_integer(out, "exit", -1) != 0 ||
×
221
          Ops.get_string(out, "stdout", "") != ""
222
        Builtins.y2milestone("Releasing sources to remove temporary files")
×
223
        Pkg.SourceReleaseAll
×
224
      end
225

226
      nil
227
    end
228

229
    def SetDeployTarImageProgress(tip)
1✔
230
      tip = deep_copy(tip)
×
231
      @tar_image_progress = deep_copy(tip)
×
232
      Builtins.y2milestone("New tar_image_progress: %1", @tar_image_progress)
×
233

234
      nil
235
    end
236

237
    def SetDownloadTarImageProgress(tip)
1✔
238
      tip = deep_copy(tip)
×
239
      @download_image_progress = deep_copy(tip)
×
240
      Builtins.y2milestone(
×
241
        "New download_image_progress: %1",
242
        @download_image_progress
243
      )
244

245
      nil
246
    end
247

248
    # BNC #449792
249
    def SetStartDownloadImageProgress(sdi)
1✔
250
      sdi = deep_copy(sdi)
×
251
      @start_download_handler = deep_copy(sdi)
×
252
      Builtins.y2milestone(
×
253
        "New start_download_handler: %1",
254
        @start_download_handler
255
      )
256

257
      nil
258
    end
259

260
    def SetOverallDeployingProgress(odp)
1✔
261
      odp = deep_copy(odp)
×
262
      @generic_set_progress = deep_copy(odp)
×
263
      Builtins.y2milestone(
×
264
        "New generic_set_progress: %1",
265
        @generic_set_progress
266
      )
267

268
      nil
269
    end
270

271
    # Deploy an image of the filesystem type
272
    # @param [String] id string the id of the image
273
    # @param [String] target string the directory to deploy the image to
274
    # @return [Boolean] true on success
275
    def DeployTarImage(id, target)
1✔
276
      InitRepo()
×
277

278
      file = Ops.get_string(@_images, [id, "file"], "")
×
279
      Builtins.y2milestone("Untarring image %1 (%2) to %3", id, file, target)
×
280
      file = Builtins.sformat("%1/%2", @_image_path, file)
×
281
      # BNC #409927
282
      # Checking files for signatures
283
      image = Pkg.SourceProvideDigestedFile(@_repo, 1, file, false)
×
284

285
      if image.nil?
×
286
        Builtins.y2error("File %1 not found on media", file)
×
287
        return false
×
288
      end
289

290
      # reset, adjust labels, etc.
291
      @tar_image_progress&.call(0)
×
292

293
      Builtins.y2milestone("Creating target directory")
×
294
      cmd = Builtins.sformat("test -d %1 || mkdir -p %1", target)
×
295
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
296
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
297

298
      if Ops.get_integer(out, "exit", -1) != 0
×
299
        Builtins.y2error("no directory to extract into, aborting")
×
300
        return false
×
301
      end
302

303
      Builtins.y2milestone("Untarring the image")
×
304

305
      # lzma
306
      cmd = if Builtins.regexpmatch(image, ".lzma$")
×
307
        Builtins.sformat(
×
308
          "lzmadec < '%1' | tar --numeric-owner --totals --checkpoint=%3 --record-size=%4 " \
309
          "-C '%2' -xf -",
310
          String.Quote(image),
311
          String.Quote(target),
312
          @_checkpoint,
313
          @_record_size
314
        )
315
        # xzdec
316
        # BNC #476079
317
      elsif Builtins.regexpmatch(image, ".xz$")
×
318
        Builtins.sformat(
×
319
          "xzdec < '%1' | tar --numeric-owner --totals --checkpoint=%3 --record-size=%4 " \
320
          "-C '%2' -xf -",
321
          String.Quote(image),
322
          String.Quote(target),
323
          @_checkpoint,
324
          @_record_size
325
        )
326
        # bzip2, gzip
327
      else
328
        Builtins.sformat(
×
329
          "tar --numeric-owner --checkpoint=%3 --record-size=%4 --totals -C '%2' -xf '%1'",
330
          String.Quote(image),
331
          String.Quote(target),
332
          @_checkpoint,
333
          @_record_size
334
        )
335
      end
336
      Builtins.y2milestone("Calling: %1", cmd)
×
337

338
      pid = Convert.to_integer(SCR.Execute(path(".process.start_shell"), cmd))
×
339

340
      read_checkpoint_str = "^tar: Read checkpoint ([0123456789]+)$"
×
341

342
      # Otherwise it will never make 100%
343
      better_feeling_constant = @_checkpoint
×
344

345
      ret = nil
×
346
      aborted = false
×
347

348
      while SCR.Read(path(".process.running"), pid) == true
×
349
        newline = Convert.to_string(
×
350
          SCR.Read(path(".process.read_line_stderr"), pid)
351
        )
352

353
        if newline.nil?
×
354
          ret = UI.PollInput
×
355
          if [:abort, :cancel].include?(ret)
×
356
            if Popup.ConfirmAbort(:unusable)
×
357
              Builtins.y2warning("Aborted!")
×
358
              aborted = true
×
359
              break
×
360
            end
361
          else
362
            SlideShow.HandleInput(ret)
×
363
            Builtins.sleep(200)
×
364
          end
365
        else
366
          if !Builtins.regexpmatch(newline, read_checkpoint_str)
×
367
            Builtins.y2milestone("Deploying image: %1", newline)
×
368
            next
×
369
          end
370

371
          newline = Builtins.regexpsub(newline, read_checkpoint_str, "\\1")
×
372

373
          next if newline.nil? || newline == ""
×
374

375
          @tar_image_progress&.call(
×
376
            Ops.add(Builtins.tointeger(newline), better_feeling_constant)
377
          )
378
        end
379
      end
380

381
      # BNC #456337
382
      # Checking the exit code (0 = OK, nil = still running, 'else' = error)
383
      exitcode = Convert.to_integer(SCR.Read(path(".process.status"), pid))
×
384

385
      if !exitcode.nil? && exitcode != 0
×
386
        Builtins.y2milestone(
×
387
          "Deploying has failed, exit code was: %1, stderr: %2",
388
          exitcode,
389
          SCR.Read(path(".process.read_stderr"), pid)
390
        )
391
        aborted = true
×
392
      end
393

394
      Builtins.y2milestone("Finished")
×
395

396
      return false if aborted
×
397

398
      # adjust labels etc.
399
      @tar_image_progress&.call(100)
×
400

401
      RemoveTemporaryImage(image)
×
402

403
      true
×
404
    end
405

406
    # Deploy an image of the filesystem type
407
    # @param [String] id string the id of the image
408
    # @param [String] target string the directory to deploy the image to
409
    # @return [Boolean] true on success
410
    def DeployFsImage(id, target)
1✔
411
      InitRepo()
×
412

413
      file = Ops.get_string(@_images, [id, "file"], "")
×
414
      Builtins.y2milestone("Deploying FS image %1 (%2) on %3", id, file, target)
×
415
      file = Builtins.sformat("%1/%2", @_image_path, file)
×
416
      # BNC #409927
417
      # Checking files for signatures
418
      image = Pkg.SourceProvideDigestedFile(@_repo, 1, file, false)
×
419

420
      if image.nil?
×
421
        Builtins.y2error("File %1 not found on media", file)
×
422
        return false
×
423
      end
424

425
      Builtins.y2milestone("Creating temporary directory")
×
426
      tmpdir = Ops.add(
×
427
        Convert.to_string(SCR.Read(path(".target.tmpdir"))),
428
        Builtins.sformat("/images/%1", id)
429
      )
430
      cmd = Builtins.sformat("test -d %1 || mkdir -p %1", tmpdir)
×
431
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
432
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
433

434
      Builtins.y2milestone("Mounting the image")
×
435
      cmd = Builtins.sformat("mount -o noatime,loop %1 %2", image, tmpdir)
×
436
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
437
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
438

439
      Builtins.y2milestone("Creating target directory")
×
440
      cmd = Builtins.sformat("test -d %1 || mkdir -p %1", target)
×
441
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
442
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
443

444
      Builtins.y2milestone("Copying contents of the image")
×
445
      cmd = Builtins.sformat("cp -a %1/* %2", tmpdir, target)
×
446
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
447
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
448

449
      Builtins.y2milestone("Unmounting image from temporary directory")
×
450
      cmd = Builtins.sformat("umount -d -f -l %1", tmpdir)
×
451
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
452
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
453

454
      RemoveTemporaryImage(image)
×
455

456
      Ops.get_integer(out, "exit", -1) == 0
×
457
      # FIXME: error checking
458
    end
459

460
    def DeployDiskImage(id, target)
1✔
461
      InitRepo()
×
462

463
      file = Ops.get_string(@_images, [id, "file"], "")
×
464
      Builtins.y2milestone("Deploying disk image %1 (%2) on %3", id, file, target)
×
465
      file = Builtins.sformat("%1/%2", @_image_path, file)
×
466
      # BNC #409927
467
      # Checking files for signatures
468
      image = Pkg.SourceProvideDigestedFile(@_repo, 1, file, false)
×
469

470
      if image.nil?
×
471
        Builtins.y2error("File %1 not found on media", file)
×
472
        return false
×
473
      end
474

475
      Builtins.y2milestone("Copying the image")
×
476
      cmd = Builtins.sformat("dd bs=1048576 if=%1 of=%2", image, target) # 1MB of block size
×
477
      out = SCR.Execute(path(".target.bash_output"), cmd)
×
478
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
479

480
      RemoveTemporaryImage(image)
×
481

482
      out["exit"] == 0
×
483
    end
484

485
    # Mount an image of the filesystem type
486
    # Does not integrate to the system, mounts on target
487
    # @param [String] id string the id of the image
488
    # @param [String] target string the directory to deploy the image to
489
    # @return [Boolean] true on success
490
    def MountFsImage(id, target)
1✔
491
      InitRepo()
×
492

493
      file = Ops.get_string(@_images, [id, "file"], "")
×
494
      Builtins.y2milestone("Mounting image %1 (%2) on %3", id, file, target)
×
495
      file = Builtins.sformat("%1/%2", @_image_path, file)
×
496
      # BNC #409927
497
      # Checking files for signatures
498
      image = Pkg.SourceProvideDigestedFile(@_repo, 1, file, false)
×
499

500
      if image.nil?
×
501
        Builtins.y2error("File %1 not found on media", file)
×
502
        return false
×
503
      end
504
      cmd = Builtins.sformat("test -d %1 || mkdir -p %1", target)
×
505
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
506
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
507
      cmd = Builtins.sformat("mount -o noatime,loop %1 %2", image, target)
×
508
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
509
      Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
510
      Ops.get_integer(out, "exit", -1) == 0
×
511
      # FIXME: error checking
512
      # FIXME: unmounting
513
    end
514

515
    def TotalSize
1✔
516
      sum = 0
×
517

518
      Builtins.y2milestone(
×
519
        "Computing total images size from [%1], data %2",
520
        @_image_order,
521
        @images_details
522
      )
523
      Builtins.foreach(@_image_order) do |image|
×
524
        # 128 MB as a fallback size
525
        # otherwise progress would not move at all
526
        sum = Ops.add(sum, Ops.get(@images_details, [image, "size"], 134_217_728))
×
527
      end
528

529
      Builtins.y2milestone("Total images size: %1", sum)
×
530
      sum
×
531
    end
532

533
    def SetCurrentImageDetails(img)
1✔
534
      img = deep_copy(img)
×
535
      @_current_image_from_imageset = Ops.add(@_current_image_from_imageset, 1)
×
536

537
      Builtins.y2warning("Images details are empty") if Builtins.size(@images_details) == 0
×
538

UNCOV
539
      @_current_image = {
×
540
        "file"         => Ops.get_string(img, "file", ""),
541
        "name"         => Ops.get_string(img, "name", ""),
542
        "size"         => Ops.get(
543
          @images_details,
544
          [Ops.get_string(img, "file", ""), "size"],
545
          0
546
        ),
547
        "files"        => Ops.get(
548
          @images_details,
549
          [Ops.get_string(img, "file", ""), "files"],
550
          0
551
        ),
552
        # 100% progress
553
        "max_progress" => Builtins.tointeger(
554
          Ops.divide(
555
            Ops.get(
556
              @images_details,
557
              [Ops.get_string(img, "file", ""), "size"],
558
              0
559
            ),
560
            @_record_size
561
          )
562
        ),
563
        "image_nr"     => @_current_image_from_imageset
564
      }
565

566
      nil
567
    end
568

569
    def GetCurrentImageDetails
1✔
570
      deep_copy(@_current_image)
×
571
    end
572

573
    # Deploy an image (internal implementation)
574
    # @param [String] id string the id of the image
575
    # @param [String] target string the directory to deploy the image to
576
    # @param [Boolean] temporary boolean true to only mount if possible (no copy)
577
    # @return [Boolean] true on success
578
    def _DeployImage(id, target, temporary)
1✔
579
      img = Ops.get(@_images, id, {})
×
580
      Builtins.y2error("Image %1 does not exist", id) if img == {}
×
581

582
      type = Ops.get_string(img, "type", "")
×
583

584
      SetCurrentImageDetails(img)
×
585

586
      case type
×
587
      when "fs"
588
        temporary ? MountFsImage(id, target) : DeployFsImage(id, target)
×
589
      when "tar"
590
        DeployTarImage(id, target)
×
591
      when "raw"
592
        DeployDiskImage(id, target)
×
593
      else
594
        Builtins.y2error("Unknown type of image: %1", type)
×
595
        false
×
596
      end
597
    end
598

599
    # Deploy an image
600
    # @param [String] id string the id of the image
601
    # @param [String] target string the directory to deploy the image to
602
    # @return [Boolean] true on success
603
    def DeployImage(id, target)
1✔
604
      Builtins.y2milestone("Deploying image %1 to %2", id, target)
×
605
      _DeployImage(id, target, false)
×
606
    end
607

608
    # Deploy an image temporarily (just mount if possible)
609
    # @param [String] id string the id of the image
610
    # @param [String] target string the directory to deploy the image to,
611
    # @return [Boolean] true on success
612
    def DeployImageTemporarily(id, target)
1✔
613
      Builtins.y2milestone("Temporarily delploying image %1 to %2", id, target)
×
614
      _DeployImage(id, target, true)
×
615
    end
616

617
    # UnDeploy an image temporarily (if possible, only for the FS images)
618
    # @param [String] id string the id of the image
619
    # @param [String] target string the directory to deploy the image to,
620
    # @return [Boolean] true on success
621
    def CleanTemporaryImage(id, target)
1✔
622
      Builtins.y2milestone(
×
623
        "UnDelploying temporary image %1 from %2",
624
        id,
625
        target
626
      )
627
      if Ops.get_string(@_images, [id, "type"], "") == "fs"
×
628
        cmd = Builtins.sformat("umount %1", target)
×
629
        out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
630
        Builtins.y2milestone("Executing %1 returned %2", cmd, out)
×
631
        return Ops.get_integer(out, "exit", -1) == 0
×
632
      end
633
      Builtins.y2milestone(
×
634
        "Cannot undeploy image of type %1",
635
        Ops.get_string(@_images, [id, "type"], "")
636
      )
637
      true
×
638
    end
639

640
    # Loads non-mandatory details for every single selected image.
641
    def FillUpImagesDetails
1✔
642
      InitRepo()
×
643

644
      # bnc #439104
645
      if @_repo.nil?
×
646
        Builtins.y2warning("No images-repository defined")
×
647
        return true
×
648
      end
649

650
      # ppc (covers also ppc64), i386, x86_64 ...
651
      filename = nil
×
652

UNCOV
653
      possible_files = [
×
654
        Builtins.sformat("%1/details-%2.xml", @_image_path, Arch.arch_short),
655
        Builtins.sformat("%1/details.xml", @_image_path)
656
      ]
657

658
      Builtins.foreach(possible_files) do |try_file|
×
659
        # BNC #409927
660
        # Checking files for signatures
661
        filename = Pkg.SourceProvideDigestedFile(@_repo, 1, try_file, true)
×
662
        if !filename.nil? && filename != ""
×
663
          Builtins.y2milestone(
×
664
            "Using details file: %1 (%2)",
665
            filename,
666
            try_file
667
          )
668
          raise Break
×
669
        end
670
      end
671

672
      if filename.nil?
×
673
        Builtins.y2milestone("No image installation details found")
×
674
        return false
×
675
      end
676

UNCOV
677
      begin
×
678
        read_details = XML.XMLToYCPFile(filename)
×
679
      rescue XMLDeserializationError => e
680
        Builtins.y2error("Cannot parse imagesets details. #{e.inspect}")
×
681
        return false
×
682
      end
683

684
      if !Builtins.haskey(read_details, "details")
×
685
        Builtins.y2warning("No images details in details.xml")
×
686
        return false
×
687
      end
688

689
      @images_details = {}
×
690

691
      Builtins.foreach(Ops.get_list(read_details, "details", [])) do |image_detail|
×
692
        file = Ops.get_string(image_detail, "file", "")
×
693
        next if file.nil? || file == ""
×
694

695
        files = Builtins.tointeger(Ops.get_string(image_detail, "files", "0"))
×
696
        isize = Builtins.tointeger(Ops.get_string(image_detail, "size", "0"))
×
697
        Ops.set(@images_details, file, "files" => files, "size" => isize)
×
698
      end
699

700
      # FIXME: y2debug
701
      Builtins.y2milestone("Details: %1", @images_details)
×
702
      true
×
703
    end
704

705
    # Deploy all images
706
    # @param [Array<String>] images a list of images to deploy
707
    # @param [String] target string directory where to deploy the images
708
    # @param [void (integer, integer)] progress a function to report overal progress
709
    def DeployImages(images, target, progress)
1✔
710
      images = deep_copy(images)
×
711
      progress = deep_copy(progress)
×
712
      # unregister callbacks
713
      PackageCallbacks.RegisterEmptyProgressCallbacks
×
714

715
      # downloads details*.xml file
716
      FillUpImagesDetails()
×
717

718
      # register own callback for downloading
719
      Pkg.CallbackProgressDownload(@download_image_progress) if !@download_image_progress.nil?
×
720

721
      # register own callback for start downloading
722
      Pkg.CallbackStartDownload(@start_download_handler) if !@start_download_handler.nil?
×
723

724
      num = -1
×
725
      @_current_image_from_imageset = -1
×
726
      aborted = nil
×
727

728
      Builtins.foreach(images) do |img|
×
729
        num = Ops.add(num, 1)
×
730
        progress&.call(num, 0)
×
731
        if !DeployImage(img, target)
×
732
          aborted = true
×
733
          Builtins.y2milestone("Aborting...")
×
734
          raise Break
×
735
        end
736
        progress&.call(num, 100)
×
737
      end
738

739
      return nil if aborted == true
×
740

741
      # unregister downloading progress
742
      Pkg.CallbackProgressDownload(nil) if !@download_image_progress.nil?
×
743

744
      # reregister callbacks
745
      PackageCallbacks.RestorePreviousProgressCallbacks
×
746

747
      true
×
748
      # TODO: error checking
749
    end
750

751
    # Returns the intersection of both patterns supported by the imageset
752
    # and patterns going to be installed.
753
    def CountMatchingPatterns(imageset_patterns, installed_patterns)
1✔
754
      imageset_patterns = deep_copy(imageset_patterns)
72✔
755
      installed_patterns = deep_copy(installed_patterns)
72✔
756
      ret = 0
72✔
757

758
      Builtins.foreach(installed_patterns) do |one_installed_pattern|
72✔
759
        ret = Ops.add(ret, 1) if Builtins.contains(imageset_patterns, one_installed_pattern)
348✔
760
      end
761

762
      ret
72✔
763
    end
764

765
    def EnoughPatternsMatching(matching_patterns, patterns_in_imagesets)
1✔
766
      return false if matching_patterns.nil? || Ops.less_than(matching_patterns, 0)
54✔
767

768
      return false if patterns_in_imagesets.nil? || Ops.less_than(patterns_in_imagesets, 0)
54✔
769

770
      # it's actually matching_patterns = patterns_in_imagesets
771
      Ops.greater_or_equal(matching_patterns, patterns_in_imagesets)
54✔
772
    end
773

774
    def PrepareOEMImage(path)
1✔
775
      AddImage(
×
776
        "OEM", path, "raw"
777
      )
778
      @_image_order = [path]
×
779
    end
780

781
    # Find a set of images which suites selected patterns
782
    # @param [Array<String>] patterns a list of patterns which are selected
783
    # @return [Boolean] true on success or when media does not contain any images
784
    def FindImageSet(patterns)
1✔
785
      patterns = deep_copy(patterns)
24✔
786
      InitRepo()
24✔
787

788
      # reset all data
789
      @_images = {}
24✔
790
      @_image_order = []
24✔
791
      @_metadata_image = ""
24✔
792

793
      # checking whether images are supported
794
      # BNC #409927
795
      # Checking files for signatures
796
      filename = Pkg.SourceProvideDigestedFile(
24✔
797
        @_repo,
798
        1,
799
        Builtins.sformat("%1/images.xml", @_image_path),
800
        false
801
      )
802

803
      if filename.nil?
24✔
804
        @image_installation_available = false
1✔
805
        Installation.image_installation = false
1✔
806
        Installation.image_only = false
1✔
807
        Builtins.y2milestone("Image list for installation not found")
1✔
808
        return true
1✔
809
      end
810

811
      begin
23✔
812
        image_descr = XML.XMLToYCPFile(filename)
23✔
813
      rescue RuntimeError => e
814
        @image_installation_available = false
1✔
815
        Installation.image_installation = false
1✔
816
        Installation.image_only = false
1✔
817
        Report.Error(_("Failed to read information about installation images"))
1✔
818
        log.error "xml failed to read #{e.inspect}"
1✔
819
        return false
1✔
820
      end
821

822
      # images are supported
823
      # bnc #492745: Do not offer images if there are none
824
      @image_installation_available = true
22✔
825

826
      image_sets = Ops.get_list(image_descr, "image_sets", [])
22✔
827
      Builtins.y2debug("Image set descriptions: %1", image_sets)
22✔
828
      result = {}
22✔
829

830
      # more patterns could match at once
831
      # as we can't merge the meta image, only one can be selected
832
      possible_patterns = {}
22✔
833
      matching_patterns = {}
22✔
834
      patterns_in_imagesets = {}
22✔
835

836
      # ppc (covers also ppc64), i386, x86_64 ...
837
      arch_short = Arch.arch_short
22✔
838
      Builtins.y2milestone("Current architecture is: %1", arch_short)
22✔
839

840
      # filter out imagesets for another architecture
841
      image_sets = Builtins.filter(image_sets) do |image|
22✔
842
        imageset_archs = Builtins.splitstring(
264✔
843
          Ops.get_string(image, "archs", ""),
844
          " ,"
845
        )
846
        # no architecture defined == noarch
847
        next true if Builtins.size(imageset_archs) == 0
264✔
848
        # does architecture match?
849
        next true if Builtins.contains(imageset_archs, arch_short)
264✔
850

851
        # For debugging purpose
852
        Builtins.y2milestone(
192✔
853
          "Filtered-out, Patterns: %1, Archs: %2",
854
          Ops.get_string(image, "patterns", ""),
855
          Ops.get_string(image, "archs", "")
856
        )
857
        false
192✔
858
      end
859

860
      # trying to find all matching patterns
861
      Builtins.foreach(image_sets) do |image|
22✔
862
        pattern = image["patterns"]
72✔
863
        imageset_patterns = Builtins.splitstring(pattern, ", ")
72✔
864
        Ops.set(
72✔
865
          patterns_in_imagesets,
866
          pattern,
867
          Builtins.size(imageset_patterns)
868
        )
869
        # no image-pattern defined, matches all patterns
870
        if Builtins.size(imageset_patterns) == 0
72✔
871
          Ops.set(possible_patterns, pattern, image)
×
872
          # image-patterns matches to patterns got as parameter
873
        else
874
          Ops.set(
72✔
875
            matching_patterns,
876
            pattern,
877
            CountMatchingPatterns(imageset_patterns, patterns)
878
          )
879

880
          if Ops.greater_than(Ops.get(matching_patterns, pattern, 0), 0)
72✔
881
            Ops.set(possible_patterns, pattern, image)
72✔
882
          else
883
            # For debugging purpose
884
            Builtins.y2milestone(
×
885
              "Filtered-out, Patterns: %1, Matching: %2",
886
              Ops.get_string(image, "patterns", ""),
887
              Ops.get(matching_patterns, pattern, -1)
888
            )
889
          end
890
        end
891
      end
892

893
      log.info "Matching patterns: #{possible_patterns}, sizes: #{matching_patterns}"
22✔
894

895
      # selecting the best imageset
896
      last_pattern = ""
22✔
897

898
      if Ops.greater_than(Builtins.size(possible_patterns), 0)
22✔
899
        last_number_of_matching_patterns = -1
18✔
900
        last_pattern = ""
18✔
901

902
        Builtins.foreach(possible_patterns) do |pattern, image|
18✔
903
          if Ops.greater_than(
904
            Ops.get(
905
              # imageset matches more patterns than the currently best-one
906
              matching_patterns,
907
              pattern,
908
              0
909
            ),
910
            last_number_of_matching_patterns
911
          ) &&
72✔
912
              # enough patterns matches the selected imageset
913
              EnoughPatternsMatching(
914
                Ops.get(matching_patterns, pattern, 0),
915
                Ops.get(patterns_in_imagesets, pattern, 0)
916
              )
917
            last_number_of_matching_patterns = Ops.get(
12✔
918
              matching_patterns,
919
              pattern,
920
              0
921
            )
922
            result = deep_copy(image)
12✔
923
            last_pattern = pattern
12✔
924
          end
925
        end
926
      end
927

928
      Builtins.y2milestone("Result: %1/%2", last_pattern, result)
22✔
929
      @selected_images = result
22✔
930

931
      # No matching pattern
932
      if result == {}
22✔
933
        Installation.image_installation = false
10✔
934
        Installation.image_only = false
10✔
935
        Builtins.y2milestone("No image for installation found")
10✔
936
        return true
10✔
937
      end
938

939
      # We've selected one
940
      Installation.image_installation = true
12✔
941

942
      if Builtins.haskey(result, "pkg_image")
12✔
943
        @_metadata_image = Ops.get_string(result, "pkg_image", "")
12✔
944
      else
945
        Installation.image_only = true
×
946
      end
947

948
      # Adding images one by one into the pool
949
      Builtins.foreach(Ops.get_list(result, "images", [])) do |img|
12✔
950
        # image must have unique <file>...</file> defined
951
        if Ops.get(img, "file", "") == ""
51✔
952
          Builtins.y2error("No file defined for %1", img)
×
953
          next
×
954
        end
955
        @_image_order = Builtins.add(@_image_order, Ops.get(img, "file", ""))
51✔
956
        AddImage(
51✔
957
          Ops.get(img, "name", ""),
958
          Ops.get(img, "file", ""),
959
          Ops.get(img, "type", "")
960
        )
961
      end
962

963
      Builtins.y2milestone(
12✔
964
        "Image-only installation: %1",
965
        Installation.image_only
966
      )
967
      Builtins.y2milestone("Images: %1", @_images)
12✔
968
      Builtins.y2milestone("Image installation order: %1", @_image_order)
12✔
969

970
      if !Installation.image_only
12✔
971
        Builtins.y2milestone(
12✔
972
          "Image with software management metadata: %1",
973
          @_metadata_image
974
        )
975
      end
976

977
      true
12✔
978
    end
979

980
    # Returns map with description which images will be used
981
    #
982
    # @return [Hash] with description
983
    #
984
    #
985
    # **Structure:**
986
    #
987
    #     $[
988
    #        "deploying_enabled" : boolean,
989
    #        "images" : returned by GetCurrentImages()
990
    #      ]
991
    #
992
    # @see #GetCurrentImages()
993
    def ImagesToUse
1✔
994
      ret = {}
×
995

996
      if Installation.image_installation == true
×
997
        ret = { "deploying_enabled" => true, "images" => GetCurrentImages() }
×
998
      else
999
        Ops.set(ret, "deploying_enabled", false)
×
1000
      end
1001

1002
      deep_copy(ret)
×
1003
    end
1004

1005
    def calculate_fs_size(mountpoint)
1✔
1006
      cmd = Builtins.sformat("df -P -k %1", mountpoint)
×
1007
      Builtins.y2milestone("Executing %1", cmd)
×
1008
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
1009
      Builtins.y2milestone("Output: %1", out)
×
1010
      total_str = Ops.get_string(out, "stdout", "")
×
1011
      total_str = Ops.get(Builtins.splitstring(total_str, "\n"), 1, "")
×
1012
      Ops.divide(
×
1013
        Builtins.tointeger(
1014
          Ops.get(Builtins.filter(Builtins.splitstring(total_str, " ")) do |s|
1015
            s != ""
×
1016
          end, 2, "0")
1017
        ),
1018
        1024
1019
      )
1020
    end
1021

1022
    # Copy a subtree, limit to a single filesystem
1023
    # @param [String] from string source directory
1024
    # @param [String] to string target directory
1025
    # @return [Boolean] true on success
1026
    def FileSystemCopy(from, to, progress_start, progress_finish)
1✔
1027
      total_mb = if from == "/"
×
1028
        # root is a merge of two filesystems, df returns only one part for /
1029
        calculate_fs_size("/read-write") + calculate_fs_size("/read-only")
×
1030
      else
1031
        0
×
1032
      end
1033
      total_mb = calculate_fs_size(from) if total_mb == 0
×
1034
      # Using df-based progress estimation, is rather faster
1035
      #    may be less precise
1036
      #    see bnc#555288
1037
      #     string cmd = sformat ("du -x -B 1048576 -s %1", from);
1038
      #     y2milestone ("Executing %1", cmd);
1039
      #     map out = (map)SCR::Execute (.target.bash_output, cmd);
1040
      #     y2milestone ("Output: %1", out);
1041
      #     string total_str = out["stdout"]:"";
1042
      #     integer total_mb = tointeger (total_str);
1043
      total_mb = (total_mb * IMAGE_COMPRESS_RATIO).to_i # compression ratio - rough estimate
×
1044
      total_mb = 4096 if total_mb == 0 # should be big enough
×
1045

1046
      tmp_pipe1 = Ops.add(
×
1047
        Convert.to_string(SCR.Read(path(".target.tmpdir"))),
1048
        "/system_clone_fifo_1"
1049
      )
1050
      tmp_pipe2 = Ops.add(
×
1051
        Convert.to_string(SCR.Read(path(".target.tmpdir"))),
1052
        "/system_clone_fifo_2"
1053
      )
1054
      # FIXME: this does not copy pipes in filesystem (usually not an issue)
1055
      cmd = Builtins.sformat(
×
1056
        "mkfifo %3 ;\n" \
1057
        "\t mkfifo %4 ;\n" \
1058
        "\t tar -C %1 --hard-dereference --numeric-owner -cSf %3 --one-file-system . &\n" \
1059
        "\t dd bs=1048576 if=%3 of=%4 >&2 &\n" \
1060
        "\t jobs -l >&2;\n" \
1061
        "\t tar -C %2 --numeric-owner -xSf %4",
1062
        from,
1063
        to,
1064
        tmp_pipe1,
1065
        tmp_pipe2
1066
      )
1067
      Builtins.y2milestone("Executing %1", cmd)
×
1068
      process = Convert.to_integer(
×
1069
        SCR.Execute(path(".process.start_shell"), cmd, {})
1070
      )
1071
      pid = ""
×
1072

1073
      while Convert.to_boolean(SCR.Read(path(".process.running"), process))
×
1074
        done = nil
×
1075
        line = Convert.to_string(
×
1076
          SCR.Read(path(".process.read_line_stderr"), process)
1077
        )
1078
        until line.nil?
×
1079
          if pid == ""
×
1080
            if Builtins.regexpmatch(
×
1081
              line,
1082
              Builtins.sformat(
1083
                "dd bs=1048576 if=%1 of=%2",
1084
                tmp_pipe1,
1085
                tmp_pipe2
1086
              )
1087
            )
1088
              pid = Builtins.regexpsub(line, "([0-9]+) [^ 0-9]+ +dd", "\\1")
×
1089
              Builtins.y2milestone("DD's pid: %1", pid)
×
1090
              # sleep in order not to kill -USR1 to dd too early, otherwise it finishes
1091
              Builtins.sleep(5000)
×
1092
            else
1093
              pid = ""
×
1094
            end
1095
          elsif Builtins.regexpmatch(line, "^[0-9]+ ")
×
1096
            done = Builtins.regexpsub(line, "^([0-9]+) ", "\\1")
×
1097
          end
1098
          Builtins.y2debug("Done: %1", done)
×
1099
          line = Convert.to_string(
×
1100
            SCR.Read(path(".process.read_line_stderr"), process)
1101
          )
1102
        end
1103
        if pid != ""
×
1104
          cmd = Builtins.sformat("/bin/kill -USR1 %1", pid)
×
1105
          Builtins.y2debug("Executing %1", cmd)
×
1106
          SCR.Execute(path(".target.bash"), cmd)
×
1107
        end
1108
        Builtins.sleep(300)
×
1109
        next if done.nil?
×
1110

1111
        progress = Ops.add(
×
1112
          progress_start,
1113
          Ops.divide(
1114
            Ops.divide(
1115
              Ops.divide(
1116
                Ops.multiply(
1117
                  Ops.subtract(progress_finish, progress_start),
1118
                  Builtins.tointeger(done) / MEGABYTE # count megabytes
1119
                ),
1120
                total_mb
1121
              ),
1122
              1024
1123
            ),
1124
            1024
1125
          )
1126
        )
1127
        Builtins.y2debug("Setting progress to %1", progress)
×
1128
        SlideShow.StageProgress(progress, nil)
×
1129
        SlideShow.SubProgress(
×
1130
          Ops.divide(
1131
            Ops.divide(
1132
              Ops.divide(
1133
                Ops.multiply(
1134
                  Ops.subtract(progress_finish, progress_start),
1135
                  Builtins.tointeger(done)
1136
                ),
1137
                total_mb
1138
              ),
1139
              1024
1140
            ),
1141
            1024
1142
          ),
1143
          nil
1144
        )
1145
      end
1146

1147
      copy_result = Convert.to_integer(
×
1148
        SCR.Read(path(".process.status"), process)
1149
      )
1150
      Builtins.y2milestone("Result: %1", copy_result)
×
1151
      SCR.Execute(path(".target.remove"), tmp_pipe1)
×
1152
      SCR.Execute(path(".target.remove"), tmp_pipe2)
×
1153
      cmd = Builtins.sformat(
×
1154
        "chown --reference=%1 %2; chmod --reference=%1 %2",
1155
        from,
1156
        to
1157
      )
1158
      Builtins.y2milestone("Executing %1", cmd)
×
1159
      out = Convert.to_map(SCR.Execute(path(".target.bash_output"), cmd))
×
1160
      Builtins.y2milestone("Result: %1", out)
×
1161
      Ops.get_integer(out, "exit", -1) == 0 && copy_result == 0
×
1162
    end
1163

1164
    def GetProgressLayoutDetails(id, details)
1✔
1165
      Ops.get_integer(@progress_layout, [id, details], 0)
×
1166
    end
1167

1168
    def GetProgressLayoutLabel(id)
1✔
1169
      Ops.get_locale(@progress_layout, [id, "label"], _("Deploying..."))
×
1170
    end
1171

1172
    def AdjustProgressLayout(id, steps_total, label)
1✔
1173
      if !Builtins.haskey(@progress_layout, id)
×
1174
        Builtins.y2error("Unknown key: %1", id)
×
1175
        return
×
1176
      end
1177

1178
      Ops.set(@progress_layout, [id, "label"], label)
×
1179
      Ops.set(@progress_layout, [id, "steps_total"], steps_total)
×
1180

1181
      nil
1182
    end
1183

1184
    # Function stores all new/requested states of all handled/supported types.
1185
    #
1186
    # @see #all_supported_types
1187
    # @see #objects_state
1188
    def StoreAllChanges
1✔
1189
      nr_steps = Ops.multiply(4, Builtins.size(@all_supported_types))
×
1190
      id = "storing_user_prefs"
×
1191

1192
      AdjustProgressLayout(id, nr_steps, _("Storing user preferences..."))
×
1193

1194
      @generic_set_progress&.call(id, 0)
×
1195

1196
      # Query for changed state of all knwon types
1197
      # 'changed' means that they were 'installed' and 'not locked' before
1198
      Builtins.foreach(@all_supported_types) do |one_type|
×
1199
        # list of $[ "name":string, "version":string, "arch":string, "source":integer,
1200
        #            "status":symbol, "locked":boolean ]
1201
        # status is `installed, `removed, `selected or `available, source is source ID or
1202
        # -1 if the resolvable is installed in the target
1203
        # if status is `available and locked is true then the object is set to taboo
1204
        # if status is `installed and locked is true then the object locked
1205
        resolvable_properties = Y2Packager::Resolvable.find(kind: one_type)
×
1206
        Ops.set(@objects_state, one_type, {})
×
1207
        remove_resolvables = Builtins.filter(resolvable_properties) do |one_object|
×
1208
          one_object.status == :removed
×
1209
        end
1210
        Ops.set(@objects_state, [one_type, "remove"], remove_resolvables)
×
1211
        @generic_set_progress&.call(id, nil)
×
1212
        install_resolvables = Builtins.filter(resolvable_properties) do |one_object|
×
1213
          one_object.status == :selected
×
1214
        end
1215
        Ops.set(@objects_state, [one_type, "install"], install_resolvables)
×
1216
        @generic_set_progress&.call(id, nil)
×
1217
        taboo_resolvables = Builtins.filter(resolvable_properties) do |one_object|
×
1218
          one_object.status == :available &&
×
1219
            one_object.locked
1220
        end
1221
        Ops.set(@objects_state, [one_type, "taboo"], taboo_resolvables)
×
1222
        @generic_set_progress&.call(id, nil)
×
1223
        lock_resolvables = Builtins.filter(resolvable_properties) do |one_object|
×
1224
          one_object.status == :installed &&
×
1225
            one_object.locked
1226
        end
1227
        Ops.set(@objects_state, [one_type, "lock"], lock_resolvables)
×
1228
        @generic_set_progress&.call(id, nil)
×
1229
      end
1230

1231
      # map <symbol, map <string, list <map> > > objects_state = $[];
1232
      Builtins.foreach(@objects_state) do |object_type, objects_status|
×
1233
        Builtins.foreach(objects_status) do |one_status, list_of_objects|
×
1234
          log.debug(
×
1235
            "Object type: #{object_type}, New status: #{one_status},"\
1236
            "List of objects: #{list_of_objects}"
1237
          )
1238
        end
1239
      end
1240

1241
      nil
1242
    end
1243

1244
    # @return [Boolean] whether the package should be additionally installed
1245
    def ProceedWithSelected(one_object, one_type)
1✔
1246
      # This package has been selected to be installed
1247

1248
      arch = Ops.get_string(one_object.value, "arch", "")
×
1249
      # Query for all packages of the same version
1250
      resolvable_properties = Y2Packager::Resolvable.find(
×
1251
        kind:    one_type.value,
1252
        name:    one_object.value.name,
1253
        version: one_object.value.version,
1254
        status:  :installed,
1255
        arch:    arch
1256
      )
1257

1258
      log.debug("Resolvables installed: #{resolvable_properties.map(&:name)}")
×
1259

1260
      ret = nil
×
1261

1262
      # There are some installed (matching the same arch, version, and name)
1263
      if Ops.greater_than(Builtins.size(resolvable_properties), 0)
×
1264
        Builtins.y2milestone(
×
1265
          "Resolvable type: %1, name: %2 already installed",
1266
          one_type.value,
1267
          Ops.get_string(one_object.value, "name", "-x-")
1268
        )
1269
        # Let's keep the installed version
1270
        Pkg.ResolvableNeutral(
×
1271
          Ops.get_string(one_object.value, "name", "-x-"),
1272
          one_type.value,
1273
          true
1274
        )
1275
        # is already installed
1276
        ret = false
×
1277
        # They are not installed
1278
      else
1279
        Builtins.y2milestone(
×
1280
          "Installing type: %1, details: %2,%3,%4",
1281
          one_type.value,
1282
          Ops.get_string(one_object.value, "name", ""),
1283
          Ops.get_string(one_object.value, "arch", ""),
1284
          Ops.get_string(one_object.value, "version", "")
1285
        )
1286
        # Confirm we want to install them (they might have been added as dependencies)
1287
        Pkg.ResolvableInstallArchVersion(
×
1288
          Ops.get_string(one_object.value, "name", ""),
1289
          one_type.value,
1290
          Ops.get_string(one_object.value, "arch", ""),
1291
          Ops.get_string(one_object.value, "version", "")
1292
        )
1293
        # should be installed
1294
        ret = true
×
1295
      end
1296

1297
      ret
×
1298
    end
1299

1300
    # Restores packages statuses from 'objects_state': Selects packages for removal, installation
1301
    # and upgrade.
1302
    #
1303
    # @return [Boolean] if successful
1304
    def RestoreAllChanges
1✔
1305
      nr_steps = Ops.multiply(4, Builtins.size(@all_supported_types))
×
1306
      id = "restoring_user_prefs"
×
1307

1308
      AdjustProgressLayout(id, nr_steps, _("Restoring user preferences..."))
×
1309

1310
      @generic_set_progress&.call(id, 0)
×
1311

1312
      Builtins.foreach(@all_supported_types) do |one_type|
×
1313
        resolvable_properties = Y2Packager::Resolvable.find(kind: one_type)
×
1314
        # All packages selected for installation
1315
        # both `to-install and `to-upgrade (already) installed
1316
        to_install = Builtins.filter(resolvable_properties) do |one_resolvable|
×
1317
          one_resolvable.status == :selected
×
1318
        end
1319
        @generic_set_progress&.call(id, nil)
×
1320
        # List of all packages selected for installation (just names)
1321
        selected_for_installation_pkgnames = Builtins.maplist(
×
1322
          Ops.get(@objects_state, [one_type, "install"], []), &:name
1323
        )
1324
        # All packages selected to be installed
1325
        # [ $[ "arch" : ... , "name" : ... , "version" : ... ], ... ]
1326
        selected_for_installation = Builtins.maplist(
×
1327
          Ops.get(@objects_state, [one_type, "install"], [])
1328
        ) do |one_resolvable|
UNCOV
1329
          {
×
1330
            "arch"    => one_resolvable.arch,
1331
            "name"    => one_resolvable.name,
1332
            "version" => one_resolvable.version
1333
          }
1334
        end
1335
        @generic_set_progress&.call(id, nil)
×
1336
        # Delete all packages that are installed but should not be
1337
        one_already_installed_resolvable = {}
×
1338
        Builtins.foreach(resolvable_properties) do |one_resolvable|
×
1339
          # We are interested in the already installed resolvables only
1340
          if one_resolvable.status != :installed &&
×
1341
              one_resolvable.status != :selected
1342
            next
×
1343
          end
1344

UNCOV
1345
          one_already_installed_resolvable = {
×
1346
            "arch"    => one_resolvable.arch,
1347
            "name"    => one_resolvable.name,
1348
            "version" => one_resolvable.version
1349
          }
1350
          # Already installed resolvable but not in list of resolvables to be installed
1351
          if !Builtins.contains(
×
1352
            selected_for_installation,
1353
            one_already_installed_resolvable
1354
          )
1355
            # BNC #489448: Do not remove package which is installed in different version and/or arch
1356
            # It will be upgraded later
1357
            if Builtins.contains(
×
1358
              selected_for_installation_pkgnames,
1359
              one_resolvable.name
1360
            )
1361
              Builtins.y2milestone(
×
1362
                "Not Removing type: %1, name: %2 version: %3",
1363
                one_type,
1364
                one_resolvable.name,
1365
                one_resolvable.version
1366
              )
1367
              # Package is installed or selected but should not be, remove it
1368
            else
1369
              Builtins.y2milestone(
×
1370
                "Removing type: %1, name: %2 version: %3",
1371
                one_type,
1372
                one_resolvable.name,
1373
                one_resolvable.version
1374
              )
1375
              Pkg.ResolvableRemove(
×
1376
                one_resolvable.name,
1377
                one_type
1378
              )
1379
            end
1380
          end
1381
        end
1382
        @generic_set_progress&.call(id, nil)
×
1383
        # Install all packages that aren't yet
1384
        Builtins.foreach(to_install) do |one_to_install|
×
1385
          one_to_install_ref = arg_ref(one_to_install)
×
1386
          one_type_ref = arg_ref(one_type)
×
1387
          ProceedWithSelected(one_to_install_ref, one_type_ref)
×
1388
          one_type = one_type_ref.value
×
1389
        end
1390
        @generic_set_progress&.call(id, nil)
×
1391
      end
1392

1393
      # Free the memory
1394
      @objects_state = {}
×
1395

1396
      # Return 'true' if YaST can solve deps. automatically
1397
      if Pkg.PkgSolve(true) == true
×
1398
        Builtins.y2milestone("Dependencies solved atomatically")
×
1399
        return true
×
1400
      end
1401

1402
      # Error message
1403
      Report.Error(
×
1404
        _(
1405
          "Installation was unable to solve package dependencies automatically.\n" \
1406
          "Software manager will be opened for you to solve them manually."
1407
        )
1408
      )
1409

1410
      ret = false
×
1411

1412
      # BNC #Trying to solve deps. manually
1413
      loop do
×
1414
        Builtins.y2warning(
×
1415
          "Cannot solve dependencies automatically, opening Packages UI"
1416
        )
1417
        diaret = PackagesUI.RunPackageSelector(
×
1418
          "enable_repo_mgr" => false, "mode" => :summaryMode
1419
        )
1420
        Builtins.y2milestone("RunPackageSelector returned %1", diaret)
×
1421

1422
        # User didn't solve the deps manually
1423
        if diaret == :cancel
×
1424
          ret = false
×
1425
          if Popup.ConfirmAbort(:unusable)
×
1426
            Builtins.y2warning("User abort...")
×
1427
            break
×
1428
          end
1429
          # Aborting not confirmed, next round
1430
          next
×
1431
          # Solved! (somehow)
1432
        else
1433
          ret = true
×
1434
          break
×
1435
        end
1436
      end
1437

1438
      Builtins.y2milestone("Dependencies solved: %1", ret)
×
1439
      ret
×
1440
    end
1441

1442
    # <-- Storing and restoring states
1443

1444
    def FreeInternalVariables
1✔
1445
      @last_patterns_selected = []
22✔
1446
      @_images = {}
22✔
1447
      @_image_order = []
22✔
1448
      @images_details = {}
22✔
1449
      @_mounted_images = []
22✔
1450
      @selected_images = {}
22✔
1451

1452
      nil
1453
    end
1454

1455
    # Only for checking in tests now
1456
    attr_reader :selected_images
1✔
1457

1458
    publish function: :SetRepo, type: "void (integer)"
1✔
1459
    publish variable: :last_patterns_selected, type: "list <string>"
1✔
1460
    publish variable: :changed_by_user, type: "boolean"
1✔
1461
    publish variable: :image_installation_available, type: "boolean"
1✔
1462
    publish function: :ImageOrder, type: "list <string> ()"
1✔
1463
    publish function: :SetDeployTarImageProgress, type: "void (void (integer))"
1✔
1464
    publish function: :SetDownloadTarImageProgress,
1✔
1465
      type: "void (boolean (integer, integer, integer))"
1466
    publish function: :SetStartDownloadImageProgress, type: "void (void (string, string))"
1✔
1467
    publish function: :SetOverallDeployingProgress, type: "void (void (string, integer))"
1✔
1468
    publish function: :TotalSize, type: "integer ()"
1✔
1469
    publish function: :GetCurrentImageDetails, type: "map <string, any> ()"
1✔
1470
    publish function: :DeployImage, type: "boolean (string, string)"
1✔
1471
    publish function: :DeployImageTemporarily, type: "boolean (string, string)"
1✔
1472
    publish function: :CleanTemporaryImage, type: "boolean (string, string)"
1✔
1473
    publish function: :FillUpImagesDetails, type: "boolean ()"
1✔
1474
    publish function: :DeployImages,
1✔
1475
      type: "boolean (list <string>, string, void (integer, integer))"
1476
    publish function: :FindImageSet, type: "boolean (list <string>)"
1✔
1477
    publish function: :ImagesToUse, type: "map ()"
1✔
1478
    publish function: :FileSystemCopy, type: "boolean (string, string, integer, integer)"
1✔
1479
    publish function: :GetProgressLayoutDetails, type: "integer (string, string)"
1✔
1480
    publish function: :GetProgressLayoutLabel, type: "string (string)"
1✔
1481
    publish function: :AdjustProgressLayout, type: "void (string, integer, string)"
1✔
1482
    publish function: :StoreAllChanges, type: "void ()"
1✔
1483
    publish function: :RestoreAllChanges, type: "boolean ()"
1✔
1484
    publish function: :FreeInternalVariables, type: "void ()"
1✔
1485
    publish function: :PrepareOEMImage, type: "void ()"
1✔
1486
  end
1487

1488
  ImageInstallation = ImageInstallationClass.new
1✔
1489
  ImageInstallation.main
1✔
1490
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

© 2025 Coveralls, Inc