Skip to content

Commit

Permalink
Merge eafda57 into e5dbfd3
Browse files Browse the repository at this point in the history
  • Loading branch information
ancorgs committed Apr 18, 2016
2 parents e5dbfd3 + eafda57 commit b39c476
Show file tree
Hide file tree
Showing 17 changed files with 464 additions and 168 deletions.
41 changes: 29 additions & 12 deletions src/lib/storage/boot_requirements_strategies/uefi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,44 @@ module BootRequirementsStrategies
class UEFI < Base
def needed_partitions
volumes = super
volumes << efi_volume if efi_partition_missing?
volumes << efi_volume
volumes
end

protected

def efi_partition_missing?
disk_analyzer.efi_partitions.empty?
end

def efi_volume
vol = PlannedVolume.new("/boot/efi", ::Storage::FsType_VFAT)
# So far we are always using msdos partition ids
vol.partition_id = ::Storage::ID_EFI
vol.min_size = DiskSize.MiB(33)
vol.max_size = DiskSize.unlimited
vol.desired_size = DiskSize.MiB(500)
vol.can_live_on_logical_volume = false
vol.max_start_offset = DiskSize.TiB(2)
if reusable_efi
vol.reuse = reusable_efi.name
else
# So far we are always using msdos partition ids
vol.partition_id = ::Storage::ID_EFI
vol.min_size = DiskSize.MiB(33)
vol.max_size = DiskSize.unlimited
vol.desired_size = DiskSize.MiB(500)
vol.can_live_on_logical_volume = false
vol.max_start_offset = DiskSize.TiB(2)
end
vol
end

def reusable_efi
@reusable_efi = biggest_efi_in_root_device || biggest_efi
end

def biggest_efi_in_root_device
biggest_partition(disk_analyzer.efi_partitions[settings.root_device])
end

def biggest_efi
biggest_partition(disk_analyzer.efi_partitions.values.flatten)
end

def biggest_partition(partitions)
return nil if partitions.nil? || partitions.empty?
partitions.sort_by.with_index { |part, idx| [part.size_k, idx] }.last
end
end
end
end
Expand Down
176 changes: 113 additions & 63 deletions src/lib/storage/disk_analyzer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
require "yast"
require "fileutils"
require "storage"
require "storage/disk_size"
require "storage/refinements/disk"
require "storage/refinements/devicegraph_lists"

Expand Down Expand Up @@ -71,21 +72,53 @@ class DiskAnalyzer

DEFAULT_DISK_CHECK_LIMIT = 10

attr_reader :installation_disks, :candidate_disks
attr_reader :windows_partitions, :linux_partitions, :efi_partitions
# @return [Array<String>] device names of installation media.
# Filled by #analyze.
attr_reader :installation_disks

# @return [Array<String>] device name of disks to install on.
# Filled by #analyze.
attr_reader :candidate_disks

# @return [Hash] Linux partitions found in each candidate disk.
# Filled by #analyze.
# @see #find_linux_partitions
attr_reader :linux_partitions

# @return [Hash] MS Windows partitions found in each candidate disk.
# Filled by #analyze only if #linux_partitions is empty.
# @see #find_windows_partitions
attr_reader :windows_partitions

# @return [Hash] EFI partitions found in each candidate disk.
# Filled by #analyze.
# @see #find_efi_partitions
attr_reader :efi_partitions

# @return [Hash] PReP partitions found in each candidate disk.
# Filled by #analyze.
# @see #find_prep_partitions
attr_reader :prep_partitions
attr_reader :devicegraph

# @return [Hash] Swap partitions found in each candidate disk.
# Filled by #analyze.
# @see #find_swap_partitions
attr_reader :swap_partitions

# @return [Fixnum] Maximum number of disks to check.
# @see #find_installation_disks
attr_accessor :disk_check_limit

def initialize
Yast.import "Arch"

@installation_disks = [] # device names of installation media
@candidate_disks = [] # device names of disks to install on
@linux_partitions = [] # device names of existing Linux parititions
@efi_partitions = [] # device names of existing EFI partitions
@prep_partitions = {} # device names of PReP partitions, indexed by disk
@windows_partitions = [] # only filled if @linux_partitions is empty
@installation_disks = []
@candidate_disks = []
@linux_partitions = {}
@windows_partitions = {}
@efi_partitions = {}
@prep_partitions = {}
@swap_partitions = {}

