Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend storage D-Bus API #1071

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion service/.rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ AllCops:
Exclude:
- vendor/**/*
- lib/agama/dbus/y2dir/**/*
- agama.gemspec
- agama-yast.gemspec
- package/*.spec

# a D-Bus method definition may take up more line lenght than usual
Expand Down
9 changes: 7 additions & 2 deletions service/lib/agama/dbus/base_tree.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Copyright (c) [2023] SUSE LLC
# Copyright (c) [2023-2024] SUSE LLC
#
# All Rights Reserved.
#
Expand Down Expand Up @@ -47,11 +47,16 @@ def initialize(service, root_path, logger: nil)
#
# @param objects [Array]
def objects=(objects)
try_add_objects(objects)
try_update_objects(objects)
try_add_objects(objects)
try_delete_objects(objects)
end

# Unexports the current D-Bus objects of this tree.
def clean
dbus_objects.each { |o| service.unexport(o) }
end

private

# @return [::DBus::ObjectServer]
Expand Down
20 changes: 16 additions & 4 deletions service/lib/agama/dbus/storage/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,15 @@ module Storage
#
# The D-Bus object includes the required interfaces for the storage object that it represents.
class Device < BaseObject
# @return [Y2Storage::Device]
attr_reader :storage_device
# sid of the Y2Storage device.
#
# @note A Y2Storage device is a wrapper over a libstorage-ng object. If the source
Copy link
Contributor

@ancorgs ancorgs Mar 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this explanation a bit too technical and a bit too close to the Y2Storage world? In fact, I would say that using the sid simply makes sense here and does not need that explanation about memory.

Just something saying this is the internal storage identifier and clarifying it's unique per device should be enough.

Copy link
Contributor Author

@joseivanlopez joseivanlopez Mar 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is. But I would prefer to keep it as a warning. I see myself trying to use storage_device again for something, which is quite unsafe.

# devicegraph does not exist anymore (e.g., after reprobing), then the Y2Storage device
# object cannot be used (memory error). The device sid is stored to avoid accessing to
# the old Y2Storage device when updating the represented device, see {#storage_device=}.
#
# @return [Integer]
attr_reader :sid

# Constructor
#
Expand All @@ -43,6 +50,7 @@ def initialize(storage_device, path, tree, logger: nil)
super(path, logger: logger)

@storage_device = storage_device
@sid = storage_device.sid
@tree = tree
add_interfaces
end
Expand All @@ -54,12 +62,13 @@ def initialize(storage_device, path, tree, logger: nil)
#
# @param value [Y2Storage::Device]
def storage_device=(value)
if value.sid != storage_device.sid
if value.sid != sid
raise "Cannot update the D-Bus object because the given device has a different sid: " \
"#{value} instead of #{storage_device.sid}"
"#{value} instead of #{sid}"
end

@storage_device = value
@sid = value.sid

interfaces_and_properties.each do |interface, properties|
dbus_properties_changed(interface, properties, [])
Expand All @@ -71,6 +80,9 @@ def storage_device=(value)
# @return [DevicesTree]
attr_reader :tree

# @return [Y2Storage::Device]
attr_reader :storage_device

# Adds the required interfaces according to the storage object.
def add_interfaces
interfaces = Interfaces::Device.constants
Expand Down
30 changes: 21 additions & 9 deletions service/lib/agama/dbus/storage/devices_tree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,19 @@ def path_for(device)
::DBus::ObjectPath.new(File.join(root_path, device.sid.to_s))
end

# Updates the D-Bus tree according to the given devicegraph
# Updates the D-Bus tree according to the given devicegraph.
#
# @note In the devices tree it is important to avoid updating D-Bus nodes. Note that an
# already exported D-Bus object could require to add or remove interfaces (e.g., an
# existing partition needs to add the Filesystem interface after formatting the
# partition). Dynamically adding or removing interfaces is not possible with ruby-dbus
# once the object is exported on D-Bus.
#
# Updating the currently exported D-Bus objects is avoided by calling to {#clean} first.
#
# @param devicegraph [Y2Storage::Devicegraph]
def update(devicegraph)
clean
self.objects = devices(devicegraph)
end

Expand All @@ -52,31 +61,34 @@ def create_dbus_object(device)
end

# @see BaseTree
# @param dbus_object [Device]
# @param device [Y2Storage::Device]
def update_dbus_object(dbus_object, device)
dbus_object.storage_device = device
#
# @note D-Bus objects representing devices cannot be safely updated, see {#update}.
def update_dbus_object(_dbus_object, _device)
nil
end

# @see BaseTree
# @param dbus_object [Device]
# @param device [Y2Storage::Device]
def dbus_object?(dbus_object, device)
dbus_object.storage_device.sid == device.sid
dbus_object.sid == device.sid
end

# Devices to be exported.
#
# 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<Y2Storage::Device>]
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

Expand Down
1 change: 1 addition & 0 deletions service/lib/agama/dbus/storage/interfaces/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 16 additions & 0 deletions service/lib/agama/dbus/storage/interfaces/device/block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,27 @@ def block_name
storage_device.name
end

# Position of the first block of the region.
#
# @return [Integer]
def block_start
storage_device.start
end

# Whether the block device is currently active
#
# @return [Boolean]
def block_active
storage_device.active?
end

# Whether the block device is encrypted.
#
# @return [Boolean]
def block_encrypted
storage_device.encrypted?
end

# Name of the udev by-id links
#
# @return [Array<String>]
Expand Down Expand Up @@ -100,7 +114,9 @@ def self.included(base)
base.class_eval do
dbus_interface BLOCK_INTERFACE do
dbus_reader :block_name, "s", dbus_name: "Name"
dbus_reader :block_start, "t", dbus_name: "Start"
dbus_reader :block_active, "b", dbus_name: "Active"
dbus_reader :block_encrypted, "b", dbus_name: "Encrypted"
dbus_reader :block_udev_ids, "as", dbus_name: "UdevIds"
dbus_reader :block_udev_paths, "as", dbus_name: "UdevPaths"
dbus_reader :block_size, "t", dbus_name: "Size"
Expand Down
2 changes: 2 additions & 0 deletions service/lib/agama/dbus/storage/interfaces/device/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
90 changes: 90 additions & 0 deletions service/lib/agama/dbus/storage/interfaces/device/lvm_vg.rb
Original file line number Diff line number Diff line change
@@ -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<String>]
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<String>]
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,22 @@ def partition_table_partitions
storage_device.partition_table.partitions.map { |p| tree.path_for(p) }
end

# Available slots within a partition table, that is, the spaces that can be used to
# create a new partition.
#
# @return [Array<Array(Integer, Integer)>] The first block and the size of each slot.
def partition_table_unused_slots
storage_device.partition_table.unused_partition_slots.map do |slot|
[slot.region.start, slot.region.size.to_i]
end
end

def self.included(base)
base.class_eval do
dbus_interface PARTITION_TABLE_INTERFACE do
dbus_reader :partition_table_type, "s", dbus_name: "Type"
dbus_reader :partition_table_partitions, "ao", dbus_name: "Partitions"
dbus_reader :partition_table_unused_slots, "a(tt)", dbus_name: "UnusedSlots"
end
end
end
Expand Down
12 changes: 11 additions & 1 deletion service/lib/agama/dbus/storage/manager.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Copyright (c) [2022-2023] SUSE LLC
# Copyright (c) [2022-2024] SUSE LLC
#
# All Rights Reserved.
#
Expand Down Expand Up @@ -289,6 +289,7 @@ def register_proposal_callbacks
proposal.on_calculate do
export_proposal
proposal_properties_changed
refresh_staging_devices
end
end

Expand Down Expand Up @@ -332,6 +333,11 @@ def refresh_system_devices
system_devices_tree.update(devicegraph)
end

def refresh_staging_devices
devicegraph = Y2Storage::StorageManager.instance.staging
staging_devices_tree.update(devicegraph)
end

def refresh_iscsi_nodes
nodes = backend.iscsi.nodes
iscsi_nodes_tree.update(nodes)
Expand All @@ -348,6 +354,10 @@ def system_devices_tree
@system_devices_tree ||= DevicesTree.new(@service, tree_path("system"), logger: logger)
end

def staging_devices_tree
@staging_devices_tree ||= DevicesTree.new(@service, tree_path("staging"), logger: logger)
end

def tree_path(tree_root)
File.join(PATH, tree_root)
end
Expand Down
1 change: 1 addition & 0 deletions service/lib/agama/dbus/storage/proposal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ def dbus_settings
# @return [Hash]
def to_dbus_action(action)
{
"Device" => action.target_device.sid,
"Text" => action.sentence,
"Subvol" => action.device_is?(:btrfs_subvolume),
"Delete" => action.delete?
Expand Down
Loading
Loading