Skip to content

Commit

Permalink
web: Revamp the Storage page UI (#1104)
Browse files Browse the repository at this point in the history
As part of the Storage UI changes described at
https://github.com/openSUSE/agama/blob/master/doc/storage_ui.md
document, this PR aims to merge into master the changes already
available in the `storage_ui` feature branch, which has been already
tested and validated. Namely,

* #1071

   Added the following information to D-Bus:

    * The list of actions includes the SID of the affected device.
    * The partition table exports the unused slots.
* The LVM devices (volume groups, physical volumes and logical volumes)
are exported.
* The block devices includes their start block and also indicates
whether the device is encrypted.
    * The staging devices are exported.
    
* #1079 

Adapt the storage client to the changes in the D-Bus API and adapt
`ProposalPage` component to read the information about the devices if
needed.


* #1082

Added new D-Bus interfaces `Device` , `Partition`, `LVM.LogicalVolume`
and adapt `Filesystem` interface.

  It requires yast/yast-storage-ng#1373.

* #1088

Replaced the `Planned Actions` section in the storage proposal for a
`Result` one which presents how the storage would look after
installation instead of just the list of actions.

* #1090

  Moved the space policy configuration to a popup.


* #1098

Replace a `Resize` by `Before` label as it was suggested during the
presentation of the UI in a review meeting.
  • Loading branch information
dgdavid authored Mar 19, 2024
2 parents d6c3fc0 + d1b2c94 commit 2c81aca
Show file tree
Hide file tree
Showing 65 changed files with 5,380 additions and 1,003 deletions.
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
# 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
4 changes: 4 additions & 0 deletions service/lib/agama/dbus/storage/interfaces/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ module Device

require "agama/dbus/storage/interfaces/device/block"
require "agama/dbus/storage/interfaces/device/component"
require "agama/dbus/storage/interfaces/device/device"
require "agama/dbus/storage/interfaces/device/drive"
require "agama/dbus/storage/interfaces/device/filesystem"
require "agama/dbus/storage/interfaces/device/lvm_lv"
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"
require "agama/dbus/storage/interfaces/device/partition_table"
require "agama/dbus/storage/interfaces/device/raid"
20 changes: 14 additions & 6 deletions service/lib/agama/dbus/storage/interfaces/device/block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ def self.apply?(storage_device)
BLOCK_INTERFACE = "org.opensuse.Agama.Storage1.Block"
private_constant :BLOCK_INTERFACE

# Name of the block device
# Position of the first block of the region.
#
# @return [String] e.g., "/dev/sda"
def block_name
storage_device.name
# @return [Integer]
def block_start
storage_device.start
end

# Whether the block device is currently active
Expand All @@ -58,6 +58,13 @@ 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 @@ -98,9 +105,10 @@ def block_systems

def self.included(base)
base.class_eval do
dbus_interface BLOCK_INTERFACE do
dbus_reader :block_name, "s", dbus_name: "Name"
dbus_interface BLOCK_INTERFACE do
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
82 changes: 82 additions & 0 deletions service/lib/agama/dbus/storage/interfaces/device/device.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# 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"
require "y2storage/device_description"

module Agama
module DBus
module Storage
module Interfaces
module Device
# Interface for a device.
#
# @note This interface is intended to be included by {Agama::DBus::Storage::Device}.
module Device
# Whether this interface should be implemented for the given device.
#
# @note All devices implement this interface.
#
# @param _storage_device [Y2Storage::Device]
# @return [Boolean]
def self.apply?(_storage_device)
true
end

DEVICE_INTERFACE = "org.opensuse.Agama.Storage1.Device"
private_constant :DEVICE_INTERFACE

# sid of the device.
#
# @return [Integer]
def device_sid
storage_device.sid
end

# Name to represent the device.
#
# @return [String] e.g., "/dev/sda".
def device_name
storage_device.display_name || ""
end

# Description of the device.
#
# @return [String] e.g., "EXT4 Partition".
def device_description
Y2Storage::DeviceDescription.new(storage_device).to_s
end

def self.included(base)
base.class_eval do
dbus_interface DEVICE_INTERFACE do
dbus_reader :device_sid, "u", dbus_name: "SID"
dbus_reader :device_name, "s", dbus_name: "Name"
dbus_reader :device_description, "s", dbus_name: "Description"
end
end
end
end
end
end
end
end
end
31 changes: 25 additions & 6 deletions service/lib/agama/dbus/storage/interfaces/device/filesystem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# find current contact information at www.suse.com.

require "dbus"
require "y2storage/filesystem_label"

module Agama
module DBus
Expand All @@ -44,25 +45,43 @@ def self.apply?(storage_device)
FILESYSTEM_INTERFACE = "org.opensuse.Agama.Storage1.Filesystem"
private_constant :FILESYSTEM_INTERFACE

# SID of the file system.
#
# It is useful to detect whether a file system is new.
#
# @return [Integer]
def filesystem_sid
storage_device.filesystem.sid
end

# File system type.
#
# @return [String] e.g., "ext4"
def filesystem_type
storage_device.filesystem.type.to_s
end

# Whether the filesystem contains the directory layout of an ESP partition.
# Mount path of the file system.
#
# @return [Boolean]
def filesystem_efi?
storage_device.filesystem.efi?
# @return [String] Empty if not mounted.
def filesystem_mount_path
storage_device.filesystem.mount_path || ""
end

# Label of the file system.
#
# @return [String] Empty if it has no label.
def filesystem_label
Y2Storage::FilesystemLabel.new(storage_device).to_s
end

def self.included(base)
base.class_eval do
dbus_interface FILESYSTEM_INTERFACE do
dbus_interface FILESYSTEM_INTERFACE do
dbus_reader :filesystem_sid, "u", dbus_name: "SID"
dbus_reader :filesystem_type, "s", dbus_name: "Type"
dbus_reader :filesystem_efi?, "b", dbus_name: "EFI"
dbus_reader :filesystem_mount_path, "s", dbus_name: "MountPath"
dbus_reader :filesystem_label, "s", dbus_name: "Label"
end
end
end
Expand Down
Loading

0 comments on commit 2c81aca

Please sign in to comment.