Hello community, here is the log from the commit of package yast2-packager for openSUSE:Factory checked in at 2014-09-24 13:09:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-packager (Old) and /work/SRC/openSUSE:Factory/.yast2-packager.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-packager" Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-packager/yast2-packager.changes 2014-09-17 21:24:31.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.yast2-packager.new/yast2-packager.changes 2014-09-24 13:09:50.000000000 +0200 @@ -1,0 +2,15 @@ +Tue Sep 23 09:48:57 UTC 2014 - [email protected] + +- fixed installation crash when a non-btrfs file system is used + (bnc#897909) +- 3.1.49 + +------------------------------------------------------------------- +Fri Sep 19 12:31:52 UTC 2014 - [email protected] + +- handle Btrfs specifically in disk usage counting (bnc#896176) + - use "btrfs filesystem df" instead of "df" (more accurate) + - pass "growonly" flag to libzypp when any snapshot is found +- 3.1.48 + +------------------------------------------------------------------- Old: ---- yast2-packager-3.1.47.tar.bz2 New: ---- yast2-packager-3.1.49.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-packager.spec ++++++ --- /var/tmp/diff_new_pack.OA768K/_old 2014-09-24 13:09:51.000000000 +0200 +++ /var/tmp/diff_new_pack.OA768K/_new 2014-09-24 13:09:51.000000000 +0200 @@ -17,7 +17,7 @@ Name: yast2-packager -Version: 3.1.47 +Version: 3.1.49 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -27,6 +27,7 @@ BuildRequires: update-desktop-files BuildRequires: yast2-country-data BuildRequires: yast2-devtools >= 3.1.10 +BuildRequires: yast2-storage BuildRequires: yast2-testsuite BuildRequires: yast2-xml BuildRequires: rubygem(rspec) @@ -34,14 +35,14 @@ # HwDetection BuildRequires: yast2 >= 3.1.19 -# Pkg::SetZConfig() -BuildRequires: yast2-pkg-bindings >= 2.21.8 +# "growonly" in Pkg::SetTargetDU() +BuildRequires: yast2-pkg-bindings >= 3.1.19 # Newly added RPM Requires: yast2-country-data >= 2.16.3 -# Pkg::SetZConfig() -Requires: yast2-pkg-bindings >= 2.21.8 +# "growonly" in Pkg::SetTargetDU() +Requires: yast2-pkg-bindings >= 3.1.19 # Fixed .proc.cmdline agent Requires: yast2 >= 3.1.89 ++++++ yast2-packager-3.1.47.tar.bz2 -> yast2-packager-3.1.49.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-3.1.47/package/yast2-packager.changes new/yast2-packager-3.1.49/package/yast2-packager.changes --- old/yast2-packager-3.1.47/package/yast2-packager.changes 2014-09-12 11:18:10.000000000 +0200 +++ new/yast2-packager-3.1.49/package/yast2-packager.changes 2014-09-23 11:58:10.000000000 +0200 @@ -1,4 +1,19 @@ ------------------------------------------------------------------- +Tue Sep 23 09:48:57 UTC 2014 - [email protected] + +- fixed installation crash when a non-btrfs file system is used + (bnc#897909) +- 3.1.49 + +------------------------------------------------------------------- +Fri Sep 19 12:31:52 UTC 2014 - [email protected] + +- handle Btrfs specifically in disk usage counting (bnc#896176) + - use "btrfs filesystem df" instead of "df" (more accurate) + - pass "growonly" flag to libzypp when any snapshot is found +- 3.1.48 + +------------------------------------------------------------------- Thu Sep 11 08:09:16 UTC 2014 - [email protected] - Improved automatic selection of packages for remote diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-3.1.47/package/yast2-packager.spec new/yast2-packager-3.1.49/package/yast2-packager.spec --- old/yast2-packager-3.1.47/package/yast2-packager.spec 2014-09-12 11:18:10.000000000 +0200 +++ new/yast2-packager-3.1.49/package/yast2-packager.spec 2014-09-23 11:58:10.000000000 +0200 @@ -17,7 +17,7 @@ Name: yast2-packager -Version: 3.1.47 +Version: 3.1.49 Release: 0 BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -28,19 +28,20 @@ License: GPL-2.0+ BuildRequires: yast2-country-data yast2-xml update-desktop-files yast2-testsuite BuildRequires: yast2-devtools >= 3.1.10 +BuildRequires: yast2-storage BuildRequires: rubygem(rspec) # HwDetection BuildRequires: yast2 >= 3.1.19 -# Pkg::SetZConfig() -BuildRequires: yast2-pkg-bindings >= 2.21.8 +# "growonly" in Pkg::SetTargetDU() +BuildRequires: yast2-pkg-bindings >= 3.1.19 # Newly added RPM Requires: yast2-country-data >= 2.16.3 -# Pkg::SetZConfig() -Requires: yast2-pkg-bindings >= 2.21.8 +# "growonly" in Pkg::SetTargetDU() +Requires: yast2-pkg-bindings >= 3.1.19 # Fixed .proc.cmdline agent Requires: yast2 >= 3.1.89 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-3.1.47/src/clients/wrapper_storage.rb new/yast2-packager-3.1.49/src/clients/wrapper_storage.rb --- old/yast2-packager-3.1.47/src/clients/wrapper_storage.rb 2014-09-12 11:18:11.000000000 +0200 +++ new/yast2-packager-3.1.49/src/clients/wrapper_storage.rb 2014-09-23 11:58:10.000000000 +0200 @@ -29,11 +29,12 @@ Yast.import "Storage" # call the required function - if @func == "GetTargetMap" + case @func + when "GetTargetMap" @ret = Storage.GetTargetMap - elsif @func == "GetTargetChangeTime" + when "GetTargetChangeTime" @ret = Storage.GetTargetChangeTime - elsif @func == "RemoveDmMapsTo" + when "RemoveDmMapsTo" if Builtins.size(@param) == 0 Builtins.y2error("Missing argument for Storage::RemoveDmMapsTo()") else @@ -41,7 +42,7 @@ @ret = Storage.RemoveDmMapsTo(@param1) end - elsif @func == "GetWinPrimPartitions" + when "GetWinPrimPartitions" if Builtins.size(@param) == 0 Builtins.y2error( "Missing argument for Storage::GetWinPrimPartitions()" @@ -54,6 +55,12 @@ ) @ret = Storage.GetWinPrimPartitions(@param1) end + when "ClassicStringToByte" + if Builtins.size(@param) == 0 + Builtins.y2error("Missing argument for Storage::ClassicStringToByte()") + else + @ret = Storage.ClassicStringToByte(@param.first) + end else # the required function is not known Builtins.y2error("unknown function: %1", @func) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-3.1.47/src/modules/SpaceCalculation.rb new/yast2-packager-3.1.49/src/modules/SpaceCalculation.rb --- old/yast2-packager-3.1.47/src/modules/SpaceCalculation.rb 2014-09-12 11:18:11.000000000 +0200 +++ new/yast2-packager-3.1.49/src/modules/SpaceCalculation.rb 2014-09-23 11:58:10.000000000 +0200 @@ -10,12 +10,22 @@ # when the installation media is available # on Installation::sourcedir # -# -# $Id$ + require "yast" +require "shellwords" module Yast class SpaceCalculationClass < Module + include Yast::Logger + + # 16 MiB (in KiB) + MIN_SPARE_KIB = 16 * 1024 + # 1 GiB (in KiB) + MAX_SPARE_KIB = 1 * 1024 * 1024 + + # 1 MiB in KiB + MIB = 1024 + def main Yast.import "Pkg" @@ -83,134 +93,100 @@ # # *** This is needed during update ! def EvaluateFreeSpace(spare_percentage) - partition = [] - # the sizes are in kB - min_spare = 10 * 1024 # 10 MB - max_spare = 1024 * 1024 # 1 GB - target = Installation.destdir # get information about diskspace ( used/free space on every partition ) - partition = Convert.convert( - SCR.Read(path(".run.df")), - :from => "any", - :to => "list <map <string, string>>" - ) + partitions = SCR.Read(path(".run.df")) # filter out headline and other invalid entries - partition = Builtins.filter(partition) do |part| - Builtins.substring(Ops.get(part, "name", ""), 0, 1) == "/" - end + partitions.select!{ |p| p["name"].start_with?("/") } + log.info "df result: #{partitions}" + + # TODO FIXME dirinstall has been dropped, probably drop this block completely if Installation.dirinstall_installing_into_dir - target = GetDirMountPoint(Installation.dirinstall_target, partition) - Builtins.y2milestone( - "Installing into a directory, target directory: %1, target mount point: %2", - Installation.dirinstall_target, - target - ) + target = GetDirMountPoint(Installation.dirinstall_target, partitions) + log.info "Installing into a directory, target directory: " \ + "#{Installation.dirinstall_target}, target mount point: #{target}" end - part_input = [] + du_partitions = [] - Builtins.foreach(partition) do |part| + partitions.each do |part| part_info = {} - free_size = 0 - spare_size = 0 - partName = "" - add_part = true - mountName = Ops.get_string(part, "name", "") - spec = Ops.get_string(part, "spec", "") - if Installation.dirinstall_installing_into_dir - if Builtins.substring(mountName, 0, 1) != "/" - mountName = Ops.add("/", mountName) - end + mountName = part["name"] || "" + # TODO FIXME dirinstall has been dropped, probably drop this block completely? + if Installation.dirinstall_installing_into_dir + mountName.prepend("/") unless mountName.start_with?("/") dir_target = Installation.dirinstall_target - Builtins.y2debug( - "mountName: %1, dir_target: %2", - mountName, - dir_target - ) + log.debug "mountName: #{mountName}, dir_target: #{dir_target}" - if Ops.greater_than( - Builtins.size(mountName), - Builtins.size(dir_target) - ) && - Builtins.substring(mountName, 0, Builtins.size(dir_target)) == dir_target - part_info = Builtins.add(part_info, "name", mountName) + if mountName.start_with?(dir_target) + part_info["name"] = mountName elsif mountName == target - part_info = Builtins.add(part_info, "name", "/") - else - add_part = false + part_info["name"] = "/" end elsif target != "/" - if Ops.greater_or_equal( - Builtins.size(mountName), - Builtins.size(target) - ) && - Builtins.substring(mountName, 0, Builtins.size(target)) == target - partName = Builtins.substring(mountName, Builtins.size(target)) + if mountName.start_with?(target) + partName = mountName[target.size..-1] # nothing left, it was target root itself - if Builtins.size(partName) == 0 - part_info = Builtins.add(part_info, "name", "/") - else - part_info = Builtins.add(part_info, "name", partName) - end - else - add_part = false + part_info["name"] = partName.empty? ? "/" : partName end # target is "/" else if mountName == "/" - part_info = Builtins.add(part_info, "name", mountName) + part_info["name"] = mountName # ignore some mount points elsif mountName != Installation.sourcedir && mountName != "/cdrom" && mountName != "/dev/shm" && - spec != "udev" && - !Builtins.regexpmatch(mountName, "^/media/") && - !Builtins.regexpmatch(mountName, "^var/adm/mount/") - part_info = Builtins.add(part_info, "name", mountName) - else - add_part = false + part["spec"] != "udev" && + !mountName.start_with?("/media/") && + !mountName.start_with?("/var/adm/mount/") + part_info["name"] = mountName end end - if add_part - part_info = Builtins.add( - part_info, - "used", - Builtins.tointeger(Ops.get_string(part, "used", "0")) - ) - free_size = Builtins.tointeger(Ops.get_string(part, "free", "0")) - spare_size = Ops.divide( - Ops.multiply(free_size, spare_percentage), - 100 - ) + next if part_info.empty? - if Ops.less_than(spare_size, min_spare) - spare_size = min_spare - elsif Ops.greater_than(spare_size, max_spare) - spare_size = max_spare - end + filesystem = part["type"] + part_info["filesystem"] = filesystem - free_size = Ops.subtract(free_size, spare_size) - free_size = 0 if Ops.less_than(free_size, 0) # don't add a negative size + if filesystem == "btrfs" + log.info "Detected btrfs at #{mountName}" + btrfs_used_kib = btrfs_used_size(mountName) / 1024 + log.info "Difference to 'df': #{(part["used"].to_i - btrfs_used_kib) / 1024}MiB" + part_info["used"] = btrfs_used_kib + part_info["growonly"] = btrfs_snapshots?(mountName) + total_kb = part["whole"].to_i + free_size_kib = total_kb - btrfs_used_kib + else + part_info["used"] = part["used"].to_i + free_size_kib = part["free"].to_i + part_info["growonly"] = false + end - part_info = Builtins.add(part_info, "free", free_size) + spare_size_kb = free_size_kib * spare_percentage / 100 - part_input = Builtins.add(part_input, part_info) + if spare_size_kb < MIN_SPARE_KIB + spare_size_kb = MIN_SPARE_KIB + elsif spare_size_kb > MAX_SPARE_KIB + spare_size_kb = MAX_SPARE_KIB end - end - Builtins.y2milestone( - "UTILS *** EvaluateFreeSpace returns: %1", - part_input - ) + free_size_kib -= spare_size_kb + # don't use a negative size + free_size_kib = 0 if free_size_kib < 0 - Pkg.TargetInitDU(part_input) + part_info["free"] = free_size_kib - deep_copy(part_input) + du_partitions << part_info + end + + log.info "UTILS *** EvaluateFreeSpace returns: #{du_partitions}" + Pkg.TargetInitDU(du_partitions) + + du_partitions end # return default ext3/4 journal size (in B) for target partition size @@ -391,25 +367,24 @@ def EstimateTargetUsage(parts) parts = deep_copy(parts) - Builtins.y2milestone("EstimateTargetUsage(%1)", parts) - mb = 1 << 10 # sizes are in kB, 1MB is 1024 kB + log.info "EstimateTargetUsage(#{parts})" # invalid or empty input - if parts == nil || Builtins.size(parts) == 0 - Builtins.y2error("Invalid input: %1", parts) + if parts == nil || parts.empty? + log.error "Invalid input: #{parts.inspect}" return [] end # the numbers are from openSUSE-11.4 default KDE installation used_mapping = { - "/var/lib/rpm" => Ops.multiply(42, mb), # RPM database - "/var/log" => Ops.multiply(14, mb), # system logs (YaST logs have ~12MB) - "/var/adm/backup" => Ops.multiply(10, mb), # backups - "/var/cache/zypp" => Ops.multiply(38, mb), # zypp metadata cache after refresh (with OSS + update repos) - "/etc" => Ops.multiply(2, mb), # various /etc config files not belonging to any package - "/usr/share" => Ops.multiply(1, mb), # some files created by postinstall scripts - "/boot/initrd" => Ops.multiply(11, mb) - } # depends on HW but better than nothing + "/var/lib/rpm" => 42 * MIB, # RPM database + "/var/log" => 14 * MIB, # system logs (YaST logs have ~12MB) + "/var/adm/backup" => 10 * MIB, # backups + "/var/cache/zypp" => 38 * MIB, # zypp metadata cache after refresh (with OSS + update repos) + "/etc" => 2 * MIB, # various /etc config files not belonging to any package + "/usr/share" => 1 * MIB, # some files created by postinstall scripts + "/boot/initrd" => 11 * MIB # depends on HW but better than nothing + } Builtins.y2milestone("Adding target size mapping: %1", used_mapping) @@ -452,7 +427,7 @@ mounted ) end - end + end # convert back to list @@ -555,36 +530,48 @@ if !Stage.initial # read /proc/mounts as a list of maps # $["file":"/boot", "freq":0, "mntops":"rw", "passno":0, "spec":"/dev/sda1", "vfstype":"ext2"] - mounts = Convert.convert( - SCR.Read(path(".proc.mounts")), - :from => "any", - :to => "list <map <string, any>>" - ) - Builtins.y2milestone("mounts %1", mounts) + mounts = SCR.Read(path(".proc.mounts")) + log.info "mounts #{mounts}" partitions = [] - Builtins.foreach(mounts) do |mpoint| - name = Ops.get_string(mpoint, "file", "") - if Builtins.substring(name, 0, 1) == "/" && - Builtins.substring(name, 0, 5) != "/dev/" && # filter out /dev/pts etc. - Ops.get_string(mpoint, "vfstype", "") != "rootfs" # filter out duplicate "/" entry + mounts.each do |mpoint| + name = mpoint["file"] + filesystem = mpoint["vfstype"] + + if name.start_with?("/") && + # filter out /dev/pts etc. + !name.start_with?("/dev/") && + # filter out duplicate "/" entry + filesystem != "rootfs" + capacity = Pkg.TargetCapacity(name) + if capacity != 0 # dont look at pseudo-devices (proc, shmfs, ...) used = Pkg.TargetUsed(name) - partitions = Builtins.add( - partitions, - { - "name" => name, - "free" => Ops.subtract(capacity, used), - "used" => used - } - ) + growonly = false + + if filesystem == "btrfs" + log.info "Btrfs file system detected at #{name}" + growonly = btrfs_snapshots?(name) + log.info "Snapshots detected: #{growonly}" + new_used = btrfs_used_size(name) / 1024 + log.info "Updated the used size by 'btrfs' utility from #{used} to #{new_used} (diff: #{new_used - used})" + used = new_used + end + + partitions << { + "name" => name, + "free" => capacity - used, + "used" => used, + "filesystem" => filesystem, + "growonly" => growonly + } end end end Pkg.TargetInitDU(partitions) Builtins.y2milestone("get_partition_info: %1", partitions) - return deep_copy(partitions) + return partitions end # !Stage::initial () # remove the previous failures @@ -598,9 +585,7 @@ :to => "map <string, map>" ) - if targets == nil - Builtins.y2error("Target map is nil, Storage:: is probably missing") - end + log.error "Target map is nil, Storage:: is probably missing" unless targets if Mode.test targets = Convert.convert( @@ -616,39 +601,30 @@ Builtins.foreach(targets) do |disk, diskinfo| part_info = Ops.get_list(diskinfo, "partitions", []) Builtins.foreach(part_info) do |part| - Builtins.y2milestone("Adding partition: %1", part) - used_fs = Ops.get_symbol(part, "used_fs", :unknown) + log.info "Adding partition: #{part}" + used_fs = part["used_fs"] # ignore VFAT and NTFS partitions (bnc#) if used_fs == :vfat || used_fs == :ntfs - Builtins.y2warning( - "Ignoring partition %1 with %2 filesystem", - Ops.get_string(part, "device", ""), - used_fs - ) + log.warn "Ignoring partition with #{used_fs} filesystem" else free_size = 0 + growonly = false if Ops.get(part, "mount") != nil && - Builtins.substring(Ops.get_string(part, "mount", ""), 0, 1) == "/" + part["mount"].start_with?("/") if Ops.get(part, "create") == true || Ops.get(part, "delete") == false || Ops.get(part, "create") == nil && Ops.get(part, "delete") == nil - Builtins.y2debug( - "get_partition_info: adding partition: %1", - part - ) + log.debug "get_partition_info: adding partition: #{part}" # get free_size on partition in kBytes - free_size = Ops.multiply( - Ops.get_integer(part, "size_k", 0), - 1024 - ) - free_size = Ops.subtract(free_size, min_spare) + free_size = part["size_k"] * 1024 + free_size -= min_spare # free_size smaller than min_spare, fix negative value - if Ops.less_than(free_size, 0) - Builtins.y2milestone("Fixing free size: %1 to 0", free_size) + if free_size < 0 + log.info "Fixing free size: #{free_size} to 0" free_size = 0 end @@ -661,12 +637,8 @@ # information for devices (even caching the information). # This part should be refactored to rely on libstorage. - tmpdir = Convert.to_string(SCR.Read(path(".target.tmpdir"))) - tmpdir = Ops.add(tmpdir, "/diskspace_mount") - SCR.Execute( - path(".target.bash"), - Builtins.sformat("test -d %1 || mkdir -p %1", tmpdir) - ) + tmpdir = SCR.Read(path(".target.tmpdir")) + "/diskspace_mount" + SCR.Execute(path(".target.bash"), "mkdir -p #{Shellwords.escape(tmpdir)})") # mount options determined by partitioner mount_options = (part["fstopt"] || "").split(",") @@ -676,8 +648,8 @@ # add "nolock" if it's a NFS share (bnc#433893) if used_fs == :nfs - Builtins.y2milestone("Mounting NFS with 'nolock' option") - mount_options = Builtins.add(mount_options, "nolock") + log.info "Mounting NFS with 'nolock' option" + mount_options << "nolock" end # join the options @@ -687,51 +659,35 @@ # (bnc#889334) device = part["crypt_device"] || part["device"] || "" - mount_command = Builtins.sformat( - "/bin/mount -o %1 %2 %3", - mount_options_str, - device, - tmpdir - ) - - Builtins.y2milestone( - "Executing mount command: %1", - mount_command - ) - - result = Convert.to_integer( - SCR.Execute(path(".target.bash"), mount_command) - ) - Builtins.y2milestone("Mount result: %1", result) + mount_command = "mount -o #{mount_options_str} " \ + "#{Shellwords.escape(device)} #{Shellwords.escape(tmpdir)}" + + log.info "Executing mount command: #{mount_command}" + + result = SCR.Execute(path(".target.bash"), mount_command) + log.info "Mount result: #{result}" if result == 0 - partition = Convert.convert( - SCR.Read(path(".run.df")), - :from => "any", - :to => "list <map <string, string>>" - ) - Builtins.foreach(partition) do |p| - if Ops.get_string(p, "name", "") == tmpdir - Builtins.y2milestone("P: %1", p) - free_size = Ops.multiply( - Builtins.tointeger(Ops.get_string(p, "free", "0")), - 1024 - ) - used = Ops.multiply( - Builtins.tointeger(Ops.get_string(p, "used", "0")), - 1024 - ) + # specific handler for btrfs + if used_fs == :btrfs + used = btrfs_used_size(tmpdir) + free_size -= used + growonly = btrfs_snapshots?(tmpdir) + else + partition = SCR.Read(path(".run.df")) + + Builtins.foreach(partition) do |p| + if p["name"] == tmpdir + log.info "Partition: #{p}" + free_size = p["free"].to_i * 1024 + used = p["used"].to_i * 1024 + end end end - SCR.Execute( - path(".target.bash"), - Builtins.sformat("/bin/umount %1", tmpdir) - ) + + SCR.Execute(path(".target.bash"), "umount #{Shellwords.escape(tmpdir)}") else - Builtins.y2error( - "Mount failed, ignoring partition %1", - device - ) + log.error "Mount failed, ignoring partition #{device}" @failed_mounts = Builtins.add(@failed_mounts, part) next @@ -740,95 +696,59 @@ # for formatted partitions estimate free system size # compute fs overhead used = EstimateFsOverhead(part) + log.info "#{device}: assuming fs overhead: #{used / 1024}KiB" - if Ops.greater_than(used, 0) - Builtins.y2milestone( - "Partition %1: assuming fs overhead: %2kB", - Ops.get_string(part, "device", ""), - Ops.divide(used, 1024) - ) - end - - # journal size - js = 0 - - if ExtFs(used_fs) + # get the journal size + case used_fs + when :ext2, :ext3, :ext4 js = ExtJournalSize(part) reserved = ReservedSpace(part) - - if Ops.greater_than(reserved, 0) - used = Ops.add(used, reserved) - end - elsif used_fs == :xfs + used += reserved if reserved > 0 + when :xfs js = XfsJournalSize(part) - elsif used_fs == :reiser + when :reiser js = ReiserJournalSize(part) - elsif used_fs == :jfs + when :jfs js = JfsJournalSize(part) + when :btrfs + # Btrfs uses temporary trees instead of a fixed journal, + # there is no journal, it's a logging FS + # http://en.wikipedia.org/wiki/Btrfs#Log_tree + js = 0 else - Builtins.y2warning( - "Unknown journal size for filesystem: %1", - used_fs - ) + log.warn "Unknown journal size for filesystem: #{used_fs}" end - if Ops.greater_than(js, 0) - Builtins.y2milestone( - "Partition %1: assuming journal size: %2kB", - Ops.get_string(part, "device", ""), - Ops.divide(js, 1024) - ) - used = Ops.add(used, js) + if js && js > 0 + log.info "Partition #{part["device"]}: assuming journal size: #{js / 1024}KiB" + used += js end # decrease free size - free_size = Ops.subtract(free_size, used) + free_size -= used # check for underflow - if Ops.less_than(free_size, 0) - Builtins.y2milestone("Fixing free size: %1 to 0", free_size) + if free_size < 0 + log.info "Fixing free size: #{free_size} to 0" free_size = 0 end end - # convert into kB for TargetInitDU - free_size = Ops.divide(free_size, 1024) - used = Ops.divide(used, 1024) - - Builtins.y2milestone( - "available partition: mount: %1, free: %2 KB, used: %3 KB", - Ops.get_string(part, "mount", ""), - free_size, - used - ) - if !remove_slash - target_partitions = Builtins.add( - target_partitions, - { - "name" => Ops.get_string(part, "mount", ""), - "used" => used, - "free" => free_size - } - ) - else - part_name = "" - mount_name = Ops.get_string(part, "mount", "") - - if mount_name != "/" - part_name = Builtins.substring( - mount_name, - 1, - Builtins.size(mount_name) - ) - else - part_name = mount_name - end - - target_partitions = Builtins.add( - target_partitions, - { "name" => part_name, "used" => used, "free" => free_size } - ) - end + # convert into KiB for TargetInitDU + free_size_kib = free_size / 1024 + used_kib = used / 1024 + mount_name = part["mount"] + log.info "partition: mount: #{mount_name}, free: #{free_size_kib}KiB, used: #{used_kib}KiB" + + mount_name = mount_name[1..-1] if remove_slash && mount_name != "/" + + target_partitions << { + "filesystem" => used_fs.to_s, + "growonly" => growonly, + "name" => mount_name, + "used" => used_kib, + "free" => free_size_kib + } end end end @@ -1028,7 +948,6 @@ used = 0 - message = "" #$[ "dir" : [ total, usednow, usedfuture ], .... ] Builtins.foreach(Pkg.TargetGetDU) do |dir, sizelist| Builtins.y2milestone("%1: %2", dir, sizelist) @@ -1120,6 +1039,61 @@ publish :function => :GetRequSpace, :type => "string (boolean)" publish :function => :CheckDiskSize, :type => "boolean ()" publish :function => :CheckDiskFreeSpace, :type => "list <map> (integer, integer)" + + # check whether the Btrfs filesystem at the specified directory contains + # any snapshot (in any subvolume) + # @param [String] directory mounted directory with Btrfs + # @return [Boolean] true when a snapshot is found + def btrfs_snapshots?(directory) + # list available snapshot subvolumes + ret = SCR.Execute(path(".target.bash_output"), "btrfs subvolume list -s #{Shellwords.escape(directory)}") + + if ret["exit"] != 0 + log.error "btrfs call failed: #{ret}" + raise "Cannot detect Btrfs snapshots, subvolume listing failed : #{ret["stderr"]}" + end + + snapshots = ret["stdout"].split("\n") + log.info "Found #{snapshots.size} btrfs snapshots" + log.debug "Snapshots: #{snapshots}" + + !snapshots.empty? + end + + # @param [String] directory mounted directory with Btrfs + # @return [Integer] used size in bytes + def btrfs_used_size(directory) + ret = SCR.Execute(path(".target.bash_output"), + "LC_ALL=C btrfs filesystem df #{Shellwords.escape(directory)}") + + if ret["exit"] != 0 + log.error "btrfs call failed: #{ret}" + raise "Cannot detect Btrfs disk usage: #{ret["stderr"]}" + end + + df_info = ret["stdout"].split("\n") + log.info "Usage reported by btrfs: #{df_info}" + + # sum the "used" sizes + used = df_info.reduce(0) do |acc, line | + size = line[/used=(\S+)/, 1] + size = size ? size_from_string(size) : 0 + acc += size + end + + log.info "Detected total used size: #{used} (#{used / 1024 / 1024}MiB)" + used + end + + # Convert textual size with optional unit suffix into a number + # @example + # size_from_string("2.45MiB") => 2569011 + # @param size_str [String] input value in format "<number>[<space>][<unit>]" + # where unit can be one of: "" (none) or "B", "KiB", "MiB", "GiB", "TiB", "PiB" + # @return [Integer] size in bytes + def size_from_string(size_str) + WFM.call("wrapper_storage", ["ClassicStringToByte", [size_str]]) + end end SpaceCalculation = SpaceCalculationClass.new diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-3.1.47/test/data/run_df.yml new/yast2-packager-3.1.49/test/data/run_df.yml --- old/yast2-packager-3.1.47/test/data/run_df.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-packager-3.1.49/test/data/run_df.yml 2014-09-23 11:58:10.000000000 +0200 @@ -0,0 +1,36 @@ +--- +- free: '1470764' + name: / + prz: 12% + spec: tmpfs + type: tmpfs + used: '194072' + whole: '1664836' +- free: '0' + name: /parts/mp_0000 + prz: 100% + spec: /dev/loop0 + type: squashfs + used: '24320' + whole: '24320' +- free: '0' + name: /parts/mp_0001 + prz: 100% + spec: /dev/loop1 + type: squashfs + used: '13568' + whole: '13568' +- free: '807152' + name: /dev + prz: 1% + spec: devtmpfs + type: devtmpfs + used: '8' + whole: '807160' +- free: '2977336' + name: /mnt + prz: 53% + spec: /dev/sda2 + type: ext4 + used: '3259080' + whole: '6717440' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-3.1.47/test/space_calculation_test.rb new/yast2-packager-3.1.49/test/space_calculation_test.rb --- old/yast2-packager-3.1.47/test/space_calculation_test.rb 2014-09-12 11:18:11.000000000 +0200 +++ new/yast2-packager-3.1.49/test/space_calculation_test.rb 2014-09-23 11:58:10.000000000 +0200 @@ -12,6 +12,7 @@ DATA_PATH = File.join(File.expand_path(File.dirname(__FILE__)), "data") SCR_TMPDIR_PATH = Yast::Path.new(".target.tmpdir") SCR_BASH_PATH = Yast::Path.new(".target.bash") +SCR_BASH_OUTPUT_PATH = Yast::Path.new(".target.bash_output") def stub_target_map(name, with_fstopt) path = File.join(DATA_PATH, "#{name}_target_map.yml") @@ -23,7 +24,7 @@ end end allow(Yast::WFM).to(receive(:call).with("wrapper_storage", - ["GetTargetMap"]).and_return(tm)) + ["GetTargetMap"]).and_return(tm)) end def expect_to_execute(command) @@ -41,8 +42,8 @@ stub_target_map(target_map, with_options) allow(Yast::SCR).to receive(:Read).with(SCR_TMPDIR_PATH).and_return "/tmp" - allow(Yast::SCR).to receive(:Execute).with(SCR_BASH_PATH, /^test -d.* mkdir -p/) - allow(Yast::SCR).to receive(:Execute).with(SCR_BASH_PATH, /^\/bin\/umount/) + allow(Yast::SCR).to receive(:Execute).with(SCR_BASH_PATH, /^mkdir -p/) + allow(Yast::SCR).to receive(:Execute).with(SCR_BASH_PATH, /^umount/) end context "on xfs" do @@ -110,4 +111,148 @@ end end end + + describe "#size_from_string" do + it "converts string without units bytes" do + expect(Yast::SpaceCalculation.size_from_string("42.00")).to eq(42) + end + + it "converts B unit to bytes" do + expect(Yast::SpaceCalculation.size_from_string("42B")).to eq(42) + end + + it "accepts KiB size parameter" do + expect(Yast::SpaceCalculation.size_from_string("42KiB")).to eq(42 * (2**10)) + end + + it "accepts MiB size parameter" do + expect(Yast::SpaceCalculation.size_from_string("42MiB")).to eq(42 * (2**20)) + end + + it "accepts GiB size parameter" do + expect(Yast::SpaceCalculation.size_from_string("42GiB")).to eq(42 * (2**30)) + end + + it "accepts TiB size parameter" do + expect(Yast::SpaceCalculation.size_from_string("42TiB")).to eq(42 * (2**40)) + end + + it "accepts PiB size parameter" do + expect(Yast::SpaceCalculation.size_from_string("42PiB")).to eq(42 * (2**50)) + end + + it "ignores space separators" do + expect(Yast::SpaceCalculation.size_from_string("42 KiB")).to eq(42 * 1024) + end + + it "accepts floats" do + expect(Yast::SpaceCalculation.size_from_string("42.42 KiB")).to eq((42.42 * 1024).to_i) + end + + it "converts '0.00' to zero" do + expect(Yast::SpaceCalculation.size_from_string("0.00")).to eq(0) + end + end + + describe "#btrfs_snapshots?" do + let(:dir) { "/mnt" } + + it "returns true when a snapshot is found" do + stdout = "ID 256 gen 5 cgen 5 top level 5 otime 2014-09-19 10:27:05 path snapshot\n" + expect(Yast::SCR).to receive(:Execute).with(SCR_BASH_OUTPUT_PATH, + "btrfs subvolume list -s #{dir}").and_return("stdout" => stdout, "exit" => 0) + expect(Yast::SpaceCalculation.btrfs_snapshots?(dir)).to be true + end + + it "returns false when a snapshot is not found" do + expect(Yast::SCR).to receive(:Execute).with(SCR_BASH_OUTPUT_PATH, + "btrfs subvolume list -s #{dir}").and_return("stdout" => "", "exit" => 0) + expect(Yast::SpaceCalculation.btrfs_snapshots?(dir)).to be false + end + + it "raises exception when btrfs tool fails" do + expect(Yast::SCR).to receive(:Execute).with(SCR_BASH_OUTPUT_PATH, + "btrfs subvolume list -s #{dir}").and_return("stdout" => "", "exit" => 127) + expect { Yast::SpaceCalculation.btrfs_snapshots?(dir) }.to raise_error( + /Cannot detect Btrfs snapshots, subvolume listing failed/) + end + end + + describe "#btrfs_used_size" do + let(:dir) { "/mnt" } + + it "returns sum of used sizes reported by btrfs tool" do + stdout = <<EOF +Data: total=1.33GiB, used=876.35MiB +System, DUP: total=8.00MiB, used=4.00KiB +System: total=4.00MiB, used=0.00B +Metadata, DUP: total=339.00MiB, used=77.03MiB +Metadata: total=8.00MiB, used=0.00B +EOF + expect(Yast::SCR).to receive(:Execute).with(SCR_BASH_OUTPUT_PATH, + "LC_ALL=C btrfs filesystem df #{dir}").and_return("stdout" => stdout, "exit" => 0) + expect(Yast::SpaceCalculation.btrfs_used_size(dir)).to eq(999_695_482) + end + + it "raises an exception when btrfs tool fails" do + expect(Yast::SCR).to receive(:Execute).with(SCR_BASH_OUTPUT_PATH, + "LC_ALL=C btrfs filesystem df #{dir}").and_return("stdout" => "", "exit" => 127) + expect { Yast::SpaceCalculation.btrfs_used_size(dir) }.to raise_error( + /Cannot detect Btrfs disk usage/) + end + + it "ignores lines without 'used' value" do + # the same as in the test above, but removed "used=0.00B" values + stdout = <<EOF +Data: total=1.33GiB, used=876.35MiB +System, DUP: total=8.00MiB, used=4.00KiB +System: total=4.00MiB +Metadata, DUP: total=339.00MiB, used=77.03MiB +Metadata: total=8.00MiB +EOF + expect(Yast::SCR).to receive(:Execute).with(SCR_BASH_OUTPUT_PATH, + "LC_ALL=C btrfs filesystem df #{dir}").and_return("stdout" => stdout, "exit" => 0) + expect(Yast::SpaceCalculation.btrfs_used_size(dir)).to eq(999_695_482) + end + end + + describe "#EvaluateFreeSpace" do + let(:run_df) { YAML.load_file(File.join(DATA_PATH, "run_df.yml")) } + let(:destdir) { "/mnt" } + + before do + expect(Yast::Installation).to receive(:destdir).and_return(destdir) + allow(Yast::Installation).to receive(:dirinstall_installing_into_dir) + end + + it "Reads current disk usage and reserves extra free space" do + expect(Yast::SCR).to receive(:Read).with(Yast::Path.new(".run.df")). + and_return(run_df) + + result = [{"name" => "/", "filesystem" => "ext4", "used" => 3259080, + "growonly" => false, "free" => 2530736}] + + expect(Yast::Pkg).to receive(:TargetInitDU).with(result) + expect(Yast::SpaceCalculation.EvaluateFreeSpace(15)).to eq(result) + end + + it "sets 'growonly' flag when btrfs with a snapshot is found" do + run_df_btrfs = run_df + run_df_btrfs.last["type"] = "btrfs" + + expect(Yast::SCR).to receive(:Read).with(Yast::Path.new(".run.df")). + and_return(run_df_btrfs) + expect(Yast::SpaceCalculation).to receive(:btrfs_used_size).with(destdir). + and_return(3259080*1024) + expect(Yast::SpaceCalculation).to receive(:btrfs_snapshots?).with(destdir). + and_return(true) + + result = [{"name" => "/", "filesystem" => "btrfs", "used" => 3259080, + "growonly" => true, "free" => 2939606}] + + expect(Yast::Pkg).to receive(:TargetInitDU).with(result) + expect(Yast::SpaceCalculation.EvaluateFreeSpace(15)).to eq(result) + end + + end end -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
