Skip to content

Commit

Permalink
Merge pull request #748 from joseivanlopez/adapt-rust-storage
Browse files Browse the repository at this point in the history
Adapt rust storage client
  • Loading branch information
joseivanlopez authored Sep 19, 2023
2 parents 7d0b821 + fa38c51 commit 2d42842
Show file tree
Hide file tree
Showing 16 changed files with 210 additions and 160 deletions.
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

0 comments on commit 2d42842

Please sign in to comment.