From: Aaron Lauterer <a.lauterer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH manager v3 1/6] api: ceph: add rbd namespace management endpoints
Date: Fri, 4 Apr 2025 16:53:34 +0200 [thread overview]
Message-ID: <20250404145339.1067261-2-a.lauterer@proxmox.com> (raw)
In-Reply-To: <20250404145339.1067261-1-a.lauterer@proxmox.com>
RBD supports namespaces. To make the management easier and possible via
the web UI, we need to add API endpoints to:
* list
* create
* delete
namespaces.
We only allow creatng namespaces for pools that have the RBD application
set.
Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com>
---
changes since
v2:
* use long params instead of positional ones when calling `rbd`
* remove default pool name 'rbd'. we are not the `rbd` utility, we can
require the parameter from the caller at all times
* limit the namespace name pattern to pve-storage-id. this is a safety
precaution to avoid any special characters in the namespace name.
* some smaller style and bug fixes
v1:
* integrated recommendations:
* code style in many places
* added check on removal if a storage config that uses the namespace
exists. I can be overriden with the new optional force flag
* on create we trim the namespace parameter and check if it has any
length. Is there an option in the API that would do that for us
already?
* added TODO note as we might want to rethink the ACL privileges
PVE/API2/Ceph/Pool.pm | 203 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 201 insertions(+), 2 deletions(-)
diff --git a/PVE/API2/Ceph/Pool.pm b/PVE/API2/Ceph/Pool.pm
index 5ee982f4..b34c3306 100644
--- a/PVE/API2/Ceph/Pool.pm
+++ b/PVE/API2/Ceph/Pool.pm
@@ -3,6 +3,8 @@ package PVE::API2::Ceph::Pool;
use strict;
use warnings;
+use JSON qw(decode_json);
+
use PVE::Ceph::Tools;
use PVE::Ceph::Services;
use PVE::JSONSchema qw(get_standard_option parse_property_string);
@@ -10,7 +12,7 @@ use PVE::RADOS;
use PVE::RESTHandler;
use PVE::RPCEnvironment;
use PVE::Storage;
-use PVE::Tools qw(extract_param);
+use PVE::Tools qw(extract_param run_command);
use PVE::API2::Storage::Config;
@@ -32,6 +34,7 @@ my $get_autoscale_status = sub {
return $data;
};
+# TODO: think about adding dedicated Ceph ACL privileges as the currently used ones don't match well
__PACKAGE__->register_method ({
name => 'lspools',
@@ -302,7 +305,7 @@ my $ceph_pool_common_options = sub {
my $add_storage = sub {
- my ($pool, $storeid, $ec_data_pool) = @_;
+ my ($pool, $storeid, $ec_data_pool, $namespace) = @_;
my $storage_params = {
type => 'rbd',
@@ -312,6 +315,8 @@ my $add_storage = sub {
content => 'rootdir,images',
};
+ $storage_params->{namespace} = $namespace if $namespace;
+
$storage_params->{'data-pool'} = $ec_data_pool if $ec_data_pool;
PVE::API2::Storage::Config->create($storage_params);
@@ -798,4 +803,198 @@ __PACKAGE__->register_method ({
}});
+my $get_rbd_namespaces = sub {
+ my ($pool) = @_;
+
+ my $raw = '';
+ run_command(
+ ['/usr/bin/rbd', 'namespace', 'list', '--pool', $pool, '--format', 'json'],
+ outfunc => sub { $raw .= shift },
+ errmsg => "rbd error",
+ errfunc => sub {},
+ );
+ return [] if $raw eq '[]';
+
+ my ($untainted_raw) = $raw =~ /^(.+)$/; # untaint
+ my $namespaces = eval { decode_json($untainted_raw) };
+ die "failed to parse as JSON - $@\n" if $@;
+
+ return [
+ map { { name => $_->{name} } } $namespaces->@*
+ ];
+};
+
+__PACKAGE__->register_method ({
+ name => 'listnamespaces',
+ path => '{name}/namespace',
+ method => 'GET',
+ permissions => {
+ check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
+ },
+ description => "Get pool RBD namespaces.",
+ proxyto => 'node',
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ name => {
+ description => 'The name of the pool.',
+ type => 'string',
+ },
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => 'object',
+ properties => {
+ name => { type => 'string', title => "Namespace" }
+ },
+ },
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $pool = extract_param($param, 'name') // 'rbd';
+ return $get_rbd_namespaces->($pool);
+ }});
+
+
+__PACKAGE__->register_method ({
+ name => 'createnamespace',
+ path => '{name}/namespace',
+ method => 'POST',
+ permissions => {
+ check => ['perm', '/', [ 'Sys.Modify' ]],
+ },
+ description => "Create new RBD namespace.",
+ proxyto => 'node',
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ name => {
+ description => 'The name of the pool.',
+ type => 'string',
+ },
+ namespace => {
+ description => 'The name of the new namespace',
+ type => 'string',
+ format => 'pve-storage-id',
+ },
+ 'add-storage' => {
+ description => "Configure VM and CT storage using the new namespace.",
+ type => 'boolean',
+ optional => 1,
+ default => "0",
+ },
+ },
+ },
+ returns => { type => 'string' },
+ code => sub {
+ my ($param) = @_;
+
+ my $pool = extract_param($param, 'name');
+ my $namespace = PVE::Tools::trim(extract_param($param, 'namespace'));
+ my $add_storages = extract_param($param, 'add-storage');
+
+ die "namespace cannot be empty\n" if length($namespace) < 1; # in case it was just whitespace
+
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $user = $rpcenv->get_user();
+ if ($add_storages) {
+ $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
+ die "namespace name contains characters which are illegal for storage naming\n"
+ if !PVE::JSONSchema::parse_storage_id("${pool}-${namespace}");
+ }
+
+ my $rados = PVE::RADOS->new();
+ my $apps = $rados->mon_command({ prefix => "osd pool application get", pool => "$pool", });
+ die "the pool does not have application 'rbd' enabled\n" if !defined($apps->{rbd});
+
+ my $current_namespaces = { map { $_->{name} => 1 } $get_rbd_namespaces->($pool)->@*};
+ die "namespace already exists\n" if $current_namespaces->{$namespace};
+
+ my $worker = sub {
+ run_command(
+ ['/usr/bin/rbd', 'namespace', 'create', '--pool', ${pool}, '--namespace', ${namespace}],
+ errmsg => "rbd create namespace error",
+ errfunc => sub {},
+ outfunc => sub {},
+ );
+ if ($add_storages) {
+ eval { $add_storage->($pool, "${pool}-${namespace}", 0, $namespace) };
+ die "adding PVE storage for ceph rbd namespace failed: pool: ${pool}, namespace: ${namespace}: $@\n" if $@;
+ }
+ };
+
+ return $rpcenv->fork_worker('cephcreaterbdnamespace', $pool, $user, $worker);
+ }});
+
+
+__PACKAGE__->register_method ({
+ name => 'destroynamespace',
+ path => '{name}/namespace',
+ method => 'DELETE',
+ permissions => {
+ check => ['perm', '/', [ 'Sys.Modify' ]],
+ },
+ description => "Delete RBD namespace.",
+ proxyto => 'node',
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ name => {
+ description => 'The name of the pool.',
+ type => 'string',
+ },
+ namespace => {
+ description => 'The name of the namespace',
+ type => 'string',
+ },
+ force => {
+ description => 'Force removal of the namespace',
+ type => 'boolean',
+ optional => 1,
+ },
+ },
+ },
+ returns => { type => 'string' },
+ code => sub {
+ my ($param) = @_;
+
+ my $pool = extract_param($param, 'name');
+ my $namespace = extract_param($param, 'namespace');
+ my $force = extract_param($param, 'force');
+
+ if (!$force) {
+ my $storages = $get_storages->($pool);
+ for my $storage (keys $storages->%*) {
+ if (
+ defined($storages->{$storage}->{namespace})
+ && $storages->{$storage}->{namespace} eq $namespace
+ ) {
+ die "namespace '${namespace}' is configured in storage '${storage}', remove it first\n";
+ }
+ }
+ }
+
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $user = $rpcenv->get_user();
+ my $worker = sub {
+ run_command(
+ ['/usr/bin/rbd', 'namespace', 'remove', '--pool', ${pool}, '--namespace', ${namespace}],
+ errmsg => "rbd remove namespace error",
+ errfunc => sub {},
+ outfunc => sub {},
+ );
+ };
+
+ return $rpcenv->fork_worker('cephdestroyrbdnamespace', $pool, $user, $worker);
+ }});
+
1;
--
2.39.5
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
next prev parent reply other threads:[~2025-04-04 14:54 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-04 14:53 [pve-devel] [PATCH manager, docs v3 0/6] Ceph: add RBD Namespace management Aaron Lauterer
2025-04-04 14:53 ` Aaron Lauterer [this message]
2025-04-04 14:53 ` [pve-devel] [PATCH manager v3 2/6] pveceph: add pool namespace subcommands Aaron Lauterer
2025-04-04 14:53 ` [pve-devel] [PATCH manager v3 3/6] ui: ceph pool: add rbd namespace panel Aaron Lauterer
2025-04-04 15:19 ` [pve-devel] [PATCH manager v3 3/6 follow up] " Aaron Lauterer
2025-04-04 14:53 ` [pve-devel] [PATCH manager v3 4/6] ui: utils: add ceph rbd namespace task names Aaron Lauterer
2025-04-04 14:53 ` [pve-devel] [PATCH manager v3 5/6] ui: storage rbd: remove hint for manual rbd namespace creation Aaron Lauterer
2025-04-04 14:53 ` [pve-devel] [PATCH docs v3] pveceph: add section for rbd namespaces Aaron Lauterer
2025-04-11 8:33 ` [pve-devel] [PATCH docs v3 follow-up] " Aaron Lauterer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250404145339.1067261-2-a.lauterer@proxmox.com \
--to=a.lauterer@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal