Skip to content

Commit

Permalink
Storage AutoYaST backward compatibility (#1284)
Browse files Browse the repository at this point in the history
Created to replace #1266 (to sanitize history).

Agama is going to offer new settings for configuring the storage
devices, see #1256. That new
settings will be applied by loading a config file (e.g., `$ agama config
load myconfig.json`). Nevertheless, Agama is also going to temporary
support the SLE15 AutoYaST schema to some extent, in order to make the
transition smoother. For that, the json config file can indicate a
"legacyAutoyastStorage" key equivalent to the partitioning section of
the AutoYaST profile:

~~~
"legacyAutoyastStorage": [
  {
    "device": "/dev/vda",
    "use": "all",
    "partitions": [...]
  }
]
~~~ 

This PR implements the support for the "legacyAutoyastStorage" settings,
making possible to calculate an AutoYaST proposal.

Converting the "partitioning" section from a XML AutoYaST profile to a
"legacyAutoyastStorage" section is implemented at
#1285

Note: The AutoYaST guided partitioning will not be supported, see
https://doc.opensuse.org/projects/autoyast/#CreateProfile-Guided-Partitioning.
  • Loading branch information
ancorgs authored Jun 6, 2024
2 parents 078be31 + 5e9794d commit df46717
Show file tree
Hide file tree
Showing 29 changed files with 1,545 additions and 388 deletions.
7 changes: 6 additions & 1 deletion rust/agama-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ enum SettingKind {
Collection,
/// The value is another FooSettings, use `#[settings(nested)]`.
Nested,
Ignored,
}

/// Represents a setting and its configuration
Expand Down Expand Up @@ -176,7 +177,7 @@ fn expand_merge_fn(settings: &SettingFieldsList) -> TokenStream2 {
let arms = settings.all().iter().map(|s| {
let field_name = &s.ident;
match s.kind {
SettingKind::Scalar => quote! {
SettingKind::Scalar | SettingKind::Ignored => quote! {
if let Some(value) = &other.#field_name {
self.#field_name = Some(value.clone())
}
Expand Down Expand Up @@ -274,6 +275,10 @@ fn parse_setting_fields(fields: Vec<&syn::Field>) -> SettingFieldsList {
setting.kind = SettingKind::Nested;
}

if meta.path.is_ident("ignored") {
setting.kind = SettingKind::Ignored;
}

if meta.path.is_ident("flatten") {
setting.flatten = true;
}
Expand Down
2 changes: 1 addition & 1 deletion rust/agama-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jsonschema = { version = "0.16.1", default-features = false }
log = "0.4"
reqwest = { version = "0.12.4", features = ["json", "cookies"] }
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.94"
serde_json = { version = "1.0.94", features = ["raw_value"] }
serde_repr = "0.1.18"
tempfile = "3.4.0"
thiserror = "1.0.39"
Expand Down
17 changes: 14 additions & 3 deletions rust/agama-lib/src/install_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
};
use agama_settings::Settings;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use std::default::Default;
use std::str::FromStr;

Expand All @@ -22,6 +23,8 @@ pub enum Scope {
Software,
/// Storage settings
Storage,
/// Storage AutoYaST settings (for backward compatibility with AutoYaST profiles)
StorageAutoyast,
/// Network settings
Network,
/// Product settings
Expand All @@ -34,13 +37,14 @@ impl Scope {
/// Returns known scopes
///
// TODO: we can rely on strum so we do not forget to add them
pub fn all() -> [Scope; 6] {
pub fn all() -> [Scope; 7] {
[
Scope::Localization,
Scope::Network,
Scope::Product,
Scope::Software,
Scope::Storage,
Scope::StorageAutoyast,
Scope::Users,
]
}
Expand All @@ -49,6 +53,9 @@ impl Scope {
impl FromStr for Scope {
type Err = &'static str;

// Do not generate the StorageAutoyast scope. Note that storage AutoYaST settings will only be
// temporary available for importing an AutoYaST profile. But CLI should not allow modifying the
// storate AutoYaST settings.
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"users" => Ok(Self::Users),
Expand Down Expand Up @@ -81,6 +88,9 @@ pub struct InstallSettings {
#[serde(default)]
#[settings(nested)]
pub storage: Option<StorageSettings>,
#[serde(default, rename = "legacyAutoyastStorage")]
#[settings(ignored)]
pub storage_autoyast: Option<Box<RawValue>>,
#[serde(default)]
#[settings(nested)]
pub network: Option<NetworkSettings>,
Expand All @@ -95,11 +105,12 @@ impl InstallSettings {
if self.user.is_some() {
scopes.push(Scope::Users);
}

if self.storage.is_some() {
scopes.push(Scope::Storage);
}

if self.storage_autoyast.is_some() {
scopes.push(Scope::StorageAutoyast);
}
if self.software.is_some() {
scopes.push(Scope::Software);
}
Expand Down
2 changes: 1 addition & 1 deletion rust/agama-lib/src/software/store.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Implements the store for the storage settings.
//! Implements the store for the software settings.

use std::collections::HashMap;

Expand Down
2 changes: 2 additions & 0 deletions rust/agama-lib/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//! Implements support for handling the storage settings

mod autoyast;
pub mod client;
pub mod model;
pub mod proxies;
mod settings;
mod store;

pub use autoyast::store::StorageAutoyastStore;
pub use client::{
iscsi::{ISCSIAuth, ISCSIClient, ISCSIInitiator, ISCSINode},
StorageClient,
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-lib/src/storage/autoyast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//! Implements support for handling the storage AutoYaST settings
pub mod store;
26 changes: 26 additions & 0 deletions rust/agama-lib/src/storage/autoyast/store.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! Implements the store for the storage AutoYaST settings.

use crate::error::ServiceError;
use crate::storage::StorageClient;
use zbus::Connection;

/// Stores the storage AutoYaST settings to the D-Bus service.
///
/// NOTE: The AutoYaST settings are not loaded from D-Bus because they cannot be modified. The only
/// way of using the storage AutoYaST settings is by loading a JSON config file.
pub struct StorageAutoyastStore<'a> {
storage_client: StorageClient<'a>,
}

impl<'a> StorageAutoyastStore<'a> {
pub async fn new(connection: Connection) -> Result<StorageAutoyastStore<'a>, ServiceError> {
Ok(Self {
storage_client: StorageClient::new(connection).await?,
})
}

pub async fn store(&self, settings: &str) -> Result<(), ServiceError> {
self.storage_client.calculate_autoyast(settings).await?;
Ok(())
}
}
4 changes: 4 additions & 0 deletions rust/agama-lib/src/storage/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ impl<'a> StorageClient<'a> {
Ok(self.calculator_proxy.calculate(settings.into()).await?)
}

pub async fn calculate_autoyast(&self, settings: &str) -> Result<u32, ServiceError> {
Ok(self.calculator_proxy.calculate_autoyast(settings).await?)
}

/// Calculates a new proposal with the given settings.
pub async fn calculate(&self, settings: &StorageSettings) -> Result<u32, ServiceError> {
let mut dbus_settings: HashMap<&str, zbus::zvariant::Value<'_>> = HashMap::new();
Expand Down
11 changes: 7 additions & 4 deletions rust/agama-lib/src/storage/proxies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ trait Storage1 {
default_path = "/org/opensuse/Agama/Storage1"
)]
trait ProposalCalculator {
/// Calculate method
/// Calculate guided proposal
fn calculate(
&self,
settings: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
) -> zbus::Result<u32>;

/// Calculate AutoYaST proposal
fn calculate_autoyast(&self, settings: &str) -> zbus::Result<u32>;

/// DefaultVolume method
fn default_volume(
&self,
Expand All @@ -53,9 +56,9 @@ trait ProposalCalculator {
#[dbus_proxy(property)]
fn product_mount_points(&self) -> zbus::Result<Vec<String>>;

/// Result property
#[dbus_proxy(property)]
fn result(&self) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
/// Proposal result
fn result(&self)
-> zbus::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>;
}

#[dbus_proxy(
Expand Down
20 changes: 17 additions & 3 deletions rust/agama-lib/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::error::ServiceError;
use crate::install_settings::{InstallSettings, Scope};
use crate::{
localization::LocalizationStore, network::NetworkStore, product::ProductStore,
software::SoftwareStore, storage::StorageStore, users::UsersStore,
software::SoftwareStore, storage::StorageAutoyastStore, storage::StorageStore,
users::UsersStore,
};
use zbus::Connection;

Expand All @@ -21,6 +22,7 @@ pub struct Store<'a> {
product: ProductStore<'a>,
software: SoftwareStore<'a>,
storage: StorageStore<'a>,
storage_autoyast: StorageAutoyastStore<'a>,
localization: LocalizationStore<'a>,
}

Expand All @@ -35,11 +37,16 @@ impl<'a> Store<'a> {
network: NetworkStore::new(http_client).await?,
product: ProductStore::new(connection.clone()).await?,
software: SoftwareStore::new(connection.clone()).await?,
storage: StorageStore::new(connection).await?,
storage: StorageStore::new(connection.clone()).await?,
storage_autoyast: StorageAutoyastStore::new(connection).await?,
})
}

/// Loads the installation settings from the D-Bus service
/// Loads the installation settings from the D-Bus service.
///
/// NOTE: The storage AutoYaST settings cannot be loaded because they cannot be modified. The
/// ability of using the storage AutoYaST settings from a JSON config file is temporary and it
/// will be removed in the future.
pub async fn load(&self, only: Option<Vec<Scope>>) -> Result<InstallSettings, ServiceError> {
let scopes = match only {
Some(scopes) => scopes,
Expand All @@ -50,6 +57,7 @@ impl<'a> Store<'a> {
if scopes.contains(&Scope::Network) {
settings.network = Some(self.network.load().await?);
}

if scopes.contains(&Scope::Storage) {
settings.storage = Some(self.storage.load().await?);
}
Expand Down Expand Up @@ -97,6 +105,12 @@ impl<'a> Store<'a> {
if let Some(storage) = &settings.storage {
self.storage.store(storage).await?;
}
if let Some(storage_autoyast) = &settings.storage_autoyast {
// Storage scope has precedence.
if settings.storage.is_none() {
self.storage_autoyast.store(storage_autoyast.get()).await?;
}
}
Ok(())
}
}
6 changes: 6 additions & 0 deletions rust/package/agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Wed Jun 5 13:53:59 UTC 2024 - José Iván López González <[email protected]>

- Process the legacyAutoyastStorage section of the profile
(gh#openSUSE/agama#1284).

-------------------------------------------------------------------
Mon Jun 3 07:49:16 UTC 2024 - Josef Reidinger <[email protected]>

Expand Down
52 changes: 1 addition & 51 deletions service/lib/agama/dbus/clients/storage.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 @@ -36,12 +36,6 @@ class Storage < Base
STORAGE_IFACE = "org.opensuse.Agama.Storage1"
private_constant :STORAGE_IFACE

PROPOSAL_CALCULATOR_IFACE = "org.opensuse.Agama.Storage1.Proposal.Calculator"
private_constant :PROPOSAL_CALCULATOR_IFACE

PROPOSAL_IFACE = "org.opensuse.Agama.Storage1.Proposal"
private_constant :PROPOSAL_IFACE

def service_name
@service_name ||= "org.opensuse.Agama.Storage1"
end
Expand All @@ -66,56 +60,12 @@ def finish
dbus_object.Finish
end

# Devices available for the installation
#
# @return [Array<String>] name of the devices
def available_devices
dbus_object[PROPOSAL_CALCULATOR_IFACE]["AvailableDevices"]
.map(&:first)
end

# Devices selected for the installation
#
# @return [Array<String>] name of the devices
def candidate_devices
return [] unless dbus_proposal

dbus_proposal[PROPOSAL_IFACE]["CandidateDevices"]
end

# Actions to perform in the storage devices
#
# @return [Array<String>]
def actions
return [] unless dbus_proposal

dbus_proposal[PROPOSAL_IFACE]["Actions"].map do |a|
a["Text"]
end
end

# Calculates the storage proposal with the given devices
#
# @param candidate_devices [Array<String>] name of the new candidate devices
def calculate(candidate_devices)
calculator_iface = dbus_object[PROPOSAL_CALCULATOR_IFACE]
calculator_iface.Calculate({ "CandidateDevices" => candidate_devices })
end

private

# @return [::DBus::Object]
def dbus_object
@dbus_object ||= service["/org/opensuse/Agama/Storage1"].tap(&:introspect)
end

# @return [::DBus::Object, nil]
def dbus_proposal
path = dbus_object["org.opensuse.Agama.Storage1.Proposal.Calculator"]["Result"]
return nil if path == "/"

service.object(path).tap(&:introspect)
end
end
end
end
Expand Down
Loading

0 comments on commit df46717

Please sign in to comment.