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

yast / yast-yast2 / 13440235285

20 Feb 2025 04:40PM UTC coverage: 41.869% (-0.02%) from 41.889%
13440235285

push

github

web-flow
Merge pull request #1316 from yast/agama_kernel_conf

Respect Agama kernel parameters

2 of 4 new or added lines in 1 file covered. (50.0%)

265 existing lines in 40 files now uncovered.

12605 of 30106 relevant lines covered (41.87%)

10.76 hits per line

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

58.87
/library/control/src/modules/InstExtensionImage.rb
1
# ***************************************************************************
2
#
3
# Copyright (c) 2002 - 2012 Novell, Inc.
4
# All Rights Reserved.
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of version 2 of the GNU General Public License as
8
# published by the Free Software Foundation.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, contact Novell, Inc.
17
#
18
# To contact Novell about this file by physical or electronic mail,
19
# you may find current contact information at www.novell.com
20
#
21
# ***************************************************************************
22
# File:  modules/InstExtensionImage.ycp
23
# Package:  Base
24
# Summary:  Functionality for downloading and merging extending
25
#    images for the inst-sys
26
# Authors:  Lukas Ocilka <locilka@suse.cz>
27
#
28
# $Id$
29
#
30
# This module provides functions that download inst-sys extension images
31
# (localization, fonts, ...) and merge them to the current int-sys.
32
# This enables inst-sys to be modular even for already running YaST.
33
# See FATE #302955: 'Split translations out of installation system'.
34
# This module is strictly installation-only!
35
require "yast"
1✔
36

37
module Yast
1✔
38
  class InstExtensionImageClass < Module
1✔
39
    def main
1✔
40
      textdomain "base"
1✔
41

42
      Yast.import "Linuxrc"
1✔
43
      Yast.import "URL"
1✔
44
      Yast.import "String"
1✔
45
      Yast.import "Directory"
1✔
46
      Yast.import "Popup"
1✔
47
      Yast.import "Stage"
1✔
48

49
      # **
50
      #
51
      # Paths where to download inst-sys extension images are taken
52
      # from '/etc/install.inf'. An extension image contains the
53
      # directory structure and files as it was in inst-sys but
54
      # it is a squashfs image of it.
55
      #
56
      # Inst-sys URL might be absolute or 'relative' to repo URL,
57
      # but only if instsys=... parameter wasn't explicitely defined.
58
      #
59
      #   When instsys=... parameter _is not_ used:
60
      #     * RepoURL: cd:/?device=sr0
61
      #     * InstsysURL: boot/<arch>/root
62
      #       (<arch> is for instance "i386", "x86_64", "ppc")
63
      #     or
64
      #     * RepoURL: nfs://server/repo/url/?device=eth0
65
      #     * InstsysURL: boot/<arch>/root
66
      #
67
      #   When instsys=... parameter _is_ used:
68
      #     * RepoURL: nfs://server/repo/url/
69
      #     * InstsysURL: http://server/inst-sys/url/
70
      #     or
71
      #     * RepoURL: cd:/?device=sr0
72
      #     * InstsysURL: nfs://server/inst-sys/url/?device=eth0
73
      #
74
      # Files to download are in the same level (directory) with
75
      # inst-sys:
76
      #
77
      #   * RepoURL: cd:/?device=sr0
78
      #   * InstsysURL: boot/<arch>/root
79
      #   -> cd:/boot/<arch>/$extension_file
80
      #
81
      #   * RepoURL: nfs://server/repo/url/?device=eth0
82
      #   * InstsysURL: http://server/inst-sys/url/?device=eth0
83
      #   -> http://server/inst-sys/$extension_file?device=eth0
84
      #
85
      # These files are always squashfs images that need to be:
86
      #
87
      #   * Downloaded: /lbin/wget -v $url $local_filename_path
88
      #   * Downloaded file needs to be checked against a SHA1
89
      #     hash defined in /content file
90
      #   * Mounted (-o loop) to a directory.
91
      #   * Directory needs to be merged into inst-sys by using
92
      #     `/lbin/lndir <image_mountpoint> /`
93
      #
94
      # This module remembers downloading a file so it does not
95
      # download any file twice.
96
      #
97
      # Additional comments on the "Installation Workflow":
98
      #
99
      #   * When Linuxrc starts loading an initial translation
100
      #     might already been selected. Linuxrc will download
101
      #     and merge the pre-selected translation itself.
102
      #   * Then Linuxrc starts YaST. YaST initializes itself
103
      #     including translations and displays the language
104
      #     dialog translated.
105
      #   * After a different language is selected, YaST downloads
106
      #     a localization inst-sys extension and merges it.
107
      #   * Then a different locale is selected and YaST redraws
108
      #     reruns the current YCP client.
109

110
      # nfs://.../, cd:/, http://.../any/