# Maximum number of disks to check. This might be important on
# architectures that tend to have very many disks (s390).
Expand All @@ -103,6 +136,7 @@ def analyze(devicegraph)
@linux_partitions = find_linux_partitions
@efi_partitions = find_efi_partitions
@prep_partitions = find_prep_partitions
@swap_partitions = find_swap_partitions

if @linux_partitions.empty?
@windows_partitions = find_windows_partitions
Expand All @@ -117,10 +151,16 @@ def analyze(devicegraph)
log.info("Installation disks: #{@installation_disks}")
log.info("Candidate disks: #{@candidate_disks}")
log.info("Linux partitions: #{@linux_partitions}")
log.info("EFI partitions: #{@efi_partitions}")
log.info("Windows partitions: #{@windows_partitions}")
log.info("EFI partitions: #{@efi_partitions}")
log.info("PReP partitions: #{@prep_partitions}")
log.info("Swap partitions: #{@swap_partitions}")
end

private

attr_reader :devicegraph

# Find disks that look like the current installation medium
# (the medium we just booted from to start the installation).
#
Expand Down Expand Up @@ -152,44 +192,20 @@ def find_candidate_disks
dev_names(candidate_disk_objects)
end

# Find partitions from any of the candidate disks that can be used as
# EFI system partitions.
#
# Checks for the partition id to return all potential partitions.
# Checking for content_info.efi? would only detect partitions that are
# going to be effectively used.
#
# @return [Array<string>] names of efi partitions
#
def find_efi_partitions
disks = devicegraph.disks.with(name: candidate_disks)
partitions = disks.partitions.with(id: ::Storage::ID_EFI)
partitions.map(&:name)
end

# Find partitions from any of the candidate disks that can be used as
# PReP partition
#
# The result is a Hash in which each key is the name of a candidate disk
# and the value is an Array with the names of all the PReP partitions
# present in that disk.
#
# @return [Hash]
def find_prep_partitions
disks = devicegraph.disks
pairs = candidate_disks.map do |name|
[name, disks.with(name: name).partitions.with(id: ::Storage::ID_PPC_PREP).to_a]
end
Hash[pairs]
end

# Array with all disks in the devicegraph
#
# @return [Array<::Storage::Disk>]
def all_disks
devicegraph.all_disks.to_a
end

# List of disks in the devicegraph
#
# @return [DisksList]
def disks
devicegraph.disks
end

# Disks that are suitable for installing Linux.
# Put any USB disks to the end of that array.
#
Expand Down Expand Up @@ -219,29 +235,29 @@ def candidate_disk_objects
@installation_disks
end

# Find partitions from any of the candidate disks that can be used as
# PReP partition
#
# @return [Hash] @see #partitions_by_id
def find_prep_partitions
partitions_with_id(::Storage::ID_PPC_PREP)
end

# Find partitions from any of the candidate disks that can be used as
# swap space
#
# @return [Hash] @see #partitions_by_id
def find_swap_partitions
partitions_with_id(::Storage::ID_SWAP)
end

# Find any Linux partitions on any of the candidate disks.
# This may be a normal Linux partition (type 0x83), a Linux swap
# partition (type 0x82), an LVM partition, or a RAID partition.
#
# @return [Array<Partition>] Linux partitions
#
# @return [Hash] @see #partitions_by_id
def find_linux_partitions
linux_partitions = []
@candidate_disks.each do |disk_name|
begin
disk = ::Storage::Disk.find(devicegraph, disk_name)
disk.partition_table.partitions.each do |partition|
if LINUX_PARTITION_IDS.include?(partition.id)
log.info("Found Linux partition #{partition} (ID 0x#{partition.id.to_s(16)})")
linux_partitions << partition.name
end
end
rescue RuntimeError => ex # FIXME: rescue ::Storage::Exception when SWIG bindings are fixed
log.info("CAUGHT exception #{ex}")
end
end
log.info("Linux part: #{linux_partitions}")
linux_partitions
partitions_with_id(LINUX_PARTITION_IDS)
end

