Skip to content

Commit

Permalink
Merge branch 'master' into set-default-subvolume-name
Browse files Browse the repository at this point in the history
* master:
  Code review changes
  version bump and change log
  more Rubyish
  Use new Subvol class
  Added fallback_list
  tests for XML parser
  Moved subvol class to separate file and added unit test
  • Loading branch information
imobachgs committed Nov 2, 2016
2 parents 6259ac0 + c83e71d commit e42402d
Show file tree
Hide file tree
Showing 5 changed files with 401 additions and 57 deletions.
7 changes: 6 additions & 1 deletion package/yast2-storage.changes
Expand Up @@ -5,12 +5,17 @@ Mon Oct 31 13:11:04 UTC 2016 - igonzalezsosa@suse.com
name (FATE#320342 and FATE#317775) using AutoYaST.
- 3.1.106

-------------------------------------------------------------------
Thu Oct 27 14:26:49 CEST 2016 - shundhammer@suse.de

- Make subvolumes configurable in control.xml (fate#321737)
- 3.1.105

-------------------------------------------------------------------
Mon Oct 24 13:53:10 CEST 2016 - aschnell@suse.com

- fixed installing required storage packages for unmounted
filesystems (bsc#907331)
- 3.1.105

-------------------------------------------------------------------
Thu Sep 29 12:19:20 CEST 2016 - aschnell@suse.com
Expand Down
173 changes: 173 additions & 0 deletions src/lib/storage/subvol.rb
@@ -0,0 +1,173 @@
# encoding: utf-8

# Copyright (c) [2012-2016] Novell, Inc.
#
# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail, you may
# find current contact information at www.novell.com.

require "yast"
require "storage"
Yast.import "Arch"

module Yast
class StorageClass < Module

# Helper class to represent a subvolume as defined in control.xml
#
class Subvol
include Yast::Logger

attr_accessor :path, :copy_on_write, :archs

COW_SUBVOL_PATHS = [
"home",
"opt",
"srv",
"tmp",
"usr/local",
"var/cache",
"var/crash",
"var/lib/machines",
"var/lib/mailman",
"var/lib/named",
"var/log",
"var/opt",
"var/spool",
"var/tmp"
]

# No Copy On Write for SQL databases and libvirt virtual disks to
# minimize performance impact
NO_COW_SUBVOL_PATHS = [
"var/lib/libvirt/images",
"var/lib/mariadb",
"var/lib/mysql",
"var/lib/pgsql"
]

def initialize(path, copy_on_write: true, archs: nil)
@path = path
@copy_on_write = copy_on_write
@archs = archs
end

def to_s
text = "Subvol #{@path}"
text += " (NoCOW)" unless @copy_on_write
text += " (archs: #{@archs})" if arch_specific?
text
end

def arch_specific?
!archs.nil?
end

def cow?
@copy_on_write
end

def no_cow?
!@copy_on_write
end

# Comparison operator for sorting
#
def <=>(other)
path <=> other.path
end

# Check if this subvolume should be used for the current architecture.
# A subvolume is used if its archs contain the current arch.
# It is not used if its archs contain the current arch negated
# (e.g. "!ppc").
#
# @return [Boolean] true if this subvolume matches the current architecture
#
def current_arch?
matches_arch? { |arch| Arch.respond_to?(arch.to_sym) && Arch.send(arch.to_sym) }
end

# Check if this subvolume should be used for an architecture.
#
# If a block is given, the block is called as the matcher with the
# architecture to be tested as its argument.
#
# If no block is given (and only then), the 'target_arch' parameter is
# used to check against.
#
# @return [Boolean] true if this subvolume matches
#
def matches_arch?(target_arch = nil, &block)
return true unless arch_specific?
use_subvol = false
archs.each do |a|
arch = a.dup
negate = arch.start_with?("!")
arch[0] = "" if negate # remove leading "!"
if block_given?
match = block.call(arch)
else
match = arch == target_arch
end
if match && negate
log.info("Not using #{self} for explicitly excluded arch #{arch}")
return false
end
use_subvol ||= match
end
log.info("Using arch specific #{self}: #{use_subvol}")
use_subvol
end

# Factory method: Create one Subvol from XML data stored as a map.
#
# @return Subvol or nil if error
#
def self.create_from_xml(xml)
return nil unless xml.key?("path")
path = xml["path"]
cow = true
if xml.key?("copy_on_write")
cow = xml["copy_on_write"]
end
archs = nil
if xml.key?("archs")
archs = xml["archs"].gsub(/\s+/, "").split(",")
end
subvol = Subvol.new(path, copy_on_write: cow, archs: archs)
log.info("Creating from XML: #{subvol}")
subvol
end

# Create a fallback list of Subvols. This is useful if nothing is
# specified in the control.xml file.
#
# @return List<Subvol>
#
def self.fallback_list
subvols = []
COW_SUBVOL_PATHS.each { |path| subvols << Subvol.new(path) }
NO_COW_SUBVOL_PATHS.each { |path| subvols << Subvol.new(path, copy_on_write: false) }
subvols << Subvol.new("boot/grub2/i386-pc", archs: ["i386", "x86_64"])
subvols << Subvol.new("boot/grub2/x86_64-efi", archs: ["x86_64"])
subvols << Subvol.new("boot/grub2/powerpc-ieee1275", archs: ["ppc", "!board_powernv"])
subvols << Subvol.new("boot/grub2/s390x-emu", archs: ["s390"])
subvols.select { |s| s.current_arch? }.sort
end
end
end
end
85 changes: 30 additions & 55 deletions src/modules/Storage.rb
Expand Up @@ -25,6 +25,7 @@
require "storage/target_map_formatter"
require "storage/used_storage_features"
require "storage/shadowed_vol_helper"
require "storage/subvol"

module Yast
class StorageClass < Module
Expand Down Expand Up @@ -5007,6 +5008,24 @@ def DetectHomeFs(p)
ret
end

# Create a list of Subvols from the <subvolumes> part of control.xml.
# The map may be empty if there is a <subvolumes> section, but it's empty.
#
# This function does not do much error handling or reporting; it is assumed
# that control.xml is validated against its schema.
#
# @param subvolumes_xml list of XML <subvolume> entries
# @return Subvolumes map or nil
#
def ReadSubvolsFromXml(subvolumes_xml)
return nil if subvolumes_xml.nil?
return nil unless subvolumes_xml.respond_to?(:map)

all_subvols = subvolumes_xml.map { |xml| Subvol.create_from_xml(xml) }
all_subvols.compact! # Remove nil subvols due to XML parse errors
relevant_subvols = all_subvols.select { |s| s.current_arch? }
relevant_subvols.sort
end

# Adds the list of subvolumes to a partition meant to be used as root (/)
#
Expand All @@ -5015,81 +5034,37 @@ def DetectHomeFs(p)
def AddSubvolRoot(part)
part = deep_copy(part)

subvol_names = [
"home",
"opt",
"srv",
"tmp",
"usr/local",
"var/cache",
"var/crash",
"var/lib/libvirt/images",
"var/lib/machines",
"var/lib/mailman",
"var/lib/mariadb",
"var/lib/mysql",
"var/lib/named",
"var/lib/pgsql",
"var/log",
"var/opt",
"var/spool",
"var/tmp"
]

# No Copy On Write for SQL databases and libvirt virtual disks to
# minimize performance impact
nocow_subvols = [
"var/lib/libvirt/images",
"var/lib/mariadb",
"var/lib/mysql",
"var/lib/pgsql"
]

if Arch.i386 || Arch.x86_64
subvol_names.push("boot/grub2/i386-pc")
end

if Arch.x86_64
subvol_names.push("boot/grub2/x86_64-efi")
end

if Arch.ppc and !Arch.board_powernv
subvol_names.push("boot/grub2/powerpc-ieee1275")
end

if Arch.s390
subvol_names.push("boot/grub2/s390x-emu")
end

subvol_names.sort!()
xml = ProductFeatures.GetSection("partitioning")
subvols = ReadSubvolsFromXml(xml["subvolumes"]) || Subvol.fallback_list
subvols.each { |s| log.info("Initial #{s}") }

subvol_prepend = ""
part["subvol"] ||= []
Builtins.y2milestone("AddSubvolRoot subvol: %1", part["subvol"])
if FileSystems.default_subvol != ""
subvol_prepend = FileSystems.default_subvol+"/"
end
fmt = part.fetch("format",false)
fmt = part["format"] || false
names = []
if !fmt
names = part["subvol"].select { |s| !s.fetch("delete", false) }.each { |s| s.fetch("name", "") }
else
part["subvol"] = []
end
Builtins.y2milestone("AddSubvolRoot subvol names: %1 subvol: %2", names, part["subvol"])
subvol_names.each do |subvol|
subvol_full_name = subvol_prepend + subvol
log.info("AddSubvolRoot subvol names: #{names} subvol: #{part['subvol']}")
subvols.each do |subvol|
subvol_full_name = subvol_prepend + subvol.path
if !names.include?( subvol_full_name )
subvol_entry = { "create" => true, "name" => subvol_full_name }
if nocow_subvols.include?( subvol )
if subvol.no_cow?
subvol_entry["nocow"] = true
Builtins.y2milestone("AddSubvolRoot: NoCOW for %1", subvol_full_name)
log.info("AddSubvolRoot: NoCOW for #{subvol_full_name}")
end
part["subvol"].push(subvol_entry)
end
end
Builtins.y2milestone("AddSubvolRoot subvol:\n%1", format_target_map(part["subvol"]))
Builtins.y2milestone("AddSubvolRoot part: \n%1", format_target_map(part))
log.info("AddSubvolRoot subvol:\n#{format_target_map(part['subvol'])}")
log.info("AddSubvolRoot part: \n#{format_target_map(part)}")
part
end

Expand Down
3 changes: 2 additions & 1 deletion test/Makefile.am
Expand Up @@ -8,7 +8,8 @@ TESTS = \
storage_get_disk_partition.rb \
storage_boot_on_raid1.rb \
partitions_test.rb \
include/partitioning_custom_part_check_generated_include_test.rb\
include/partitioning_custom_part_check_generated_include_test.rb \
subvol_test.rb \
ro_text_test.rb

TEST_EXTENSIONS = .rb
Expand Down

0 comments on commit e42402d

Please sign in to comment.