Skip to content

Commit

Permalink
Merge pull request #82 from aschnell/master
Browse files Browse the repository at this point in the history
- added support for lvm in yaml reader
  • Loading branch information
aschnell authored Jul 15, 2016
2 parents 226f4ee + 02ccfe4 commit f4f1fad
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 15 deletions.
10 changes: 7 additions & 3 deletions src/lib/storage/abstract_device_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ def initialize(devicegraph)

# Read a YAML file and build a fake device tree from it.
#
# @param filename [String] name of the YAML file
# @param yaml_file [String, IO] YAML file
#
def load_yaml_file(filename)
File.open(filename) { |file| YAML.load_stream(file, filename) { |doc| build_tree(doc) } }
def load_yaml_file(yaml_file)
if yaml_file.respond_to?(:read)
YAML.load_stream(yaml_file) { |doc| build_tree(doc) }
else
File.open(yaml_file) { |file| YAML.load_stream(file, yaml_file) { |doc| build_tree(doc) } }
end
rescue SystemCallError => ex
log.error("#{ex}")
raise
Expand Down
135 changes: 123 additions & 12 deletions src/lib/storage/fake_device_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# encoding: utf-8

# Copyright (c) [2015] SUSE LLC
# Copyright (c) [2015-2016] SUSE LLC
#
# All Rights Reserved.
#
Expand Down Expand Up @@ -39,15 +39,20 @@ class FakeDeviceFactory < AbstractDeviceFactory
include EnumMappings

# Valid toplevel products of this factory
VALID_TOPLEVEL = ["disk"]
VALID_TOPLEVEL = ["disk", "lvm_vg"]

# Valid hierarchy within the products of this factory.
# This indicates the permitted children types for each parent.
VALID_HIERARCHY =
{
"disk" => ["partition_table", "partitions", "file_system"],
"partitions" => ["partition", "free"],
"partition" => ["file_system"]
"partition" => ["file_system"],
"lvm_vg" => ["lvm_lvs", "lvm_pvs"],
"lvm_lvs" => ["lvm_lv"],
"lvm_lv" => ["file_system"],
"lvm_pvs" => ["lvm_pv"],
"lvm_pv" => []
}

# Valid parameters for each product of this factory.
Expand All @@ -63,7 +68,11 @@ class FakeDeviceFactory < AbstractDeviceFactory
"size", "start", "align", "name", "type", "id", "mount_point", "label", "uuid"
],
"file_system" => [],
"free" => ["size", "start"]
"free" => ["size", "start"],
"lvm_vg" => ["vg_name", "extent_size"],
"lvm_lv" => ["lv_name", "size", "stripes", "stripe_size", "mount_point",
"label", "uuid"],
"lvm_pv" => ["blk_device"]
}

class << self
Expand Down Expand Up @@ -133,7 +142,8 @@ def valid_param
#
def fixup_param(name, param)
log.info("Fixing up #{param} for #{name}")
["size", "start", "block_size", "io_size", "min_grain", "align_ofs", "mbr_gap"].each do |key|
["size", "start", "block_size", "io_size", "min_grain", "align_ofs",
"mbr_gap", "extent_size", "stripe_size"].each do |key|
param[key] = DiskSize.new(param[key]) if param.key?(key)
end
param
Expand Down Expand Up @@ -275,11 +285,7 @@ def create_partition(parent, args)
raise ArgumentError, "\"name\" missing for partition #{args} on #{disk_name}" unless part_name
raise ArgumentError, "Duplicate partition #{part_name}" if @partitions.include?(part_name)

# Keep some parameters that are really file system related in @partitions
# to be picked up later by create_file_system.
@partitions[part_name] = args.select do |k, _v|
["mount_point", "label", "uuid"].include?(k)
end
file_system_data_picker(part_name, args)