# Find any MS Windows partitions that could possibly be resized.
Expand All @@ -258,14 +274,17 @@ def find_linux_partitions
#
def find_windows_partitions
return [] unless Arch.x86_64 || Arch.i386
windows_partitions = []
windows_partitions = {}

# No need to limit checking - PC arch only (few disks)
@candidate_disks.each do |disk_name|
begin
disk = ::Storage::Disk.find(devicegraph, disk_name)
disk.partition_table.partitions.each do |partition|
windows_partitions << partition.name if windows_partition?(partition)
next unless windows_partition?(partition)

windows_partitions[disk_name] ||= []
windows_partitions[disk_name] << partition
end
rescue RuntimeError => ex # FIXME: rescue ::Storage::Exception when SWIG bindings are fixed
log.info("CAUGHT exception #{ex}")
Expand Down Expand Up @@ -422,8 +441,6 @@ def umount(mount_point)
raise "umount failed for #{mount_point}" unless system(cmd)
end

private

# Remove any installation disks from 'disks' and return a disks array
# containing the disks that are not installation media.
#
Expand All @@ -438,6 +455,39 @@ def remove_installation_disks(disks)

disks.delete_if { |disk| @installation_disks.include?(disk.name) }
end

# Find partitions from any of the candidate disks that have a given (set
# of) partition id(s).
#
# The result is a Hash in which each key is the name of a candidate disk
# and the value is an Array of ::Storage::Partition objects
# representing the matching partitions in that disk.
#
# @param ids [::Storage::ID, Array<::Storage::ID>]
# @return [Hash]
def partitions_with_id(ids)
pairs = candidate_disks.map do |disk_name|
# Skip extended partitions
partitions = disks.with(name: disk_name).partitions.with do |part|
part.type != ::Storage::PartitionType_EXTENDED
end
partitions = partitions.with(id: ids).to_a
[disk_name, partitions]
end
Hash[pairs]
end

# Find partitions from any of the candidate disks that can be used as
# EFI system partitions.
#
# Checks for the partition id to return all potential partitions.
# Checking for content_info.efi? would only detect partitions that are
# going to be effectively used.
#
# @return [Hash] @see #partitions_by_id
def find_efi_partitions
partitions_with_id(::Storage::ID_EFI)
end
end
end
end
14 changes: 12 additions & 2 deletions src/lib/storage/planned_volume.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ class PlannedVolume
# get, like ::Storage::FsType_BTRFS or ::Storage::FsType_SWAP. A value of
# nil means the volume will not be formatted.
attr_accessor :filesystem_type
# @return [String] device name of an existing partition to reuse for this
# purpose. That means that no new partition will be created and, thus,
# most of the other attributes (with the obvious exception of mount_point)
# will be most likely ignored
attr_accessor :reuse
# @return [::Storage::IdNum] id of the partition in a ms-dos style
# partition table. If nil, the final id is expected to be inferred from
# the filesystem type.
Expand Down Expand Up @@ -73,8 +78,8 @@ class PlannedVolume
# flag but is not needed in our grub2 setup.
attr_accessor :bootable

TO_STRING_ATTRS = [:mount_point, :min_size, :max_size, :desired_size,
:disk, :max_start_offset]
TO_STRING_ATTRS = [:mount_point, :reuse, :min_size, :max_size,
:desired_size, :disk, :max_start_offset]

alias_method :desired, :desired_size
alias_method :min, :min_size
Expand All @@ -90,6 +95,7 @@ class PlannedVolume
def initialize(mount_point, filesystem_type = nil)
@mount_point = mount_point
@filesystem_type = filesystem_type
@reuse = nil
@partition_id = nil
@disk = nil
@size = DiskSize.zero
Expand Down Expand Up @@ -117,9 +123,13 @@ def initialize(mount_point, filesystem_type = nil)
# Minimum size that should be granted for the partition when applying a
# given strategy
#
# Returns zero for reused volumes
#
# @param strategy [Symbol] :desired or :min_size
# @return [DiskSize]
def min_valid_size(strategy)
# No need to provide space for reused volumes
return DiskSize.zero if reuse
size = send(strategy)
size = min_size if size.unlimited?
size
Expand Down

0 comments on commit b39c476

Please sign in to comment.