111
      # always ends with a slash "/"
112
      @base_url = ""
1✔
113

114
      # if there are any params $url?param1=xx&param2=...
115
      # always only params
116
      @base_url_params = ""
1✔
117

118
      # Directory used for storing images
119
      @base_tmpdir = Builtins.sformat(
1✔
120
        "%1/%2/",
121
        Directory.tmpdir,
122
        "instsys_extensions"
123
      )
124
      # Directory used for mounting images
125
      @base_mounts = Builtins.sformat(
1✔
126
        "%1/%2/",
127
        Directory.tmpdir,
128
        "instsys_extmounts"
129
      )
130

131
      @initialized = false
1✔
132

133
      # Already downloaded (and mounted and merged) files
134
      @already_downloaded_files = []
1✔
135

136
      # All integrated extensions
137
      @integrated_extensions = []
1✔
138

139
      # $["extension_name" : "mounted_as_directory", ...]
140
      @extensions_mounted_as = {}
1✔
141

142
      # $["extension_name" : "downloaded_to_file", ...]
143
      @extension_downloaded_as = {}
1✔
144
    end
145

146
    def IsURLRelative(url)
1✔
147
      return nil if url.nil?
×
148

149
      # "http://..." -> not-relative
150
      # "cd:/" -> not relative
151
      # "boot/i386/root -> relative
152
      !Builtins.regexpmatch(url, "^[[:alpha:]]+:/")
×
153
    end
154

155
    # Merges two different URLs, repspectively their parameters
156
    # to one string with parameters. See the example.
157
    #
158
    # @param [String] base_url URL with params
159
    # @param [String] url_with_modifs URL with modifications (added or changed params)
160
    # @return [String] merged params
161
    #
162
    # @example
163
    #   MergeURLsParams (
164
    #     "http://server.net/dir/?param1=x&param2=y",
165
    #     "http://server.net/dir/?param2=z&param3=a",
166
    #   // param2 from the first URL has been replaced by tho one from the second URL
167
    #   ) -> "param1=x&param2=z&param3=a"
168
    def MergeURLsParams(base_url, url_with_modifs)
1✔
169
      if base_url.nil? || url_with_modifs.nil?
×
170
        Builtins.y2error("Wrong params: %1 or %2", base_url, url_with_modifs)
×
171
        return nil
×
172
      end
173

174
      # base URL params
175
      base_params_pos = Builtins.search(base_url, "?")
×
176
      base_params = ""
×
177

178
      base_params = Builtins.substring(base_url, Ops.add(base_params_pos, 1)) if !base_params_pos.nil? && Ops.greater_or_equal(base_params_pos, 0)
×
179

180
      # URL params with modifications
181
      modif_params_pos = Builtins.search(url_with_modifs, "?")
×
182
      modif_params = ""
×
183

184
      if !modif_params_pos.nil? && Ops.greater_or_equal(modif_params_pos, 0)
×
185
        modif_params = Builtins.substring(
×
186
          url_with_modifs,
187
          Ops.add(modif_params_pos, 1)
188
        )
189
      end
190

191
      # Nothing to merge
192
      return modif_params if base_params == ""
×
193
      return base_params if modif_params == ""
×
194

195
      base_params_map = URL.MakeMapFromParams(base_params)
×
196
      modif_params_map = URL.MakeMapFromParams(modif_params)
×
197
      final_params_map = Convert.convert(
×
198
        Builtins.union(base_params_map, modif_params_map),
199
        from: "map",
200
        to:   "map <string, string>"
201
      )
202

203
      URL.MakeParamsFromMap(final_params_map)
×
204
    end
205

206
    # Removes the last url item.
207
    #
208
    # @example
209
    #   CutLastDirOrFile ("http://server/some/dir/") -> "http://server/some/"
210
    #   CutLastDirOrFile ("http://server/some/dir")  -> "http://server/some/"
211
    def CutLastDirOrFile(url)
1✔
212
      if url.nil? || url == "" || url == "/" ||
×
213
          !Builtins.regexpmatch(url, "/")
214
        Builtins.y2error(-1, "Wrong URL: %1", url)
×
215
        return ""
×
216
      end
217

218
      # final "/" is needed for regexp
219
      url = Ops.add(url, "/") if !Builtins.regexpmatch(url, "/$")
×
220

221
      Builtins.regexpsub(url, "^(.*)/[^/]+/$", "\\1/")
×
222
    end
223

224
    # Merges two URLs into one and removes parameters from both.
225
    # If the second URL is strictly relative, e.g., "boot/i386/root",
226
    # it is merged with the first one, otherwise the second one is
227
    # returned (with params cut).
228
    #
229
    # @param [String] url_base URL
230
    # @param [String] url_with_modifs URL (relative or absolute)
231
    # @return [String] merged URL
232
    #
233
    # @example
