Skip to content
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
17 changes: 5 additions & 12 deletions autoinstallation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ differences:
"product": "ALP-Bedrock"
},
"storage": {
"devices": [
{
"name": "/dev/sda"
}
]
"bootDevice": "/dev/sda"
},
"user": {
"fullName": "Jane Doe",
Expand All @@ -62,8 +58,9 @@ and `root`.
- **`localization`** *(object)*: Localization settings.
- **`language`** *(string)*: System language ID (e.g., "en_US").
- **`storage`** *(object)*: Storage settings.
- **`devices`** *(array of strings)*: Storage devices to install the system to (e.g,
["/dev/sda"]).
- **`bootDevice`** *(string)*: Storage device used for booting (e.g., "/dev/sda"). By default, all file system are created in the boot device.
- **`lvm`** *(boolean)*: Whether LVM is used.
- **`encryptionPassword`** *(string)*: If set, the devices are encrypted using the given password.
- **`user`** *(object)*: First user settings.
- **`fullName`** *(string)*: Full name (e.g., "Jane Doe").
- **`userName`** *(string)*: User login name (e.g., "jane.doe").
Expand Down Expand Up @@ -100,11 +97,7 @@ local findBiggestDisk(disks) =
language: 'en_US',
},
storage: {
devices: [
{
name: findBiggestDisk(agama.disks),
},
],
bootDevice: findBiggestDisk(agama.disks),
},
}
```
Expand Down
6 changes: 1 addition & 5 deletions rust/agama-lib/share/examples/profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
"product": "ALP"
},
"storage": {
"devices": [
{
"name": "/dev/dm-1"
}
]
"bootDevice": "/dev/dm-1"
},
"user": {
"fullName": "Jane Doe",
Expand Down
6 changes: 1 addition & 5 deletions rust/agama-lib/share/examples/profile.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,7 @@ local findBiggestDisk(disks) =
keyboard: 'en_US',
},
storage: {
devices: [
{
name: findBiggestDisk(agama.disks),
},
],
bootDevice: findBiggestDisk(agama.disks),
},
network: {
connections: [
Expand Down
24 changes: 11 additions & 13 deletions rust/agama-lib/share/profile.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,19 +193,17 @@
"description": "Storage settings",
"type": "object",
"properties": {
"devices": {
"description": "Storage devices to install the system to",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"description": "Storage device name (e.g., '/dev/sda')",
"type": "string"
}
}
}
"bootDevice": {
"description": "Device used for booting (e.g., '/dev/sda'). By default, all file systems are created in the boot device.",
"type": "string"
},
"lvm": {
"description": "Whether LVM is used.",
"type": "boolean"
},
"encryptionPassword": {
"description": "If set, the devices are encrypted using the given password.",
"type": "string"
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion rust/agama-lib/src/software/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

use super::{SoftwareClient, SoftwareSettings};
use crate::error::ServiceError;
use crate::manager::ManagerClient;
use zbus::Connection;

/// Loads and stores the software settings from/to the D-Bus service.
pub struct SoftwareStore<'a> {
software_client: SoftwareClient<'a>,
manager_client: ManagerClient<'a>,
}

impl<'a> SoftwareStore<'a> {
pub async fn new(connection: Connection) -> Result<SoftwareStore<'a>, ServiceError> {
Ok(Self {
software_client: SoftwareClient::new(connection).await?,
software_client: SoftwareClient::new(connection.clone()).await?,
manager_client: ManagerClient::new(connection).await?,
})
}

Expand All @@ -30,6 +33,7 @@ impl<'a> SoftwareStore<'a> {
let ids: Vec<String> = products.into_iter().map(|p| p.id).collect();
if ids.contains(product) {
self.software_client.select_product(product).await?;
self.manager_client.probe().await?;
} else {
return Err(ServiceError::UnknownProduct(product.clone(), ids));
}
Expand Down
110 changes: 81 additions & 29 deletions rust/agama-lib/src/storage/client.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//! Implements a client to access Agama's storage service.

use super::proxies::{CalculatorProxy, Storage1Proxy, StorageProposalProxy};
use super::proxies::{BlockDeviceProxy, ProposalCalculatorProxy, ProposalProxy, Storage1Proxy};
use super::StorageSettings;
use crate::error::ServiceError;
use futures::future::join_all;
use serde::Serialize;
use std::collections::HashMap;
use zbus::zvariant::OwnedObjectPath;
use zbus::Connection;

/// Represents a storage device
Expand All @@ -16,14 +19,14 @@ pub struct StorageDevice {
/// D-Bus client for the storage service
pub struct StorageClient<'a> {
pub connection: Connection,
calculator_proxy: CalculatorProxy<'a>,
calculator_proxy: ProposalCalculatorProxy<'a>,
storage_proxy: Storage1Proxy<'a>,
}

impl<'a> StorageClient<'a> {
pub async fn new(connection: Connection) -> Result<StorageClient<'a>, ServiceError> {
Ok(Self {
calculator_proxy: CalculatorProxy::new(&connection).await?,
calculator_proxy: ProposalCalculatorProxy::new(&connection).await?,
storage_proxy: Storage1Proxy::new(&connection).await?,
connection,
})
Expand All @@ -33,8 +36,8 @@ impl<'a> StorageClient<'a> {
///
/// The proposal might not exist.
// NOTE: should we implement some kind of memoization?
async fn proposal_proxy(&self) -> Result<StorageProposalProxy<'a>, ServiceError> {
Ok(StorageProposalProxy::new(&self.connection).await?)
async fn proposal_proxy(&self) -> Result<ProposalProxy<'a>, ServiceError> {
Ok(ProposalProxy::new(&self.connection).await?)
}

/// Returns the available devices
Expand All @@ -46,20 +49,67 @@ impl<'a> StorageClient<'a> {
.available_devices()
.await?
.into_iter()
.map(|(name, description, _)| StorageDevice { name, description })
.map(|path| self.storage_device(path))
.collect();
Ok(devices)

return join_all(devices).await.into_iter().collect();
}

/// Returns the storage device for the given D-Bus path
async fn storage_device(
&self,
dbus_path: OwnedObjectPath,
) -> Result<StorageDevice, ServiceError> {
let proxy = BlockDeviceProxy::builder(&self.connection)
.path(dbus_path)?
.build()
.await?;

let name = proxy.name().await?;
// TODO: The description is not used yet. Decide what info to show, for example the device
// size, see https://crates.io/crates/size.
let description = name.clone();

Ok(StorageDevice { name, description })
}

/// Returns the boot device proposal setting
pub async fn boot_device(&self) -> Result<Option<String>, ServiceError> {
let proxy = self.proposal_proxy().await?;
let value = self.proposal_value(proxy.boot_device().await)?;

match value {
Some(v) if v.is_empty() => Ok(None),
Some(v) => Ok(Some(v)),
None => Ok(None),
}
}

/// Returns the lvm proposal setting
pub async fn lvm(&self) -> Result<Option<bool>, ServiceError> {
let proxy = self.proposal_proxy().await?;
self.proposal_value(proxy.lvm().await)
}

/// Returns the candidate devices for the proposal
pub async fn candidate_devices(&self) -> Result<Vec<String>, ServiceError> {
/// Returns the encryption password proposal setting
pub async fn encryption_password(&self) -> Result<Option<String>, ServiceError> {
let proxy = self.proposal_proxy().await?;
match proxy.candidate_devices().await {
Ok(devices) => Ok(devices),
let value = self.proposal_value(proxy.encryption_password().await)?;

match value {
Some(v) if v.is_empty() => Ok(None),
Some(v) => Ok(Some(v)),
None => Ok(None),
}
}

fn proposal_value<T>(&self, value: Result<T, zbus::Error>) -> Result<Option<T>, ServiceError> {
match value {
Ok(v) => Ok(Some(v)),
Err(zbus::Error::MethodError(name, _, _))
if name.as_str() == "org.freedesktop.DBus.Error.UnknownObject" =>
{
Ok(vec![])
Ok(None)
}
Err(e) => Err(e.into()),
}
Expand All @@ -70,22 +120,24 @@ impl<'a> StorageClient<'a> {
Ok(self.storage_proxy.probe().await?)
}

pub async fn calculate(
&self,
candidate_devices: Vec<String>,
encryption_password: String,
lvm: bool,
) -> Result<u32, ServiceError> {
let mut settings: HashMap<&str, zbus::zvariant::Value<'_>> = HashMap::new();
settings.insert(
"CandidateDevices",
zbus::zvariant::Value::new(candidate_devices),
);
settings.insert(
"EncryptionPassword",
zbus::zvariant::Value::new(encryption_password),
);
settings.insert("LVM", zbus::zvariant::Value::new(lvm));
Ok(self.calculator_proxy.calculate(settings).await?)
pub async fn calculate(&self, settings: &StorageSettings) -> Result<u32, ServiceError> {
let mut dbus_settings: HashMap<&str, zbus::zvariant::Value<'_>> = HashMap::new();

if let Some(boot_device) = settings.boot_device.clone() {
dbus_settings.insert("BootDevice", zbus::zvariant::Value::new(boot_device));
}

if let Some(encryption_password) = settings.encryption_password.clone() {
dbus_settings.insert(
"EncryptionPassword",
zbus::zvariant::Value::new(encryption_password),
);
}

if let Some(lvm) = settings.lvm {
dbus_settings.insert("LVM", zbus::zvariant::Value::new(lvm));
}

Ok(self.calculator_proxy.calculate(dbus_settings).await?)
}
}
Loading