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

feat(storage): initial support for a config model #1763

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
4 changes: 2 additions & 2 deletions doc/dbus/bus/org.opensuse.Agama.Storage1.bus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
<method name="GetConfig">
<arg name="serialized_config" direction="out" type="s"/>
</method>
<method name="GetSolvedConfig">
<arg name="serialized_config" direction="out" type="s"/>
<method name="GetConfigModel">
<arg name="serialized_model" direction="out" type="s"/>
</method>
<method name="Install">
</method>
Expand Down
43 changes: 22 additions & 21 deletions doc/dbus/org.opensuse.Agama.Storage1.doc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,34 +56,35 @@
<arg name="serialized_config" direction="out" type="s"/>
</method>
<!--
Gets the solved storage config.
Gets the storage config model.
-->
<method name="GetSolvedConfig">
<method name="GetConfigModel">
<!--
E.g.,
{
"storage": {
"drives": [
{
"search": "/dev/vda",
"partitions": [
{
"filesystem": {
"path": "/home",
"type": "xfs"
},
"size": {
"min": "10 GiB",
"max": "10 GiB"
}
"drives": [
{
"name": "/dev/vda",
"spacePolicy": "keep",
"partitions": [
{
"mountPath": "/",
"filesystem": {
"default": true,
"type": "xfs"
},
"size": {
"default": true,
"min": "10 GiB",
"max": "10 GiB"
}
]
}
]
}
}
]
}
]
}
-->
<arg name="serialized_config" direction="out" type="s"/>
<arg name="serialized_model" direction="out" type="s"/>
</method>
<method name="Install">
</method>
Expand Down
69 changes: 69 additions & 0 deletions rust/agama-lib/share/examples/storage/model.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"drives": [
{
"name": "/dev/vda",
"alias": "root",
"mountPath": "/",
"filesystem": {
"default": true,
"type": "btrfs",
"snapshots": true
},
"spacePolicy": "delete"
},
{
"name": "/dev/vdb",
"spacePolicy": "custom",
"ptableType": "gpt",
"partitions": [
{
"name": "/dev/vda1",
"size": {
"default": false,
"min": 0,
"max": 1234567
},
"deleteIfNeeded": true,
"resizeIfNeeded": true
},
{
"name": "/dev/vda2",
"size": {
"default": false,
"min": 1234567,
"max": 1234567
},
"resize": true
},
{
"name": "/dev/vdb3",
"delete": true
},
{
"size": {
"default": true,
"min": 4444,
"max": 8888
},
"mountPath": "swap",
"filesystem": {
"default": true,
"type": "swap"
}
},
{
"size": {
"default": false,
"min": 100000,
"max": 100000
},
"mountPath": "/home",
"filesystem": {
"default": false,
"type": "xfs"
}
}
]
}
]
}
96 changes: 96 additions & 0 deletions rust/agama-lib/share/storage.model.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"title": "Config",
"description": "Config model",
"type": "object",
"additionalProperties": false,
"properties": {
"drives": {
"type": "array",
"items": { "$ref": "#/$defs/drive" }
}
},
"$defs": {
"drive": {
"type": "object",
"additionalProperties": false,
"required": ["name"],
"properties": {
"name": { "type": "string" },
"alias": { "type": "string" },
"mountPath": { "type": "string" },
"filesystem": { "$ref": "#/$defs/filesystem" },
"spacePolicy": { "$ref": "#/$defs/spacePolicy" },
"ptableType": { "$ref": "#/$defs/ptableType" },
"partitions": {
"type": "array",
"items": { "$ref": "#/$defs/partition" }
}
}
},
"partition": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": { "type": "string" },
"alias": { "type": "string" },
"id": { "$ref": "#/$defs/partitionId" },
"mountPath": { "type": "string" },
"filesystem": { "$ref": "#/$defs/filesystem" },
"size": { "$ref": "#/$defs/size" },
"delete": { "type": "boolean" },
"deleteIfNeeded": { "type": "boolean" },
"resize": { "type": "boolean" },
"resizeIfNeeded": { "type": "boolean" }
}
},
"spacePolicy": {
"enum": ["delete", "resize", "keep", "custom"]
},
"ptableType": {
"enum": ["gpt", "msdos", "dasd"]
},
"partitionId": {
"enum": ["linux", "swap", "lvm", "raid", "esp", "prep", "bios_boot"]
},
"filesystem": {
"type": "object",
"additionalProperties": false,
"required": ["default"],
"properties": {
"default": { "type": "boolean" },
"type": { "$ref": "#/$defs/filesystemType" },
"snapshots": { "type": "boolean" }
}
},
"filesystemType": {
"enum": [
"bcachefs",
"btrfs",
"exfat",
"ext2",
"ext3",
"ext4",
"f2fs",
"jfs",
"nfs",
"nilfs2",
"ntfs",
"reiserfs",
"swap",
"tmpfs",
"vfat",
"xfs"
]
},
"size": {
"type": "object",
"additionalProperties": false,
"required": ["default", "min"],
"properties": {
"default": { "type": "boolean" },
"min": { "type": "integer", "minimum": 0 },
"max": { "type": "integer", "minimum": 0 }
}
}
}
}
11 changes: 6 additions & 5 deletions rust/agama-lib/src/storage/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use super::proxies::{DevicesProxy, ProposalCalculatorProxy, ProposalProxy, Stora
use super::StorageSettings;
use crate::dbus::get_property;
use crate::error::ServiceError;
use serde_json::value::RawValue;
use std::collections::HashMap;
use zbus::fdo::ObjectManagerProxy;
use zbus::names::{InterfaceName, OwnedInterfaceName};
Expand Down Expand Up @@ -153,11 +154,11 @@ impl<'a> StorageClient<'a> {
Ok(settings)
}

/// Get the storage solved config according to the JSON schema
pub async fn get_solved_config(&self) -> Result<StorageSettings, ServiceError> {
let serialized_settings = self.storage_proxy.get_solved_config().await?;
let settings = serde_json::from_str(serialized_settings.as_str()).unwrap();
Ok(settings)
/// Get the storage config model according to the JSON schema
pub async fn get_config_model(&self) -> Result<Box<RawValue>, ServiceError> {
let serialized_config_model = self.storage_proxy.get_config_model().await?;
let config_model = serde_json::from_str(serialized_config_model.as_str()).unwrap();
Ok(config_model)
}

pub async fn calculate(&self, settings: ProposalSettingsPatch) -> Result<u32, ServiceError> {
Expand Down
4 changes: 2 additions & 2 deletions rust/agama-lib/src/storage/proxies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ trait Storage1 {
/// Get the current storage config according to the JSON schema
fn get_config(&self) -> zbus::Result<String>;

/// Get the current storage solved config according to the JSON schema
fn get_solved_config(&self) -> zbus::Result<String>;
/// Get the storage config model according to the JSON schema
fn get_config_model(&self) -> zbus::Result<String>;

/// DeprecatedSystem property
#[dbus_proxy(property)]
Expand Down
22 changes: 11 additions & 11 deletions rust/agama-server/src/storage/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use axum::{
Json, Router,
};
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use tokio_stream::{Stream, StreamExt};
use zfcp::{zfcp_service, zfcp_stream};

Expand Down Expand Up @@ -113,7 +114,7 @@ pub async fn storage_service(dbus: zbus::Connection) -> Result<Router, ServiceEr
let state = StorageState { client };
let router = Router::new()
.route("/config", put(set_config).get(get_config))
.route("/solved_config", get(get_solved_config))
.route("/config_model", get(get_config_model))
.route("/probe", post(probe))
.route("/devices/dirty", get(devices_dirty))
.route("/devices/system", get(system_devices))
Expand Down Expand Up @@ -156,29 +157,28 @@ async fn get_config(State(state): State<StorageState<'_>>) -> Result<Json<Storag
Ok(Json(settings))
}

/// Returns the solved storage configuration.
/// Returns the storage config model.
///
/// * `state` : service state.
#[utoipa::path(
get,
path = "/solved_config",
path = "/config_model",
context_path = "/api/storage",
operation_id = "get_storage_solved_config",
operation_id = "get_storage_config_model",
responses(
(status = 200, description = "storage solved configuration", body = StorageSettings),
(status = 200, description = "storage config model", body = Box<RawValue>),
(status = 400, description = "The D-Bus service could not perform the action")
)
)]
async fn get_solved_config(
async fn get_config_model(
State(state): State<StorageState<'_>>,
) -> Result<Json<StorageSettings>, Error> {
// StorageSettings is just a wrapper over serde_json::value::RawValue
let settings = state
) -> Result<Json<Box<RawValue>>, Error> {
let config_model = state
.client
.get_solved_config()
.get_config_model()
.await
.map_err(Error::Service)?;
Ok(Json(settings))
Ok(Json(config_model))
}

/// Sets the storage configuration.
Expand Down
14 changes: 14 additions & 0 deletions service/lib/agama/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,20 @@ def mandatory_paths
default_paths.select { |p| mandatory_path?(p) }
end

# Default policy to make space.
#
# @return [String]
def space_policy
data.dig("storage", "space_policy") || "keep"
end

# Whether LVM must be used by default.
#
# @return [Boolean]
def lvm?
data.dig("storage", "lvm") || false
end

private

def mandatory_path?(path)
Expand Down
23 changes: 15 additions & 8 deletions service/lib/agama/dbus/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module Agama
module DBus
module Storage
# D-Bus object to manage storage installation
class Manager < BaseObject
class Manager < BaseObject # rubocop:disable Metrics/ClassLength
include WithISCSIAuth
include WithServiceStatus
include ::DBus::ObjectManager
Expand Down Expand Up @@ -111,12 +111,19 @@ def apply_config(serialized_config)
proposal.success? ? 0 : 1
end

# Gets and serializes the storage config used to calculate the current proposal.
# Gets and serializes the storage config used for calculating the current proposal.
#
# @param solved [Boolean] Whether to recover the solved config.
# @return [String] Serialized config according to the JSON schema.
def recover_config(solved: false)
json = proposal.storage_json(solved: solved)
# @return [String]
def recover_config
json = proposal.storage_json
JSON.pretty_generate(json)
end

# Gets and serializes the storage config model.
#
# @return [String]
def recover_model
json = proposal.model_json
JSON.pretty_generate(json)
end

Expand All @@ -141,7 +148,7 @@ def deprecated_system
busy_while { apply_config(serialized_config) }
end
dbus_method(:GetConfig, "out serialized_config:s") { recover_config }
dbus_method(:GetSolvedConfig, "out serialized_config:s") { recover_config(solved: true) }
dbus_method(:GetConfigModel, "out serialized_model:s") { recover_model }
dbus_method(:Install) { install }
dbus_method(:Finish) { finish }
dbus_reader(:deprecated_system, "b")
Expand Down Expand Up @@ -458,7 +465,7 @@ def tree_path(tree_root)

# @return [Agama::Config]
def config
backend.config
backend.product_config
end

# @return [Agama::VolumeTemplatesBuilder]
Expand Down
Loading
Loading