234
    #   MergeURLs (
235
    #     "nfs://server.name/11-repo/?device=eth0&xxx=zzz",
236
    #     "boot/i386/root?device=eth1&aaa=bbb"
237
    #   ) -> "nfs://server.name/11-repo/boot/i386/"
238
    #   MergeURLs (
239
    #     "nfs://server.name/11-repo/?device=eth0&xxx=zzz",
240
    #     "nfs://server2.net/boot/i386/root?device=eth1&aaa=bbb"
241
    #   ) -> "nfs://server2.net/boot/i386/"
242
    def MergeURLs(url_base, url_with_modifs)
1✔
243
      if url_base.nil? || url_with_modifs.nil?
×
244
        Builtins.y2error("Wrong URLs: %1 or %2", url_base, url_with_modifs)
×
245
        return nil
×
246
      end
247

248
      # relative (to base URL) or absolute URL
249
      url_with_modifs_pos = Builtins.search(url_with_modifs, "?")
×
250
      url_with_modifs_onlyurl = url_with_modifs
×
251

252
      if !url_with_modifs_pos.nil? &&
×
253
          Ops.greater_or_equal(url_with_modifs_pos, 0)
254
        url_with_modifs_onlyurl = Builtins.substring(
×
255
          url_with_modifs,
256
          0,
257
          url_with_modifs_pos
258
        )
259
      end
260

261
      # Modif URL is not relative, not using the base URL at all
262
      return CutLastDirOrFile(url_with_modifs_onlyurl) if !IsURLRelative(url_with_modifs_onlyurl)
×
263

264
      # base URL
265
      url_base_pos = Builtins.search(url_base, "?")
×
266
      url_base_onlyurl = url_base
×
267

268
      url_base_onlyurl = Builtins.substring(url_base, 0, url_base_pos) if !url_base_pos.nil? && Ops.greater_or_equal(url_base_pos, 0)
×
269

270
      url_base_onlyurl = Ops.add(url_base_onlyurl, "/") if !Builtins.regexpmatch(url_base_onlyurl, "/$")
×
271

272
      CutLastDirOrFile(Ops.add(url_base_onlyurl, url_with_modifs_onlyurl))
×
273
    end
274

275
    # Every global function should call LazyInit in the beginning.
276
    def LazyInit
1✔
277
      # already initialized
278
      return if @initialized
×
279

280
      Builtins.y2milestone("Initializing...")
×
281
      @initialized = true
×
282

283
      # base repo URL
284
      repo_url = Linuxrc.InstallInf("RepoURL")
×
285
      # inst-sys URL
286
      inst_sys_url = Linuxrc.InstallInf("InstsysURL")
×
287

288
      # non-relative inst-sys, repo is not taken into account
289
      repo_url = "" if !IsURLRelative(inst_sys_url)
×
290

291
      # final base URL (last file/dir already removed)
292
      @base_url = MergeURLs(repo_url, inst_sys_url)
×
293
      Builtins.y2milestone("Base URL: %1", @base_url)
×
294

295
      # final params
296
      @base_url_params = MergeURLsParams(repo_url, inst_sys_url)
×
297
      Builtins.y2milestone("Base URL params: %1", @base_url_params)
×
298

299
      run = Convert.to_map(
×
300
        WFM.Execute(
301
          path(".local.bash_output"),
302
          Builtins.sformat("/bin/mkdir -p '%1'", String.Quote(@base_tmpdir))
303
        )
304
      )
305
      if Ops.get_integer(run, "exit", -1) != 0
×
306
        Builtins.y2error(
×
307
          "Cannot create temporary directory: %1: %2",
308
          @base_tmpdir,
309
          run
310
        )
311
      end
312

313
      run = Convert.to_map(
×
314
        WFM.Execute(
315
          path(".local.bash_output"),
316
          Builtins.sformat("/bin/mkdir -p '%1'", String.Quote(@base_mounts))
317
        )
318
      )
319
      if Ops.get_integer(run, "exit", -1) != 0
×
320
        Builtins.y2error(
×
321
          "Cannot create mounts directory: %1: %2",
322
          @base_mounts,
323
          run
324
        )
325
      end
326

UNCOV
327
      nil
×
328
    end
329

330
    # Load a rpm package from the media into the inst-sys and ensure its
331
    # unloading after end of block.
332
    # @param [String] package to load
333
    # @yield context when extension is available
334
    # @raise [RuntimeError] when package loading failed
335
    #
336
    # @example
337
    #   InstExtensionImage.with_extension("snapper") do
338
    #      WFM.Execute(path(".local.bash"), "snapper magic")
339
    #   end
340
    #
341
    def with_extension(package, &block)
1✔
342
      loading_msg = format(_("Loading to memory package '%s'"), package)
3✔
343
      res = LoadExtension(package, loading_msg)