id = id.to_i(16) if id.is_a?(::String) && id.start_with?("0x")
id = fetch(PARTITION_IDS, id, "partition ID", part_name) unless id.is_a?(Fixnum)
Expand Down Expand Up @@ -352,14 +358,28 @@ def create_file_system(parent, args)
label = fs_param["label"]
uuid = fs_param["uuid"]

partition = ::Storage::Partition.find_by_name(@devicegraph, part_name)
file_system = partition.create_filesystem(fs_type)
blk_device = ::Storage::BlkDevice.find_by_name(@devicegraph, part_name)
file_system = blk_device.create_filesystem(fs_type)
file_system.add_mountpoint(mount_point) if mount_point
file_system.label = label if label
file_system.uuid = uuid if uuid
part_name
end

# Picks some parameters that are really file system related from args
# and places them in @partitions to be picked up later by
# create_file_system.
#
# @param [String] name of blk_device file system is on
#
# @param args [Hash] hash with data from yaml file
#
def file_system_data_picker(name, args)
@partitions[name] = args.select do |k, _v|
["mount_point", "label", "uuid"].include?(k)
end
end

# Factory method to create a slot of free space.
#
# We just remember the value and take it into account when we create the next partition.
Expand All @@ -380,6 +400,97 @@ def create_free(parent, args)
disk_name
end

# Factory method to create a lvm volume group.
#
# @param _parent [nil] (volume groups are toplevel)
# @param args [Hash] volume group parameters:
# "vg_name" volume group name
# "extent_size" extent size
#
# @return [Object] new volume group object
#
def create_lvm_vg(_parent, args)
log.info("#{__method__}( #{args} )")

@partitions = {}

vg_name = args["vg_name"]
lvm_vg = ::Storage::LvmVg.create(@devicegraph, vg_name)

extent_size = args["extent_size"] || DiskSize.zero
lvm_vg.extent_size = extent_size.size if extent_size.size > 0

lvm_vg
end

# Factory method to create a lvm logical volume.
#
# Some of the parameters ("mount_point", "label"...) really belong to the
# file system which is a separate factory product, but it is more natural
# to specify this for the logical volume, so those data are kept in
# @partitions to be picked up in create_file_system when needed.
#
# @param parent [Object] volume group object
#
# @param args [Hash] lvm logical volume parameters:
# "lv_name" logical volume name
# "size" partition size
# "stripes" number of stripes
# "stripe_size" stripe size
# "mount_point" mount point for the associated file system
# "label" file system label
# "uuid" file system UUID
#
# @return [String] device name of new logical volume
#
def create_lvm_lv(parent, args)
log.info("#{__method__}( #{parent}, #{args} )")

lv_name = args["lv_name"]
raise ArgumentError, "\"lv_name\" missing for lvm_lv #{args} on #{vg_name}" unless lv_name
raise ArgumentError, "Duplicate lvm_lv #{lv_name}" if @partitions.include?(lv_name)

size = args["size"] || DiskSize.zero
raise ArgumentError, "\"size\" missing for lvm_lv #{lv_name}" if size.zero?

lvm_lv = parent.create_lvm_lv(lv_name, size.size)

create_lvm_lv_stripe_parameters(lvm_lv, args)

file_system_data_picker(lvm_lv.name, args)

lvm_lv.name
end

# Helper class for create_lvm_lv handling the stripes related parameters.
#
def create_lvm_lv_stripe_parameters(lvm_lv, args)
stripes = args["stripes"] || 0
lvm_lv.stripes = stripes if stripes > 0

stripe_size = args["stripe_size"] || DiskSize.zero
lvm_lv.stripe_size = stripe_size.size if stripe_size.size > 0
end

# Factory method to create a lvm physical volume.
#
# @param parent [Object] volume group object
#
# @param args [Hash] lvm physical volume parameters:
# "blk_device" block device used by physical volume
#
# @return [Object] new physical volume object
#
def create_lvm_pv(parent, args)
log.info("#{__method__}( #{parent}, #{args} )")

blk_device_name = args["blk_device"]

