diff --git a/service/lib/agama/dbus/storage/devices_tree.rb b/service/lib/agama/dbus/storage/devices_tree.rb index c06501bd1e..a930c7840b 100644 --- a/service/lib/agama/dbus/storage/devices_tree.rb +++ b/service/lib/agama/dbus/storage/devices_tree.rb @@ -70,13 +70,16 @@ def dbus_object?(dbus_object, device) # Right now, only the required information for calculating a proposal is exported, that is: # * Potential candidate devices (i.e., disk devices, MDs). # * Partitions of the candidate devices in order to indicate how to find free space. - # - # TODO: export LVM VGs and file systems of directly formatted devices. + # * LVM volume groups and logical volumes. # # @param devicegraph [Y2Storage::Devicegraph] # @return [Array] def devices(devicegraph) - devices = devicegraph.disk_devices + devicegraph.software_raids + devices = devicegraph.disk_devices + + devicegraph.software_raids + + devicegraph.lvm_vgs + + devicegraph.lvm_lvs + devices + partitions_from(devices) end diff --git a/service/lib/agama/dbus/storage/interfaces/device.rb b/service/lib/agama/dbus/storage/interfaces/device.rb index 98f09690c2..f721da72ef 100644 --- a/service/lib/agama/dbus/storage/interfaces/device.rb +++ b/service/lib/agama/dbus/storage/interfaces/device.rb @@ -35,6 +35,7 @@ module Device require "agama/dbus/storage/interfaces/device/component" require "agama/dbus/storage/interfaces/device/drive" require "agama/dbus/storage/interfaces/device/filesystem" +require "agama/dbus/storage/interfaces/device/lvm_vg" require "agama/dbus/storage/interfaces/device/md" require "agama/dbus/storage/interfaces/device/multipath" require "agama/dbus/storage/interfaces/device/partition_table" diff --git a/service/lib/agama/dbus/storage/interfaces/device/component.rb b/service/lib/agama/dbus/storage/interfaces/device/component.rb index c66422b2b4..a4de9ce3e3 100644 --- a/service/lib/agama/dbus/storage/interfaces/device/component.rb +++ b/service/lib/agama/dbus/storage/interfaces/device/component.rb @@ -84,6 +84,8 @@ def self.included(base) base.class_eval do dbus_interface COMPONENT_INTERFACE do dbus_reader :component_type, "s", dbus_name: "Type" + # The names are provided just in case the device is component of a device that + # is not exported yet (e.g., Bcache devices). dbus_reader :component_device_names, "as", dbus_name: "DeviceNames" dbus_reader :component_devices, "ao", dbus_name: "Devices" end diff --git a/service/lib/agama/dbus/storage/interfaces/device/lvm_vg.rb b/service/lib/agama/dbus/storage/interfaces/device/lvm_vg.rb new file mode 100644 index 0000000000..b69b84a5f5 --- /dev/null +++ b/service/lib/agama/dbus/storage/interfaces/device/lvm_vg.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] 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 "dbus" + +module Agama + module DBus + module Storage + module Interfaces + module Device + # Interface for a LVM Volume Group. + # + # @note This interface is intended to be included by {Agama::DBus::Storage::Device} if + # needed. + module LvmVg + # Whether this interface should be implemented for the given device. + # + # @note LVM Volume Groups implement this interface. + # + # @param storage_device [Y2Storage::Device] + # @return [Boolean] + def self.apply?(storage_device) + storage_device.is?(:lvm_vg) + end + + VOLUME_GROUP_INTERFACE = "org.opensuse.Agama.Storage1.LVM.VolumeGroup" + private_constant :VOLUME_GROUP_INTERFACE + + # Name of the volume group + # + # @return [String] e.g., "/dev/mapper/vg0" + def lvm_vg_name + storage_device.name + end + + # Size of the volume group in bytes + # + # @return [Integer] + def lvm_vg_size + storage_device.size.to_i + end + + # D-Bus paths of the objects representing the physical volumes. + # + # @return [Array] + def lvm_vg_pvs + storage_device.lvm_pvs.map { |p| tree.path_for(p.plain_blk_device) } + end + + # D-Bus paths of the objects representing the logical volumes. + # + # @return [Array] + def lvm_vg_lvs + storage_device.lvm_lvs.map { |l| tree.path_for(l) } + end + + def self.included(base) + base.class_eval do + dbus_interface VOLUME_GROUP_INTERFACE do + dbus_reader :lvm_vg_name, "s", dbus_name: "Name" + dbus_reader :lvm_vg_size, "t", dbus_name: "Size" + dbus_reader :lvm_vg_pvs, "ao", dbus_name: "PhysicalVolumes" + dbus_reader :lvm_vg_lvs, "ao", dbus_name: "LogicalVolumes" + end + end + end + end + end + end + end + end +end diff --git a/service/test/agama/dbus/storage/device_test.rb b/service/test/agama/dbus/storage/device_test.rb index 649131a83f..457951fadb 100644 --- a/service/test/agama/dbus/storage/device_test.rb +++ b/service/test/agama/dbus/storage/device_test.rb @@ -19,19 +19,20 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. -require "agama/dbus/storage/device" -require "agama/dbus/storage/devices_tree" -require "dbus" require_relative "../../../test_helper" require_relative "../../storage/storage_helpers" require_relative "./interfaces/device/block_examples" require_relative "./interfaces/device/component_examples" require_relative "./interfaces/device/drive_examples" require_relative "./interfaces/device/filesystem_examples" +require_relative "./interfaces/device/lvm_vg_examples" require_relative "./interfaces/device/md_examples" require_relative "./interfaces/device/multipath_examples" require_relative "./interfaces/device/partition_table_examples" require_relative "./interfaces/device/raid_examples" +require "agama/dbus/storage/device" +require "agama/dbus/storage/devices_tree" +require "dbus" describe Agama::DBus::Storage::Device do include Agama::RSpec::StorageHelpers @@ -110,6 +111,16 @@ end end + context "when the given device is a LVM volume group" do + let(:scenario) { "trivial_lvm.yml" } + + let(:device) { devicegraph.find_by_name("/dev/vg0") } + + it "defines the LVM.VolumeGroup interface" do + expect(subject).to include_dbus_interface("org.opensuse.Agama.Storage1.LVM.VolumeGroup") + end + end + context "when the given device has a partition table" do let(:scenario) { "partitioned_md.yml" } @@ -142,6 +153,8 @@ include_examples "Block interface" + include_examples "LVM.VolumeGroup interface" + include_examples "PartitionTable interface" include_examples "Filesystem interface" diff --git a/service/test/agama/dbus/storage/interfaces/device/lvm_vg_examples.rb b/service/test/agama/dbus/storage/interfaces/device/lvm_vg_examples.rb new file mode 100644 index 0000000000..ad646ca618 --- /dev/null +++ b/service/test/agama/dbus/storage/interfaces/device/lvm_vg_examples.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] 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 "../../../../../test_helper" + +shared_examples "LVM.VolumeGroup interface" do + describe "LVM.VolumeGroup D-Bus interface" do + let(:scenario) { "trivial_lvm.yml" } + + let(:device) { devicegraph.find_by_name("/dev/vg0") } + + describe "#lvm_vg_name" do + it "returns the name of the volume group" do + expect(subject.lvm_vg_name).to eq("/dev/vg0") + end + end + + describe "#lvm_vg_size" do + before do + allow(device).to receive(:size).and_return(size) + end + + let(:size) { Y2Storage::DiskSize.new(1024) } + + it "returns the size in bytes" do + expect(subject.lvm_vg_size).to eq(1024) + end + end + + describe "#lvm_vg_pvs" do + it "returns the D-Bus path of the physical volumes" do + sda1 = devicegraph.find_by_name("/dev/sda1") + + expect(subject.lvm_vg_pvs).to contain_exactly(tree.path_for(sda1)) + end + end + + describe "#lvm_vg_lvs" do + it "returns the D-Bus path of the logical volumes" do + lv1 = devicegraph.find_by_name("/dev/vg0/lv1") + + expect(subject.lvm_vg_lvs).to contain_exactly(tree.path_for(lv1)) + end + end + end +end diff --git a/service/test/fixtures/trivial_lvm.yml b/service/test/fixtures/trivial_lvm.yml new file mode 100644 index 0000000000..86f3fc904e --- /dev/null +++ b/service/test/fixtures/trivial_lvm.yml @@ -0,0 +1,24 @@ +--- +- disk: + name: /dev/sda + size: 200 GiB + partition_table: gpt + partitions: + + - partition: + size: unlimited + name: /dev/sda1 + id: lvm + +- lvm_vg: + vg_name: vg0 + lvm_pvs: + - lvm_pv: + blk_device: /dev/sda1 + + lvm_lvs: + - lvm_lv: + size: unlimited + lv_name: lv1 + file_system: btrfs + mount_point: /