-
Notifications
You must be signed in to change notification settings - Fork 44
/
instsys_cleaner.rb
129 lines (104 loc) · 4.65 KB
/
instsys_cleaner.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# ------------------------------------------------------------------------------
# Copyright (c) 2016 SUSE LLC, All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of version 2 of the GNU General Public License as published by the
# Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# ------------------------------------------------------------------------------
#
# This is a library for cleaning up the inst-sys on low memory systems.
# The goal is to have enough free memory so the installation can finish successfuly.
#
require "fileutils"
require "yast"
require "yast/logger"
# memory size detection
require "yast2/hw_detection"
require "yast2/execute"
module Installation
class InstsysCleaner
extend Yast::Logger
Yast.import "Mode"
Yast.import "Stage"
# memory limit for removing the kernel modules from inst-sys (1GB)
KERNEL_MODULES_WATERLINE = 1 << 30
KERNEL_MODULES_MOUNT_POINT = "/parts/mp_0000".freeze
# Remove some files in inst-sys to have more free space if the system
# has too low memory. If the system has enough memory keep everything in place.
# This method might remove the kernel modules from the system, make sure
# *all* needed kernel modules are aready loaded before calling this method.
def self.make_clean
# just a sanity check to make sure it's not called in an unexpected situation
if !Yast::Stage.initial || !(Yast::Mode.installation || Yast::Mode.update || Yast::Mode.auto)
log.warn("Skipping inst-sys cleanup (not in installation/update)")
return
end
# memory size in bytes
memory = Yast2::HwDetection.memory
# run the cleaning actions depending on the available memory
unmount_kernel_modules if memory < KERNEL_MODULES_WATERLINE
end
########################## Internal methods ################################
# Remove the kernel modules squashfs image.
# It assumes that all needed kernel drivers are already loaded and active
# so we can remove the files to save some space.
# The result highly depends on the architecture, the number of available
# kernel modules can vary significantly. This saves about 29MB on x86_64
# and about 5MB on s390x.
def self.unmount_kernel_modules
if !File.exist?(File.join(KERNEL_MODULES_MOUNT_POINT, "lib/modules"))
log.warn("Kernel modules not found at #{KERNEL_MODULES_MOUNT_POINT}")
log.warn("Skipping module cleanup")
return
end
log.info("Removing the kernel modules inst-sys image")
log_space_usage("Before removing the kernel modules:")
# find the image for the mount point
device = find_device || return
image = losetup_backing_file(device) || return
# unmount the image
Yast::Execute.locally("umount", KERNEL_MODULES_MOUNT_POINT)
# remove the loop device binding
Yast::Execute.locally("losetup", "-d", device)
# remove the image file
::FileUtils.rm_rf(image)
log_space_usage("After removing the kernel modules:")
end
# Log the memory and disk usage to see how the clean up was effective
def self.log_space_usage(msg)
log.info(msg)
# the Cheetah backend by default logs the output
Yast::Execute.locally("df", "-m")
Yast::Execute.locally("free", "-m")
end
# Find the device for the kernel modules mount point.
# @return [String,nil] device name (/dev/loopN) or nil if not found
def self.find_device
mounts = Yast::Execute.locally("mount", stdout: :capture).split("\n")
mounts.find { |m| m.match(/\A(\/dev\/loop.*) on #{Regexp.escape(KERNEL_MODULES_MOUNT_POINT)} /) }
device = Regexp.last_match(1)
if !device
log.warn("Cannot find the loop device for the #{KERNEL_MODULES_MOUNT_POINT} mount point")
end
device
end
# Find the backend file for a loop device.
# @param device [String] device name
# @return [String,nil] backing file or nil if not found
def self.losetup_backing_file(device)
# find the backend file for the loop device
file = Yast::Execute.locally("losetup", "-n", "-O", "BACK-FILE", device, stdout: :capture).strip
if file.nil? || file.empty?
log.warn("Cannot find the backend file for the #{device} device")
return nil
end
file
end
private_class_method :log_space_usage, :unmount_kernel_modules,
:find_device, :losetup_backing_file
end
end