3✔
344
      raise "Failed to load package. Please check logs." unless res
3✔
345

346
      begin
347
        block.call
2✔
348
      ensure
349
        unloading_msg = format(_("Removing from memory package '%s'"), package)
2✔
350
        UnLoadExtension(package, unloading_msg)
2✔
351
      end
352
    end
353

354
    # Load a rpm package from the media into the inst-sys
355
    # @param [String] package  The path to package to be loaded (by default,
356
    # the package is expected in the /boot/<arch>/ directory of the media
357
    # @param [String] message  The message to be shown in the progress popup
358
    def LoadExtension(package, message)
1✔
359
      Builtins.y2error("This module should be used in Stage::initial only!") if !Stage.initial
9✔
360

361
      if package.nil? || package == ""
9✔
362
        Builtins.y2error("Such package name can't work: %1", package)
2✔
363
        return false
2✔
364
      end
365

366
      if Builtins.contains(@integrated_extensions, package)
7✔
367
        Builtins.y2milestone("Package %1 has already been integrated", package)
1✔
368
        return true
1✔
369
      end
370

371
      Popup.ShowFeedback("", message) if message != "" && !message.nil?
6✔
372

373
      # See BNC #376870
374
      cmd = Builtins.sformat("/bin/extend '%1'", String.Quote(package))
6✔
375
      Builtins.y2milestone("Calling: %1", cmd)
6✔
376
      cmd_out = Convert.to_map(WFM.Execute(path(".local.bash_output"), cmd))
6✔
377
      Builtins.y2milestone("Returned: %1", cmd_out)
6✔
378

379
      ret = true
6✔
380
      if Ops.get_integer(cmd_out, "exit", -1) == 0
6✔
381
        @integrated_extensions = Builtins.add(@integrated_extensions, package)
3✔
382
      else
383
        Builtins.y2error("'extend' failed!")
3✔
384
        ret = false
3✔
385
      end
386

387
      Popup.ClearFeedback if message != "" && !message.nil?
6✔
388

389
      ret
6✔
390
    end
391

392
    # Remove given package from the inst-sys
393
    # @param [String] package  The path to package to be unloaded (by default,
394
    # the package is expected in the /boot/<arch>/ directory of the media
395
    # @param [String] message  The message to be shown in the progress popup
396
    def UnLoadExtension(package, message)
1✔
397
      Builtins.y2error("This module should be used in Stage::initial only!") if !Stage.initial
8✔
398

399
      if package.nil? || package == ""
8✔
400
        Builtins.y2error("Such package name can't work: %1", package)
2✔
401
        return false
2✔
402
      end
403

404
      if !Builtins.contains(@integrated_extensions, package)
6✔
405
        Builtins.y2milestone("Package %1 wasn't integrated", package)
1✔
406
        return true
1✔
407
      end
408

409
      Popup.ShowFeedback("", message) if message != "" && !message.nil?
5✔
410

411
      cmd = Builtins.sformat("/bin/extend -r '%1'", String.Quote(package))
5✔
412
      Builtins.y2milestone("Calling: %1", cmd)
5✔
413
      cmd_out = Convert.to_map(WFM.Execute(path(".local.bash_output"), cmd))
5✔
414
      Builtins.y2milestone("Returned: %1", cmd_out)
5✔
415

416
      ret = true
5✔
417
      if Ops.get_integer(cmd_out, "exit", -1) == 0
5✔
418
        @integrated_extensions = Builtins.filter(@integrated_extensions) do |p|
3✔
419
          p != package
3✔
420
        end
421
      else
422
        Builtins.y2error("'extend' failed!")
2✔
423
        ret = false
2✔
424
      end
425

426
      Popup.ClearFeedback if message != "" && !message.nil?
5✔
427

428
      ret
5✔
429
    end
430

431
    def DownloadAndIntegrateExtension(extension)
1✔
432
      LoadExtension(extension, "")
×
433
    end
434

435
    def DesintegrateExtension(_extension)
1✔
436
      Builtins.y2warning("Function is empty, see BNC #376870")
×
437
      true
×
438
    end
439

440
    def DisintegrateAllExtensions
1✔
441
      Builtins.y2warning("Function is empty, see BNC #376870")
×
442
      true
×
443
    end
444

445
    publish function: :LoadExtension, type: "boolean (string, string)"
1✔
446
    publish function: :UnLoadExtension, type: "boolean (string, string)"
1✔
447
    publish function: :DownloadAndIntegrateExtension, type: "boolean (string)"
1✔
448
    publish function: :DesintegrateExtension, type: "boolean (string)"
1✔
449
    publish function: :DisintegrateAllExtensions, type: "boolean ()"
1✔
450
  end
451

452
  InstExtensionImage = InstExtensionImageClass.new
1✔
453
  InstExtensionImage.main
1✔
454
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