blk_device = ::Storage::BlkDevice.find_by_name(devicegraph, blk_device_name)

parent.add_lvm_pv(blk_device)
end

private

# Fetch hash[key] and raise an exception if there is no such key.
Expand Down
136 changes: 136 additions & 0 deletions test/fake_device_factory_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env rspec
# encoding: utf-8

# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require_relative "spec_helper"
require "storage"
require "storage/fake_device_factory"

describe Yast::Storage::FakeDeviceFactory do

it "reads yaml of simple disk and partition setup" do

environment = Storage::Environment.new(true, Storage::ProbeMode_NONE, Storage::TargetMode_DIRECT)
storage = Storage::Storage.new(environment)
staging = storage.staging

# rubocop:disable Style/StringLiterals

input = ['---',
'- disk:',
' name: "/dev/sda"',
' size: 256 GiB',
' block_size: 4 KiB',
' partition_table: gpt',
' partitions:',
' - free:',
' size: 1 MiB',
' start: 0 B',
' - partition:',
' size: 0.5 GiB',
' start: 1 MiB',
' name: "/dev/sda1"',
' type: primary',
' id: swap',
' file_system: swap',
' mount_point: swap',
' - partition:',
' size: 16 GiB',
' start: 513 MiB (0.50 GiB)',
' name: "/dev/sda2"',
' type: primary',
' id: linux',
' file_system: ext4',
' mount_point: "/"',
' - free:',
' size: 245247 MiB (239.50 GiB)',
' start: 16897 MiB (16.50 GiB)']

# rubocop:enable all

io = StringIO.new(input.join("\n"))
Yast::Storage::FakeDeviceFactory.load_yaml_file(staging, io)

expect(staging.num_devices).to eq 6
expect(staging.num_holders).to eq 5

sda = Storage.to_disk(Storage::BlkDevice.find_by_name(staging, "/dev/sda"))
expect(sda.size).to eq 256 * Storage.GiB
expect(sda.region.block_size).to eq 4 * Storage.KiB

sda1 = Storage.to_partition(Storage::BlkDevice.find_by_name(staging, "/dev/sda1"))
expect(sda1.size).to eq 512 * Storage.MiB

sda2 = Storage.to_partition(Storage::BlkDevice.find_by_name(staging, "/dev/sda2"))
expect(sda2.size).to eq 16 * Storage.GiB

end

it "reads yaml of simple lvm setup" do

environment = Storage::Environment.new(true, Storage::ProbeMode_NONE, Storage::TargetMode_DIRECT)
storage = Storage::Storage.new(environment)
staging = storage.staging

# rubocop:disable Style/StringLiterals

input = ['---',
'- disk:',
' name: "/dev/sda"',
' size: 1 TiB',
' block_size: 0.5 KiB',
' io_size: 0 B',
' min_grain: 1 MiB',
' align_ofs: 0 B',
'- lvm_vg:',
' vg_name: system',
' extent_size: 8 MiB',
' lvm_lvs:',
' - lvm_lv:',
' lv_name: root',
' size: 16 GiB',
' file_system: ext4',
' mount_point: "/"',
' lvm_pvs:',
' - lvm_pv:',
' blk_device: "/dev/sda"']

# rubocop:enable all

io = StringIO.new(input.join("\n"))
Yast::Storage::FakeDeviceFactory.load_yaml_file(staging, io)

expect(staging.num_devices).to eq 5
expect(staging.num_holders).to eq 4

root = Storage.to_lvm_lv(Storage::BlkDevice.find_by_name(staging, "/dev/system/root"))
expect(root.lv_name).to eq "root"
expect(root.size).to eq 16 * Storage.GiB

system = root.lvm_vg
expect(system.vg_name).to eq "system"
expect(system.extent_size).to eq 8 * Storage.MiB
expect(system.lvm_lvs.size).to eq 1
expect(system.lvm_pvs.size).to eq 1

end

end

0 comments on commit f4f1fad

Please sign in to comment.