From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <pbs-devel-bounces@lists.proxmox.com>
Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9])
	by lore.proxmox.com (Postfix) with ESMTPS id CFBAB1FF191
	for <inbox@lore.proxmox.com>; Mon, 16 Jun 2025 16:21:51 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
	by firstgate.proxmox.com (Proxmox) with ESMTP id E66389E09;
	Mon, 16 Jun 2025 16:22:13 +0200 (CEST)
From: Christian Ebner <c.ebner@proxmox.com>
To: pbs-devel@lists.proxmox.com
Date: Mon, 16 Jun 2025 16:21:15 +0200
Message-Id: <20250616142156.413652-3-c.ebner@proxmox.com>
X-Mailer: git-send-email 2.39.5
In-Reply-To: <20250616142156.413652-1-c.ebner@proxmox.com>
References: <20250616142156.413652-1-c.ebner@proxmox.com>
MIME-Version: 1.0
X-SPAM-LEVEL: Spam detection results:  0
 AWL 0.032 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 DMARC_MISSING             0.1 Missing DMARC policy
 KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment
 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to
 Validity was blocked. See
 https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more
 information.
 RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to
 Validity was blocked. See
 https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more
 information.
 RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to
 Validity was blocked. See
 https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more
 information.
 SPF_HELO_NONE           0.001 SPF: HELO does not publish an SPF Record
 SPF_PASS               -0.001 SPF: sender matches SPF record
 URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See
 http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more
 information. [datastore.rs]
Subject: [pbs-devel] [PATCH proxmox v3 2/2] pbs-api-types: extend datastore
 config by backend config enum
X-BeenThere: pbs-devel@lists.proxmox.com
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Proxmox Backup Server development discussion
 <pbs-devel.lists.proxmox.com>
List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pbs-devel>, 
 <mailto:pbs-devel-request@lists.proxmox.com?subject=unsubscribe>
List-Archive: <http://lists.proxmox.com/pipermail/pbs-devel/>
List-Post: <mailto:pbs-devel@lists.proxmox.com>
List-Help: <mailto:pbs-devel-request@lists.proxmox.com?subject=help>
List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel>, 
 <mailto:pbs-devel-request@lists.proxmox.com?subject=subscribe>
Reply-To: Proxmox Backup Server development discussion
 <pbs-devel@lists.proxmox.com>
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: pbs-devel-bounces@lists.proxmox.com
Sender: "pbs-devel" <pbs-devel-bounces@lists.proxmox.com>

Allows to configure a backend config variant for a datastore on
creation. The current default `Filesystem` backend variant is
introduced to be compatible with existing storages. A new S3 backend
variant allows to create datastores backed by an S3 compatible object
store instead.

For S3 backends, the type, id of the corresponding S3 client
configuration as well as the bucket name are stored as property
string. A valid datastore backend configuration for S3 therefore
contains:
```
    ...
    backend bucket=<BUCKET_NAME>,client=<S3_CONFIG_ID>,type=s3
    ...
```

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 pbs-api-types/src/datastore.rs | 100 ++++++++++++++++++++++++++++++++-
 1 file changed, 99 insertions(+), 1 deletion(-)

diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index 5bd953ac..9efec432 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -286,6 +286,94 @@ pub const DATASTORE_TUNING_STRING_SCHEMA: Schema = StringSchema::new("Datastore
     ))
     .schema();
 
+#[api]
+#[derive(Copy, Clone, Default, Deserialize, Serialize, Updater, PartialEq)]
+#[serde(rename_all = "kebab-case")]
+/// Datastore backend type
+pub enum DatastoreBackendType {
+    /// Local filesystem
+    #[default]
+    Filesystem,
+    /// S3 object store
+    S3,
+}
+serde_plain::derive_display_from_serialize!(DatastoreBackendType);
+serde_plain::derive_fromstr_from_deserialize!(DatastoreBackendType);
+
+#[api(
+    properties: {
+        type: {
+            type: DatastoreBackendType,
+            optional: true,
+        },
+        client: {
+            schema: super::s3::S3_CLIENT_ID_SCHEMA,
+            optional: true,
+        },
+        bucket: {
+            schema: super::s3::S3_BUCKET_NAME_SCHEMA,
+            optional: true,
+        },
+    },
+    default_key: "type",
+)]
+#[derive(Default, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+/// Datastore backend config
+pub struct DatastoreBackendConfig {
+    /// backend type
+    #[serde(rename = "type")]
+    pub ty: Option<DatastoreBackendType>,
+    /// s3 client id
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub client: Option<String>,
+    /// s3 bucket name
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub bucket: Option<String>,
+}
+
+pub const DATASTORE_BACKEND_CONFIG_STRING_SCHEMA: Schema =
+    StringSchema::new("Datastore backend config")
+        .format(&ApiStringFormat::VerifyFn(verify_datastore_backend_config))
+        .type_text("<backend-config>")
+        .schema();
+
+fn verify_datastore_backend_config(input: &str) -> Result<(), Error> {
+    DatastoreBackendConfig::from_str(input).map(|_| ())
+}
+
+impl FromStr for DatastoreBackendConfig {
+    type Err = Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let backend_config: DatastoreBackendConfig =
+            proxmox_schema::property_string::parse_with_schema(
+                s,
+                &DatastoreBackendConfig::API_SCHEMA,
+            )?;
+        let backend_type = backend_config.ty.unwrap_or_default();
+        match backend_type {
+            DatastoreBackendType::Filesystem => {
+                if backend_config.client.is_some() {
+                    bail!("additional option client, not allowed for backend type filesystem");
+                }
+                if backend_config.bucket.is_some() {
+                    bail!("additional option bucket, not allowed for backend type filesystem");
+                }
+            }
+            DatastoreBackendType::S3 => {
+                if backend_config.client.is_none() {
+                    bail!("missing option client, required for backend type s3");
+                }
+                if backend_config.bucket.is_none() {
+                    bail!("missing option bucket, required for backend type s3");
+                }
+            }
+        }
+        Ok(backend_config)
+    }
+}
+
 #[api(
     properties: {
         name: {
@@ -336,7 +424,11 @@ pub const DATASTORE_TUNING_STRING_SCHEMA: Schema = StringSchema::new("Datastore
             optional: true,
             format: &proxmox_schema::api_types::UUID_FORMAT,
             type: String,
-        }
+        },
+        backend: {
+            schema: DATASTORE_BACKEND_CONFIG_STRING_SCHEMA,
+            optional: true,
+        },
     }
 )]
 #[derive(Serialize, Deserialize, Updater, Clone, PartialEq)]
@@ -389,6 +481,11 @@ pub struct DataStoreConfig {
     #[updater(skip)]
     #[serde(skip_serializing_if = "Option::is_none")]
     pub backing_device: Option<String>,
+
+    /// Backend configuration for datastore
+    #[updater(skip)]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub backend: Option<String>,
 }
 
 #[api]
@@ -424,6 +521,7 @@ impl DataStoreConfig {
             tuning: None,
             maintenance_mode: None,
             backing_device: None,
+            backend: None,
         }
     }
 
-- 
2.39.5



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel