auto-format code using perltidy with Proxmox style guide
using the new top-level `make tidy` target, which calls perltidy via our wrapper to enforce the desired style as closely as possible. Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
parent
a750c44c42
commit
138cae897a
88 changed files with 19180 additions and 17920 deletions
24
PVE/API2.pm
24
PVE/API2.pm
|
|
@ -18,32 +18,32 @@ use PVE::API2::Pool;
|
|||
use PVE::API2::AccessControl;
|
||||
use PVE::API2::Storage::Config;
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster",
|
||||
path => 'cluster',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Nodes",
|
||||
path => 'nodes',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Storage::Config",
|
||||
path => 'storage',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::AccessControl",
|
||||
path => 'access',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Pool",
|
||||
path => 'pools',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -61,12 +61,12 @@ __PACKAGE__->register_method ({
|
|||
subdir => { type => 'string' },
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{subdir}" } ],
|
||||
links => [{ rel => 'child', href => "{subdir}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $res = [ { subdir => 'version' } ];
|
||||
my $res = [{ subdir => 'version' }];
|
||||
|
||||
my $ma = PVE::API2->method_attributes();
|
||||
|
||||
|
|
@ -79,9 +79,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'version',
|
||||
path => 'version',
|
||||
method => 'GET',
|
||||
|
|
@ -132,5 +133,6 @@ __PACKAGE__->register_method ({
|
|||
$res->{$_} = $version_info->{$_} for qw(version release repoid);
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use base qw(PVE::RESTHandler);
|
|||
|
||||
my $acme_account_dir = PVE::CertHelpers::acme_account_dir();
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -35,7 +35,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -43,7 +43,8 @@ __PACKAGE__->register_method ({
|
|||
return [
|
||||
{ name => 'certificate' },
|
||||
];
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $order_certificate = sub {
|
||||
my ($acme, $acme_node_config) = @_;
|
||||
|
|
@ -51,9 +52,9 @@ my $order_certificate = sub {
|
|||
my $plugins = PVE::API2::ACMEPlugin::load_config();
|
||||
|
||||
print "Placing ACME order\n";
|
||||
my ($order_url, $order) = $acme->new_order([ keys %{$acme_node_config->{domains}} ]);
|
||||
my ($order_url, $order) = $acme->new_order([keys %{ $acme_node_config->{domains} }]);
|
||||
print "Order URL: $order_url\n";
|
||||
for my $auth_url (@{$order->{authorizations}}) {
|
||||
for my $auth_url (@{ $order->{authorizations} }) {
|
||||
print "\nGetting authorization details from '$auth_url'\n";
|
||||
my $auth = $acme->get_authorization($auth_url);
|
||||
|
||||
|
|
@ -123,9 +124,7 @@ my $order_certificate = sub {
|
|||
# finalize even at the 'pending' state and give up after 5
|
||||
# unsuccessful tries this can be removed when the letsencrypt api
|
||||
# definitely has implemented the 'ready' state
|
||||
eval {
|
||||
$acme->finalize_order($order, PVE::Certificate::pem_to_der($csr));
|
||||
};
|
||||
eval { $acme->finalize_order($order, PVE::Certificate::pem_to_der($csr)); };
|
||||
if (my $err = $@) {
|
||||
die $err if $finalize_error_cnt >= 5;
|
||||
|
||||
|
|
@ -156,12 +155,12 @@ my $order_certificate = sub {
|
|||
return ($cert, $key);
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'new_certificate',
|
||||
path => 'certificate',
|
||||
method => 'POST',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Order a new certificate from ACME-compatible CA.",
|
||||
protected => 1,
|
||||
|
|
@ -187,13 +186,13 @@ __PACKAGE__->register_method ({
|
|||
my $node = extract_param($param, 'node');
|
||||
my $cert_prefix = PVE::CertHelpers::cert_path_prefix($node);
|
||||
|
||||
raise_param_exc({'force' => "Custom certificate exists but 'force' is not set."})
|
||||
raise_param_exc({ 'force' => "Custom certificate exists but 'force' is not set." })
|
||||
if !$param->{force} && -e "${cert_prefix}.pem";
|
||||
|
||||
my $node_config = PVE::NodeConfig::load_config($node);
|
||||
my $acme_node_config = PVE::NodeConfig::get_acme_conf($node_config);
|
||||
raise("ACME domain list in node configuration is missing!", 400)
|
||||
if !$acme_node_config || !%{$acme_node_config->{domains}};
|
||||
if !$acme_node_config || !%{ $acme_node_config->{domains} };
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
|
|
@ -204,7 +203,7 @@ __PACKAGE__->register_method ({
|
|||
my $account = $acme_node_config->{account};
|
||||
my $account_file = "${acme_account_dir}/${account}";
|
||||
die "ACME account config file '$account' does not exist.\n"
|
||||
if ! -e $account_file;
|
||||
if !-e $account_file;
|
||||
|
||||
my $acme = PVE::ACME->new($account_file);
|
||||
|
||||
|
|
@ -225,14 +224,15 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker("acmenewcert", undef, $authuser, $realcmd);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'renew_certificate',
|
||||
path => 'certificate',
|
||||
method => 'PUT',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Renew existing certificate from CA.",
|
||||
protected => 1,
|
||||
|
|
@ -259,16 +259,21 @@ __PACKAGE__->register_method ({
|
|||
my $cert_prefix = PVE::CertHelpers::cert_path_prefix($node);
|
||||
|
||||
raise("No current (custom) certificate found, please order a new certificate!\n")
|
||||
if ! -e "${cert_prefix}.pem";
|
||||
if !-e "${cert_prefix}.pem";
|
||||
|
||||
my $expires_soon = PVE::Certificate::check_expiry("${cert_prefix}.pem", time() + 30*24*60*60);
|
||||
raise_param_exc({'force' => "Certificate does not expire within the next 30 days, and 'force' is not set."})
|
||||
if !$expires_soon && !$param->{force};
|
||||
my $expires_soon =
|
||||
PVE::Certificate::check_expiry("${cert_prefix}.pem", time() + 30 * 24 * 60 * 60);
|
||||
raise_param_exc(
|
||||
{
|
||||
'force' =>
|
||||
"Certificate does not expire within the next 30 days, and 'force' is not set.",
|
||||
},
|
||||
) if !$expires_soon && !$param->{force};
|
||||
|
||||
my $node_config = PVE::NodeConfig::load_config($node);
|
||||
my $acme_node_config = PVE::NodeConfig::get_acme_conf($node_config);
|
||||
raise("ACME domain list in node configuration is missing!", 400)
|
||||
if !$acme_node_config || !%{$acme_node_config->{domains}};
|
||||
if !$acme_node_config || !%{ $acme_node_config->{domains} };
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
|
|
@ -281,7 +286,7 @@ __PACKAGE__->register_method ({
|
|||
my $account = $acme_node_config->{account};
|
||||
my $account_file = "${acme_account_dir}/${account}";
|
||||
die "ACME account config file '$account' does not exist.\n"
|
||||
if ! -e $account_file;
|
||||
if !-e $account_file;
|
||||
|
||||
my $acme = PVE::ACME->new($account_file);
|
||||
|
||||
|
|
@ -306,14 +311,15 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker("acmerenew", undef, $authuser, $realcmd);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'revoke_certificate',
|
||||
path => 'certificate',
|
||||
method => 'DELETE',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Revoke existing certificate from CA.",
|
||||
protected => 1,
|
||||
|
|
@ -336,7 +342,7 @@ __PACKAGE__->register_method ({
|
|||
my $node_config = PVE::NodeConfig::load_config($node);
|
||||
my $acme_node_config = PVE::NodeConfig::get_acme_conf($node_config);
|
||||
raise("ACME domain list in node configuration is missing!", 400)
|
||||
if !$acme_node_config || !%{$acme_node_config->{domains}};
|
||||
if !$acme_node_config || !%{ $acme_node_config->{domains} };
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
|
|
@ -349,7 +355,7 @@ __PACKAGE__->register_method ({
|
|||
my $account = $acme_node_config->{account};
|
||||
my $account_file = "${acme_account_dir}/${account}";
|
||||
die "ACME account config file '$account' does not exist.\n"
|
||||
if ! -e $account_file;
|
||||
if !-e $account_file;
|
||||
|
||||
my $acme = PVE::ACME->new($account_file);
|
||||
|
||||
|
|
@ -377,6 +383,7 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker("acmerevoke", undef, $authuser, $realcmd);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use PVE::API2::ACMEPlugin;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::ACMEPlugin",
|
||||
path => 'plugins',
|
||||
});
|
||||
|
|
@ -33,11 +33,11 @@ my $acme_directories = [
|
|||
my $acme_default_directory_url = $acme_directories->[0]->{url};
|
||||
my $account_contact_from_param = sub {
|
||||
my @addresses = PVE::Tools::split_list(extract_param($_[0], 'contact'));
|
||||
return [ map { "mailto:$_" } @addresses ];
|
||||
return [map { "mailto:$_" } @addresses];
|
||||
};
|
||||
my $acme_account_dir = PVE::CertHelpers::acme_account_dir();
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -45,8 +45,7 @@ __PACKAGE__->register_method ({
|
|||
description => "ACMEAccount index.",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
},
|
||||
properties => {},
|
||||
},
|
||||
returns => {
|
||||
type => 'array',
|
||||
|
|
@ -54,7 +53,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -67,9 +66,10 @@ __PACKAGE__->register_method ({
|
|||
{ name => 'plugins' },
|
||||
{ name => 'challenge-schema' },
|
||||
];
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'account_index',
|
||||
path => 'account',
|
||||
method => 'GET',
|
||||
|
|
@ -78,8 +78,7 @@ __PACKAGE__->register_method ({
|
|||
protected => 1,
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
},
|
||||
properties => {},
|
||||
},
|
||||
returns => {
|
||||
type => 'array',
|
||||
|
|
@ -87,16 +86,17 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $accounts = PVE::CertHelpers::list_acme_accounts();
|
||||
return [ map { { name => $_ } } @$accounts ];
|
||||
}});
|
||||
return [map { { name => $_ } } @$accounts];
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'register_account',
|
||||
path => 'account',
|
||||
method => 'POST',
|
||||
|
|
@ -112,10 +112,13 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
directory => get_standard_option('pve-acme-directory-url', {
|
||||
directory => get_standard_option(
|
||||
'pve-acme-directory-url',
|
||||
{
|
||||
default => $acme_default_directory_url,
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
'eab-kid' => {
|
||||
description => 'Key Identifier for External Account Binding.',
|
||||
type => 'string',
|
||||
|
|
@ -141,19 +144,23 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $account_name = extract_param($param, 'name') // 'default';
|
||||
my $account_file = "${acme_account_dir}/${account_name}";
|
||||
mkdir $acme_account_dir if ! -e $acme_account_dir;
|
||||
mkdir $acme_account_dir if !-e $acme_account_dir;
|
||||
|
||||
my $eab_kid = extract_param($param, 'eab-kid');
|
||||
my $eab_hmac_key = extract_param($param, 'eab-hmac-key');
|
||||
|
||||
raise_param_exc({'name' => "ACME account config file '${account_name}' already exists."})
|
||||
raise_param_exc({
|
||||
'name' => "ACME account config file '${account_name}' already exists." })
|
||||
if -e $account_file;
|
||||
|
||||
my $directory = extract_param($param, 'directory') // $acme_default_directory_url;
|
||||
my $contact = $account_contact_from_param->($param);
|
||||
|
||||
my $realcmd = sub {
|
||||
PVE::Cluster::cfs_lock_acme($account_name, 10, sub {
|
||||
PVE::Cluster::cfs_lock_acme(
|
||||
$account_name,
|
||||
10,
|
||||
sub {
|
||||
die "ACME account config file '${account_name}' already exists.\n"
|
||||
if -e $account_file;
|
||||
|
||||
|
|
@ -166,7 +173,7 @@ __PACKAGE__->register_method ({
|
|||
if (defined($eab_kid)) {
|
||||
$info{eab} = {
|
||||
kid => $eab_kid,
|
||||
hmac_key => $eab_hmac_key
|
||||
hmac_key => $eab_hmac_key,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -177,12 +184,14 @@ __PACKAGE__->register_method ({
|
|||
die "Registration failed: $err\n";
|
||||
}
|
||||
print "Registration successful, account URL: '$acme->{location}'\n";
|
||||
});
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('acmeregister', undef, $authuser, $realcmd);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $update_account = sub {
|
||||
my ($param, $msg, %info) = @_;
|
||||
|
|
@ -190,50 +199,56 @@ my $update_account = sub {
|
|||
my $account_name = extract_param($param, 'name') // 'default';
|
||||
my $account_file = "${acme_account_dir}/${account_name}";
|
||||
|
||||
raise_param_exc({'name' => "ACME account config file '${account_name}' does not exist."})
|
||||
if ! -e $account_file;
|
||||
|
||||
raise_param_exc({ 'name' => "ACME account config file '${account_name}' does not exist." })
|
||||
if !-e $account_file;
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $realcmd = sub {
|
||||
PVE::Cluster::cfs_lock_acme($account_name, 10, sub {
|
||||
PVE::Cluster::cfs_lock_acme(
|
||||
$account_name,
|
||||
10,
|
||||
sub {
|
||||
die "ACME account config file '${account_name}' does not exist.\n"
|
||||
if ! -e $account_file;
|
||||
if !-e $account_file;
|
||||
|
||||
my $acme = PVE::ACME->new($account_file);
|
||||
$acme->load();
|
||||
$acme->update_account(%info);
|
||||
if ($info{status} && $info{status} eq 'deactivated') {
|
||||
my $deactivated_name;
|
||||
for my $i (0..100) {
|
||||
for my $i (0 .. 100) {
|
||||
my $candidate = "${acme_account_dir}/_deactivated_${account_name}_${i}";
|
||||
if (! -e $candidate) {
|
||||
if (!-e $candidate) {
|
||||
$deactivated_name = $candidate;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ($deactivated_name) {
|
||||
print "Renaming account file from '$account_file' to '$deactivated_name'\n";
|
||||
rename($account_file, $deactivated_name) or
|
||||
warn ".. failed - $!\n";
|
||||
print
|
||||
"Renaming account file from '$account_file' to '$deactivated_name'\n";
|
||||
rename($account_file, $deactivated_name)
|
||||
or warn ".. failed - $!\n";
|
||||
} else {
|
||||
warn "No free slot to rename deactivated account file '$account_file', leaving in place\n";
|
||||
warn
|
||||
"No free slot to rename deactivated account file '$account_file', leaving in place\n";
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker("acme${msg}", undef, $authuser, $realcmd);
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update_account',
|
||||
path => 'account/{name}',
|
||||
method => 'PUT',
|
||||
description => "Update existing ACME account information with CA. Note: not specifying any new account information triggers a refresh.",
|
||||
description =>
|
||||
"Update existing ACME account information with CA. Note: not specifying any new account information triggers a refresh.",
|
||||
protected => 1,
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -256,9 +271,10 @@ __PACKAGE__->register_method ({
|
|||
} else {
|
||||
return $update_account->($param, 'refresh');
|
||||
}
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_account',
|
||||
path => 'account/{name}',
|
||||
method => 'GET',
|
||||
|
|
@ -298,8 +314,9 @@ __PACKAGE__->register_method ({
|
|||
my $account_name = extract_param($param, 'name') // 'default';
|
||||
my $account_file = "${acme_account_dir}/${account_name}";
|
||||
|
||||
raise_param_exc({'name' => "ACME account config file '${account_name}' does not exist."})
|
||||
if ! -e $account_file;
|
||||
raise_param_exc({
|
||||
'name' => "ACME account config file '${account_name}' does not exist." })
|
||||
if !-e $account_file;
|
||||
|
||||
my $acme = PVE::ACME->new($account_file);
|
||||
$acme->load();
|
||||
|
|
@ -311,9 +328,10 @@ __PACKAGE__->register_method ({
|
|||
$res->{tos} = $acme->{tos};
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'deactivate_account',
|
||||
path => 'account/{name}',
|
||||
method => 'DELETE',
|
||||
|
|
@ -332,22 +350,27 @@ __PACKAGE__->register_method ({
|
|||
my ($param) = @_;
|
||||
|
||||
return $update_account->($param, 'deactivate', status => 'deactivated');
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
# TODO: deprecated, remove with pve 9
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_tos',
|
||||
path => 'tos',
|
||||
method => 'GET',
|
||||
description => "Retrieve ACME TermsOfService URL from CA. Deprecated, please use /cluster/acme/meta.",
|
||||
description =>
|
||||
"Retrieve ACME TermsOfService URL from CA. Deprecated, please use /cluster/acme/meta.",
|
||||
permissions => { user => 'all' },
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
directory => get_standard_option('pve-acme-directory-url', {
|
||||
directory => get_standard_option(
|
||||
'pve-acme-directory-url',
|
||||
{
|
||||
default => $acme_default_directory_url,
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -364,23 +387,27 @@ __PACKAGE__->register_method ({
|
|||
my $meta = $acme->get_meta();
|
||||
|
||||
return $meta ? $meta->{termsOfService} : undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_meta',
|
||||
path => 'meta',
|
||||
method => 'GET',
|
||||
description => "Retrieve ACME Directory Meta Information",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
directory => get_standard_option('pve-acme-directory-url', {
|
||||
directory => get_standard_option(
|
||||
'pve-acme-directory-url',
|
||||
{
|
||||
default => $acme_default_directory_url,
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -421,9 +448,10 @@ __PACKAGE__->register_method ({
|
|||
my $meta = $acme->get_meta();
|
||||
|
||||
return $meta;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_directories',
|
||||
path => 'directories',
|
||||
method => 'GET',
|
||||
|
|
@ -450,9 +478,10 @@ __PACKAGE__->register_method ({
|
|||
my ($param) = @_;
|
||||
|
||||
return $acme_directories;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'challengeschema',
|
||||
path => 'challenge-schema',
|
||||
method => 'GET',
|
||||
|
|
@ -499,7 +528,8 @@ __PACKAGE__->register_method ({
|
|||
my $plugins = $plugin->get_supported_plugins();
|
||||
for my $id (sort keys %$plugins) {
|
||||
my $schema = $plugins->{$id};
|
||||
push @$res, {
|
||||
push @$res,
|
||||
{
|
||||
id => $id,
|
||||
name => $schema->{name} // $id,
|
||||
type => $plugin_type,
|
||||
|
|
@ -509,6 +539,7 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ use base qw(PVE::RESTHandler);
|
|||
|
||||
my $plugin_config_file = "priv/acme/plugins.cfg";
|
||||
|
||||
cfs_register_file($plugin_config_file,
|
||||
cfs_register_file(
|
||||
$plugin_config_file,
|
||||
sub { PVE::ACME::Challenge->parse_config(@_); },
|
||||
sub { PVE::ACME::Challenge->write_config(@_); },
|
||||
);
|
||||
|
|
@ -26,11 +27,14 @@ PVE::ACME::DNSChallenge->register();
|
|||
PVE::ACME::StandAlone->register();
|
||||
PVE::ACME::Challenge->init();
|
||||
|
||||
PVE::JSONSchema::register_standard_option('pve-acme-pluginid', {
|
||||
PVE::JSONSchema::register_standard_option(
|
||||
'pve-acme-pluginid',
|
||||
{
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
description => 'Unique identifier for ACME plugin instance.',
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
my $plugin_type_enum = PVE::ACME::Challenge->lookup_types();
|
||||
|
||||
|
|
@ -46,12 +50,12 @@ my $modify_cfg_for_api = sub {
|
|||
return $plugin_cfg;
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
description => "ACME plugin index.",
|
||||
protected => 1,
|
||||
|
|
@ -74,7 +78,7 @@ __PACKAGE__->register_method ({
|
|||
plugin => get_standard_option('pve-acme-pluginid'),
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{plugin}" } ],
|
||||
links => [{ rel => 'child', href => "{plugin}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -82,14 +86,14 @@ __PACKAGE__->register_method ({
|
|||
my $cfg = load_config();
|
||||
|
||||
my $res = [];
|
||||
foreach my $pluginid (keys %{$cfg->{ids}}) {
|
||||
foreach my $pluginid (keys %{ $cfg->{ids} }) {
|
||||
my $plugin_cfg = $modify_cfg_for_api->($cfg, $pluginid);
|
||||
next if $param->{type} && $param->{type} ne $plugin_cfg->{type};
|
||||
push @$res, $plugin_cfg;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
|
|
@ -98,7 +102,7 @@ __PACKAGE__->register_method({
|
|||
method => 'GET',
|
||||
description => "Get ACME plugin configuration.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
parameters => {
|
||||
|
|
@ -115,7 +119,7 @@ __PACKAGE__->register_method({
|
|||
|
||||
my $cfg = load_config();
|
||||
return $modify_cfg_for_api->($cfg, $param->{id});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
|
|
@ -124,12 +128,12 @@ __PACKAGE__->register_method({
|
|||
method => 'POST',
|
||||
description => "Add ACME plugin configuration.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
parameters => PVE::ACME::Challenge->createSchema(),
|
||||
returns => {
|
||||
type => "null"
|
||||
type => "null",
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -137,7 +141,10 @@ __PACKAGE__->register_method({
|
|||
my $id = extract_param($param, 'id');
|
||||
my $type = extract_param($param, 'type');
|
||||
|
||||
cfs_lock_file($plugin_config_file, undef, sub {
|
||||
cfs_lock_file(
|
||||
$plugin_config_file,
|
||||
undef,
|
||||
sub {
|
||||
my $cfg = load_config();
|
||||
die "ACME plugin ID '$id' already exists\n" if defined($cfg->{ids}->{$id});
|
||||
|
||||
|
|
@ -148,11 +155,12 @@ __PACKAGE__->register_method({
|
|||
$cfg->{ids}->{$id}->{type} = $type;
|
||||
|
||||
cfs_write_file($plugin_config_file, $cfg);
|
||||
});
|
||||
},
|
||||
);
|
||||
die "$@" if $@;
|
||||
|
||||
return undef;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
|
|
@ -161,12 +169,12 @@ __PACKAGE__->register_method({
|
|||
method => 'PUT',
|
||||
description => "Update ACME plugin configuration.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
parameters => PVE::ACME::Challenge->updateSchema(),
|
||||
returns => {
|
||||
type => "null"
|
||||
type => "null",
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -175,7 +183,10 @@ __PACKAGE__->register_method({
|
|||
my $delete = extract_param($param, 'delete');
|
||||
my $digest = extract_param($param, 'digest');
|
||||
|
||||
cfs_lock_file($plugin_config_file, undef, sub {
|
||||
cfs_lock_file(
|
||||
$plugin_config_file,
|
||||
undef,
|
||||
sub {
|
||||
my $cfg = load_config();
|
||||
PVE::Tools::assert_if_modified($cfg->{digest}, $digest);
|
||||
my $plugin_cfg = $cfg->{ids}->{$id};
|
||||
|
|
@ -201,11 +212,12 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
cfs_write_file($plugin_config_file, $cfg);
|
||||
});
|
||||
},
|
||||
);
|
||||
die "$@" if $@;
|
||||
|
||||
return undef;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
|
|
@ -214,7 +226,7 @@ __PACKAGE__->register_method({
|
|||
method => 'DELETE',
|
||||
description => "Delete ACME plugin configuration.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
parameters => {
|
||||
|
|
@ -224,24 +236,28 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
},
|
||||
returns => {
|
||||
type => "null"
|
||||
type => "null",
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $id = extract_param($param, 'id');
|
||||
|
||||
cfs_lock_file($plugin_config_file, undef, sub {
|
||||
cfs_lock_file(
|
||||
$plugin_config_file,
|
||||
undef,
|
||||
sub {
|
||||
my $cfg = load_config();
|
||||
|
||||
delete $cfg->{ids}->{$id};
|
||||
|
||||
cfs_write_file($plugin_config_file, $cfg);
|
||||
});
|
||||
},
|
||||
);
|
||||
die "$@" if $@;
|
||||
|
||||
return undef;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
sub load_config {
|
||||
|
|
|
|||
120
PVE/API2/APT.pm
120
PVE/API2/APT.pm
|
|
@ -62,7 +62,7 @@ __PACKAGE__->register_method({
|
|||
id => { type => 'string' },
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -75,12 +75,13 @@ __PACKAGE__->register_method({
|
|||
];
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $get_pkgfile = sub {
|
||||
my ($veriter) = @_;
|
||||
|
||||
foreach my $verfile (@{$veriter->{FileList}}) {
|
||||
foreach my $verfile (@{ $veriter->{FileList} }) {
|
||||
my $pkgfile = $verfile->{File};
|
||||
next if !$pkgfile->{Origin};
|
||||
return $pkgfile;
|
||||
|
|
@ -121,7 +122,9 @@ my $assemble_pkginfo = sub {
|
|||
# we try to cache results
|
||||
my $pve_pkgstatus_fn = "/var/lib/pve-manager/pkgupdates";
|
||||
my $read_cached_pkgstatus = sub {
|
||||
my $data = eval { decode_json(PVE::Tools::file_get_contents($pve_pkgstatus_fn, 5*1024*1024)) } // [];
|
||||
my $data =
|
||||
eval { decode_json(PVE::Tools::file_get_contents($pve_pkgstatus_fn, 5 * 1024 * 1024)) }
|
||||
// [];
|
||||
warn "error reading cached package status in '$pve_pkgstatus_fn' - $@\n" if $@;
|
||||
return $data;
|
||||
};
|
||||
|
|
@ -186,7 +189,7 @@ my $update_pve_pkgstatus = sub {
|
|||
|
||||
# keep notification status (avoid sending mails abou new packages more than once)
|
||||
foreach my $pi (@$pkglist) {
|
||||
if (my $ns = $notify_status->{$pi->{Package}}) {
|
||||
if (my $ns = $notify_status->{ $pi->{Package} }) {
|
||||
$pi->{NotifyStatus} = $ns if $ns eq $pi->{Version};
|
||||
}
|
||||
}
|
||||
|
|
@ -202,7 +205,7 @@ __PACKAGE__->register_method({
|
|||
method => 'GET',
|
||||
description => "List available updates.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
|
|
@ -236,15 +239,17 @@ __PACKAGE__->register_method({
|
|||
my $pkglist = &$update_pve_pkgstatus();
|
||||
|
||||
return $pkglist;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update_database',
|
||||
path => 'update',
|
||||
method => 'POST',
|
||||
description => "This is used to resynchronize the package index files from their sources (apt-get update).",
|
||||
description =>
|
||||
"This is used to resynchronize the package index files from their sources (apt-get update).",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
|
|
@ -260,7 +265,8 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
quiet => {
|
||||
type => 'boolean',
|
||||
description => "Only produces output suitable for logging, omitting progress indicators.",
|
||||
description =>
|
||||
"Only produces output suitable for logging, omitting progress indicators.",
|
||||
optional => 1,
|
||||
default => 0,
|
||||
},
|
||||
|
|
@ -294,7 +300,7 @@ __PACKAGE__->register_method({
|
|||
print "starting apt-get update\n" if !$param->{quiet};
|
||||
|
||||
if ($param->{quiet}) {
|
||||
PVE::Tools::run_command($cmd, outfunc => sub {}, errfunc => sub {});
|
||||
PVE::Tools::run_command($cmd, outfunc => sub { }, errfunc => sub { });
|
||||
} else {
|
||||
PVE::Tools::run_command($cmd);
|
||||
}
|
||||
|
|
@ -316,21 +322,22 @@ __PACKAGE__->register_method({
|
|||
{
|
||||
label => "Available Version",
|
||||
id => "available-version",
|
||||
}
|
||||
]
|
||||
},
|
||||
data => []
|
||||
],
|
||||
},
|
||||
data => [],
|
||||
};
|
||||
|
||||
my $count = 0;
|
||||
foreach my $p (sort {$a->{Package} cmp $b->{Package} } @$pkglist) {
|
||||
foreach my $p (sort { $a->{Package} cmp $b->{Package} } @$pkglist) {
|
||||
next if $p->{NotifyStatus} && $p->{NotifyStatus} eq $p->{Version};
|
||||
$count++;
|
||||
|
||||
push @{$updates_table->{data}}, {
|
||||
push @{ $updates_table->{data} },
|
||||
{
|
||||
"package-name" => $p->{Package},
|
||||
"installed-version" => $p->{OldVersion},
|
||||
"available-version" => $p->{Version}
|
||||
"available-version" => $p->{Version},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -348,9 +355,7 @@ __PACKAGE__->register_method({
|
|||
};
|
||||
|
||||
PVE::Notify::info(
|
||||
"package-updates",
|
||||
$template_data,
|
||||
$metadata_fields,
|
||||
"package-updates", $template_data, $metadata_fields,
|
||||
);
|
||||
|
||||
foreach my $pi (@$pkglist) {
|
||||
|
|
@ -364,9 +369,8 @@ __PACKAGE__->register_method({
|
|||
|
||||
return $rpcenv->fork_worker('aptupdate', undef, $authuser, $realcmd);
|
||||
|
||||
}});
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'changelog',
|
||||
|
|
@ -374,7 +378,7 @@ __PACKAGE__->register_method({
|
|||
method => 'GET',
|
||||
description => "Get package changelogs.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
proxyto => 'node',
|
||||
parameters => {
|
||||
|
|
@ -422,7 +426,8 @@ __PACKAGE__->register_method({
|
|||
$output .= "RC: $rc" if $rc != 0;
|
||||
|
||||
return $output;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'repositories',
|
||||
|
|
@ -431,7 +436,7 @@ __PACKAGE__->register_method({
|
|||
proxyto => 'node',
|
||||
description => "Get APT repository information.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -455,7 +460,7 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
'file-type' => {
|
||||
type => "string",
|
||||
enum => [ 'list', 'sources' ],
|
||||
enum => ['list', 'sources'],
|
||||
description => "Format of the file.",
|
||||
},
|
||||
repositories => {
|
||||
|
|
@ -469,7 +474,7 @@ __PACKAGE__->register_method({
|
|||
description => "List of package types.",
|
||||
items => {
|
||||
type => "string",
|
||||
enum => [ 'deb', 'deb-src' ],
|
||||
enum => ['deb', 'deb-src'],
|
||||
},
|
||||
},
|
||||
URIs => {
|
||||
|
|
@ -520,12 +525,13 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
FileType => {
|
||||
type => "string",
|
||||
enum => [ 'list', 'sources' ],
|
||||
enum => ['list', 'sources'],
|
||||
description => "Format of the defining file.",
|
||||
},
|
||||
Enabled => {
|
||||
type => "boolean",
|
||||
description => "Whether the repository is enabled or not",
|
||||
description =>
|
||||
"Whether the repository is enabled or not",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -573,7 +579,8 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
index => {
|
||||
type => "string",
|
||||
description => "Index of the associated repository within the file.",
|
||||
description =>
|
||||
"Index of the associated repository within the file.",
|
||||
},
|
||||
property => {
|
||||
type => "string",
|
||||
|
|
@ -587,7 +594,7 @@ __PACKAGE__->register_method({
|
|||
message => {
|
||||
type => "string",
|
||||
description => "Information message.",
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -608,8 +615,8 @@ __PACKAGE__->register_method({
|
|||
status => {
|
||||
type => "boolean",
|
||||
optional => 1,
|
||||
description => "Indicating enabled/disabled status, if the " .
|
||||
"repository is configured.",
|
||||
description => "Indicating enabled/disabled status, if the "
|
||||
. "repository is configured.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -620,7 +627,8 @@ __PACKAGE__->register_method({
|
|||
my ($param) = @_;
|
||||
|
||||
return Proxmox::RS::APT::Repositories::repositories("pve");
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'add_repository',
|
||||
|
|
@ -628,7 +636,7 @@ __PACKAGE__->register_method({
|
|||
method => 'PUT',
|
||||
description => "Add a standard repository to the configuration",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
|
|
@ -654,16 +662,20 @@ __PACKAGE__->register_method({
|
|||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
Proxmox::RS::APT::Repositories::add_repository($param->{handle}, "pve", $param->{digest});
|
||||
}});
|
||||
Proxmox::RS::APT::Repositories::add_repository(
|
||||
$param->{handle}, "pve", $param->{digest},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'change_repository',
|
||||
path => 'repositories',
|
||||
method => 'POST',
|
||||
description => "Change the properties of a repository. Currently only allows enabling/disabling.",
|
||||
description =>
|
||||
"Change the properties of a repository. Currently only allows enabling/disabling.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
|
|
@ -707,9 +719,10 @@ __PACKAGE__->register_method({
|
|||
$param->{path},
|
||||
int($param->{index}),
|
||||
$options,
|
||||
$param->{digest}
|
||||
$param->{digest},
|
||||
);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'versions',
|
||||
|
|
@ -718,7 +731,7 @@ __PACKAGE__->register_method({
|
|||
proxyto => 'node',
|
||||
description => "Get package information for important Proxmox packages.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -744,8 +757,16 @@ __PACKAGE__->register_method({
|
|||
my @list = qw(proxmox-ve pve-manager);
|
||||
|
||||
my $aptver = $AptPkg::System::_system->versioning();
|
||||
my $byver = sub { $aptver->compare($cache->{$b}->{CurrentVer}->{VerStr}, $cache->{$a}->{CurrentVer}->{VerStr}) };
|
||||
push @list, sort $byver grep { /^(?:pve|proxmox)-kernel-/ && $cache->{$_}->{CurrentState} eq 'Installed' } keys %$cache;
|
||||
my $byver = sub {
|
||||
$aptver->compare(
|
||||
$cache->{$b}->{CurrentVer}->{VerStr},
|
||||
$cache->{$a}->{CurrentVer}->{VerStr},
|
||||
);
|
||||
};
|
||||
push @list,
|
||||
sort $byver
|
||||
grep { /^(?:pve|proxmox)-kernel-/ && $cache->{$_}->{CurrentState} eq 'Installed' }
|
||||
keys %$cache;
|
||||
|
||||
my @opt_pack = qw(
|
||||
amd64-microcode
|
||||
|
|
@ -830,7 +851,9 @@ __PACKAGE__->register_method({
|
|||
my $candidate_ver = defined($p) ? $policy->candidate($p) : undef;
|
||||
my $res;
|
||||
if (my $current_ver = $p->{CurrentVer}) {
|
||||
$res = $assemble_pkginfo->($pkgname, $info, $current_ver, $candidate_ver || $current_ver);
|
||||
$res = $assemble_pkginfo->(
|
||||
$pkgname, $info, $current_ver, $candidate_ver || $current_ver,
|
||||
);
|
||||
} elsif ($candidate_ver) {
|
||||
$res = $assemble_pkginfo->($pkgname, $info, $candidate_ver, $candidate_ver);
|
||||
delete $res->{OldVersion};
|
||||
|
|
@ -845,7 +868,7 @@ __PACKAGE__->register_method({
|
|||
} elsif ($pkgname eq 'proxmox-ve') {
|
||||
$res->{RunningKernel} = $kernel_release;
|
||||
}
|
||||
if (grep( /^$pkgname$/, @opt_pack)) {
|
||||
if (grep(/^$pkgname$/, @opt_pack)) {
|
||||
next if $res->{CurrentState} eq 'NotInstalled';
|
||||
}
|
||||
|
||||
|
|
@ -853,6 +876,7 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
return $pkglist;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use base qw(PVE::RESTHandler);
|
|||
use constant ALL_DAYS => 'mon,tue,wed,thu,fri,sat,sun';
|
||||
|
||||
PVE::JSONSchema::register_format('pve-day-of-week', \&verify_day_of_week);
|
||||
|
||||
sub verify_day_of_week {
|
||||
my ($value, $noerr) = @_;
|
||||
|
||||
|
|
@ -40,16 +41,16 @@ sub assert_param_permission_common {
|
|||
return if $user eq 'root@pam'; # always OK
|
||||
|
||||
for my $key (qw(tmpdir dumpdir script job-id)) {
|
||||
raise_param_exc({ $key => "Only root may set this option."}) if exists $param->{$key};
|
||||
raise_param_exc({ $key => "Only root may set this option." }) if exists $param->{$key};
|
||||
}
|
||||
|
||||
if (grep { defined($param->{$_}) } qw(bwlimit ionice performance)) {
|
||||
$rpcenv->check($user, "/", [ 'Sys.Modify' ]);
|
||||
$rpcenv->check($user, "/", ['Sys.Modify']);
|
||||
}
|
||||
|
||||
if ($param->{fleecing} && !$is_delete) {
|
||||
my $fleecing = PVE::VZDump::parse_fleecing($param) // {};
|
||||
$rpcenv->check($user, "/storage/$fleecing->{storage}", [ 'Datastore.AllocateSpace' ])
|
||||
$rpcenv->check($user, "/storage/$fleecing->{storage}", ['Datastore.AllocateSpace'])
|
||||
if $fleecing->{storage};
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +62,7 @@ my sub assert_param_permission_create {
|
|||
assert_param_permission_common($rpcenv, $user, $param);
|
||||
|
||||
if (my $storeid = PVE::VZDump::get_storage_param($param)) {
|
||||
$rpcenv->check($user, "/storage/$storeid", [ 'Datastore.Allocate' ]);
|
||||
$rpcenv->check($user, "/storage/$storeid", ['Datastore.Allocate']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,9 +74,9 @@ my sub assert_param_permission_update {
|
|||
assert_param_permission_common($rpcenv, $user, $delete, 1);
|
||||
|
||||
if ($update->{storage}) {
|
||||
$rpcenv->check($user, "/storage/$update->{storage}", [ 'Datastore.Allocate' ])
|
||||
$rpcenv->check($user, "/storage/$update->{storage}", ['Datastore.Allocate']);
|
||||
} elsif ($delete->{storage}) {
|
||||
$rpcenv->check($user, "/storage/local", [ 'Datastore.Allocate' ]);
|
||||
$rpcenv->check($user, "/storage/local", ['Datastore.Allocate']);
|
||||
}
|
||||
|
||||
return if !$current; # early check done
|
||||
|
|
@ -84,7 +85,7 @@ my sub assert_param_permission_update {
|
|||
die "only root\@pam may edit jobs with a 'dumpdir' option.";
|
||||
} else {
|
||||
if (my $storeid = PVE::VZDump::get_storage_param($current)) {
|
||||
$rpcenv->check($user, "/storage/$storeid", [ 'Datastore.Allocate' ]);
|
||||
$rpcenv->check($user, "/storage/$storeid", ['Datastore.Allocate']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -139,7 +140,7 @@ __PACKAGE__->register_method({
|
|||
id => get_standard_option('pve-backup-jobid'),
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -178,7 +179,8 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'create_job',
|
||||
|
|
@ -188,7 +190,8 @@ __PACKAGE__->register_method({
|
|||
description => "Create new vzdump backup job.",
|
||||
permissions => {
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
description => "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
|
||||
description =>
|
||||
"The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -200,8 +203,10 @@ __PACKAGE__->register_method({
|
|||
optional => 1, # FIXME: make required on 8.0
|
||||
},
|
||||
schedule => {
|
||||
description => "Backup schedule. The format is a subset of `systemd` calendar events.",
|
||||
type => 'string', format => 'pve-calendar-event',
|
||||
description =>
|
||||
"Backup schedule. The format is a subset of `systemd` calendar events.",
|
||||
type => 'string',
|
||||
format => 'pve-calendar-event',
|
||||
maxLength => 128,
|
||||
optional => 1,
|
||||
},
|
||||
|
|
@ -213,7 +218,8 @@ __PACKAGE__->register_method({
|
|||
optional => 1,
|
||||
},
|
||||
dow => {
|
||||
type => 'string', format => 'pve-day-of-week-list',
|
||||
type => 'string',
|
||||
format => 'pve-day-of-week-list',
|
||||
optional => 1,
|
||||
description => "Day of week selection.",
|
||||
requires => 'starttime',
|
||||
|
|
@ -228,8 +234,9 @@ __PACKAGE__->register_method({
|
|||
'repeat-missed' => {
|
||||
optional => 1,
|
||||
type => 'boolean',
|
||||
description => "If true, the job will be run as soon as possible if it was missed".
|
||||
" while the scheduler was not running.",
|
||||
description =>
|
||||
"If true, the job will be run as soon as possible if it was missed"
|
||||
. " while the scheduler was not running.",
|
||||
default => 0,
|
||||
},
|
||||
comment => {
|
||||
|
|
@ -261,7 +268,10 @@ __PACKAGE__->register_method({
|
|||
# autogenerate id for api compatibility FIXME remove with 8.0
|
||||
my $id = extract_param($param, 'id') // UUID::uuid();
|
||||
|
||||
cfs_lock_file('jobs.cfg', undef, sub {
|
||||
cfs_lock_file(
|
||||
'jobs.cfg',
|
||||
undef,
|
||||
sub {
|
||||
my $data = cfs_read_file('jobs.cfg');
|
||||
|
||||
die "Job '$id' already exists\n"
|
||||
|
|
@ -275,11 +285,13 @@ __PACKAGE__->register_method({
|
|||
PVE::Jobs::create_job($id, 'vzdump', $opts);
|
||||
|
||||
cfs_write_file('jobs.cfg', $data);
|
||||
});
|
||||
},
|
||||
);
|
||||
die "$@" if ($@);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'read_job',
|
||||
|
|
@ -316,7 +328,7 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
my $jobs_data = cfs_read_file('jobs.cfg');
|
||||
my $job = $jobs_data->{ids}->{$param->{id}};
|
||||
my $job = $jobs_data->{ids}->{ $param->{id} };
|
||||
if ($job && $job->{type} eq 'vzdump') {
|
||||
# FIXME remove in PVE 8.0?
|
||||
# backwards compat: before moving the job registry to pve-common, id was auto-injected
|
||||
|
|
@ -326,7 +338,8 @@ __PACKAGE__->register_method({
|
|||
|
||||
raise_param_exc({ id => "No such job '$param->{id}'" });
|
||||
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete_job',
|
||||
|
|
@ -368,7 +381,10 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
if (!$found) {
|
||||
cfs_lock_file('jobs.cfg', undef, sub {
|
||||
cfs_lock_file(
|
||||
'jobs.cfg',
|
||||
undef,
|
||||
sub {
|
||||
my $jobs_data = cfs_read_file('jobs.cfg');
|
||||
|
||||
if (!defined($jobs_data->{ids}->{$id})) {
|
||||
|
|
@ -379,7 +395,8 @@ __PACKAGE__->register_method({
|
|||
PVE::Jobs::remove_job($id, 'vzdump');
|
||||
|
||||
cfs_write_file('jobs.cfg', $jobs_data);
|
||||
});
|
||||
},
|
||||
);
|
||||
die "$@" if $@;
|
||||
} else {
|
||||
$data->{jobs} = $newjobs;
|
||||
|
|
@ -391,7 +408,8 @@ __PACKAGE__->register_method({
|
|||
die "$@" if ($@);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update_job',
|
||||
|
|
@ -401,15 +419,18 @@ __PACKAGE__->register_method({
|
|||
description => "Update vzdump backup job definition.",
|
||||
permissions => {
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
description => "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
|
||||
description =>
|
||||
"The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => PVE::VZDump::Common::json_config_properties({
|
||||
id => get_standard_option('pve-backup-jobid'),
|
||||
schedule => {
|
||||
description => "Backup schedule. The format is a subset of `systemd` calendar events.",
|
||||
type => 'string', format => 'pve-calendar-event',
|
||||
description =>
|
||||
"Backup schedule. The format is a subset of `systemd` calendar events.",
|
||||
type => 'string',
|
||||
format => 'pve-calendar-event',
|
||||
maxLength => 128,
|
||||
optional => 1,
|
||||
},
|
||||
|
|
@ -421,13 +442,15 @@ __PACKAGE__->register_method({
|
|||
optional => 1,
|
||||
},
|
||||
dow => {
|
||||
type => 'string', format => 'pve-day-of-week-list',
|
||||
type => 'string',
|
||||
format => 'pve-day-of-week-list',
|
||||
optional => 1,
|
||||
requires => 'starttime',
|
||||
description => "Day of week selection.",
|
||||
},
|
||||
delete => {
|
||||
type => 'string', format => 'pve-configid-list',
|
||||
type => 'string',
|
||||
format => 'pve-configid-list',
|
||||
description => "A list of settings you want to delete.",
|
||||
optional => 1,
|
||||
},
|
||||
|
|
@ -440,8 +463,9 @@ __PACKAGE__->register_method({
|
|||
'repeat-missed' => {
|
||||
optional => 1,
|
||||
type => 'boolean',
|
||||
description => "If true, the job will be run as soon as possible if it was missed".
|
||||
" while the scheduler was not running.",
|
||||
description =>
|
||||
"If true, the job will be run as soon as possible if it was missed"
|
||||
. " while the scheduler was not running.",
|
||||
default => 0,
|
||||
},
|
||||
comment => {
|
||||
|
|
@ -478,7 +502,8 @@ __PACKAGE__->register_method({
|
|||
|
||||
my $jobs = $data->{jobs} || [];
|
||||
|
||||
die "no options specified\n" if !scalar(keys $param->%*) && !scalar(keys $delete->%*);
|
||||
die "no options specified\n"
|
||||
if !scalar(keys $param->%*) && !scalar(keys $delete->%*);
|
||||
|
||||
PVE::VZDump::verify_vzdump_parameters($param);
|
||||
my $opts = PVE::VZDump::JobBase->check_config($id, $param, 0, 1);
|
||||
|
|
@ -545,19 +570,25 @@ __PACKAGE__->register_method({
|
|||
|
||||
return;
|
||||
};
|
||||
cfs_lock_file('vzdump.cron', undef, sub {
|
||||
cfs_lock_file(
|
||||
'vzdump.cron',
|
||||
undef,
|
||||
sub {
|
||||
cfs_lock_file('jobs.cfg', undef, $update_job);
|
||||
die "$@" if ($@);
|
||||
});
|
||||
},
|
||||
);
|
||||
die "$@" if ($@);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_volume_backup_included',
|
||||
path => '{id}/included_volumes',
|
||||
method => 'GET',
|
||||
protected => 1,
|
||||
description => "Returns included guests and the backup status of their disks. Optimized to be used in ExtJS tree views.",
|
||||
description =>
|
||||
"Returns included guests and the backup status of their disks. Optimized to be used in ExtJS tree views.",
|
||||
permissions => {
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
|
|
@ -569,7 +600,8 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
returns => {
|
||||
type => 'object',
|
||||
description => 'Root node of the tree object. Children represent guests, grandchildren represent volumes of that guest.',
|
||||
description =>
|
||||
'Root node of the tree object. Children represent guests, grandchildren represent volumes of that guest.',
|
||||
properties => {
|
||||
children => {
|
||||
type => 'array',
|
||||
|
|
@ -587,13 +619,15 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
type => {
|
||||
type => 'string',
|
||||
description => 'Type of the guest, VM, CT or unknown for removed but not purged guests.',
|
||||
description =>
|
||||
'Type of the guest, VM, CT or unknown for removed but not purged guests.',
|
||||
enum => ['qemu', 'lxc', 'unknown'],
|
||||
},
|
||||
children => {
|
||||
type => 'array',
|
||||
optional => 1,
|
||||
description => 'The volumes of the guest with the information if they will be included in backups.',
|
||||
description =>
|
||||
'The volumes of the guest with the information if they will be included in backups.',
|
||||
items => {
|
||||
type => 'object',
|
||||
properties => {
|
||||
|
|
@ -607,11 +641,13 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
included => {
|
||||
type => 'boolean',
|
||||
description => 'Whether the volume is included in the backup or not.',
|
||||
description =>
|
||||
'Whether the volume is included in the backup or not.',
|
||||
},
|
||||
reason => {
|
||||
type => 'string',
|
||||
description => 'The reason why the volume is included (or excluded).',
|
||||
description =>
|
||||
'The reason why the volume is included (or excluded).',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -641,7 +677,7 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
if (!$job) {
|
||||
my $jobs_data = cfs_read_file('jobs.cfg');
|
||||
my $j = $jobs_data->{ids}->{$param->{id}};
|
||||
my $j = $jobs_data->{ids}->{ $param->{id} };
|
||||
if ($j && $j->{type} eq 'vzdump') {
|
||||
$job = $j;
|
||||
}
|
||||
|
|
@ -662,7 +698,7 @@ __PACKAGE__->register_method({
|
|||
# remove VMIDs to which the user has no permission to not leak infos
|
||||
# like the guest name
|
||||
my @allowed_vmids = grep {
|
||||
$rpcenv->check($user, "/vms/$_", [ 'VM.Audit' ], 1);
|
||||
$rpcenv->check($user, "/vms/$_", ['VM.Audit'], 1);
|
||||
} @job_vmids;
|
||||
|
||||
my $result = {
|
||||
|
|
@ -678,11 +714,14 @@ __PACKAGE__->register_method({
|
|||
# Since there is no more data available we can only deliver the VMID
|
||||
# and no volumes.
|
||||
if (!defined $vmlist->{ids}->{$vmid}) {
|
||||
push(@{$result->{children}}, {
|
||||
push(
|
||||
@{ $result->{children} },
|
||||
{
|
||||
id => int($vmid),
|
||||
type => 'unknown',
|
||||
leaf => 1,
|
||||
});
|
||||
},
|
||||
);
|
||||
next;
|
||||
}
|
||||
|
||||
|
|
@ -709,8 +748,9 @@ __PACKAGE__->register_method({
|
|||
my $disk = {
|
||||
# id field must be unique for ExtJS tree view
|
||||
id => "$vmid:$volume->{key}",
|
||||
name => $volume->{volume_config}->{file} // $volume->{volume_config}->{volume},
|
||||
included=> $volume->{included},
|
||||
name => $volume->{volume_config}->{file}
|
||||
// $volume->{volume_config}->{volume},
|
||||
included => $volume->{included},
|
||||
reason => $volume->{reason},
|
||||
leaf => 1,
|
||||
};
|
||||
|
|
@ -721,16 +761,20 @@ __PACKAGE__->register_method({
|
|||
# it's possible for a guest to have no volumes configured
|
||||
$leaf = 1 if !@{$children};
|
||||
|
||||
push(@{$result->{children}}, {
|
||||
push(
|
||||
@{ $result->{children} },
|
||||
{
|
||||
id => int($vmid),
|
||||
type => $type,
|
||||
name => $name,
|
||||
children => $children,
|
||||
leaf => $leaf,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -10,18 +10,17 @@ use PVE::API2::Qemu::Machine;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Qemu::CPU",
|
||||
path => 'qemu/cpu',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Qemu::Machine",
|
||||
path => 'qemu/machines',
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -39,7 +38,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -49,11 +48,10 @@ __PACKAGE__->register_method ({
|
|||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'qemu_caps_index',
|
||||
path => 'qemu',
|
||||
method => 'GET',
|
||||
|
|
@ -71,18 +69,17 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $result = [
|
||||
{ name => 'cpu' },
|
||||
{ name => 'machines' },
|
||||
{ name => 'cpu' }, { name => 'machines' },
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
158
PVE/API2/Ceph.pm
158
PVE/API2/Ceph.pm
|
|
@ -29,51 +29,51 @@ use PVE::API2::Storage::Config;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
my $pve_osd_default_journal_size = 1024*5;
|
||||
my $pve_osd_default_journal_size = 1024 * 5;
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Ceph::Cfg",
|
||||
path => 'cfg',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Ceph::OSD",
|
||||
path => 'osd',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Ceph::MDS",
|
||||
path => 'mds',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Ceph::MGR",
|
||||
path => 'mgr',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Ceph::MON",
|
||||
path => 'mon',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Ceph::FS",
|
||||
path => 'fs',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Ceph::Pool",
|
||||
path => 'pool',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
description => "Directory index.",
|
||||
permissions => { user => 'all' },
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -87,7 +87,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -112,9 +112,10 @@ __PACKAGE__->register_method ({
|
|||
];
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'init',
|
||||
path => 'init',
|
||||
method => 'POST',
|
||||
|
|
@ -122,7 +123,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -130,14 +131,16 @@ __PACKAGE__->register_method ({
|
|||
node => get_standard_option('pve-node'),
|
||||
network => {
|
||||
description => "Use specific network for all ceph related traffic",
|
||||
type => 'string', format => 'CIDR',
|
||||
type => 'string',
|
||||
format => 'CIDR',
|
||||
optional => 1,
|
||||
maxLength => 128,
|
||||
},
|
||||
'cluster-network' => {
|
||||
description => "Declare a separate cluster network, OSDs will route" .
|
||||
"heartbeat, object replication and recovery traffic over it",
|
||||
type => 'string', format => 'CIDR',
|
||||
description => "Declare a separate cluster network, OSDs will route"
|
||||
. "heartbeat, object replication and recovery traffic over it",
|
||||
type => 'string',
|
||||
format => 'CIDR',
|
||||
requires => 'network',
|
||||
optional => 1,
|
||||
maxLength => 128,
|
||||
|
|
@ -160,9 +163,9 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
# TODO: deprecrated, remove with PVE 9
|
||||
pg_bits => {
|
||||
description => "Placement group bits, used to specify the " .
|
||||
"default number of placement groups.\n\nDepreacted. This " .
|
||||
"setting was deprecated in recent Ceph versions.",
|
||||
description => "Placement group bits, used to specify the "
|
||||
. "default number of placement groups.\n\nDepreacted. This "
|
||||
. "setting was deprecated in recent Ceph versions.",
|
||||
type => 'integer',
|
||||
default => 6,
|
||||
optional => 1,
|
||||
|
|
@ -170,10 +173,10 @@ __PACKAGE__->register_method ({
|
|||
maximum => 14,
|
||||
},
|
||||
disable_cephx => {
|
||||
description => "Disable cephx authentication.\n\n" .
|
||||
"WARNING: cephx is a security feature protecting against " .
|
||||
"man-in-the-middle attacks. Only consider disabling cephx ".
|
||||
"if your network is private!",
|
||||
description => "Disable cephx authentication.\n\n"
|
||||
. "WARNING: cephx is a security feature protecting against "
|
||||
. "man-in-the-middle attacks. Only consider disabling cephx "
|
||||
. "if your network is private!",
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 0,
|
||||
|
|
@ -193,14 +196,17 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
my $pve_ceph_cfgdir = PVE::Ceph::Tools::get_config('pve_ceph_cfgdir');
|
||||
if (! -d $pve_ceph_cfgdir) {
|
||||
if (!-d $pve_ceph_cfgdir) {
|
||||
File::Path::make_path($pve_ceph_cfgdir);
|
||||
}
|
||||
|
||||
my $auth = $param->{disable_cephx} ? 'none' : 'cephx';
|
||||
|
||||
# simply load old config if it already exists
|
||||
PVE::Cluster::cfs_lock_file('ceph.conf', undef, sub {
|
||||
PVE::Cluster::cfs_lock_file(
|
||||
'ceph.conf',
|
||||
undef,
|
||||
sub {
|
||||
my $cfg = cfs_read_file('ceph.conf');
|
||||
|
||||
if (!$cfg->{global}) {
|
||||
|
|
@ -245,13 +251,15 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Tools::get_or_create_admin_keyring();
|
||||
}
|
||||
PVE::Ceph::Tools::setup_pve_symlinks();
|
||||
});
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'stop',
|
||||
path => 'stop',
|
||||
method => 'POST',
|
||||
|
|
@ -259,7 +267,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -270,7 +278,8 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
optional => 1,
|
||||
default => 'ceph.target',
|
||||
pattern => '(ceph|mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
|
||||
pattern => '(ceph|mon|mds|osd|mgr)(\.'
|
||||
. PVE::Ceph::Services::SERVICE_REGEX . ')?',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -298,11 +307,11 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Services::ceph_service_cmd(@$cmd);
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('srvstop', $param->{service} || 'ceph',
|
||||
$authuser, $worker);
|
||||
}});
|
||||
return $rpcenv->fork_worker('srvstop', $param->{service} || 'ceph', $authuser, $worker);
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'start',
|
||||
path => 'start',
|
||||
method => 'POST',
|
||||
|
|
@ -310,7 +319,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -321,7 +330,8 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
optional => 1,
|
||||
default => 'ceph.target',
|
||||
pattern => '(ceph|mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
|
||||
pattern => '(ceph|mon|mds|osd|mgr)(\.'
|
||||
. PVE::Ceph::Services::SERVICE_REGEX . ')?',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -351,9 +361,10 @@ __PACKAGE__->register_method ({
|
|||
|
||||
return $rpcenv->fork_worker('srvstart', $param->{service} || 'ceph',
|
||||
$authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'restart',
|
||||
path => 'restart',
|
||||
method => 'POST',
|
||||
|
|
@ -361,7 +372,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -372,7 +383,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
optional => 1,
|
||||
default => 'ceph.target',
|
||||
pattern => '(mon|mds|osd|mgr)(\.'.PVE::Ceph::Services::SERVICE_REGEX.')?',
|
||||
pattern => '(mon|mds|osd|mgr)(\.' . PVE::Ceph::Services::SERVICE_REGEX . ')?',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -402,9 +413,10 @@ __PACKAGE__->register_method ({
|
|||
|
||||
return $rpcenv->fork_worker('srvrestart', $param->{service} || 'ceph',
|
||||
$authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'status',
|
||||
path => 'status',
|
||||
method => 'GET',
|
||||
|
|
@ -412,7 +424,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -427,10 +439,10 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Tools::check_ceph_inited();
|
||||
|
||||
return PVE::Ceph::Tools::ceph_cluster_status();
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'crush',
|
||||
path => 'crush',
|
||||
method => 'GET',
|
||||
|
|
@ -438,7 +450,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -463,7 +475,8 @@ __PACKAGE__->register_method ({
|
|||
my $rados = PVE::RADOS->new();
|
||||
|
||||
eval {
|
||||
my $bindata = $rados->mon_command({ prefix => 'osd getcrushmap', format => 'plain' });
|
||||
my $bindata =
|
||||
$rados->mon_command({ prefix => 'osd getcrushmap', format => 'plain' });
|
||||
file_set_contents($mapfile, $bindata);
|
||||
run_command(['crushtool', '-d', $mapfile, '-o', $mapdata]);
|
||||
$txt = file_get_contents($mapdata);
|
||||
|
|
@ -476,7 +489,8 @@ __PACKAGE__->register_method ({
|
|||
die $err if $err;
|
||||
|
||||
return $txt;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'log',
|
||||
|
|
@ -485,7 +499,7 @@ __PACKAGE__->register_method({
|
|||
description => "Read ceph log",
|
||||
proxyto => 'node',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Syslog']],
|
||||
},
|
||||
protected => 1,
|
||||
parameters => {
|
||||
|
|
@ -510,15 +524,15 @@ __PACKAGE__->register_method({
|
|||
type => "object",
|
||||
properties => {
|
||||
n => {
|
||||
description=> "Line number",
|
||||
type=> 'integer',
|
||||
description => "Line number",
|
||||
type => 'integer',
|
||||
},
|
||||
t => {
|
||||
description=> "Line text",
|
||||
description => "Line text",
|
||||
type => 'string',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -530,14 +544,16 @@ __PACKAGE__->register_method({
|
|||
my $node = $param->{node};
|
||||
|
||||
my $logfile = "/var/log/ceph/ceph.log";
|
||||
my ($count, $lines) = PVE::Tools::dump_logfile($logfile, $param->{start}, $param->{limit});
|
||||
my ($count, $lines) =
|
||||
PVE::Tools::dump_logfile($logfile, $param->{start}, $param->{limit});
|
||||
|
||||
$rpcenv->set_result_attrib('total', $count);
|
||||
|
||||
return $lines;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'rules',
|
||||
path => 'rules',
|
||||
method => 'GET',
|
||||
|
|
@ -545,7 +561,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -561,10 +577,10 @@ __PACKAGE__->register_method ({
|
|||
name => {
|
||||
description => "Name of the CRUSH rule.",
|
||||
type => "string",
|
||||
}
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
},
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -582,9 +598,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'cmd_safety',
|
||||
path => 'cmd-safety',
|
||||
method => 'GET',
|
||||
|
|
@ -592,7 +609,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -624,7 +641,7 @@ __PACKAGE__->register_method ({
|
|||
status => {
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
description => 'Status message given by Ceph.'
|
||||
description => 'Status message given by Ceph.',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -668,7 +685,7 @@ __PACKAGE__->register_method ({
|
|||
if ($service eq 'mon' && $action eq 'destroy') {
|
||||
$params->{id} = $id;
|
||||
} else {
|
||||
$params->{ids} = [ $id ];
|
||||
$params->{ids} = [$id];
|
||||
}
|
||||
|
||||
$result = $rados->mon_cmd($params, 1);
|
||||
|
|
@ -678,6 +695,7 @@ __PACKAGE__->register_method ({
|
|||
$result->{status} = $result->{status_message};
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use PVE::Tools qw(file_get_contents);
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -29,27 +29,26 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $result = [
|
||||
{ name => 'raw' },
|
||||
{ name => 'db' },
|
||||
{ name => 'value' },
|
||||
{ name => 'raw' }, { name => 'db' }, { name => 'value' },
|
||||
];
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'raw',
|
||||
path => 'raw',
|
||||
method => 'GET',
|
||||
proxyto => 'node',
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
description => "Get the Ceph configuration file.",
|
||||
parameters => {
|
||||
|
|
@ -67,16 +66,17 @@ __PACKAGE__->register_method ({
|
|||
my $path = PVE::Ceph::Tools::get_config('pve_ceph_cfgpath');
|
||||
return file_get_contents($path);
|
||||
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'db',
|
||||
path => 'db',
|
||||
method => 'GET',
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
description => "Get the Ceph configuration database.",
|
||||
parameters => {
|
||||
|
|
@ -90,11 +90,11 @@ __PACKAGE__->register_method ({
|
|||
items => {
|
||||
type => 'object',
|
||||
properties => {
|
||||
section => { type => "string", },
|
||||
name => { type => "string", },
|
||||
value => { type => "string", },
|
||||
level => { type => "string", },
|
||||
'can_update_at_runtime' => { type => "boolean", },
|
||||
section => { type => "string" },
|
||||
name => { type => "string" },
|
||||
value => { type => "string" },
|
||||
level => { type => "string" },
|
||||
'can_update_at_runtime' => { type => "boolean" },
|
||||
mask => { type => "string" },
|
||||
},
|
||||
},
|
||||
|
|
@ -105,26 +105,26 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Tools::check_ceph_inited();
|
||||
|
||||
my $rados = PVE::RADOS->new();
|
||||
my $res = $rados->mon_command( { prefix => 'config dump', format => 'json' });
|
||||
my $res = $rados->mon_command({ prefix => 'config dump', format => 'json' });
|
||||
foreach my $entry (@$res) {
|
||||
$entry->{can_update_at_runtime} = $entry->{can_update_at_runtime}? 1 : 0; # JSON::true/false -> 1/0
|
||||
$entry->{can_update_at_runtime} = $entry->{can_update_at_runtime} ? 1 : 0; # JSON::true/false -> 1/0
|
||||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
my $SINGLE_CONFIGKEY_RE = qr/[0-9a-z\-_\.]+:[0-9a-zA-Z\-_]+/i;
|
||||
my $CONFIGKEYS_RE = qr/^(:?${SINGLE_CONFIGKEY_RE})(:?[;, ]${SINGLE_CONFIGKEY_RE})*$/;
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'value',
|
||||
path => 'value',
|
||||
method => 'GET',
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
description => "Get configured values from either the config file or config DB.",
|
||||
parameters => {
|
||||
|
|
@ -136,7 +136,7 @@ __PACKAGE__->register_method ({
|
|||
typetext => "<section>:<config key>[;<section>:<config key>]",
|
||||
pattern => $CONFIGKEYS_RE,
|
||||
description => "List of <section>:<config key> items.",
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -167,9 +167,9 @@ __PACKAGE__->register_method ({
|
|||
my $config = {};
|
||||
|
||||
my $rados = PVE::RADOS->new();
|
||||
my $configdb = $rados->mon_command( { prefix => 'config dump', format => 'json' });
|
||||
my $configdb = $rados->mon_command({ prefix => 'config dump', format => 'json' });
|
||||
for my $s (@{$configdb}) {
|
||||
my ($section, $name, $value) = $s->@{'section', 'name', 'value'};
|
||||
my ($section, $name, $value) = $s->@{ 'section', 'name', 'value' };
|
||||
my $n_section = $normalize->($section);
|
||||
my $n_name = $normalize->($name);
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ __PACKAGE__->register_method ({
|
|||
my $n_section = $normalize->($section);
|
||||
next if !defined $requested_keys->{$n_section};
|
||||
|
||||
for my $key (keys %{$config_file->{$section}}) {
|
||||
for my $key (keys %{ $config_file->{$section} }) {
|
||||
my $n_key = $normalize->($key);
|
||||
$config->{$n_section}->{$n_key} = $config_file->{$section}->{$key}
|
||||
if $requested_keys->{$n_section}->{$n_key};
|
||||
|
|
@ -191,4 +191,5 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $config;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ use PVE::API2::Storage::Config;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
proxyto => 'node',
|
||||
description => "Directory index.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
protected => 1,
|
||||
parameters => {
|
||||
|
|
@ -50,7 +50,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -62,18 +62,18 @@ __PACKAGE__->register_method ({
|
|||
my $cephfs_list = PVE::Ceph::Tools::ls_fs($rados);
|
||||
|
||||
my $res = [
|
||||
map {{
|
||||
map { {
|
||||
name => $_->{name},
|
||||
metadata_pool => $_->{metadata_pool},
|
||||
data_pool => $_->{data_pools}->[0],
|
||||
}} @$cephfs_list
|
||||
} } @$cephfs_list
|
||||
];
|
||||
|
||||
return $res;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'createfs',
|
||||
path => '{name}',
|
||||
method => 'POST',
|
||||
|
|
@ -81,7 +81,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -95,7 +95,8 @@ __PACKAGE__->register_method ({
|
|||
pattern => qr|^[^:/\s]+$|,
|
||||
},
|
||||
pg_num => {
|
||||
description => "Number of placement groups for the backing data pool. The metadata pool will use a quarter of this.",
|
||||
description =>
|
||||
"Number of placement groups for the backing data pool. The metadata pool will use a quarter of this.",
|
||||
type => 'integer',
|
||||
default => 128,
|
||||
optional => 1,
|
||||
|
|
@ -204,10 +205,11 @@ __PACKAGE__->register_method ({
|
|||
storage => $fs_name,
|
||||
content => 'backup,iso,vztmpl',
|
||||
'fs-name' => $fs_name,
|
||||
})
|
||||
});
|
||||
};
|
||||
die "adding storage for CephFS '$fs_name' failed, check log ".
|
||||
"and add manually!\n$@\n" if $@;
|
||||
die "adding storage for CephFS '$fs_name' failed, check log "
|
||||
. "and add manually!\n$@\n"
|
||||
if $@;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -215,7 +217,7 @@ __PACKAGE__->register_method ({
|
|||
my $user = $rpcenv->get_user();
|
||||
|
||||
return $rpcenv->fork_worker('cephfscreate', $fs_name, $user, $worker);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ use PVE::RPCEnvironment;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
description => "MDS directory index.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
|
|
@ -53,7 +53,8 @@ __PACKAGE__->register_method ({
|
|||
standby_replay => {
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
description => 'If true, the standby MDS is polling the active MDS for faster recovery (hot standby).',
|
||||
description =>
|
||||
'If true, the standby MDS is polling the active MDS for faster recovery (hot standby).',
|
||||
},
|
||||
rank => {
|
||||
type => 'integer',
|
||||
|
|
@ -61,7 +62,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -83,10 +84,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return PVE::RESTHandler::hash_to_array($mds_hash, 'name');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'createmds',
|
||||
path => '{name}',
|
||||
method => 'POST',
|
||||
|
|
@ -94,7 +95,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -112,8 +113,9 @@ __PACKAGE__->register_method ({
|
|||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => '0',
|
||||
description => "Determines whether a ceph-mds daemon should poll and replay the log of an active MDS. ".
|
||||
"Faster switch on MDS failure, but needs more idle resources.",
|
||||
description =>
|
||||
"Determines whether a ceph-mds daemon should poll and replay the log of an active MDS. "
|
||||
. "Faster switch on MDS failure, but needs more idle resources.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -144,7 +146,7 @@ __PACKAGE__->register_method ({
|
|||
my $section = "mds.$mds_id";
|
||||
|
||||
if (defined($cfg->{$section})) {
|
||||
die "MDS '$mds_id' already referenced in ceph config, abort!\n"
|
||||
die "MDS '$mds_id' already referenced in ceph config, abort!\n";
|
||||
}
|
||||
|
||||
if (!defined($cfg->{mds}->{keyring})) {
|
||||
|
|
@ -178,10 +180,10 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephcreatemds', "mds.$mds_id", $authuser, $worker);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'destroymds',
|
||||
path => '{name}',
|
||||
method => 'DELETE',
|
||||
|
|
@ -189,7 +191,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -229,7 +231,7 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephdestroymds', "mds.$mds_id", $authuser, $worker);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ use PVE::Tools qw(run_command);
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
description => "MGR directory index.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
|
|
@ -53,7 +53,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -72,15 +72,15 @@ __PACKAGE__->register_method ({
|
|||
my $active_name = $mgr_dump->{active_name};
|
||||
$mgr_hash->{$active_name}->{state} = 'active' if $active_name;
|
||||
|
||||
foreach my $mgr (@{$mgr_dump->{standbys}}) {
|
||||
$mgr_hash->{$mgr->{name}}->{state} = 'standby';
|
||||
foreach my $mgr (@{ $mgr_dump->{standbys} }) {
|
||||
$mgr_hash->{ $mgr->{name} }->{state} = 'standby';
|
||||
}
|
||||
|
||||
return PVE::RESTHandler::hash_to_array($mgr_hash, 'name');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'createmgr',
|
||||
path => '{id}',
|
||||
method => 'POST',
|
||||
|
|
@ -88,7 +88,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -126,9 +126,10 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephcreatemgr', "mgr.$mgrid", $authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'destroymgr',
|
||||
path => '{id}',
|
||||
method => 'DELETE',
|
||||
|
|
@ -136,7 +137,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -168,4 +169,5 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephdestroymgr', "mgr.$mgrid", $authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,13 +23,17 @@ use base qw(PVE::RESTHandler);
|
|||
my $find_mon_ips = sub {
|
||||
my ($cfg, $rados, $node, $mon_address) = @_;
|
||||
|
||||
my $overwrite_ips = [ PVE::Tools::split_list($mon_address) ];
|
||||
my $overwrite_ips = [PVE::Tools::split_list($mon_address)];
|
||||
$overwrite_ips = PVE::Network::unique_ips($overwrite_ips);
|
||||
|
||||
my $pubnet;
|
||||
if ($rados) {
|
||||
$pubnet = $rados->mon_command({ prefix => "config get" , who => "mon.",
|
||||
key => "public_network", format => 'plain' });
|
||||
$pubnet = $rados->mon_command({
|
||||
prefix => "config get",
|
||||
who => "mon.",
|
||||
key => "public_network",
|
||||
format => 'plain',
|
||||
});
|
||||
# if not defined in the db, the result is empty, it is also always
|
||||
# followed by a newline
|
||||
($pubnet) = $pubnet =~ m/^(\S+)$/;
|
||||
|
|
@ -42,11 +46,11 @@ my $find_mon_ips = sub {
|
|||
} else {
|
||||
# don't refactor into '[ PVE::Cluster::remote... ]' as it uses wantarray
|
||||
my $ip = PVE::Cluster::remote_node_ip($node);
|
||||
return [ $ip ];
|
||||
return [$ip];
|
||||
}
|
||||
}
|
||||
|
||||
my $public_nets = [ PVE::Tools::split_list($pubnet) ];
|
||||
my $public_nets = [PVE::Tools::split_list($pubnet)];
|
||||
if (scalar(@{$public_nets}) > 1) {
|
||||
warn "Multiple Ceph public networks detected on $node: $pubnet\n";
|
||||
warn "Networks must be capable of routing to each other.\n";
|
||||
|
|
@ -59,21 +63,23 @@ my $find_mon_ips = sub {
|
|||
my $allowed_ips = PVE::Network::get_local_ip_from_cidr($net);
|
||||
$allowed_ips = PVE::Network::unique_ips($allowed_ips);
|
||||
|
||||
die "No active IP found for the requested ceph public network '$net' on node '$node'\n"
|
||||
die
|
||||
"No active IP found for the requested ceph public network '$net' on node '$node'\n"
|
||||
if scalar(@$allowed_ips) < 1;
|
||||
|
||||
if (scalar(@$allowed_ips) == 1) {
|
||||
push @{$res}, $allowed_ips->[0];
|
||||
} else {
|
||||
die "Multiple IPs for ceph public network '$net' detected on $node:\n".
|
||||
join("\n", @$allowed_ips) ."\nuse 'mon-address' to specify one of them.\n";
|
||||
die "Multiple IPs for ceph public network '$net' detected on $node:\n"
|
||||
. join("\n", @$allowed_ips)
|
||||
. "\nuse 'mon-address' to specify one of them.\n";
|
||||
}
|
||||
}
|
||||
} else { # check if overwrite IPs are active and in any of the public networks
|
||||
my $allowed_list = [];
|
||||
|
||||
for my $net (@{$public_nets}) {
|
||||
push @{$allowed_list}, @{PVE::Network::get_local_ip_from_cidr($net)};
|
||||
push @{$allowed_list}, @{ PVE::Network::get_local_ip_from_cidr($net) };
|
||||
}
|
||||
|
||||
my $allowed_ips = PVE::Network::unique_ips($allowed_list);
|
||||
|
|
@ -148,13 +154,15 @@ my $assert_mon_can_remove = sub {
|
|||
my ($monhash, $monlist, $monid, $mondir) = @_;
|
||||
|
||||
if (
|
||||
!defined($monhash->{$monid} ||
|
||||
grep { defined($_->{name}) && $_->{name} eq $monid } $monlist->@*)
|
||||
!defined(
|
||||
$monhash->{$monid}
|
||||
|| grep { defined($_->{name}) && $_->{name} eq $monid } $monlist->@*
|
||||
)
|
||||
) {
|
||||
die "no such monitor id '$monid'\n"
|
||||
die "no such monitor id '$monid'\n";
|
||||
}
|
||||
|
||||
die "monitor filesystem '$mondir' does not exist on this node\n" if ! -d $mondir;
|
||||
die "monitor filesystem '$mondir' does not exist on this node\n" if !-d $mondir;
|
||||
die "can't remove last monitor\n" if scalar($monlist->@*) <= 1;
|
||||
};
|
||||
|
||||
|
|
@ -188,7 +196,7 @@ my $remove_addr_from_mon_host = sub {
|
|||
return $monhost;
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'listmon',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -196,7 +204,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -221,7 +229,7 @@ __PACKAGE__->register_method ({
|
|||
state => { type => 'string', optional => 1 },
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -245,7 +253,7 @@ __PACKAGE__->register_method ({
|
|||
my $name = $d->{name};
|
||||
$monhash->{$name}->{rank} = $d->{rank};
|
||||
$monhash->{$name}->{addr} = $d->{addr};
|
||||
if (grep { $_ eq $d->{rank} } @{$monstat->{quorum}}) {
|
||||
if (grep { $_ eq $d->{rank} } @{ $monstat->{quorum} }) {
|
||||
$monhash->{$name}->{quorum} = 1;
|
||||
$monhash->{$name}->{state} = 'running';
|
||||
}
|
||||
|
|
@ -260,9 +268,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return PVE::RESTHandler::hash_to_array($monhash, 'name');
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'createmon',
|
||||
path => '{monid}',
|
||||
method => 'POST',
|
||||
|
|
@ -270,7 +279,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -284,9 +293,10 @@ __PACKAGE__->register_method ({
|
|||
description => "The ID for the monitor, when omitted the same as the nodename",
|
||||
},
|
||||
'mon-address' => {
|
||||
description => 'Overwrites autodetected monitor IP address(es). ' .
|
||||
'Must be in the public network(s) of Ceph.',
|
||||
type => 'string', format => 'ip-list',
|
||||
description => 'Overwrites autodetected monitor IP address(es). '
|
||||
. 'Must be in the public network(s) of Ceph.',
|
||||
type => 'string',
|
||||
format => 'ip-list',
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
|
|
@ -321,12 +331,16 @@ __PACKAGE__->register_method ({
|
|||
my $worker = sub {
|
||||
my $upid = shift;
|
||||
|
||||
PVE::Cluster::cfs_lock_file('ceph.conf', undef, sub {
|
||||
PVE::Cluster::cfs_lock_file(
|
||||
'ceph.conf',
|
||||
undef,
|
||||
sub {
|
||||
# update cfg content and reassert prereqs inside the lock
|
||||
$cfg = cfs_read_file('ceph.conf');
|
||||
# reopen with longer timeout
|
||||
if (defined($rados)) {
|
||||
$rados = PVE::RADOS->new(timeout => PVE::Ceph::Tools::get_config('long_rados_timeout'));
|
||||
$rados = PVE::RADOS->new(
|
||||
timeout => PVE::Ceph::Tools::get_config('long_rados_timeout'));
|
||||
}
|
||||
$monhash = PVE::Ceph::Services::get_services_info('mon', $cfg, $rados);
|
||||
$assert_mon_prerequisites->($cfg, $monhash, $monid, $ips);
|
||||
|
|
@ -334,7 +348,7 @@ __PACKAGE__->register_method ({
|
|||
my $client_keyring = PVE::Ceph::Tools::get_or_create_admin_keyring();
|
||||
my $mon_keyring = PVE::Ceph::Tools::get_config('pve_mon_key_path');
|
||||
|
||||
if (! -f $mon_keyring) {
|
||||
if (!-f $mon_keyring) {
|
||||
print "creating new monitor keyring\n";
|
||||
run_command([
|
||||
'ceph-authtool',
|
||||
|
|
@ -348,10 +362,7 @@ __PACKAGE__->register_method ({
|
|||
'allow *',
|
||||
]);
|
||||
run_command([
|
||||
'ceph-authtool',
|
||||
$mon_keyring,
|
||||
'--import-keyring',
|
||||
$client_keyring,
|
||||
'ceph-authtool', $mon_keyring, '--import-keyring', $client_keyring,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -397,7 +408,8 @@ __PACKAGE__->register_method ({
|
|||
];
|
||||
|
||||
if (defined($rados)) { # we can only have a RADOS object if we have a monitor
|
||||
my $mapdata = $rados->mon_command({ prefix => 'mon getmap', format => 'plain' });
|
||||
my $mapdata =
|
||||
$rados->mon_command({ prefix => 'mon getmap', format => 'plain' });
|
||||
file_set_contents($monmap, $mapdata);
|
||||
run_command($monmaptool_cmd);
|
||||
} else { # we need to create a monmap for the first monitor
|
||||
|
|
@ -442,20 +454,29 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Services::ceph_service_cmd('start', $monsection);
|
||||
|
||||
if ($is_first_monitor) {
|
||||
print "created the first monitor, assume it's safe to disable insecure global"
|
||||
." ID reclaim for new setup\n";
|
||||
print
|
||||
"created the first monitor, assume it's safe to disable insecure global"
|
||||
. " ID reclaim for new setup\n";
|
||||
eval {
|
||||
run_command(
|
||||
['ceph', 'config', 'set', 'mon', 'auth_allow_insecure_global_id_reclaim', 'false'],
|
||||
[
|
||||
'ceph',
|
||||
'config',
|
||||
'set',
|
||||
'mon',
|
||||
'auth_allow_insecure_global_id_reclaim',
|
||||
'false',
|
||||
],
|
||||
errfunc => sub { print STDERR "$_[0]\n" },
|
||||
)
|
||||
);
|
||||
};
|
||||
warn "$@" if $@;
|
||||
|
||||
print "Configuring keyring for ceph-crash.service\n";
|
||||
eval {
|
||||
PVE::Ceph::Tools::create_or_update_crash_keyring_file();
|
||||
$cfg->{'client.crash'}->{keyring} = '/etc/pve/ceph/$cluster.$name.keyring';
|
||||
$cfg->{'client.crash'}->{keyring} =
|
||||
'/etc/pve/ceph/$cluster.$name.keyring';
|
||||
cfs_write_file('ceph.conf', $cfg);
|
||||
};
|
||||
warn "Unable to configure keyring for ceph-crash.service: $@" if $@;
|
||||
|
|
@ -465,21 +486,23 @@ __PACKAGE__->register_method ({
|
|||
warn "Enable ceph-mon\@${monid}.service failed, do manually: $@\n" if $@;
|
||||
|
||||
PVE::Ceph::Services::broadcast_ceph_services();
|
||||
});
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
# automatically create manager after the first monitor is created
|
||||
if ($is_first_monitor) {
|
||||
PVE::API2::Ceph::MGR->createmgr({
|
||||
node => $param->{node},
|
||||
id => $param->{node}
|
||||
})
|
||||
id => $param->{node},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'destroymon',
|
||||
path => '{monid}',
|
||||
method => 'DELETE',
|
||||
|
|
@ -487,7 +510,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -527,12 +550,16 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $worker = sub {
|
||||
my $upid = shift;
|
||||
PVE::Cluster::cfs_lock_file('ceph.conf', undef, sub {
|
||||
PVE::Cluster::cfs_lock_file(
|
||||
'ceph.conf',
|
||||
undef,
|
||||
sub {
|
||||
# reload info and recheck
|
||||
$cfg = cfs_read_file('ceph.conf');
|
||||
|
||||
# reopen with longer timeout
|
||||
$rados = PVE::RADOS->new(timeout => PVE::Ceph::Tools::get_config('long_rados_timeout'));
|
||||
$rados = PVE::RADOS->new(
|
||||
timeout => PVE::Ceph::Tools::get_config('long_rados_timeout'));
|
||||
$monhash = PVE::Ceph::Services::get_services_info('mon', $cfg, $rados);
|
||||
$monstat = $rados->mon_command({ prefix => 'quorum_status' });
|
||||
$monlist = $monstat->{monmap}->{mons};
|
||||
|
|
@ -565,7 +592,8 @@ __PACKAGE__->register_method ({
|
|||
$assert_mon_can_remove->($monhash, $monlist, $monid, $mondir);
|
||||
|
||||
# this also stops the service
|
||||
$rados->mon_command({ prefix => "mon remove", name => $monid, format => 'plain' });
|
||||
$rados->mon_command(
|
||||
{ prefix => "mon remove", name => $monid, format => 'plain' });
|
||||
|
||||
# delete section
|
||||
delete $cfg->{$monsection};
|
||||
|
|
@ -584,7 +612,8 @@ __PACKAGE__->register_method ({
|
|||
for my $mon_host_ip (@{$mon_host_ips}) {
|
||||
# match canonical addresses, but remove as present in mon_host
|
||||
if (PVE::Network::canonical_ip($mon_host_ip) eq $addr) {
|
||||
$monhost = $remove_addr_from_mon_host->($monhost, $mon_host_ip);
|
||||
$monhost =
|
||||
$remove_addr_from_mon_host->($monhost, $mon_host_ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -597,12 +626,14 @@ __PACKAGE__->register_method ({
|
|||
eval { PVE::Ceph::Services::ceph_service_cmd('disable', $monsection) };
|
||||
warn $@ if $@;
|
||||
PVE::Ceph::Services::broadcast_ceph_services();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
die $@ if $@;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephdestroymon', $monsection, $authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ my $get_osd_status = sub {
|
|||
|
||||
my $osdstat;
|
||||
foreach my $d (@$osdlist) {
|
||||
$osdstat->{$d->{osd}} = $d if defined($d->{osd});
|
||||
$osdstat->{ $d->{osd} } = $d if defined($d->{osd});
|
||||
}
|
||||
if (defined($osdid)) {
|
||||
die "no such OSD '$osdid'\n" if !$osdstat->{$osdid};
|
||||
|
|
@ -52,7 +52,7 @@ my $get_osd_status = sub {
|
|||
my $get_osd_usage = sub {
|
||||
my ($rados) = @_;
|
||||
|
||||
my $osdlist = $rados->mon_command({ prefix => 'pg dump', dumpcontents => [ 'osds' ]});
|
||||
my $osdlist = $rados->mon_command({ prefix => 'pg dump', dumpcontents => ['osds'] });
|
||||
if (!($osdlist && ref($osdlist))) {
|
||||
warn "got unknown result format for 'pg dump osds' command\n";
|
||||
return [];
|
||||
|
|
@ -64,7 +64,7 @@ my $get_osd_usage = sub {
|
|||
|
||||
my $osdstat = {};
|
||||
for my $d (@$osdlist) {
|
||||
$osdstat->{$d->{osd}} = $d if defined($d->{osd});
|
||||
$osdstat->{ $d->{osd} } = $d if defined($d->{osd});
|
||||
}
|
||||
|
||||
return $osdstat;
|
||||
|
|
@ -74,7 +74,7 @@ my sub get_proc_pss_from_pid {
|
|||
my ($pid) = @_;
|
||||
return if !defined($pid) || $pid <= 1;
|
||||
|
||||
open (my $SMAPS_FH, '<', "/proc/$pid/smaps_rollup")
|
||||
open(my $SMAPS_FH, '<', "/proc/$pid/smaps_rollup")
|
||||
or die "failed to open PSS memory-stat from process - $!\n";
|
||||
|
||||
while (my $line = <$SMAPS_FH>) {
|
||||
|
|
@ -87,8 +87,7 @@ my sub get_proc_pss_from_pid {
|
|||
die "internal error: failed to find PSS memory-stat in procfs for PID $pid\n";
|
||||
}
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -96,7 +95,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -124,7 +123,7 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Tools::check_ceph_inited();
|
||||
|
||||
my $rados = PVE::RADOS->new();
|
||||
my $res = $rados->mon_command({ prefix => 'osd df', output_method => 'tree', });
|
||||
my $res = $rados->mon_command({ prefix => 'osd df', output_method => 'tree' });
|
||||
|
||||
die "no tree nodes found\n" if !($res && $res->{nodes});
|
||||
|
||||
|
|
@ -139,7 +138,7 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $nodes = {};
|
||||
my $newnodes = {};
|
||||
foreach my $e (@{$res->{nodes}}) {
|
||||
foreach my $e (@{ $res->{nodes} }) {
|
||||
my ($id, $name) = $e->@{qw(id name)};
|
||||
|
||||
$nodes->{$id} = $e;
|
||||
|
|
@ -147,7 +146,7 @@ __PACKAGE__->register_method ({
|
|||
my $new = {
|
||||
id => $id,
|
||||
name => $name,
|
||||
type => $e->{type}
|
||||
type => $e->{type},
|
||||
};
|
||||
|
||||
foreach my $opt (qw(status crush_weight reweight device_class pgs)) {
|
||||
|
|
@ -161,7 +160,7 @@ __PACKAGE__->register_method ({
|
|||
if (my $stat = $osd_usage->{$id}) {
|
||||
$new->{total_space} = ($stat->{kb} || 1) * 1024;
|
||||
$new->{bytes_used} = ($stat->{kb_used} || 0) * 1024;
|
||||
$new->{percent_used} = ($new->{bytes_used}*100)/$new->{total_space};
|
||||
$new->{percent_used} = ($new->{bytes_used} * 100) / $new->{total_space};
|
||||
if (my $d = $stat->{perf_stat}) {
|
||||
$new->{commit_latency_ms} = $d->{commit_latency_ms};
|
||||
$new->{apply_latency_ms} = $d->{apply_latency_ms};
|
||||
|
|
@ -186,19 +185,19 @@ __PACKAGE__->register_method ({
|
|||
$newnodes->{$id} = $new;
|
||||
}
|
||||
|
||||
foreach my $e (@{$res->{nodes}}) {
|
||||
foreach my $e (@{ $res->{nodes} }) {
|
||||
my ($id, $name) = $e->@{qw(id name)};
|
||||
my $new = $newnodes->{$id};
|
||||
|
||||
if ($e->{children} && scalar(@{$e->{children}})) {
|
||||
if ($e->{children} && scalar(@{ $e->{children} })) {
|
||||
$new->{children} = [];
|
||||
$new->{leaf} = 0;
|
||||
foreach my $cid (@{$e->{children}}) {
|
||||
foreach my $cid (@{ $e->{children} }) {
|
||||
$nodes->{$cid}->{parent} = $id;
|
||||
if ($nodes->{$cid}->{type} eq 'osd' && $e->{type} eq 'host') {
|
||||
$newnodes->{$cid}->{host} = $name;
|
||||
}
|
||||
push @{$new->{children}}, $newnodes->{$cid};
|
||||
push @{ $new->{children} }, $newnodes->{$cid};
|
||||
}
|
||||
} else {
|
||||
$new->{leaf} = ($id >= 0) ? 1 : 0;
|
||||
|
|
@ -210,7 +209,7 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
my $realroots = [];
|
||||
foreach my $e (@{$res->{nodes}}) {
|
||||
foreach my $e (@{ $res->{nodes} }) {
|
||||
my $id = $e->{id};
|
||||
if (!$nodes->{$id}->{parent}) {
|
||||
push @$realroots, $newnodes->{$id};
|
||||
|
|
@ -222,16 +221,17 @@ __PACKAGE__->register_method ({
|
|||
my $data = {
|
||||
root => {
|
||||
leaf => 0,
|
||||
children => $realroots
|
||||
children => $realroots,
|
||||
},
|
||||
};
|
||||
|
||||
$data->{flags} = $flags if $flags; # we want this for the noout flag
|
||||
|
||||
return $data;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'createosd',
|
||||
path => '',
|
||||
method => 'POST',
|
||||
|
|
@ -253,11 +253,12 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
db_dev_size => {
|
||||
description => "Size in GiB for block.db.",
|
||||
verbose_description => "If a block.db is requested but the size is not given, will"
|
||||
." be automatically selected by: bluestore_block_db_size from the ceph database"
|
||||
." (osd or global section) or config (osd or global section) in that order."
|
||||
." If this is not available, it will be sized 10% of the size of the OSD device."
|
||||
." Fails if the available size is not enough.",
|
||||
verbose_description =>
|
||||
"If a block.db is requested but the size is not given, will"
|
||||
. " be automatically selected by: bluestore_block_db_size from the ceph database"
|
||||
. " (osd or global section) or config (osd or global section) in that order."
|
||||
. " If this is not available, it will be sized 10% of the size of the OSD device."
|
||||
. " Fails if the available size is not enough.",
|
||||
optional => 1,
|
||||
type => 'number',
|
||||
default => 'bluestore_block_db_size or 10% of OSD size',
|
||||
|
|
@ -271,11 +272,12 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
wal_dev_size => {
|
||||
description => "Size in GiB for block.wal.",
|
||||
verbose_description => "If a block.wal is requested but the size is not given, will"
|
||||
." be automatically selected by: bluestore_block_wal_size from the ceph database"
|
||||
." (osd or global section) or config (osd or global section) in that order."
|
||||
." If this is not available, it will be sized 1% of the size of the OSD device."
|
||||
." Fails if the available size is not enough.",
|
||||
verbose_description =>
|
||||
"If a block.wal is requested but the size is not given, will"
|
||||
. " be automatically selected by: bluestore_block_wal_size from the ceph database"
|
||||
. " (osd or global section) or config (osd or global section) in that order."
|
||||
. " If this is not available, it will be sized 1% of the size of the OSD device."
|
||||
. " Fails if the available size is not enough.",
|
||||
optional => 1,
|
||||
minimum => 0.5,
|
||||
default => 'bluestore_block_wal_size or 1% of OSD size',
|
||||
|
|
@ -286,18 +288,19 @@ __PACKAGE__->register_method ({
|
|||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 0,
|
||||
description => "Enables encryption of the OSD."
|
||||
description => "Enables encryption of the OSD.",
|
||||
},
|
||||
'crush-device-class' => {
|
||||
optional => 1,
|
||||
type => 'string',
|
||||
description => "Set the device class of the OSD in crush."
|
||||
description => "Set the device class of the OSD in crush.",
|
||||
},
|
||||
'osds-per-device' => {
|
||||
optional => 1,
|
||||
type => 'integer',
|
||||
minimum => '1',
|
||||
description => 'OSD services per physical device. Only useful for fast NVMe devices"
|
||||
description =>
|
||||
'OSD services per physical device. Only useful for fast NVMe devices"
|
||||
." to utilize their performance better.',
|
||||
},
|
||||
},
|
||||
|
|
@ -322,8 +325,9 @@ __PACKAGE__->register_method ({
|
|||
# allow 'osds-per-device' only without dedicated db and/or wal devs. We cannot specify them with
|
||||
# 'ceph-volume lvm batch' and they don't make a lot of sense on fast NVMEs anyway.
|
||||
if ($param->{'osds-per-device'}) {
|
||||
for my $type ( qw(db_dev wal_dev) ) {
|
||||
raise_param_exc({ $type => "cannot use 'osds-per-device' parameter with '${type}'" })
|
||||
for my $type (qw(db_dev wal_dev)) {
|
||||
raise_param_exc(
|
||||
{ $type => "cannot use 'osds-per-device' parameter with '${type}'" })
|
||||
if $param->{$type};
|
||||
}
|
||||
}
|
||||
|
|
@ -339,12 +343,13 @@ __PACKAGE__->register_method ({
|
|||
my $osd_net_obj = PVE::Network::IP_from_cidr($osd_network);
|
||||
my $osd_base_cidr = $osd_net_obj->{ip} . "/" . $osd_net_obj->{prefixlen};
|
||||
|
||||
die "No address from ceph cluster network (${osd_base_cidr}) found on node '$nodename'. ".
|
||||
"Check your network config.\n";
|
||||
die
|
||||
"No address from ceph cluster network (${osd_base_cidr}) found on node '$nodename'. "
|
||||
. "Check your network config.\n";
|
||||
}
|
||||
}
|
||||
|
||||
for my $type ( qw(dev db_dev wal_dev) ) {
|
||||
for my $type (qw(dev db_dev wal_dev)) {
|
||||
next if !$param->{$type};
|
||||
|
||||
my $type_dev = PVE::Diskmanage::verify_blockdev_path($param->{$type});
|
||||
|
|
@ -359,7 +364,7 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
if (my $size = $param->{"${type}_size"}) {
|
||||
$devs->{$type}->{size} = PVE::Tools::convert_size($size, 'gb' => 'b') ;
|
||||
$devs->{$type}->{size} = PVE::Tools::convert_size($size, 'gb' => 'b');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -371,12 +376,13 @@ __PACKAGE__->register_method ({
|
|||
die "unable to get device info for '$dev'\n" if !$disklist->{$devname};
|
||||
die "device '$dev' is already in use\n" if $disklist->{$devname}->{used};
|
||||
|
||||
for my $type ( qw(db_dev wal_dev) ) {
|
||||
for my $type (qw(db_dev wal_dev)) {
|
||||
my $d = $devs->{$type};
|
||||
next if !$d;
|
||||
my $name = $d->{name};
|
||||
my $info = $disklist->{$name};
|
||||
die "unable to get device info for '$d->{dev}' for type $type\n" if !$disklist->{$name};
|
||||
die "unable to get device info for '$d->{dev}' for type $type\n"
|
||||
if !$disklist->{$name};
|
||||
if (my $usage = $info->{used}) {
|
||||
if ($usage eq 'partitions') {
|
||||
die "device '$d->{dev}' is not GPT partitioned\n" if !$info->{gpt};
|
||||
|
|
@ -387,9 +393,8 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
# test disk requirements early
|
||||
my $devlist = [ map { $_->{name} } values %$devs ];
|
||||
my $devlist = [map { $_->{name} } values %$devs];
|
||||
my $disklist = PVE::Diskmanage::get_disks($devlist, 1, 1);
|
||||
$test_disk_requirements->($disklist);
|
||||
|
||||
|
|
@ -397,19 +402,23 @@ __PACKAGE__->register_method ({
|
|||
my $rados = PVE::RADOS->new();
|
||||
my $monstat = $rados->mon_command({ prefix => 'quorum_status' });
|
||||
|
||||
my $ceph_bootstrap_osd_keyring = PVE::Ceph::Tools::get_config('ceph_bootstrap_osd_keyring');
|
||||
my $ceph_bootstrap_osd_keyring =
|
||||
PVE::Ceph::Tools::get_config('ceph_bootstrap_osd_keyring');
|
||||
|
||||
if (! -f $ceph_bootstrap_osd_keyring && $ceph_conf->{global}->{auth_client_required} eq 'cephx') {
|
||||
if (
|
||||
!-f $ceph_bootstrap_osd_keyring
|
||||
&& $ceph_conf->{global}->{auth_client_required} eq 'cephx'
|
||||
) {
|
||||
my $bindata = $rados->mon_command({
|
||||
prefix => 'auth get-or-create',
|
||||
entity => 'client.bootstrap-osd',
|
||||
caps => [
|
||||
'mon' => 'allow profile bootstrap-osd'
|
||||
'mon' => 'allow profile bootstrap-osd',
|
||||
],
|
||||
format => 'plain',
|
||||
});
|
||||
file_set_contents($ceph_bootstrap_osd_keyring, $bindata);
|
||||
};
|
||||
}
|
||||
|
||||
# See FIXME below
|
||||
my @udev_trigger_devs = ();
|
||||
|
|
@ -450,10 +459,10 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $vgs = PVE::Storage::LVMPlugin::lvm_vgs(1);
|
||||
my $vg;
|
||||
for my $vgname ( sort keys %$vgs ) {
|
||||
for my $vgname (sort keys %$vgs) {
|
||||
next if $vgname !~ /^ceph-/;
|
||||
|
||||
for my $pv ( @{$vgs->{$vgname}->{pvs}} ) {
|
||||
for my $pv (@{ $vgs->{$vgname}->{pvs} }) {
|
||||
next if $pv->{name} ne $dev->{devpath};
|
||||
$vg = $vgname;
|
||||
last;
|
||||
|
|
@ -503,7 +512,7 @@ __PACKAGE__->register_method ({
|
|||
# create allows for detailed configuration of DB and WAL devices
|
||||
# batch for easy creation of multiple OSDs (per device)
|
||||
my $create_mode = $param->{'osds-per-device'} ? 'batch' : 'create';
|
||||
my $cmd = ['ceph-volume', 'lvm', $create_mode ];
|
||||
my $cmd = ['ceph-volume', 'lvm', $create_mode];
|
||||
push @$cmd, '--crush-device-class', $dev_class if $dev_class;
|
||||
|
||||
my $devname = $devs->{dev}->{name};
|
||||
|
|
@ -519,7 +528,7 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
my $sizes;
|
||||
foreach my $type ( qw(db wal) ) {
|
||||
foreach my $type (qw(db wal)) {
|
||||
my $fallback_size = $size_map->{$type};
|
||||
my $d = $devs->{"${type}_dev"};
|
||||
next if !$d;
|
||||
|
|
@ -531,7 +540,8 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
print "creating block.$type on '$d->{dev}'\n";
|
||||
my $name = $d->{name};
|
||||
my $part_or_lv = $create_part_or_lv->($disklist->{$name}, $d->{size}, "osd-$type");
|
||||
my $part_or_lv =
|
||||
$create_part_or_lv->($disklist->{$name}, $d->{size}, "osd-$type");
|
||||
|
||||
print "using '$part_or_lv' for block.$type\n";
|
||||
push @$cmd, "--block.$type", $part_or_lv;
|
||||
|
|
@ -566,7 +576,8 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephcreateosd', $devs->{dev}->{name}, $authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $OSD_DEV_RETURN_PROPS = {
|
||||
device => {
|
||||
|
|
@ -596,7 +607,7 @@ my $OSD_DEV_RETURN_PROPS = {
|
|||
},
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'osdindex',
|
||||
path => '{osdid}',
|
||||
method => 'GET',
|
||||
|
|
@ -618,20 +629,20 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $result = [
|
||||
{ name => 'metadata' },
|
||||
{ name => 'lv-info' },
|
||||
{ name => 'metadata' }, { name => 'lv-info' },
|
||||
];
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'osddetails',
|
||||
path => '{osdid}/metadata',
|
||||
method => 'GET',
|
||||
|
|
@ -639,7 +650,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -711,8 +722,8 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => $OSD_DEV_RETURN_PROPS,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -735,13 +746,13 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
my $cmd = [
|
||||
'/bin/systemctl',
|
||||
'show',
|
||||
"ceph-osd\@${osdid}.service",
|
||||
'--property',
|
||||
'MainPID',
|
||||
'/bin/systemctl', 'show', "ceph-osd\@${osdid}.service", '--property', 'MainPID',
|
||||
];
|
||||
run_command($cmd, errmsg => 'fetching OSD PID and memory usage failed', outfunc => $parser);
|
||||
run_command(
|
||||
$cmd,
|
||||
errmsg => 'fetching OSD PID and memory usage failed',
|
||||
outfunc => $parser,
|
||||
);
|
||||
|
||||
my $osd_pss_memory = eval { get_proc_pss_from_pid($pid) } // 0;
|
||||
warn $@ if $@;
|
||||
|
|
@ -766,8 +777,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $get_data = sub {
|
||||
my ($dev, $prefix, $device) = @_;
|
||||
push (
|
||||
@{$data->{devices}},
|
||||
push(
|
||||
@{ $data->{devices} },
|
||||
{
|
||||
dev_node => $metadata->{"${prefix}_${dev}_dev_node"},
|
||||
physical_device => $metadata->{"${prefix}_${dev}_devices"},
|
||||
|
|
@ -775,7 +786,7 @@ __PACKAGE__->register_method ({
|
|||
support_discard => int($metadata->{"${prefix}_${dev}_support_discard"}),
|
||||
type => $metadata->{"${prefix}_${dev}_type"},
|
||||
device => $device,
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -784,9 +795,10 @@ __PACKAGE__->register_method ({
|
|||
$get_data->("wal", "bluefs", "wal") if $metadata->{bluefs_dedicated_wal};
|
||||
|
||||
return $data;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'osdvolume',
|
||||
path => '{osdid}/lv-info',
|
||||
method => 'GET',
|
||||
|
|
@ -794,7 +806,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -863,11 +875,12 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
if (!$result->{$osdid}) {
|
||||
die "OSD '${osdid}' not found in 'ceph-volume lvm list' on node '${nodename}'.\n"
|
||||
."Maybe it was created before LVM became the default?\n";
|
||||
. "Maybe it was created before LVM became the default?\n";
|
||||
}
|
||||
|
||||
my $lv_data = { map { $_->{type} => $_ } @{$result->{$osdid}} };
|
||||
my $volume = $lv_data->{$type} || die "volume type '${type}' not found for OSD ${osdid}\n";
|
||||
my $lv_data = { map { $_->{type} => $_ } @{ $result->{$osdid} } };
|
||||
my $volume = $lv_data->{$type}
|
||||
|| die "volume type '${type}' not found for OSD ${osdid}\n";
|
||||
|
||||
$raw = '';
|
||||
$cmd = ['/sbin/lvs', $volume->{lv_path}, '--reportformat', 'json', '-o', 'lv_time'];
|
||||
|
|
@ -882,10 +895,11 @@ __PACKAGE__->register_method ({
|
|||
my $data = { map { $_ => $volume->{$_} } qw(lv_name lv_path lv_uuid vg_name) };
|
||||
$data->{lv_size} = int($volume->{lv_size});
|
||||
|
||||
$data->{creation_time} = @{$result->{report}}[0]->{lv}[0]->{lv_time};
|
||||
$data->{creation_time} = @{ $result->{report} }[0]->{lv}->[0]->{lv_time};
|
||||
|
||||
return $data;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
# Check if $osdid belongs to $nodename
|
||||
# $tree ... rados osd tree (passing the tree makes it easy to test)
|
||||
|
|
@ -894,7 +908,7 @@ sub osd_belongs_to_node {
|
|||
return 0 if !($tree && $tree->{nodes});
|
||||
|
||||
my $node_map = {};
|
||||
for my $el (grep { defined($_->{type}) && $_->{type} eq 'host' } @{$tree->{nodes}}) {
|
||||
for my $el (grep { defined($_->{type}) && $_->{type} eq 'host' } @{ $tree->{nodes} }) {
|
||||
my $name = $el->{name};
|
||||
die "internal error: duplicate host name found '$name'\n" if $node_map->{$name};
|
||||
$node_map->{$name} = $el;
|
||||
|
|
@ -906,7 +920,7 @@ sub osd_belongs_to_node {
|
|||
return grep($_ == $osdid, @$osds);
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'destroyosd',
|
||||
path => '{osdid}',
|
||||
method => 'DELETE',
|
||||
|
|
@ -966,7 +980,8 @@ __PACKAGE__->register_method ({
|
|||
my $upid = shift;
|
||||
|
||||
# reopen with longer timeout
|
||||
$rados = PVE::RADOS->new(timeout => PVE::Ceph::Tools::get_config('long_rados_timeout'));
|
||||
$rados =
|
||||
PVE::RADOS->new(timeout => PVE::Ceph::Tools::get_config('long_rados_timeout'));
|
||||
|
||||
print "destroy OSD $osdsection\n";
|
||||
|
||||
|
|
@ -977,17 +992,32 @@ __PACKAGE__->register_method ({
|
|||
warn $@ if $@;
|
||||
|
||||
print "Remove $osdsection from the CRUSH map\n";
|
||||
$rados->mon_command({ prefix => "osd crush remove", name => $osdsection, format => 'plain' });
|
||||
$rados->mon_command(
|
||||
{ prefix => "osd crush remove", name => $osdsection, format => 'plain' });
|
||||
|
||||
print "Remove the $osdsection authentication key.\n";
|
||||
$rados->mon_command({ prefix => "auth del", entity => $osdsection, format => 'plain' });
|
||||
$rados->mon_command({
|
||||
prefix => "auth del", entity => $osdsection, format => 'plain' });
|
||||
|
||||
print "Remove OSD $osdsection\n";
|
||||
$rados->mon_command({ prefix => "osd rm", ids => [ $osdsection ], format => 'plain' });
|
||||
$rados->mon_command({
|
||||
prefix => "osd rm", ids => [$osdsection], format => 'plain' });
|
||||
|
||||
print "Remove $osdsection mclock max capacity iops settings from config\n";
|
||||
$rados->mon_command({ prefix => "config rm", who => $osdsection, name => 'osd_mclock_max_capacity_iops_ssd' });
|
||||
$rados->mon_command({ prefix => "config rm", who => $osdsection, name => 'osd_mclock_max_capacity_iops_hdd' });
|
||||
$rados->mon_command(
|
||||
{
|
||||
prefix => "config rm",
|
||||
who => $osdsection,
|
||||
name => 'osd_mclock_max_capacity_iops_ssd',
|
||||
},
|
||||
);
|
||||
$rados->mon_command(
|
||||
{
|
||||
prefix => "config rm",
|
||||
who => $osdsection,
|
||||
name => 'osd_mclock_max_capacity_iops_hdd',
|
||||
},
|
||||
);
|
||||
|
||||
# try to unmount from standard mount point
|
||||
my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
|
||||
|
|
@ -998,7 +1028,7 @@ __PACKAGE__->register_method ({
|
|||
my $remove_partition = sub {
|
||||
my ($part) = @_;
|
||||
|
||||
return if !$part || (! -b $part );
|
||||
return if !$part || (!-b $part);
|
||||
my $partnum = PVE::Diskmanage::get_partnum($part);
|
||||
my $devpath = PVE::Diskmanage::get_blockdev($part);
|
||||
|
||||
|
|
@ -1019,11 +1049,13 @@ __PACKAGE__->register_method ({
|
|||
|
||||
if ($cleanup) {
|
||||
# try to remove pvs, but do not fail if it does not work
|
||||
for my $osd_part (@{$osd_list->{$osdid}}) {
|
||||
for my $dev (@{$osd_part->{devices}}) {
|
||||
for my $osd_part (@{ $osd_list->{$osdid} }) {
|
||||
for my $dev (@{ $osd_part->{devices} }) {
|
||||
($dev) = ($dev =~ m|^(/dev/[-_.a-zA-Z0-9\/]+)$|); #untaint
|
||||
|
||||
eval { run_command(['/sbin/pvremove', $dev], errfunc => sub {}) };
|
||||
eval {
|
||||
run_command(['/sbin/pvremove', $dev], errfunc => sub { });
|
||||
};
|
||||
warn $@ if $@;
|
||||
|
||||
$udev_trigger_devs->{$dev} = 1;
|
||||
|
|
@ -1048,7 +1080,8 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
foreach my $path (qw(journal block block.db block.wal)) {
|
||||
abs_path("$mountpoint/$path") =~ m|^(/.+)| or die "invalid path: $path\n";
|
||||
abs_path("$mountpoint/$path") =~ m|^(/.+)|
|
||||
or die "invalid path: $path\n";
|
||||
push @$partitions_to_remove, $1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1075,9 +1108,10 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'in',
|
||||
path => '{osdid}/in',
|
||||
method => 'POST',
|
||||
|
|
@ -1085,7 +1119,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -1111,12 +1145,13 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $osdsection = "osd.$osdid";
|
||||
|
||||
$rados->mon_command({ prefix => "osd in", ids => [ $osdsection ], format => 'plain' });
|
||||
$rados->mon_command({ prefix => "osd in", ids => [$osdsection], format => 'plain' });
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'out',
|
||||
path => '{osdid}/out',
|
||||
method => 'POST',
|
||||
|
|
@ -1124,7 +1159,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -1150,12 +1185,13 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $osdsection = "osd.$osdid";
|
||||
|
||||
$rados->mon_command({ prefix => "osd out", ids => [ $osdsection ], format => 'plain' });
|
||||
$rados->mon_command({ prefix => "osd out", ids => [$osdsection], format => 'plain' });
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'scrub',
|
||||
path => '{osdid}/scrub',
|
||||
method => 'POST',
|
||||
|
|
@ -1163,7 +1199,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -1198,6 +1234,7 @@ __PACKAGE__->register_method ({
|
|||
$rados->mon_command({ prefix => $prefix, who => $osdid });
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -21,27 +21,26 @@ my $get_autoscale_status = sub {
|
|||
|
||||
$rados = PVE::RADOS->new() if !defined($rados);
|
||||
|
||||
my $autoscale = $rados->mon_command({
|
||||
prefix => 'osd pool autoscale-status'});
|
||||
my $autoscale = $rados->mon_command({ prefix => 'osd pool autoscale-status' });
|
||||
|
||||
my $data;
|
||||
foreach my $p (@$autoscale) {
|
||||
$data->{$p->{pool_name}} = $p;
|
||||
$data->{ $p->{pool_name} } = $p;
|
||||
}
|
||||
|
||||
return $data;
|
||||
};
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'lspools',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
description => "List all pools and their settings (which are settable by the POST/PUT endpoints).",
|
||||
description =>
|
||||
"List all pools and their settings (which are settable by the POST/PUT endpoints).",
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -132,7 +131,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{pool_name}" } ],
|
||||
links => [{ rel => 'child', href => "{pool_name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -144,18 +143,18 @@ __PACKAGE__->register_method ({
|
|||
my $stats = {};
|
||||
my $res = $rados->mon_command({ prefix => 'df' });
|
||||
|
||||
foreach my $d (@{$res->{pools}}) {
|
||||
foreach my $d (@{ $res->{pools} }) {
|
||||
next if !$d->{stats};
|
||||
next if !defined($d->{id});
|
||||
$stats->{$d->{id}} = $d->{stats};
|
||||
$stats->{ $d->{id} } = $d->{stats};
|
||||
}
|
||||
|
||||
$res = $rados->mon_command({ prefix => 'osd dump' });
|
||||
my $rulestmp = $rados->mon_command({ prefix => 'osd crush rule dump'});
|
||||
my $rulestmp = $rados->mon_command({ prefix => 'osd crush rule dump' });
|
||||
|
||||
my $rules = {};
|
||||
for my $rule (@$rulestmp) {
|
||||
$rules->{$rule->{rule_id}} = $rule->{rule_name};
|
||||
$rules->{ $rule->{rule_id} } = $rule->{rule_name};
|
||||
}
|
||||
|
||||
my $data = [];
|
||||
|
|
@ -173,14 +172,14 @@ __PACKAGE__->register_method ({
|
|||
# pg_autoscaler module is not enabled in Nautilus
|
||||
my $autoscale = eval { $get_autoscale_status->($rados) };
|
||||
|
||||
foreach my $e (@{$res->{pools}}) {
|
||||
foreach my $e (@{ $res->{pools} }) {
|
||||
my $d = {};
|
||||
foreach my $attr (@$attr_list) {
|
||||
$d->{$attr} = $e->{$attr} if defined($e->{$attr});
|
||||
}
|
||||
|
||||
if ($autoscale) {
|
||||
$d->{autoscale_status} = $autoscale->{$d->{pool_name}};
|
||||
$d->{autoscale_status} = $autoscale->{ $d->{pool_name} };
|
||||
$d->{pg_num_final} = $d->{autoscale_status}->{pg_num_final};
|
||||
# some info is nested under options instead
|
||||
$d->{pg_num_min} = $e->{options}->{pg_num_min};
|
||||
|
|
@ -188,11 +187,11 @@ __PACKAGE__->register_method ({
|
|||
$d->{target_size_ratio} = $e->{options}->{target_size_ratio};
|
||||
}
|
||||
|
||||
if (defined($d->{crush_rule}) && defined($rules->{$d->{crush_rule}})) {
|
||||
$d->{crush_rule_name} = $rules->{$d->{crush_rule}};
|
||||
if (defined($d->{crush_rule}) && defined($rules->{ $d->{crush_rule} })) {
|
||||
$d->{crush_rule_name} = $rules->{ $d->{crush_rule} };
|
||||
}
|
||||
|
||||
if (my $s = $stats->{$d->{pool}}) {
|
||||
if (my $s = $stats->{ $d->{pool} }) {
|
||||
$d->{bytes_used} = $s->{bytes_used};
|
||||
$d->{percent_used} = $s->{percent_used};
|
||||
}
|
||||
|
|
@ -210,10 +209,9 @@ __PACKAGE__->register_method ({
|
|||
push @$data, $d;
|
||||
}
|
||||
|
||||
|
||||
return $data;
|
||||
}});
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
my $ceph_pool_common_options = sub {
|
||||
my ($nodefault) = shift;
|
||||
|
|
@ -301,7 +299,6 @@ my $ceph_pool_common_options = sub {
|
|||
return $options;
|
||||
};
|
||||
|
||||
|
||||
my $add_storage = sub {
|
||||
my ($pool, $storeid, $ec_data_pool) = @_;
|
||||
|
||||
|
|
@ -330,8 +327,8 @@ my $get_storages = sub {
|
|||
next if $curr->{type} ne 'rbd';
|
||||
$curr->{pool} = 'rbd' if !defined $curr->{pool}; # set default
|
||||
if (
|
||||
$pool eq $curr->{pool} ||
|
||||
(defined $curr->{'data-pool'} && $pool eq $curr->{'data-pool'})
|
||||
$pool eq $curr->{pool}
|
||||
|| (defined $curr->{'data-pool'} && $pool eq $curr->{'data-pool'})
|
||||
) {
|
||||
$res->{$storeid} = $storages->{$storeid};
|
||||
}
|
||||
|
|
@ -344,19 +341,19 @@ my $ec_format = {
|
|||
k => {
|
||||
type => 'integer',
|
||||
description => "Number of data chunks. Will create an erasure coded pool plus a"
|
||||
." replicated pool for metadata.",
|
||||
. " replicated pool for metadata.",
|
||||
minimum => 2,
|
||||
},
|
||||
m => {
|
||||
type => 'integer',
|
||||
description => "Number of coding chunks. Will create an erasure coded pool plus a"
|
||||
." replicated pool for metadata.",
|
||||
. " replicated pool for metadata.",
|
||||
minimum => 1,
|
||||
},
|
||||
'failure-domain' => {
|
||||
type => 'string',
|
||||
description => "CRUSH failure domain. Default is 'host'. Will create an erasure"
|
||||
." coded pool plus a replicated pool for metadata.",
|
||||
. " coded pool plus a replicated pool for metadata.",
|
||||
format_description => 'domain',
|
||||
optional => 1,
|
||||
default => 'host',
|
||||
|
|
@ -364,13 +361,13 @@ my $ec_format = {
|
|||
'device-class' => {
|
||||
type => 'string',
|
||||
description => "CRUSH device class. Will create an erasure coded pool plus a"
|
||||
." replicated pool for metadata.",
|
||||
. " replicated pool for metadata.",
|
||||
format_description => 'class',
|
||||
optional => 1,
|
||||
},
|
||||
profile => {
|
||||
description => "Override the erasure code (EC) profile to use. Will create an"
|
||||
." erasure coded pool plus a replicated pool for metadata.",
|
||||
. " erasure coded pool plus a replicated pool for metadata.",
|
||||
type => 'string',
|
||||
format_description => 'profile',
|
||||
optional => 1,
|
||||
|
|
@ -389,8 +386,7 @@ sub ec_parse_and_check {
|
|||
return $ec;
|
||||
}
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'createpool',
|
||||
path => '',
|
||||
method => 'POST',
|
||||
|
|
@ -398,7 +394,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -412,8 +408,8 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
'erasure-coding' => {
|
||||
description => "Create an erasure coded pool for RBD with an accompaning"
|
||||
." replicated pool for metadata storage. With EC, the common ceph options 'size',"
|
||||
." 'min_size' and 'crush_rule' parameters will be applied to the metadata pool.",
|
||||
. " replicated pool for metadata storage. With EC, the common ceph options 'size',"
|
||||
. " 'min_size' and 'crush_rule' parameters will be applied to the metadata pool.",
|
||||
type => 'string',
|
||||
format => $ec_format,
|
||||
optional => 1,
|
||||
|
|
@ -459,14 +455,15 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $worker = sub {
|
||||
# reopen with longer timeout
|
||||
$rados = PVE::RADOS->new(timeout => PVE::Ceph::Tools::get_config('long_rados_timeout'));
|
||||
$rados =
|
||||
PVE::RADOS->new(timeout => PVE::Ceph::Tools::get_config('long_rados_timeout'));
|
||||
|
||||
if ($ec) {
|
||||
if (!$ec->{profile}) {
|
||||
$ec->{profile} = PVE::Ceph::Tools::get_ecprofile_name($pool, $rados);
|
||||
eval {
|
||||
PVE::Ceph::Tools::create_ecprofile(
|
||||
$ec->@{'profile', 'k', 'm', 'failure-domain', 'device-class'},
|
||||
$ec->@{ 'profile', 'k', 'm', 'failure-domain', 'device-class' },
|
||||
$rados,
|
||||
);
|
||||
};
|
||||
|
|
@ -503,10 +500,10 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'destroypool',
|
||||
path => '{name}',
|
||||
method => 'DELETE',
|
||||
|
|
@ -514,7 +511,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -537,7 +534,8 @@ __PACKAGE__->register_method ({
|
|||
default => 0,
|
||||
},
|
||||
remove_ecprofile => {
|
||||
description => "Remove the erasure code profile. Defaults to true, if applicable.",
|
||||
description =>
|
||||
"Remove the erasure code profile. Defaults to true, if applicable.",
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 1,
|
||||
|
|
@ -571,7 +569,7 @@ __PACKAGE__->register_method ({
|
|||
print "checking storage '$storeid' for RBD images..\n";
|
||||
my $res = PVE::Storage::vdisk_list($storagecfg, $storeid);
|
||||
die "ceph pool '$pool' still in use by storage '$storeid'\n"
|
||||
if @{$res->{$storeid}} != 0;
|
||||
if @{ $res->{$storeid} } != 0;
|
||||
}
|
||||
}
|
||||
my $rados = PVE::RADOS->new();
|
||||
|
|
@ -598,7 +596,7 @@ __PACKAGE__->register_method ({
|
|||
foreach my $storeid (keys %$storages) {
|
||||
# skip external clusters, not managed by pveceph
|
||||
next if $storages->{$storeid}->{monhost};
|
||||
eval { PVE::API2::Storage::Config->delete({storage => $storeid}) };
|
||||
eval { PVE::API2::Storage::Config->delete({ storage => $storeid }) };
|
||||
if ($@) {
|
||||
warn "failed to remove storage '$storeid': $@\n";
|
||||
$err = 1;
|
||||
|
|
@ -609,10 +607,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
};
|
||||
return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'setpool',
|
||||
path => '{name}',
|
||||
method => 'PUT',
|
||||
|
|
@ -620,7 +618,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -652,14 +650,15 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephsetpool', $pool, $authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'poolindex',
|
||||
path => '{name}',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
description => "Pool index.",
|
||||
parameters => {
|
||||
|
|
@ -678,7 +677,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -688,10 +687,10 @@ __PACKAGE__->register_method ({
|
|||
];
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'getpool',
|
||||
path => '{name}/status',
|
||||
method => 'GET',
|
||||
|
|
@ -699,7 +698,7 @@ __PACKAGE__->register_method ({
|
|||
proxyto => 'node',
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -713,8 +712,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'boolean',
|
||||
default => 0,
|
||||
optional => 1,
|
||||
description => "If enabled, will display additional data".
|
||||
"(eg. statistics).",
|
||||
description => "If enabled, will display additional data" . "(eg. statistics).",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -734,7 +732,8 @@ __PACKAGE__->register_method ({
|
|||
fast_read => { type => 'boolean', title => 'Fast Read' },
|
||||
application_list => { type => 'array', title => 'Application', optional => 1 },
|
||||
statistics => { type => 'object', title => 'Statistics', optional => 1 },
|
||||
autoscale_status => { type => 'object', title => 'Autoscale Status', optional => 1 },
|
||||
autoscale_status =>
|
||||
{ type => 'object', title => 'Autoscale Status', optional => 1 },
|
||||
%{ $ceph_pool_common_options->() },
|
||||
},
|
||||
},
|
||||
|
|
@ -785,18 +784,19 @@ __PACKAGE__->register_method ({
|
|||
my $autoscale_status = eval { $get_autoscale_status->() };
|
||||
$data->{autoscale_status} = $autoscale_status->{$pool};
|
||||
|
||||
foreach my $d (@{$res->{pools}}) {
|
||||
foreach my $d (@{ $res->{pools} }) {
|
||||
next if !$d->{stats};
|
||||
next if !defined($d->{name}) && !$d->{name} ne "$pool";
|
||||
$data->{statistics} = $d->{stats};
|
||||
}
|
||||
|
||||
my $apps = $rados->mon_command({ prefix => "osd pool application get", pool => "$pool", });
|
||||
$data->{application_list} = [ keys %$apps ];
|
||||
my $apps =
|
||||
$rados->mon_command({ prefix => "osd pool application get", pool => "$pool" });
|
||||
$data->{application_list} = [keys %$apps];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}});
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -5,20 +5,19 @@ use warnings;
|
|||
|
||||
use PVE::API2::ACME;
|
||||
use PVE::Certificate;
|
||||
use PVE::CertHelpers;;
|
||||
use PVE::CertHelpers;
|
||||
use PVE::Exception qw(raise_param_exc);
|
||||
use PVE::JSONSchema qw(get_standard_option);
|
||||
use PVE::Tools qw(extract_param file_get_contents file_set_contents);
|
||||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::ACME",
|
||||
path => 'acme',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -36,20 +35,18 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
return [
|
||||
{ name => 'acme' },
|
||||
{ name => 'custom' },
|
||||
{ name => 'info' },
|
||||
{ name => 'acme' }, { name => 'custom' }, { name => 'info' },
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'info',
|
||||
path => 'info',
|
||||
method => 'GET',
|
||||
|
|
@ -73,9 +70,7 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $res = [];
|
||||
my $cert_paths = [
|
||||
'/etc/pve/pve-root-ca.pem',
|
||||
"$node_path/pve-ssl.pem",
|
||||
"$node_path/pveproxy-ssl.pem",
|
||||
'/etc/pve/pve-root-ca.pem', "$node_path/pve-ssl.pem", "$node_path/pveproxy-ssl.pem",
|
||||
];
|
||||
for my $path (@$cert_paths) {
|
||||
eval {
|
||||
|
|
@ -87,12 +82,12 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'upload_custom_cert',
|
||||
path => 'custom',
|
||||
method => 'POST',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => 'Upload or update custom certificate chain and key.',
|
||||
protected => 1,
|
||||
|
|
@ -140,15 +135,17 @@ __PACKAGE__->register_method ({
|
|||
if ($key) {
|
||||
$key = PVE::Certificate::strip_leading_text($key);
|
||||
} else {
|
||||
raise_param_exc({'key' => "Attempted to upload custom certificate without (existing) key."})
|
||||
if ! -e "${cert_prefix}.key";
|
||||
raise_param_exc(
|
||||
{ 'key' => "Attempted to upload custom certificate without (existing) key." })
|
||||
if !-e "${cert_prefix}.key";
|
||||
}
|
||||
|
||||
my $info;
|
||||
|
||||
my $code = sub {
|
||||
print "Setting custom certificate files\n";
|
||||
$info = PVE::CertHelpers::set_cert_files($certs, $key, $cert_prefix, $param->{force});
|
||||
$info =
|
||||
PVE::CertHelpers::set_cert_files($certs, $key, $cert_prefix, $param->{force});
|
||||
|
||||
if ($param->{restart}) {
|
||||
print "Restarting pveproxy\n";
|
||||
|
|
@ -160,14 +157,15 @@ __PACKAGE__->register_method ({
|
|||
die "$@\n" if $@;
|
||||
|
||||
return $info;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'remove_custom_cert',
|
||||
path => 'custom',
|
||||
method => 'DELETE',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => 'DELETE custom certificate chain and key.',
|
||||
protected => 1,
|
||||
|
|
@ -208,6 +206,7 @@ __PACKAGE__->register_method ({
|
|||
die "$@\n" if $@;
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -43,68 +43,68 @@ eval {
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::ReplicationConfig",
|
||||
path => 'replication',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::MetricServer",
|
||||
path => 'metrics',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::Notifications",
|
||||
path => 'notifications',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::ClusterConfig",
|
||||
path => 'config',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Firewall::Cluster",
|
||||
path => 'firewall',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Backup",
|
||||
path => 'backup',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::BackupInfo",
|
||||
path => 'backup-info',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::HAConfig",
|
||||
path => 'ha',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::ACMEAccount",
|
||||
path => 'acme',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::Ceph",
|
||||
path => 'ceph',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::Jobs",
|
||||
path => 'jobs',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::Mapping",
|
||||
path => 'mapping',
|
||||
});
|
||||
|
||||
if ($have_sdn) {
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Network::SDN",
|
||||
path => 'sdn',
|
||||
});
|
||||
|
|
@ -113,16 +113,17 @@ if ($have_sdn) {
|
|||
my $dc_schema = PVE::DataCenterConfig::get_datacenter_schema();
|
||||
my $dc_properties = {
|
||||
delete => {
|
||||
type => 'string', format => 'pve-configid-list',
|
||||
type => 'string',
|
||||
format => 'pve-configid-list',
|
||||
description => "A list of settings you want to delete.",
|
||||
optional => 1,
|
||||
}
|
||||
},
|
||||
};
|
||||
foreach my $opt (keys %{$dc_schema->{properties}}) {
|
||||
foreach my $opt (keys %{ $dc_schema->{properties} }) {
|
||||
$dc_properties->{$opt} = $dc_schema->{properties}->{$opt};
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -138,7 +139,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -169,7 +170,8 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'log',
|
||||
|
|
@ -185,7 +187,7 @@ __PACKAGE__->register_method({
|
|||
description => "Maximum number of entries.",
|
||||
optional => 1,
|
||||
minimum => 1,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -203,18 +205,19 @@ __PACKAGE__->register_method({
|
|||
my $max = $param->{max} || 0;
|
||||
my $user = $rpcenv->get_user();
|
||||
|
||||
my $admin = $rpcenv->check($user, "/", [ 'Sys.Syslog' ], 1);
|
||||
my $admin = $rpcenv->check($user, "/", ['Sys.Syslog'], 1);
|
||||
|
||||
my $loguser = $admin ? '' : $user;
|
||||
|
||||
my $res = decode_json(PVE::Cluster::get_cluster_log($loguser, $max));
|
||||
|
||||
foreach my $entry (@{$res->{data}}) {
|
||||
foreach my $entry (@{ $res->{data} }) {
|
||||
$entry->{id} = "$entry->{uid}:$entry->{node}";
|
||||
}
|
||||
|
||||
return $res->{data};
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'resources',
|
||||
|
|
@ -257,15 +260,21 @@ __PACKAGE__->register_method({
|
|||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
node => get_standard_option('pve-node', {
|
||||
node => get_standard_option(
|
||||
'pve-node',
|
||||
{
|
||||
description => "The cluster node name"
|
||||
." (for types 'node', 'storage', 'qemu', and 'lxc').",
|
||||
. " (for types 'node', 'storage', 'qemu', and 'lxc').",
|
||||
optional => 1,
|
||||
}),
|
||||
storage => get_standard_option('pve-storage-id', {
|
||||
},
|
||||
),
|
||||
storage => get_standard_option(
|
||||
'pve-storage-id',
|
||||
{
|
||||
description => "The storage identifier (for type 'storage').",
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
pool => {
|
||||
description => "The pool name (for types 'pool', 'qemu' and 'lxc').",
|
||||
type => 'string',
|
||||
|
|
@ -279,7 +288,8 @@ __PACKAGE__->register_method({
|
|||
renderer => 'fraction_as_percentage',
|
||||
},
|
||||
maxcpu => {
|
||||
description => "Number of available CPUs (for types 'node', 'qemu' and 'lxc').",
|
||||
description =>
|
||||
"Number of available CPUs (for types 'node', 'qemu' and 'lxc').",
|
||||
type => 'number',
|
||||
optional => 1,
|
||||
minimum => 0,
|
||||
|
|
@ -293,21 +303,22 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
maxmem => {
|
||||
description => "Number of available memory in bytes"
|
||||
." (for types 'node', 'qemu' and 'lxc').",
|
||||
. " (for types 'node', 'qemu' and 'lxc').",
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
renderer => 'bytes',
|
||||
},
|
||||
netin => {
|
||||
description => "The amount of traffic in bytes that was sent to the guest over"
|
||||
." the network since it was started. (for types 'qemu' and 'lxc')",
|
||||
description =>
|
||||
"The amount of traffic in bytes that was sent to the guest over"
|
||||
. " the network since it was started. (for types 'qemu' and 'lxc')",
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
renderer => 'bytes',
|
||||
},
|
||||
netout => {
|
||||
description => "The amount of traffic in bytes that was sent from the guest"
|
||||
." over the network since it was started. (for types 'qemu' and 'lxc')",
|
||||
. " over the network since it was started. (for types 'qemu' and 'lxc')",
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
renderer => 'bytes',
|
||||
|
|
@ -318,13 +329,14 @@ __PACKAGE__->register_method({
|
|||
optional => 1,
|
||||
},
|
||||
lock => {
|
||||
description => "The guest's current config lock (for types 'qemu' and 'lxc')",
|
||||
description =>
|
||||
"The guest's current config lock (for types 'qemu' and 'lxc')",
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
uptime => {
|
||||
description => "Uptime of node or virtual guest in seconds"
|
||||
." (for types 'node', 'qemu' and 'lxc').",
|
||||
. " (for types 'node', 'qemu' and 'lxc').",
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
renderer => 'duration',
|
||||
|
|
@ -336,7 +348,7 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
disk => {
|
||||
description => "Used disk space in bytes (for type 'storage'),"
|
||||
." used root image space for VMs (for types 'qemu' and 'lxc').",
|
||||
. " used root image space for VMs (for types 'qemu' and 'lxc').",
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
renderer => 'bytes',
|
||||
|
|
@ -344,24 +356,26 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
maxdisk => {
|
||||
description => "Storage size in bytes (for type 'storage'),"
|
||||
." root image size for VMs (for types 'qemu' and 'lxc').",
|
||||
. " root image size for VMs (for types 'qemu' and 'lxc').",
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
renderer => 'bytes',
|
||||
minimum => 0,
|
||||
},
|
||||
diskread => {
|
||||
description => "The amount of bytes the guest read from its block devices since"
|
||||
." the guest was started. This info is not available for all storage types."
|
||||
." (for types 'qemu' and 'lxc')",
|
||||
description =>
|
||||
"The amount of bytes the guest read from its block devices since"
|
||||
. " the guest was started. This info is not available for all storage types."
|
||||
. " (for types 'qemu' and 'lxc')",
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
renderer => 'bytes',
|
||||
},
|
||||
diskwrite => {
|
||||
description => "The amount of bytes the guest wrote to its block devices since"
|
||||
." the guest was started. This info is not available for all storage types."
|
||||
." (for types 'qemu' and 'lxc')",
|
||||
description =>
|
||||
"The amount of bytes the guest wrote to its block devices since"
|
||||
. " the guest was started. This info is not available for all storage types."
|
||||
. " (for types 'qemu' and 'lxc')",
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
renderer => 'bytes',
|
||||
|
|
@ -377,10 +391,13 @@ __PACKAGE__->register_method({
|
|||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
vmid => get_standard_option('pve-vmid', {
|
||||
vmid => get_standard_option(
|
||||
'pve-vmid',
|
||||
{
|
||||
description => "The numerical vmid (for types 'qemu' and 'lxc').",
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
'cgroup-mode' => {
|
||||
description => "The cgroup mode the node operates under (for type 'node').",
|
||||
type => 'integer',
|
||||
|
|
@ -393,7 +410,7 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
template => {
|
||||
description => "Determines if the guest is a template."
|
||||
." (for types 'qemu' and 'lxc')",
|
||||
. " (for types 'qemu' and 'lxc')",
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 0,
|
||||
|
|
@ -422,15 +439,15 @@ __PACKAGE__->register_method({
|
|||
my $haresources = PVE::HA::Config::read_resources_config();
|
||||
my $hatypemap = {
|
||||
'qemu' => 'vm',
|
||||
'lxc' => 'ct'
|
||||
'lxc' => 'ct',
|
||||
};
|
||||
|
||||
my $pooldata = {};
|
||||
if (!$param->{type} || $param->{type} eq 'pool') {
|
||||
for my $pool (sort keys %{$usercfg->{pools}}) {
|
||||
for my $pool (sort keys %{ $usercfg->{pools} }) {
|
||||
my $d = $usercfg->{pools}->{$pool};
|
||||
|
||||
next if !$rpcenv->check($authuser, "/pool/$pool", [ 'Pool.Audit' ], 1);
|
||||
next if !$rpcenv->check($authuser, "/pool/$pool", ['Pool.Audit'], 1);
|
||||
|
||||
my $entry = {
|
||||
id => "/pool/$pool",
|
||||
|
|
@ -458,7 +475,8 @@ __PACKAGE__->register_method({
|
|||
$entry->{pool} = $pool;
|
||||
if (my $pe = $pooldata->{$pool}) {
|
||||
if ($entry->{uptime}) {
|
||||
$pe->{uptime} = $entry->{uptime} if !$pe->{uptime} || $entry->{uptime} > $pe->{uptime};
|
||||
$pe->{uptime} = $entry->{uptime}
|
||||
if !$pe->{uptime} || $entry->{uptime} > $pe->{uptime};
|
||||
$pe->{mem} = 0 if !$pe->{mem};
|
||||
$pe->{mem} += $entry->{mem};
|
||||
$pe->{maxmem} = 0 if !$pe->{maxmem};
|
||||
|
|
@ -470,14 +488,16 @@ __PACKAGE__->register_method({
|
|||
# so we calculate the current % of the cpu
|
||||
# but we had already the old cpu % before this vm, so:
|
||||
# new% = (old%*oldmax + cur%*curmax) / (oldmax+curmax)
|
||||
$pe->{cpu} = (($pe->{cpu} * $pe->{maxcpu}) + ($entry->{cpu} * $entry->{maxcpu})) / ($pe->{maxcpu} + $entry->{maxcpu});
|
||||
$pe->{cpu} =
|
||||
(($pe->{cpu} * $pe->{maxcpu}) + ($entry->{cpu} * $entry->{maxcpu}))
|
||||
/ ($pe->{maxcpu} + $entry->{maxcpu});
|
||||
$pe->{maxcpu} += $entry->{maxcpu};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# only skip now to next to ensure that the pool stats above are filled, if eligible
|
||||
next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
|
||||
next if !$rpcenv->check($authuser, "/vms/$vmid", ['VM.Audit'], 1);
|
||||
|
||||
for my $prop (@$prop_list) {
|
||||
if (defined(my $value = $props->{$vmid}->{$prop})) {
|
||||
|
|
@ -485,13 +505,15 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
}
|
||||
|
||||
if (defined($entry->{pool}) &&
|
||||
!$rpcenv->check($authuser, "/pool/$entry->{pool}", ['Pool.Audit'], 1)) {
|
||||
if (
|
||||
defined($entry->{pool})
|
||||
&& !$rpcenv->check($authuser, "/pool/$entry->{pool}", ['Pool.Audit'], 1)
|
||||
) {
|
||||
delete $entry->{pool};
|
||||
}
|
||||
|
||||
# get ha status
|
||||
if (my $hatype = $hatypemap->{$entry->{type}}) {
|
||||
if (my $hatype = $hatypemap->{ $entry->{type} }) {
|
||||
my $sid = "$hatype:$vmid";
|
||||
my $service;
|
||||
if ($service = $hastatus->{service_status}->{$sid}) {
|
||||
|
|
@ -509,8 +531,9 @@ __PACKAGE__->register_method({
|
|||
|
||||
if (!$param->{type} || $param->{type} eq 'node') {
|
||||
foreach my $node (@$nodelist) {
|
||||
my $can_audit = $rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ], 1);
|
||||
my $entry = PVE::API2Tools::extract_node_stats($node, $members, $rrd, !$can_audit);
|
||||
my $can_audit = $rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1);
|
||||
my $entry =
|
||||
PVE::API2Tools::extract_node_stats($node, $members, $rrd, !$can_audit);
|
||||
|
||||
my $info = eval { decode_json($static_node_info->{$node}); };
|
||||
if (defined(my $mode = $info->{'cgroup-mode'})) {
|
||||
|
|
@ -527,17 +550,18 @@ __PACKAGE__->register_method({
|
|||
if (!$param->{type} || $param->{type} eq 'storage') {
|
||||
|
||||
my $cfg = PVE::Storage::config();
|
||||
my @sids = PVE::Storage::storage_ids ($cfg);
|
||||
my @sids = PVE::Storage::storage_ids($cfg);
|
||||
|
||||
foreach my $storeid (@sids) {
|
||||
next if !$rpcenv->check($authuser, "/storage/$storeid", [ 'Datastore.Audit' ], 1);
|
||||
next if !$rpcenv->check($authuser, "/storage/$storeid", ['Datastore.Audit'], 1);
|
||||
|
||||
my $scfg = PVE::Storage::storage_config($cfg, $storeid);
|
||||
# we create a entry for each node
|
||||
foreach my $node (@$nodelist) {
|
||||
next if !PVE::Storage::storage_check_enabled($cfg, $storeid, $node, 1);
|
||||
|
||||
my $entry = PVE::API2Tools::extract_storage_stats($storeid, $scfg, $node, $rrd);
|
||||
my $entry =
|
||||
PVE::API2Tools::extract_storage_stats($storeid, $scfg, $node, $rrd);
|
||||
push @$res, $entry;
|
||||
}
|
||||
}
|
||||
|
|
@ -545,7 +569,7 @@ __PACKAGE__->register_method({
|
|||
|
||||
if (!$param->{type} || $param->{type} eq 'sdn') {
|
||||
#add default "localnetwork" zone
|
||||
if ($rpcenv->check($authuser, "/sdn/zones/localnetwork", [ 'SDN.Audit' ], 1)) {
|
||||
if ($rpcenv->check($authuser, "/sdn/zones/localnetwork", ['SDN.Audit'], 1)) {
|
||||
foreach my $node (@$nodelist) {
|
||||
my $local_sdn = {
|
||||
id => "sdn/$node/localnetwork",
|
||||
|
|
@ -565,7 +589,7 @@ __PACKAGE__->register_method({
|
|||
my $sdns = decode_json($nodes->{$node});
|
||||
|
||||
for my $id (sort keys %{$sdns}) {
|
||||
next if !$rpcenv->check($authuser, "/sdn/zones/$id", [ 'SDN.Audit' ], 1);
|
||||
next if !$rpcenv->check($authuser, "/sdn/zones/$id", ['SDN.Audit'], 1);
|
||||
my $sdn = $sdns->{$id};
|
||||
my $entry = {
|
||||
id => "sdn/$node/$id",
|
||||
|
|
@ -581,7 +605,8 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'tasks',
|
||||
|
|
@ -611,27 +636,30 @@ __PACKAGE__->register_method({
|
|||
my $tlist = PVE::Cluster::get_tasklist();
|
||||
return [] if !$tlist;
|
||||
|
||||
my $all = $rpcenv->check($authuser, "/", [ 'Sys.Audit' ], 1);
|
||||
my $all = $rpcenv->check($authuser, "/", ['Sys.Audit'], 1);
|
||||
|
||||
my $res = [];
|
||||
foreach my $task (@$tlist) {
|
||||
if (PVE::AccessControl::pve_verify_tokenid($task->{user}, 1)) {
|
||||
($task->{user}, $task->{tokenid}) = PVE::AccessControl::split_tokenid($task->{user});
|
||||
($task->{user}, $task->{tokenid}) =
|
||||
PVE::AccessControl::split_tokenid($task->{user});
|
||||
}
|
||||
push @$res, $task if $all || ($task->{user} eq $authuser);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_options',
|
||||
path => 'options',
|
||||
method => 'GET',
|
||||
description => "Get datacenter options. Without 'Sys.Audit' on '/' not all options are returned.",
|
||||
description =>
|
||||
"Get datacenter options. Without 'Sys.Audit' on '/' not all options are returned.",
|
||||
permissions => {
|
||||
user => 'all',
|
||||
check => ['perm', '/', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -663,7 +691,8 @@ __PACKAGE__->register_method({
|
|||
$res->{'allowed-tags'} = [sort keys $tags->%*];
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'set_options',
|
||||
|
|
@ -671,7 +700,7 @@ __PACKAGE__->register_method({
|
|||
method => 'PUT',
|
||||
description => "Set datacenter options.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
parameters => {
|
||||
|
|
@ -684,7 +713,10 @@ __PACKAGE__->register_method({
|
|||
|
||||
my $delete = extract_param($param, 'delete');
|
||||
|
||||
cfs_lock_file('datacenter.cfg', undef, sub {
|
||||
cfs_lock_file(
|
||||
'datacenter.cfg',
|
||||
undef,
|
||||
sub {
|
||||
my $conf = cfs_read_file('datacenter.cfg');
|
||||
|
||||
$conf->{$_} = $param->{$_} for keys $param->%*;
|
||||
|
|
@ -692,11 +724,13 @@ __PACKAGE__->register_method({
|
|||
delete $conf->{$_} for PVE::Tools::split_list($delete);
|
||||
|
||||
cfs_write_file('datacenter.cfg', $conf);
|
||||
});
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_status',
|
||||
|
|
@ -704,7 +738,7 @@ __PACKAGE__->register_method({
|
|||
method => 'GET',
|
||||
description => "Get cluster status information.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
protected => 1,
|
||||
parameters => {
|
||||
|
|
@ -719,7 +753,8 @@ __PACKAGE__->register_method({
|
|||
type => {
|
||||
type => 'string',
|
||||
enum => ['cluster', 'node'],
|
||||
description => 'Indicates the type, either cluster or node. The type defines the object properties e.g. quorate available for type cluster.'
|
||||
description =>
|
||||
'Indicates the type, either cluster or node. The type defines the object properties e.g. quorate available for type cluster.',
|
||||
},
|
||||
id => {
|
||||
type => 'string',
|
||||
|
|
@ -735,12 +770,14 @@ __PACKAGE__->register_method({
|
|||
version => {
|
||||
type => 'integer',
|
||||
optional => 1,
|
||||
description => '[cluster] Current version of the corosync configuration file.',
|
||||
description =>
|
||||
'[cluster] Current version of the corosync configuration file.',
|
||||
},
|
||||
quorate => {
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
description => '[cluster] Indicates if there is a majority of nodes online to make decisions',
|
||||
description =>
|
||||
'[cluster] Indicates if there is a majority of nodes online to make decisions',
|
||||
},
|
||||
nodeid => {
|
||||
type => 'integer',
|
||||
|
|
@ -765,8 +802,9 @@ __PACKAGE__->register_method({
|
|||
level => {
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
description => '[node] Proxmox VE Subscription level, indicates if eligible for enterprise support as well as access to the stable Proxmox VE Enterprise Repository.',
|
||||
}
|
||||
description =>
|
||||
'[node] Proxmox VE Subscription level, indicates if eligible for enterprise support as well as access to the stable Proxmox VE Enterprise Repository.',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -786,7 +824,8 @@ __PACKAGE__->register_method({
|
|||
my $res = [];
|
||||
|
||||
if (my $d = $clinfo->{cluster}) {
|
||||
push @$res, {
|
||||
push @$res,
|
||||
{
|
||||
type => 'cluster',
|
||||
id => 'cluster',
|
||||
nodes => $d->{nodes},
|
||||
|
|
@ -836,13 +875,15 @@ __PACKAGE__->register_method({
|
|||
level => $sublevel,
|
||||
}];
|
||||
}
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'nextid',
|
||||
path => 'nextid',
|
||||
method => 'GET',
|
||||
description => "Get next free VMID. Pass a VMID to assert that its free (at time of check).",
|
||||
description =>
|
||||
"Get next free VMID. Pass a VMID to assert that its free (at time of check).",
|
||||
permissions => { user => 'all' },
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -878,6 +919,7 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
die "unable to get any free VMID in range [$lower, $upper]\n";
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -53,13 +53,14 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{subdir}" } ],
|
||||
links => [{ rel => 'child', href => "{subdir}" }],
|
||||
},
|
||||
code => sub {
|
||||
return [
|
||||
{ subdir => 'not-backed-up' },
|
||||
];
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_guests_not_in_backup',
|
||||
|
|
@ -105,13 +106,14 @@ __PACKAGE__->register_method({
|
|||
my $vmlist = PVE::Cluster::get_vmlist();
|
||||
|
||||
# remove VMIDs to which the user has no permission to not leak infos like the guest name
|
||||
my @allowed_vmids = grep { $rpcenv->check($user, "/vms/$_", [ 'VM.Audit' ], 1) } keys $vmlist->{ids}->%*;
|
||||
my @allowed_vmids =
|
||||
grep { $rpcenv->check($user, "/vms/$_", ['VM.Audit'], 1) } keys $vmlist->{ids}->%*;
|
||||
|
||||
my $result = [];
|
||||
for my $vmid (@allowed_vmids) {
|
||||
next if $included_vmids->{$vmid};
|
||||
|
||||
my ($type, $node) = $vmlist->{ids}->{$vmid}->@{'type', 'node'};
|
||||
my ($type, $node) = $vmlist->{ids}->{$vmid}->@{ 'type', 'node' };
|
||||
|
||||
my ($conf, $name);
|
||||
if ($type eq 'qemu') {
|
||||
|
|
@ -121,7 +123,8 @@ __PACKAGE__->register_method({
|
|||
$conf = PVE::LXC::Config->load_config($vmid, $node);
|
||||
$name = $conf->{hostname};
|
||||
} else {
|
||||
die "Unexpected error: unknown guest type for VMID $vmid, neither QEMU nor LXC\n";
|
||||
die
|
||||
"Unexpected error: unknown guest type for VMID $vmid, neither QEMU nor LXC\n";
|
||||
}
|
||||
|
||||
my $entry = {
|
||||
|
|
@ -134,6 +137,7 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
139
PVE/API2/Cluster/BulkActions.pm
Normal file
139
PVE/API2/Cluster/BulkActions.pm
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package PVE::API2::Cluster::BulkActions;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use Storable qw(dclone);
|
||||
use JSON;
|
||||
|
||||
use PVE::Exception qw(raise_param_exc);
|
||||
use PVE::Tools qw(extract_param);
|
||||
use PVE::JSONSchema qw(get_standard_option);
|
||||
use PVE::RESTHandler;
|
||||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
description => 'Index for cluster-wide bulk-action API endpoints.',
|
||||
permissions => { user => 'all' },
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {},
|
||||
},
|
||||
returns => {
|
||||
type => 'array',
|
||||
items => {
|
||||
type => 'object',
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => '{name}' } ],
|
||||
},
|
||||
code => sub {
|
||||
my $result = [
|
||||
{ name => 'migrate' },
|
||||
{ name => 'start' },
|
||||
{ name => 'stop' },
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
});
|
||||
|
||||
my $guest_format = {
|
||||
vmid => {
|
||||
defau
|
||||
},
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
name => 'migrate',
|
||||
path => 'migrate',
|
||||
method => 'POST',
|
||||
description => 'Returns a list of all entities that can be used as notification targets' .
|
||||
' (endpoints and groups).',
|
||||
permissions => {
|
||||
description => "The 'VM.Migrate' permission is required on '/' or on '/vms/<ID>' for each "
|
||||
."ID passed via the 'vms' parameter.",
|
||||
user => 'all',
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
'guests' => {
|
||||
type => 'array',
|
||||
description => '',
|
||||
items => {
|
||||
type => 'string',
|
||||
format => $guest_format,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
type => 'string',
|
||||
items => {
|
||||
type => 'object',
|
||||
properties => {
|
||||
name => {
|
||||
description => 'Name of the endpoint/group.',
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
'type' => {
|
||||
description => 'Type of the endpoint or group.',
|
||||
type => 'string',
|
||||
enum => [qw(sendmail gotify group)],
|
||||
},
|
||||
'comment' => {
|
||||
description => 'Comment',
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => '{name}' } ],
|
||||
},
|
||||
code => sub {
|
||||
my $config = PVE::Notify::read_config();
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $targets = eval {
|
||||
my $result = [];
|
||||
|
||||
for my $target (@{$config->get_sendmail_endpoints()}) {
|
||||
push @$result, {
|
||||
name => $target->{name},
|
||||
comment => $target->{comment},
|
||||
type => 'sendmail',
|
||||
};
|
||||
}
|
||||
|
||||
for my $target (@{$config->get_gotify_endpoints()}) {
|
||||
push @$result, {
|
||||
name => $target->{name},
|
||||
comment => $target->{comment},
|
||||
type => 'gotify',
|
||||
};
|
||||
}
|
||||
|
||||
for my $target (@{$config->get_groups()}) {
|
||||
push @$result, {
|
||||
name => $target->{name},
|
||||
comment => $target->{comment},
|
||||
type => 'group',
|
||||
};
|
||||
}
|
||||
|
||||
$result
|
||||
};
|
||||
|
||||
raise_api_error($@) if $@;
|
||||
|
||||
return filter_entities_by_privs($rpcenv, $targets);
|
||||
}
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
@ -17,7 +17,7 @@ use PVE::Tools qw(extract_param);
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'cephindex',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -33,19 +33,17 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $result = [
|
||||
{ name => 'metadata' },
|
||||
{ name => 'status' },
|
||||
{ name => 'flags' },
|
||||
{ name => 'metadata' }, { name => 'status' }, { name => 'flags' },
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
my $metadata_common_props = {
|
||||
|
|
@ -75,14 +73,14 @@ my $metadata_common_props = {
|
|||
},
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'metadata',
|
||||
path => 'metadata',
|
||||
method => 'GET',
|
||||
description => "Get ceph metadata.",
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -91,7 +89,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
optional => 1,
|
||||
default => 'all',
|
||||
enum => ['all', 'versions', ],
|
||||
enum => ['all', 'versions'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -101,7 +99,8 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
mds => {
|
||||
type => "object",
|
||||
description => "Metadata servers configured in the cluster and their properties.",
|
||||
description =>
|
||||
"Metadata servers configured in the cluster and their properties.",
|
||||
properties => {
|
||||
"{id}" => {
|
||||
type => "object",
|
||||
|
|
@ -205,11 +204,13 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
front_addr => {
|
||||
type => "string",
|
||||
description => "Bind addresses and ports for frontend traffic to OSDs.",
|
||||
description =>
|
||||
"Bind addresses and ports for frontend traffic to OSDs.",
|
||||
},
|
||||
back_addr => {
|
||||
type => "string",
|
||||
description => "Bind addresses and ports for backend inter OSD traffic.",
|
||||
description =>
|
||||
"Bind addresses and ports for backend inter OSD traffic.",
|
||||
},
|
||||
device_id => {
|
||||
type => "string",
|
||||
|
|
@ -247,18 +248,18 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Tools::check_ceph_inited();
|
||||
my $rados = PVE::RADOS->new();
|
||||
|
||||
for my $type ( qw(mon mgr mds) ) {
|
||||
for my $type (qw(mon mgr mds)) {
|
||||
my $typedata = PVE::Ceph::Services::get_cluster_service($type);
|
||||
my $data = {};
|
||||
for my $host (sort keys %$typedata) {
|
||||
for my $service (sort keys %{$typedata->{$host}}) {
|
||||
for my $service (sort keys %{ $typedata->{$host} }) {
|
||||
$data->{"$service\@$host"} = $typedata->{$host}->{$service};
|
||||
}
|
||||
}
|
||||
|
||||
# get data from metadata call and merge 'our' data
|
||||
my $services = $rados->mon_command({ prefix => "$type metadata" });
|
||||
for my $service ( @$services ) {
|
||||
for my $service (@$services) {
|
||||
my $hostname = $service->{hostname};
|
||||
next if !defined($hostname); # can happen if node is dead
|
||||
|
||||
|
|
@ -266,7 +267,7 @@ __PACKAGE__->register_method ({
|
|||
my $id = "$servicename\@$hostname";
|
||||
|
||||
if ($data->{$id}) { # copy values over to the metadata hash
|
||||
for my $k (keys %{$data->{$id}}) {
|
||||
for my $k (keys %{ $data->{$id} }) {
|
||||
$service->{$k} = $data->{$id}->{$k};
|
||||
}
|
||||
}
|
||||
|
|
@ -279,21 +280,21 @@ __PACKAGE__->register_method ({
|
|||
$res->{osd} = $rados->mon_command({ prefix => "osd metadata" });
|
||||
|
||||
return $res;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'status',
|
||||
path => 'status',
|
||||
method => 'GET',
|
||||
description => "Get ceph status.",
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
|
||||
check => ['perm', '/', ['Sys.Audit', 'Datastore.Audit'], any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => { },
|
||||
properties => {},
|
||||
},
|
||||
returns => { type => 'object' },
|
||||
code => sub {
|
||||
|
|
@ -302,11 +303,11 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Tools::check_ceph_inited();
|
||||
|
||||
return PVE::Ceph::Tools::ceph_cluster_status();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
my $possible_flags = PVE::Ceph::Tools::get_possible_osd_flags();
|
||||
my $possible_flags_list = [ sort keys %$possible_flags ];
|
||||
my $possible_flags_list = [sort keys %$possible_flags];
|
||||
|
||||
my $get_current_set_flags = sub {
|
||||
my $rados = shift;
|
||||
|
|
@ -318,19 +319,18 @@ my $get_current_set_flags = sub {
|
|||
return { map { $_ => 1 } PVE::Tools::split_list($setflags) };
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_all_flags',
|
||||
path => 'flags',
|
||||
method => 'GET',
|
||||
description => "get the status of all ceph flags",
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
},
|
||||
properties => {},
|
||||
},
|
||||
returns => {
|
||||
type => 'array',
|
||||
|
|
@ -340,7 +340,8 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
name => {
|
||||
description => "Flag name.",
|
||||
type => 'string', enum => $possible_flags_list,
|
||||
type => 'string',
|
||||
enum => $possible_flags_list,
|
||||
},
|
||||
description => {
|
||||
description => "Flag description.",
|
||||
|
|
@ -352,7 +353,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -378,17 +379,17 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'set_flags',
|
||||
path => 'flags',
|
||||
method => 'PUT',
|
||||
description => "Set/Unset multiple ceph flags at once.",
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -418,7 +419,7 @@ __PACKAGE__->register_method ({
|
|||
my $prefix = $val ? 'set' : 'unset';
|
||||
eval {
|
||||
print "$prefix $flag\n";
|
||||
$rados->mon_command({ prefix => "osd $prefix", key => $flag, });
|
||||
$rados->mon_command({ prefix => "osd $prefix", key => $flag });
|
||||
};
|
||||
if (my $err = $@) {
|
||||
warn "error with $flag: '$err'\n";
|
||||
|
|
@ -432,24 +433,25 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('cephsetflags', undef, $user, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get_flag',
|
||||
path => 'flags/{flag}',
|
||||
method => 'GET',
|
||||
description => "Get the status of a specific ceph flag.",
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
flag => {
|
||||
description => "The name of the flag name to get.",
|
||||
type => 'string', enum => $possible_flags_list,
|
||||
type => 'string',
|
||||
enum => $possible_flags_list,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -469,16 +471,17 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return 0;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update_flag',
|
||||
path => 'flags/{flag}',
|
||||
method => 'PUT',
|
||||
description => "Set or clear (unset) a specific ceph flag",
|
||||
protected => 1,
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -509,7 +512,7 @@ __PACKAGE__->register_method ({
|
|||
});
|
||||
|
||||
return undef;
|
||||
}});
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use PVE::API2::Jobs::RealmSync;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Jobs::RealmSync",
|
||||
path => 'realm-sync',
|
||||
});
|
||||
|
|
@ -37,14 +37,14 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{subdir}" } ],
|
||||
links => [{ rel => 'child', href => "{subdir}" }],
|
||||
},
|
||||
code => sub {
|
||||
return [
|
||||
{ subdir => 'schedule-analyze' },
|
||||
{ subdir => 'realm-sync' },
|
||||
{ subdir => 'schedule-analyze' }, { subdir => 'realm-sync' },
|
||||
];
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'schedule-analyze',
|
||||
|
|
@ -56,12 +56,15 @@ __PACKAGE__->register_method({
|
|||
additionalProperties => 0,
|
||||
properties => {
|
||||
schedule => {
|
||||
description => "Job schedule. The format is a subset of `systemd` calendar events.",
|
||||
type => 'string', format => 'pve-calendar-event',
|
||||
description =>
|
||||
"Job schedule. The format is a subset of `systemd` calendar events.",
|
||||
type => 'string',
|
||||
format => 'pve-calendar-event',
|
||||
maxLength => 128,
|
||||
},
|
||||
starttime => {
|
||||
description => "UNIX timestamp to start the calculation from. Defaults to the current time.",
|
||||
description =>
|
||||
"UNIX timestamp to start the calculation from. Defaults to the current time.",
|
||||
optional => 1,
|
||||
type => 'integer',
|
||||
},
|
||||
|
|
@ -106,7 +109,8 @@ __PACKAGE__->register_method({
|
|||
for (my $count = 0; $count < $iterations; $count++) {
|
||||
my $next = PVE::CalendarEvent::compute_next_event($event, $starttime);
|
||||
last if !defined($next);
|
||||
push @$result, {
|
||||
push @$result,
|
||||
{
|
||||
timestamp => $next,
|
||||
utc => scalar(gmtime($next)),
|
||||
};
|
||||
|
|
@ -114,6 +118,7 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -9,22 +9,22 @@ use PVE::API2::Cluster::Mapping::USB;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::Mapping::Dir",
|
||||
path => 'dir',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::Mapping::PCI",
|
||||
path => 'pci',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Cluster::Mapping::USB",
|
||||
path => 'usb',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -41,18 +41,17 @@ __PACKAGE__->register_method ({
|
|||
items => {
|
||||
type => "object",
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $result = [
|
||||
{ name => 'dir' },
|
||||
{ name => 'pci' },
|
||||
{ name => 'usb' },
|
||||
{ name => 'dir' }, { name => 'pci' }, { name => 'usb' },
|
||||
];
|
||||
|
||||
return $result;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use PVE::Tools qw(extract_param);
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -26,17 +26,20 @@ __PACKAGE__->register_method ({
|
|||
description => "List directory mapping",
|
||||
permissions => {
|
||||
description => "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or"
|
||||
." 'Mapping.Audit' permissions on '/mapping/dir/<id>'.",
|
||||
. " 'Mapping.Audit' permissions on '/mapping/dir/<id>'.",
|
||||
user => 'all',
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
'check-node' => get_standard_option('pve-node', {
|
||||
'check-node' => get_standard_option(
|
||||
'pve-node',
|
||||
{
|
||||
description => "If given, checks the configurations on the given node for"
|
||||
." correctness, and adds relevant diagnostics for the directory to the response.",
|
||||
. " correctness, and adds relevant diagnostics for the directory to the response.",
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -46,7 +49,7 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
id => {
|
||||
type => 'string',
|
||||
description => "The logical ID of the mapping."
|
||||
description => "The logical ID of the mapping.",
|
||||
},
|
||||
map => {
|
||||
type => 'array',
|
||||
|
|
@ -77,11 +80,11 @@ __PACKAGE__->register_method ({
|
|||
description => "The message of the error",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
},
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -101,7 +104,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $res = [];
|
||||
for my $id (keys $cfg->{ids}->%*) {
|
||||
next if !$rpcenv->check_any($authuser, "/mapping/dir/$id", $can_see_mapping_privs, 1);
|
||||
next
|
||||
if !$rpcenv->check_any($authuser, "/mapping/dir/$id", $can_see_mapping_privs, 1);
|
||||
next if !$cfg->{ids}->{$id};
|
||||
|
||||
my $entry = dclone($cfg->{ids}->{$id});
|
||||
|
|
@ -110,9 +114,12 @@ __PACKAGE__->register_method ({
|
|||
|
||||
if (defined($check_node)) {
|
||||
$entry->{checks} = [];
|
||||
if (my $mappings = PVE::Mapping::Dir::get_node_mapping($cfg, $id, $check_node)) {
|
||||
if (
|
||||
my $mappings = PVE::Mapping::Dir::get_node_mapping($cfg, $id, $check_node)
|
||||
) {
|
||||
if (!scalar($mappings->@*)) {
|
||||
push $entry->{checks}->@*, {
|
||||
push $entry->{checks}->@*,
|
||||
{
|
||||
severity => 'warning',
|
||||
message => "No mapping for node $check_node.",
|
||||
};
|
||||
|
|
@ -120,7 +127,8 @@ __PACKAGE__->register_method ({
|
|||
for my $mapping ($mappings->@*) {
|
||||
eval { PVE::Mapping::Dir::assert_valid($mapping) };
|
||||
if (my $err = $@) {
|
||||
push $entry->{checks}->@*, {
|
||||
push $entry->{checks}->@*,
|
||||
{
|
||||
severity => 'error',
|
||||
message => "Invalid configuration: $err",
|
||||
};
|
||||
|
|
@ -136,14 +144,15 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
method => 'GET',
|
||||
description => "Get directory mapping.",
|
||||
permissions => {
|
||||
check =>['or',
|
||||
check => [
|
||||
'or',
|
||||
['perm', '/mapping/dir/{id}', ['Mapping.Use']],
|
||||
['perm', '/mapping/dir/{id}', ['Mapping.Modify']],
|
||||
['perm', '/mapping/dir/{id}', ['Mapping.Audit']],
|
||||
|
|
@ -156,7 +165,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'object' },
|
||||
code => sub {
|
||||
|
|
@ -173,9 +182,10 @@ __PACKAGE__->register_method ({
|
|||
$data->{digest} = $cfg->{digest};
|
||||
|
||||
return $data;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'create',
|
||||
protected => 1,
|
||||
path => '',
|
||||
|
|
@ -199,7 +209,8 @@ __PACKAGE__->register_method ({
|
|||
my $map_list = $opts->{map};
|
||||
PVE::Mapping::Dir::assert_valid_map_list($map_list);
|
||||
|
||||
PVE::Mapping::Dir::lock_dir_config(sub {
|
||||
PVE::Mapping::Dir::lock_dir_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::Dir::config();
|
||||
|
||||
die "dir ID '$id' already defined\n" if defined($cfg->{ids}->{$id});
|
||||
|
|
@ -208,13 +219,15 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::Dir::write_dir_config($cfg);
|
||||
|
||||
}, "create directory mapping failed");
|
||||
},
|
||||
"create directory mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
|
|
@ -235,10 +248,11 @@ __PACKAGE__->register_method ({
|
|||
my $id = extract_param($param, 'id');
|
||||
|
||||
if ($delete) {
|
||||
$delete = [ PVE::Tools::split_list($delete) ];
|
||||
$delete = [PVE::Tools::split_list($delete)];
|
||||
}
|
||||
|
||||
PVE::Mapping::Dir::lock_dir_config(sub {
|
||||
PVE::Mapping::Dir::lock_dir_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::Dir::config();
|
||||
|
||||
PVE::Tools::assert_if_modified($cfg->{digest}, $digest) if defined($digest);
|
||||
|
|
@ -260,20 +274,22 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::Dir::write_dir_config($cfg);
|
||||
|
||||
}, "update directory mapping failed");
|
||||
},
|
||||
"update directory mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
method => 'DELETE',
|
||||
description => "Remove directory mapping.",
|
||||
permissions => {
|
||||
check => [ 'perm', '/mapping/dir', ['Mapping.Modify']],
|
||||
check => ['perm', '/mapping/dir', ['Mapping.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -282,7 +298,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
|
@ -290,7 +306,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $id = $param->{id};
|
||||
|
||||
PVE::Mapping::Dir::lock_dir_config(sub {
|
||||
PVE::Mapping::Dir::lock_dir_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::Dir::config();
|
||||
|
||||
if ($cfg->{ids}->{$id}) {
|
||||
|
|
@ -299,10 +316,12 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::Dir::write_dir_config($cfg);
|
||||
|
||||
}, "delete dir mapping failed");
|
||||
},
|
||||
"delete dir mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use PVE::Tools qw(extract_param);
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -22,18 +22,21 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
description => "List PCI Hardware Mapping",
|
||||
permissions => {
|
||||
description => "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or".
|
||||
" 'Mapping.Audit' permissions on '/mapping/pci/<id>'.",
|
||||
description => "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or"
|
||||
. " 'Mapping.Audit' permissions on '/mapping/pci/<id>'.",
|
||||
user => 'all',
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
'check-node' => get_standard_option('pve-node', {
|
||||
description => "If given, checks the configurations on the given node for ".
|
||||
"correctness, and adds relevant diagnostics for the devices to the response.",
|
||||
'check-node' => get_standard_option(
|
||||
'pve-node',
|
||||
{
|
||||
description => "If given, checks the configurations on the given node for "
|
||||
. "correctness, and adds relevant diagnostics for the devices to the response.",
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -43,7 +46,7 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
id => {
|
||||
type => 'string',
|
||||
description => "The logical ID of the mapping."
|
||||
description => "The logical ID of the mapping.",
|
||||
},
|
||||
map => {
|
||||
type => 'array',
|
||||
|
|
@ -74,11 +77,11 @@ __PACKAGE__->register_method ({
|
|||
description => "The message of the error",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
},
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -98,7 +101,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $res = [];
|
||||
for my $id (keys $cfg->{ids}->%*) {
|
||||
next if !$rpcenv->check_any($authuser, "/mapping/pci/$id", $can_see_mapping_privs, 1);
|
||||
next
|
||||
if !$rpcenv->check_any($authuser, "/mapping/pci/$id", $can_see_mapping_privs, 1);
|
||||
next if !$cfg->{ids}->{$id};
|
||||
|
||||
my $entry = dclone($cfg->{ids}->{$id});
|
||||
|
|
@ -107,9 +111,12 @@ __PACKAGE__->register_method ({
|
|||
|
||||
if (defined($check_node)) {
|
||||
$entry->{checks} = [];
|
||||
if (my $mappings = PVE::Mapping::PCI::get_node_mapping($cfg, $id, $check_node)) {
|
||||
if (
|
||||
my $mappings = PVE::Mapping::PCI::get_node_mapping($cfg, $id, $check_node)
|
||||
) {
|
||||
if (!scalar($mappings->@*)) {
|
||||
push $entry->{checks}->@*, {
|
||||
push $entry->{checks}->@*,
|
||||
{
|
||||
severity => 'warning',
|
||||
message => "No mapping for node $check_node.",
|
||||
};
|
||||
|
|
@ -117,7 +124,8 @@ __PACKAGE__->register_method ({
|
|||
for my $mapping ($mappings->@*) {
|
||||
eval { PVE::Mapping::PCI::assert_valid($id, $mapping, $entry) };
|
||||
if (my $err = $@) {
|
||||
push $entry->{checks}->@*, {
|
||||
push $entry->{checks}->@*,
|
||||
{
|
||||
severity => 'error',
|
||||
message => "Invalid configuration: $err",
|
||||
};
|
||||
|
|
@ -133,14 +141,15 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
method => 'GET',
|
||||
description => "Get PCI Mapping.",
|
||||
permissions => {
|
||||
check =>['or',
|
||||
check => [
|
||||
'or',
|
||||
['perm', '/mapping/pci/{id}', ['Mapping.Use']],
|
||||
['perm', '/mapping/pci/{id}', ['Mapping.Modify']],
|
||||
['perm', '/mapping/pci/{id}', ['Mapping.Audit']],
|
||||
|
|
@ -153,7 +162,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'object' },
|
||||
code => sub {
|
||||
|
|
@ -170,9 +179,10 @@ __PACKAGE__->register_method ({
|
|||
$data->{digest} = $cfg->{digest};
|
||||
|
||||
return $data;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'create',
|
||||
protected => 1,
|
||||
path => '',
|
||||
|
|
@ -193,7 +203,8 @@ __PACKAGE__->register_method ({
|
|||
my $plugin = PVE::Mapping::PCI->lookup('pci');
|
||||
my $opts = $plugin->check_config($id, $param, 1, 1);
|
||||
|
||||
PVE::Mapping::PCI::lock_pci_config(sub {
|
||||
PVE::Mapping::PCI::lock_pci_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::PCI::config();
|
||||
|
||||
die "pci ID '$id' already defined\n" if defined($cfg->{ids}->{$id});
|
||||
|
|
@ -202,13 +213,15 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::PCI::write_pci_config($cfg);
|
||||
|
||||
}, "create hardware mapping failed");
|
||||
},
|
||||
"create hardware mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
|
|
@ -229,10 +242,11 @@ __PACKAGE__->register_method ({
|
|||
my $id = extract_param($param, 'id');
|
||||
|
||||
if ($delete) {
|
||||
$delete = [ PVE::Tools::split_list($delete) ];
|
||||
$delete = [PVE::Tools::split_list($delete)];
|
||||
}
|
||||
|
||||
PVE::Mapping::PCI::lock_pci_config(sub {
|
||||
PVE::Mapping::PCI::lock_pci_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::PCI::config();
|
||||
|
||||
PVE::Tools::assert_if_modified($cfg->{digest}, $digest) if defined($digest);
|
||||
|
|
@ -251,20 +265,22 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::PCI::write_pci_config($cfg);
|
||||
|
||||
}, "update hardware mapping failed");
|
||||
},
|
||||
"update hardware mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
method => 'DELETE',
|
||||
description => "Remove Hardware Mapping.",
|
||||
permissions => {
|
||||
check => [ 'perm', '/mapping/pci', ['Mapping.Modify']],
|
||||
check => ['perm', '/mapping/pci', ['Mapping.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -273,7 +289,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
|
@ -281,7 +297,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $id = $param->{id};
|
||||
|
||||
PVE::Mapping::PCI::lock_pci_config(sub {
|
||||
PVE::Mapping::PCI::lock_pci_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::PCI::config();
|
||||
|
||||
if ($cfg->{ids}->{$id}) {
|
||||
|
|
@ -290,10 +307,12 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::PCI::write_pci_config($cfg);
|
||||
|
||||
}, "delete pci mapping failed");
|
||||
},
|
||||
"delete pci mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use PVE::Tools qw(extract_param);
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -22,18 +22,21 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
description => "List USB Hardware Mappings",
|
||||
permissions => {
|
||||
description => "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or".
|
||||
" 'Mapping.Audit' permissions on '/mapping/usb/<id>'.",
|
||||
description => "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or"
|
||||
. " 'Mapping.Audit' permissions on '/mapping/usb/<id>'.",
|
||||
user => 'all',
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
'check-node' => get_standard_option('pve-node', {
|
||||
description => "If given, checks the configurations on the given node for ".
|
||||
"correctness, and adds relevant errors to the devices.",
|
||||
'check-node' => get_standard_option(
|
||||
'pve-node',
|
||||
{
|
||||
description => "If given, checks the configurations on the given node for "
|
||||
. "correctness, and adds relevant errors to the devices.",
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -43,7 +46,7 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
id => {
|
||||
type => 'string',
|
||||
description => "The logical ID of the mapping."
|
||||
description => "The logical ID of the mapping.",
|
||||
},
|
||||
map => {
|
||||
type => 'array',
|
||||
|
|
@ -71,11 +74,11 @@ __PACKAGE__->register_method ({
|
|||
description => "The message of the error",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
},
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -105,17 +108,17 @@ __PACKAGE__->register_method ({
|
|||
$entry->{errors} = [];
|
||||
if (my $mappings = PVE::Mapping::USB::get_node_mapping($cfg, $id, $node)) {
|
||||
if (!scalar($mappings->@*)) {
|
||||
push $entry->{errors}->@*, {
|
||||
push $entry->{errors}->@*,
|
||||
{
|
||||
severity => 'warning',
|
||||
message => "No mapping for node $node.",
|
||||
};
|
||||
}
|
||||
for my $mapping ($mappings->@*) {
|
||||
eval {
|
||||
PVE::Mapping::USB::assert_valid($id, $mapping);
|
||||
};
|
||||
eval { PVE::Mapping::USB::assert_valid($id, $mapping); };
|
||||
if (my $err = $@) {
|
||||
push $entry->{errors}->@*, {
|
||||
push $entry->{errors}->@*,
|
||||
{
|
||||
severity => 'error',
|
||||
message => "Invalid configuration: $err",
|
||||
};
|
||||
|
|
@ -131,14 +134,15 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
method => 'GET',
|
||||
description => "Get USB Mapping.",
|
||||
permissions => {
|
||||
check =>['or',
|
||||
check => [
|
||||
'or',
|
||||
['perm', '/mapping/usb/{id}', ['Mapping.Audit']],
|
||||
['perm', '/mapping/usb/{id}', ['Mapping.Use']],
|
||||
['perm', '/mapping/usb/{id}', ['Mapping.Modify']],
|
||||
|
|
@ -151,7 +155,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'object' },
|
||||
code => sub {
|
||||
|
|
@ -168,9 +172,10 @@ __PACKAGE__->register_method ({
|
|||
$data->{digest} = $cfg->{digest};
|
||||
|
||||
return $data;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'create',
|
||||
protected => 1,
|
||||
path => '',
|
||||
|
|
@ -191,7 +196,8 @@ __PACKAGE__->register_method ({
|
|||
my $plugin = PVE::Mapping::USB->lookup('usb');
|
||||
my $opts = $plugin->check_config($id, $param, 1, 1);
|
||||
|
||||
PVE::Mapping::USB::lock_usb_config(sub {
|
||||
PVE::Mapping::USB::lock_usb_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::USB::config();
|
||||
|
||||
die "usb ID '$id' already defined\n" if defined($cfg->{ids}->{$id});
|
||||
|
|
@ -200,13 +206,15 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::USB::write_usb_config($cfg);
|
||||
|
||||
}, "create hardware mapping failed");
|
||||
},
|
||||
"create hardware mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
|
|
@ -227,10 +235,11 @@ __PACKAGE__->register_method ({
|
|||
my $id = extract_param($param, 'id');
|
||||
|
||||
if ($delete) {
|
||||
$delete = [ PVE::Tools::split_list($delete) ];
|
||||
$delete = [PVE::Tools::split_list($delete)];
|
||||
}
|
||||
|
||||
PVE::Mapping::USB::lock_usb_config(sub {
|
||||
PVE::Mapping::USB::lock_usb_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::USB::config();
|
||||
|
||||
PVE::Tools::assert_if_modified($cfg->{digest}, $digest) if defined($digest);
|
||||
|
|
@ -249,20 +258,22 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::USB::write_usb_config($cfg);
|
||||
|
||||
}, "update hardware mapping failed");
|
||||
},
|
||||
"update hardware mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
method => 'DELETE',
|
||||
description => "Remove Hardware Mapping.",
|
||||
permissions => {
|
||||
check => [ 'perm', '/mapping/usb', ['Mapping.Modify']],
|
||||
check => ['perm', '/mapping/usb', ['Mapping.Modify']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -271,7 +282,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
|
@ -279,7 +290,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $id = $param->{id};
|
||||
|
||||
PVE::Mapping::USB::lock_usb_config(sub {
|
||||
PVE::Mapping::USB::lock_usb_config(
|
||||
sub {
|
||||
my $cfg = PVE::Mapping::USB::config();
|
||||
|
||||
if ($cfg->{ids}->{$id}) {
|
||||
|
|
@ -288,10 +300,12 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Mapping::USB::write_usb_config($cfg);
|
||||
|
||||
}, "delete usb mapping failed");
|
||||
},
|
||||
"delete usb mapping failed",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use PVE::RESTHandler;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -32,7 +32,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -42,10 +42,10 @@ __PACKAGE__->register_method ({
|
|||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'server_index',
|
||||
path => 'server',
|
||||
method => 'GET',
|
||||
|
|
@ -64,7 +64,7 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
id => {
|
||||
description => "The ID of the entry.",
|
||||
type => 'string'
|
||||
type => 'string',
|
||||
},
|
||||
disable => {
|
||||
description => "Flag to disable the plugin.",
|
||||
|
|
@ -84,7 +84,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -92,9 +92,10 @@ __PACKAGE__->register_method ({
|
|||
my $res = [];
|
||||
my $status_cfg = PVE::Cluster::cfs_read_file('status.cfg');
|
||||
|
||||
for my $id (sort keys %{$status_cfg->{ids}}) {
|
||||
for my $id (sort keys %{ $status_cfg->{ids} }) {
|
||||
my $plugin_config = $status_cfg->{ids}->{$id};
|
||||
push @$res, {
|
||||
push @$res,
|
||||
{
|
||||
id => $id,
|
||||
disable => $plugin_config->{disable} // 0,
|
||||
type => $plugin_config->{type},
|
||||
|
|
@ -104,9 +105,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'read',
|
||||
path => 'server/{id}',
|
||||
method => 'GET',
|
||||
|
|
@ -135,9 +137,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $status_cfg->{ids}->{$id};
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'create',
|
||||
path => 'server/{id}',
|
||||
protected => 1,
|
||||
|
|
@ -157,7 +160,10 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $sensitive_params = extract_sensitive_params($param, ['token'], []);
|
||||
|
||||
PVE::Cluster::cfs_lock_file('status.cfg', undef, sub {
|
||||
PVE::Cluster::cfs_lock_file(
|
||||
'status.cfg',
|
||||
undef,
|
||||
sub {
|
||||
my $cfg = PVE::Cluster::cfs_read_file('status.cfg');
|
||||
|
||||
die "Metric server '$id' already exists\n"
|
||||
|
|
@ -169,9 +175,7 @@ __PACKAGE__->register_method ({
|
|||
|
||||
$plugin->on_add_hook($id, $opts, $sensitive_params);
|
||||
|
||||
eval {
|
||||
$plugin->test_connection($opts, $id);
|
||||
};
|
||||
eval { $plugin->test_connection($opts, $id); };
|
||||
|
||||
if (my $err = $@) {
|
||||
eval { $plugin->on_delete_hook($id, $opts) };
|
||||
|
|
@ -180,14 +184,15 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
PVE::Cluster::cfs_write_file('status.cfg', $cfg);
|
||||
});
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
|
||||
return;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update',
|
||||
protected => 1,
|
||||
path => 'server/{id}',
|
||||
|
|
@ -211,7 +216,10 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $sensitive_params = extract_sensitive_params($param, ['token'], $delete);
|
||||
|
||||
PVE::Cluster::cfs_lock_file('status.cfg', undef, sub {
|
||||
PVE::Cluster::cfs_lock_file(
|
||||
'status.cfg',
|
||||
undef,
|
||||
sub {
|
||||
my $cfg = PVE::Cluster::cfs_read_file('status.cfg');
|
||||
|
||||
PVE::SectionConfig::assert_if_modified($cfg, $digest);
|
||||
|
|
@ -227,7 +235,7 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
if ($delete) {
|
||||
my $options = $plugin->private()->{options}->{$data->{type}};
|
||||
my $options = $plugin->private()->{options}->{ $data->{type} };
|
||||
for my $k (@$delete) {
|
||||
my $d = $options->{$k} || die "no such option '$k'\n";
|
||||
die "unable to delete required option '$k'\n" if !$d->{optional};
|
||||
|
|
@ -244,13 +252,15 @@ __PACKAGE__->register_method ({
|
|||
$plugin->test_connection($data, $id);
|
||||
|
||||
PVE::Cluster::cfs_write_file('status.cfg', $cfg);
|
||||
});
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
|
||||
return;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete',
|
||||
protected => 1,
|
||||
path => 'server/{id}',
|
||||
|
|
@ -266,13 +276,16 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
PVE::Cluster::cfs_lock_file('status.cfg', undef, sub {
|
||||
PVE::Cluster::cfs_lock_file(
|
||||
'status.cfg',
|
||||
undef,
|
||||
sub {
|
||||
my $cfg = PVE::Cluster::cfs_read_file('status.cfg');
|
||||
|
||||
my $id = $param->{id};
|
||||
|
|
@ -285,13 +298,15 @@ __PACKAGE__->register_method ({
|
|||
|
||||
delete $cfg->{ids}->{$id};
|
||||
PVE::Cluster::cfs_write_file('status.cfg', $cfg);
|
||||
});
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
|
||||
return;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'export',
|
||||
path => 'export',
|
||||
method => 'GET',
|
||||
|
|
@ -331,7 +346,8 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
data => {
|
||||
type => 'array',
|
||||
description => 'Array of system metrics. Metrics are sorted by their timestamp.',
|
||||
description =>
|
||||
'Array of system metrics. Metrics are sorted by their timestamp.',
|
||||
items => {
|
||||
type => 'object',
|
||||
additionalProperties => 0,
|
||||
|
|
@ -344,7 +360,7 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
description => "Unique identifier for this metric object,"
|
||||
. " for instance 'node/<nodename>' or"
|
||||
. " 'qemu/<vmid>'."
|
||||
. " 'qemu/<vmid>'.",
|
||||
},
|
||||
metric => {
|
||||
type => 'string',
|
||||
|
|
@ -358,13 +374,13 @@ __PACKAGE__->register_method ({
|
|||
type => 'string',
|
||||
description => 'Type of the metric.',
|
||||
enum => [qw(gauge counter derive)],
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -386,9 +402,9 @@ __PACKAGE__->register_method ({
|
|||
|
||||
} else {
|
||||
$generations = 0;
|
||||
};
|
||||
}
|
||||
|
||||
my @metrics = @{PVE::PullMetric::get_local_metrics($generations)};
|
||||
my @metrics = @{ PVE::PullMetric::get_local_metrics($generations) };
|
||||
|
||||
if (defined($start)) {
|
||||
@metrics = grep {
|
||||
|
|
@ -463,7 +479,7 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
}
|
||||
|
||||
my @sorted = sort {$a->{timestamp} <=> $b->{timestamp}} @metrics;
|
||||
my @sorted = sort { $a->{timestamp} <=> $b->{timestamp} } @metrics;
|
||||
|
||||
return {
|
||||
data => \@sorted,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -16,17 +16,17 @@ use PVE::API2::HA::Status;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::HA::Resources",
|
||||
path => 'resources',
|
||||
});
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::HA::Groups",
|
||||
path => 'groups',
|
||||
});
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::HA::Status",
|
||||
path => 'status',
|
||||
});
|
||||
|
|
@ -37,7 +37,7 @@ __PACKAGE__->register_method({
|
|||
method => 'GET',
|
||||
description => "Directory index.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -51,19 +51,17 @@ __PACKAGE__->register_method({
|
|||
id => { type => 'string' },
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $res = [
|
||||
{ id => 'status' },
|
||||
{ id => 'resources' },
|
||||
{ id => 'groups' },
|
||||
{ id => 'status' }, { id => 'resources' }, { id => 'groups' },
|
||||
];
|
||||
|
||||
return $res;
|
||||
}});
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ use PVE::API2::Hardware::USB;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Hardware::PCI",
|
||||
path => 'pci',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
subclass => "PVE::API2::Hardware::USB",
|
||||
path => 'usb',
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -39,19 +39,18 @@ __PACKAGE__->register_method ({
|
|||
type => 'array',
|
||||
items => {
|
||||
type => "object",
|
||||
properties => { type => { type => 'string'} },
|
||||
properties => { type => { type => 'string' } },
|
||||
},
|
||||
links => [ { rel => 'child', href => "{type}" } ],
|
||||
links => [{ rel => 'child', href => "{type}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $res = [
|
||||
{ type => 'pci' },
|
||||
{ type => 'usb' },
|
||||
{ type => 'pci' }, { type => 'usb' },
|
||||
];
|
||||
|
||||
return $res;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use base qw(PVE::RESTHandler);
|
|||
|
||||
my $default_class_blacklist = "05;06;0b";
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'pci_scan',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -32,20 +32,20 @@ __PACKAGE__->register_method ({
|
|||
default => $default_class_blacklist,
|
||||
optional => 1,
|
||||
description => "A list of blacklisted PCI classes, which will not be returned."
|
||||
." Following are filtered by default: Memory Controller (05), Bridge (06) and"
|
||||
." Processor (0b).",
|
||||
. " Following are filtered by default: Memory Controller (05), Bridge (06) and"
|
||||
. " Processor (0b).",
|
||||
},
|
||||
verbose => {
|
||||
type => 'boolean',
|
||||
default => 1,
|
||||
optional => 1,
|
||||
description => "If disabled, does only print the PCI IDs. Otherwise, additional"
|
||||
." information like vendor and device will be returned.",
|
||||
. " information like vendor and device will be returned.",
|
||||
},
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
type => 'array',
|
||||
items => {
|
||||
type => "object",
|
||||
|
|
@ -94,14 +94,16 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
iommugroup => {
|
||||
type => 'integer',
|
||||
description => "The IOMMU group in which the device is in. If no IOMMU group is"
|
||||
." detected, it is set to -1.",
|
||||
description =>
|
||||
"The IOMMU group in which the device is in. If no IOMMU group is"
|
||||
. " detected, it is set to -1.",
|
||||
},
|
||||
mdev => {
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
description => "If set, marks that the device is capable of creating mediated"
|
||||
." devices.",
|
||||
description =>
|
||||
"If set, marks that the device is capable of creating mediated"
|
||||
. " devices.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -129,9 +131,10 @@ __PACKAGE__->register_method ({
|
|||
my $verbose = $param->{verbose} // 1;
|
||||
|
||||
return PVE::SysFSTools::lspci($filter, $verbose);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'pci_index',
|
||||
path => '{pci-id-or-mapping}',
|
||||
method => 'GET',
|
||||
|
|
@ -145,7 +148,8 @@ __PACKAGE__->register_method ({
|
|||
node => get_standard_option('pve-node'),
|
||||
'pci-id-or-mapping' => {
|
||||
type => 'string',
|
||||
pattern => '(?:(?:[0-9a-fA-F]{4}:)?[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])|([a-zA-Z][a-zA-Z0-9_-]+)',
|
||||
pattern =>
|
||||
'(?:(?:[0-9a-fA-F]{4}:)?[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])|([a-zA-Z][a-zA-Z0-9_-]+)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -153,9 +157,9 @@ __PACKAGE__->register_method ({
|
|||
type => 'array',
|
||||
items => {
|
||||
type => "object",
|
||||
properties => { method => { type => 'string'} },
|
||||
properties => { method => { type => 'string' } },
|
||||
},
|
||||
links => [ { rel => 'child', href => "{method}" } ],
|
||||
links => [{ rel => 'child', href => "{method}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -165,9 +169,10 @@ __PACKAGE__->register_method ({
|
|||
];
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'mdevscan',
|
||||
path => '{pci-id-or-mapping}/mdev',
|
||||
method => 'GET',
|
||||
|
|
@ -183,8 +188,9 @@ __PACKAGE__->register_method ({
|
|||
node => get_standard_option('pve-node'),
|
||||
'pci-id-or-mapping' => {
|
||||
type => 'string',
|
||||
pattern => '(?:(?:[0-9a-fA-F]{4}:)?[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])|([a-zA-Z][a-zA-Z0-9_-]+)',
|
||||
description => "The PCI ID or mapping to list the mdev types for."
|
||||
pattern =>
|
||||
'(?:(?:[0-9a-fA-F]{4}:)?[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])|([a-zA-Z][a-zA-Z0-9_-]+)',
|
||||
description => "The PCI ID or mapping to list the mdev types for.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -203,7 +209,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
description => {
|
||||
type => 'string',
|
||||
description => "Additional description of the type."
|
||||
description => "Additional description of the type.",
|
||||
},
|
||||
name => {
|
||||
type => 'string',
|
||||
|
|
@ -216,7 +222,9 @@ __PACKAGE__->register_method ({
|
|||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
if ($param->{'pci-id-or-mapping'} =~ m/^(?:[0-9a-fA-F]{4}:)?[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]$/) {
|
||||
if ($param->{'pci-id-or-mapping'} =~
|
||||
m/^(?:[0-9a-fA-F]{4}:)?[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]$/
|
||||
) {
|
||||
return PVE::SysFSTools::get_mdev_types($param->{'pci-id-or-mapping'}); # PCI ID
|
||||
} else {
|
||||
my $mapping = $param->{'pci-id-or-mapping'};
|
||||
|
|
@ -240,4 +248,5 @@ __PACKAGE__->register_method ({
|
|||
return [sort { $a->{type} cmp $b->{type} } values($types->%*)];
|
||||
}
|
||||
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,18 +30,18 @@ __PACKAGE__->register_method({
|
|||
items => {
|
||||
type => "object",
|
||||
properties => {
|
||||
busnum => { type => 'integer'},
|
||||
class => { type => 'integer'},
|
||||
devnum => { type => 'integer'},
|
||||
level => { type => 'integer'},
|
||||
busnum => { type => 'integer' },
|
||||
class => { type => 'integer' },
|
||||
devnum => { type => 'integer' },
|
||||
level => { type => 'integer' },
|
||||
manufacturer => { type => 'string', optional => 1 },
|
||||
port => { type => 'integer'},
|
||||
prodid => { type => 'string'},
|
||||
port => { type => 'integer' },
|
||||
prodid => { type => 'string' },
|
||||
product => { type => 'string', optional => 1 },
|
||||
serial => { type => 'string', optional => 1 },
|
||||
speed => { type => 'string'},
|
||||
usbpath => { type => 'string', optional => 1},
|
||||
vendid => { type => 'string'},
|
||||
speed => { type => 'string' },
|
||||
usbpath => { type => 'string', optional => 1 },
|
||||
vendid => { type => 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -49,5 +49,5 @@ __PACKAGE__->register_method({
|
|||
my ($param) = @_;
|
||||
|
||||
return PVE::SysFSTools::scan_usb();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,10 +35,20 @@ my $bond_mode_enum = [
|
|||
'balance-slb', # OVS
|
||||
'lacp-balance-slb', # OVS
|
||||
'lacp-balance-tcp', # OVS
|
||||
];
|
||||
];
|
||||
|
||||
my $network_type_enum = ['bridge', 'bond', 'eth', 'alias', 'vlan',
|
||||
'OVSBridge', 'OVSBond', 'OVSPort', 'OVSIntPort', 'vnet'];
|
||||
my $network_type_enum = [
|
||||
'bridge',
|
||||
'bond',
|
||||
'eth',
|
||||
'alias',
|
||||
'vlan',
|
||||
'OVSBridge',
|
||||
'OVSBond',
|
||||
'OVSPort',
|
||||
'OVSIntPort',
|
||||
'vnet',
|
||||
];
|
||||
|
||||
my $confdesc = {
|
||||
type => {
|
||||
|
|
@ -67,19 +77,23 @@ my $confdesc = {
|
|||
optional => 1,
|
||||
},
|
||||
bridge_vids => {
|
||||
description => "Specify the allowed VLANs. For example: '2 4 100-200'. Only used if the bridge is VLAN aware.",
|
||||
description =>
|
||||
"Specify the allowed VLANs. For example: '2 4 100-200'. Only used if the bridge is VLAN aware.",
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-vlan-id-or-range-list',
|
||||
type => 'string',
|
||||
format => 'pve-vlan-id-or-range-list',
|
||||
},
|
||||
bridge_ports => {
|
||||
description => "Specify the interfaces you want to add to your bridge.",
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-iface-list',
|
||||
type => 'string',
|
||||
format => 'pve-iface-list',
|
||||
},
|
||||
ovs_ports => {
|
||||
description => "Specify the interfaces you want to add to your bridge.",
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-iface-list',
|
||||
type => 'string',
|
||||
format => 'pve-iface-list',
|
||||
},
|
||||
ovs_tag => {
|
||||
description => "Specify a VLan tag (used by OVSPort, OVSIntPort, OVSBond)",
|
||||
|
|
@ -95,40 +109,48 @@ my $confdesc = {
|
|||
maxLength => 1024,
|
||||
},
|
||||
ovs_bridge => {
|
||||
description => "The OVS bridge associated with a OVS port. This is required when you create an OVS port.",
|
||||
description =>
|
||||
"The OVS bridge associated with a OVS port. This is required when you create an OVS port.",
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-iface',
|
||||
type => 'string',
|
||||
format => 'pve-iface',
|
||||
},
|
||||
slaves => {
|
||||
description => "Specify the interfaces used by the bonding device.",
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-iface-list',
|
||||
type => 'string',
|
||||
format => 'pve-iface-list',
|
||||
},
|
||||
ovs_bonds => {
|
||||
description => "Specify the interfaces used by the bonding device.",
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-iface-list',
|
||||
type => 'string',
|
||||
format => 'pve-iface-list',
|
||||
},
|
||||
bond_mode => {
|
||||
description => "Bonding mode.",
|
||||
optional => 1,
|
||||
type => 'string', enum => $bond_mode_enum,
|
||||
type => 'string',
|
||||
enum => $bond_mode_enum,
|
||||
},
|
||||
'bond-primary' => {
|
||||
description => "Specify the primary interface for active-backup bond.",
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-iface',
|
||||
type => 'string',
|
||||
format => 'pve-iface',
|
||||
},
|
||||
bond_xmit_hash_policy => {
|
||||
description => "Selects the transmit hash policy to use for slave selection in balance-xor and 802.3ad modes.",
|
||||
description =>
|
||||
"Selects the transmit hash policy to use for slave selection in balance-xor and 802.3ad modes.",
|
||||
optional => 1,
|
||||
type => 'string',
|
||||
enum => ['layer2', 'layer2+3', 'layer3+4' ],
|
||||
enum => ['layer2', 'layer2+3', 'layer3+4'],
|
||||
},
|
||||
'vlan-raw-device' => {
|
||||
description => "Specify the raw interface for the vlan interface.",
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-iface',
|
||||
type => 'string',
|
||||
format => 'pve-iface',
|
||||
},
|
||||
'vlan-id' => {
|
||||
description => "vlan-id for a custom named vlan interface (ifupdown2 only).",
|
||||
|
|
@ -139,24 +161,28 @@ my $confdesc = {
|
|||
},
|
||||
gateway => {
|
||||
description => 'Default gateway address.',
|
||||
type => 'string', format => 'ipv4',
|
||||
type => 'string',
|
||||
format => 'ipv4',
|
||||
optional => 1,
|
||||
},
|
||||
netmask => {
|
||||
description => 'Network mask.',
|
||||
type => 'string', format => 'ipv4mask',
|
||||
type => 'string',
|
||||
format => 'ipv4mask',
|
||||
optional => 1,
|
||||
requires => 'address',
|
||||
},
|
||||
address => {
|
||||
description => 'IP address.',
|
||||
type => 'string', format => 'ipv4',
|
||||
type => 'string',
|
||||
format => 'ipv4',
|
||||
optional => 1,
|
||||
requires => 'netmask',
|
||||
},
|
||||
cidr => {
|
||||
description => 'IPv4 CIDR.',
|
||||
type => 'string', format => 'CIDRv4',
|
||||
type => 'string',
|
||||
format => 'CIDRv4',
|
||||
optional => 1,
|
||||
},
|
||||
mtu => {
|
||||
|
|
@ -168,24 +194,29 @@ my $confdesc = {
|
|||
},
|
||||
gateway6 => {
|
||||
description => 'Default ipv6 gateway address.',
|
||||
type => 'string', format => 'ipv6',
|
||||
type => 'string',
|
||||
format => 'ipv6',
|
||||
optional => 1,
|
||||
},
|
||||
netmask6 => {
|
||||
description => 'Network mask.',
|
||||
type => 'integer', minimum => 0, maximum => 128,
|
||||
type => 'integer',
|
||||
minimum => 0,
|
||||
maximum => 128,
|
||||
optional => 1,
|
||||
requires => 'address6',
|
||||
},
|
||||
address6 => {
|
||||
description => 'IP address.',
|
||||
type => 'string', format => 'ipv6',
|
||||
type => 'string',
|
||||
format => 'ipv6',
|
||||
optional => 1,
|
||||
requires => 'netmask6',
|
||||
},
|
||||
cidr6 => {
|
||||
description => 'IPv6 CIDR.',
|
||||
type => 'string', format => 'CIDRv6',
|
||||
type => 'string',
|
||||
format => 'CIDRv6',
|
||||
optional => 1,
|
||||
},
|
||||
};
|
||||
|
|
@ -214,7 +245,7 @@ __PACKAGE__->register_method({
|
|||
type => {
|
||||
description => "Only list specific interface types.",
|
||||
type => 'string',
|
||||
enum => [ @$network_type_enum, 'any_bridge', 'any_local_bridge' ],
|
||||
enum => [@$network_type_enum, 'any_bridge', 'any_local_bridge'],
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
|
|
@ -343,7 +374,7 @@ __PACKAGE__->register_method({
|
|||
},
|
||||
}),
|
||||
},
|
||||
links => [ { rel => 'child', href => "{iface}" } ],
|
||||
links => [{ rel => 'child', href => "{iface}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -377,29 +408,34 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
if (defined($vnets)) {
|
||||
$ifaces->{$_} = $vnets->{$_} for keys $vnets->%*
|
||||
$ifaces->{$_} = $vnets->{$_} for keys $vnets->%*;
|
||||
}
|
||||
}
|
||||
|
||||
#always check bridge access
|
||||
my $can_access_vnet = sub {
|
||||
return 1 if $authuser eq 'root@pam';
|
||||
return 1 if $rpcenv->check_sdn_bridge($authuser, "localnetwork", $_[0], ['SDN.Audit', 'SDN.Use'], 1);
|
||||
return 1
|
||||
if $rpcenv->check_sdn_bridge(
|
||||
$authuser, "localnetwork", $_[0], ['SDN.Audit', 'SDN.Use'], 1,
|
||||
);
|
||||
};
|
||||
for my $k (sort keys $ifaces->%*) {
|
||||
my $type = $ifaces->{$k}->{type};
|
||||
delete $ifaces->{$k} if ($type eq 'bridge' || $type eq 'OVSBridge') && !$can_access_vnet->($k);
|
||||
delete $ifaces->{$k}
|
||||
if ($type eq 'bridge' || $type eq 'OVSBridge') && !$can_access_vnet->($k);
|
||||
}
|
||||
|
||||
return PVE::RESTHandler::hash_to_array($ifaces, 'iface');
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'revert_network_changes',
|
||||
path => '',
|
||||
method => 'DELETE',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
description => "Revert network configuration changes.",
|
||||
|
|
@ -417,7 +453,8 @@ __PACKAGE__->register_method({
|
|||
unlink "/etc/network/interfaces.new";
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $check_duplicate = sub {
|
||||
my ($config, $newiface, $key, $name) = @_;
|
||||
|
|
@ -490,7 +527,8 @@ my $check_ipv6_settings = sub {
|
|||
my $type = ($binip eq $binmask) ? 'ANYCAST' : Net::IP::ip_iptypev6($binip);
|
||||
|
||||
if (defined($type) && $type !~ /^(?:(?:GLOBAL|(?:UNIQUE|LINK)-LOCAL)-UNICAST)$/) {
|
||||
raise_param_exc({ address => "$address with type '$type', cannot be used as host IPv6 address." });
|
||||
raise_param_exc(
|
||||
{ address => "$address with type '$type', cannot be used as host IPv6 address." });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -527,7 +565,7 @@ __PACKAGE__->register_method({
|
|||
path => '',
|
||||
method => 'POST',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Create network device configuration",
|
||||
protected => 1,
|
||||
|
|
@ -536,7 +574,8 @@ __PACKAGE__->register_method({
|
|||
additionalProperties => 0,
|
||||
properties => json_config_properties({
|
||||
node => get_standard_option('pve-node'),
|
||||
iface => get_standard_option('pve-iface')}),
|
||||
iface => get_standard_option('pve-iface'),
|
||||
}),
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
|
@ -575,8 +614,8 @@ __PACKAGE__->register_method({
|
|||
$param->{method6} = $param->{address6} ? 'static' : 'manual';
|
||||
|
||||
if ($param->{type} =~ m/^OVS/) {
|
||||
-x '/usr/bin/ovs-vsctl' ||
|
||||
die "Open VSwitch is not installed (need package 'openvswitch-switch')\n";
|
||||
-x '/usr/bin/ovs-vsctl'
|
||||
|| die "Open VSwitch is not installed (need package 'openvswitch-switch')\n";
|
||||
}
|
||||
|
||||
if ($param->{type} eq 'OVSIntPort' || $param->{type} eq 'OVSBond') {
|
||||
|
|
@ -587,12 +626,15 @@ __PACKAGE__->register_method({
|
|||
raise_param_exc({ ovs_bridge => "interface '$brname' is no OVS bridge" })
|
||||
if $br->{type} ne 'OVSBridge';
|
||||
|
||||
my @ports = split (/\s+/, $br->{ovs_ports} || '');
|
||||
my @ports = split(/\s+/, $br->{ovs_ports} || '');
|
||||
$br->{ovs_ports} = join(' ', @ports, $iface)
|
||||
if ! grep { $_ eq $iface } @ports;
|
||||
if !grep { $_ eq $iface } @ports;
|
||||
}
|
||||
|
||||
if ($param->{bridge_vids} && scalar(PVE::Tools::split_list($param->{bridge_vids}) == 0)) {
|
||||
if (
|
||||
$param->{bridge_vids}
|
||||
&& scalar(PVE::Tools::split_list($param->{bridge_vids}) == 0)
|
||||
) {
|
||||
raise_param_exc({ bridge_vids => "VLAN list items are empty" });
|
||||
}
|
||||
# make sure the list is space separated! other separators will cause problems in the
|
||||
|
|
@ -609,14 +651,15 @@ __PACKAGE__->register_method({
|
|||
die $@ if $@;
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update_network',
|
||||
path => '{iface}',
|
||||
method => 'PUT',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Update network device configuration",
|
||||
protected => 1,
|
||||
|
|
@ -627,10 +670,12 @@ __PACKAGE__->register_method({
|
|||
node => get_standard_option('pve-node'),
|
||||
iface => get_standard_option('pve-iface'),
|
||||
delete => {
|
||||
type => 'string', format => 'pve-configid-list',
|
||||
type => 'string',
|
||||
format => 'pve-configid-list',
|
||||
description => "A list of settings you want to delete.",
|
||||
optional => 1,
|
||||
}}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
|
@ -690,7 +735,10 @@ __PACKAGE__->register_method({
|
|||
$ifaces->{$iface}->{$k} = $param->{$k};
|
||||
}
|
||||
|
||||
if ($param->{bridge_vids} && scalar(PVE::Tools::split_list($param->{bridge_vids}) == 0)) {
|
||||
if (
|
||||
$param->{bridge_vids}
|
||||
&& scalar(PVE::Tools::split_list($param->{bridge_vids}) == 0)
|
||||
) {
|
||||
raise_param_exc({ bridge_vids => "VLAN list items are empty" });
|
||||
}
|
||||
# make sure the list is space separated! other separators will cause problems in the
|
||||
|
|
@ -705,14 +753,15 @@ __PACKAGE__->register_method({
|
|||
die $@ if $@;
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'network_config',
|
||||
path => '{iface}',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||
},
|
||||
description => "Read network device configuration",
|
||||
proxyto => 'node',
|
||||
|
|
@ -741,10 +790,11 @@ __PACKAGE__->register_method({
|
|||
my $ifaces = $config->{ifaces};
|
||||
|
||||
raise_param_exc({ iface => "interface does not exist" })
|
||||
if !$ifaces->{$param->{iface}};
|
||||
if !$ifaces->{ $param->{iface} };
|
||||
|
||||
return $ifaces->{$param->{iface}};
|
||||
}});
|
||||
return $ifaces->{ $param->{iface} };
|
||||
},
|
||||
});
|
||||
|
||||
sub ifupdown2_version {
|
||||
my $v;
|
||||
|
|
@ -756,11 +806,13 @@ sub ifupdown2_version {
|
|||
|
||||
return ($major * 100000 + $minor * 1000 + $extra * 10, $is_pve, $v);
|
||||
}
|
||||
|
||||
sub assert_ifupdown2_installed {
|
||||
die "you need ifupdown2 to reload network configuration\n" if ! -e '/usr/share/ifupdown2';
|
||||
die "you need ifupdown2 to reload network configuration\n" if !-e '/usr/share/ifupdown2';
|
||||
my ($v, $pve, $v_str) = ifupdown2_version();
|
||||
die "incompatible 'ifupdown2' package version '$v_str'! Did you installed from Proxmox repositories?\n"
|
||||
if $v < (1*100000 + 2*1000 + 8*10) || !$pve;
|
||||
die
|
||||
"incompatible 'ifupdown2' package version '$v_str'! Did you installed from Proxmox repositories?\n"
|
||||
if $v < (1 * 100000 + 2 * 1000 + 8 * 10) || !$pve;
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
|
|
@ -768,7 +820,7 @@ __PACKAGE__->register_method({
|
|||
path => '',
|
||||
method => 'PUT',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Reload network configuration",
|
||||
protected => 1,
|
||||
|
|
@ -815,14 +867,15 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
};
|
||||
return $rpcenv->fork_worker('srvreload', 'networking', $authuser, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete_network',
|
||||
path => '{iface}',
|
||||
method => 'DELETE',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Delete network device configuration",
|
||||
protected => 1,
|
||||
|
|
@ -843,14 +896,14 @@ __PACKAGE__->register_method({
|
|||
my $ifaces = $config->{ifaces};
|
||||
|
||||
raise_param_exc({ iface => "interface does not exist" })
|
||||
if !$ifaces->{$param->{iface}};
|
||||
if !$ifaces->{ $param->{iface} };
|
||||
|
||||
my $d = $ifaces->{$param->{iface}};
|
||||
my $d = $ifaces->{ $param->{iface} };
|
||||
if ($d->{type} eq 'OVSIntPort' || $d->{type} eq 'OVSBond') {
|
||||
if (my $brname = $d->{ovs_bridge}) {
|
||||
if (my $br = $ifaces->{$brname}) {
|
||||
if ($br->{ovs_ports}) {
|
||||
my @ports = split (/\s+/, $br->{ovs_ports});
|
||||
my @ports = split(/\s+/, $br->{ovs_ports});
|
||||
my @new = grep { $_ ne $param->{iface} } @ports;
|
||||
$br->{ovs_ports} = join(' ', @new);
|
||||
}
|
||||
|
|
@ -858,7 +911,7 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
}
|
||||
|
||||
delete $ifaces->{$param->{iface}};
|
||||
delete $ifaces->{ $param->{iface} };
|
||||
|
||||
PVE::INotify::write_file('interfaces', $config);
|
||||
};
|
||||
|
|
@ -867,4 +920,5 @@ __PACKAGE__->register_method({
|
|||
die $@ if $@;
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ use PVE::Tools qw(extract_param);
|
|||
use base qw(PVE::RESTHandler);
|
||||
|
||||
my $node_config_schema = PVE::NodeConfig::get_nodeconfig_schema();
|
||||
my $node_config_keys = [ sort keys %$node_config_schema ];
|
||||
my $node_config_keys = [sort keys %$node_config_schema];
|
||||
my $node_config_return_properties = {
|
||||
digest => {
|
||||
type => 'string',
|
||||
description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
|
||||
description =>
|
||||
'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
|
||||
maxLength => 40,
|
||||
optional => 1,
|
||||
},
|
||||
|
|
@ -22,7 +23,8 @@ my $node_config_return_properties = {
|
|||
};
|
||||
my $node_config_properties = {
|
||||
delete => {
|
||||
type => 'string', format => 'pve-configid-list',
|
||||
type => 'string',
|
||||
format => 'pve-configid-list',
|
||||
description => "A list of settings you want to delete.",
|
||||
optional => 1,
|
||||
},
|
||||
|
|
@ -36,7 +38,7 @@ __PACKAGE__->register_method({
|
|||
method => 'GET',
|
||||
description => "Get node configuration options.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/', ['Sys.Audit']],
|
||||
},
|
||||
proxyto => 'node',
|
||||
parameters => {
|
||||
|
|
@ -61,13 +63,14 @@ __PACKAGE__->register_method({
|
|||
|
||||
my $config = PVE::NodeConfig::load_config($param->{node});
|
||||
|
||||
if (defined (my $prop = $param->{property})) {
|
||||
if (defined(my $prop = $param->{property})) {
|
||||
return {} if !exists $config->{$prop};
|
||||
return { $prop => $config->{$prop} };
|
||||
}
|
||||
|
||||
return $config;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'set_options',
|
||||
|
|
@ -75,7 +78,7 @@ __PACKAGE__->register_method({
|
|||
method => 'PUT',
|
||||
description => "Set node configuration options.",
|
||||
permissions => {
|
||||
check => ['perm', '/', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/', ['Sys.Modify']],
|
||||
},
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
|
|
@ -102,7 +105,7 @@ __PACKAGE__->register_method({
|
|||
|
||||
foreach my $opt (PVE::Tools::split_list($delete)) {
|
||||
delete $conf->{$opt};
|
||||
};
|
||||
}
|
||||
|
||||
PVE::NodeConfig::verify_conf($conf);
|
||||
PVE::NodeConfig::write_config($node, $conf);
|
||||
|
|
@ -112,6 +115,7 @@ __PACKAGE__->register_method({
|
|||
die $@ if $@;
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
154
PVE/API2/Pool.pm
154
PVE/API2/Pool.pm
|
|
@ -16,13 +16,14 @@ use PVE::RESTHandler;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
description => "List pools or get pool configuration.",
|
||||
permissions => {
|
||||
description => "List all pools where you have Pool.Audit permissions on /pool/<pool>, or the pool specific with {poolid}",
|
||||
description =>
|
||||
"List all pools where you have Pool.Audit permissions on /pool/<pool>, or the pool specific with {poolid}",
|
||||
user => 'all',
|
||||
},
|
||||
parameters => {
|
||||
|
|
@ -35,7 +36,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
type => {
|
||||
type => 'string',
|
||||
enum => [ 'qemu', 'lxc', 'storage' ],
|
||||
enum => ['qemu', 'lxc', 'storage'],
|
||||
optional => 1,
|
||||
requires => 'poolid',
|
||||
},
|
||||
|
|
@ -60,7 +61,7 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
type => {
|
||||
type => 'string',
|
||||
enum => [ 'qemu', 'lxc', 'openvz', 'storage' ],
|
||||
enum => ['qemu', 'lxc', 'openvz', 'storage'],
|
||||
},
|
||||
id => {
|
||||
type => 'string',
|
||||
|
|
@ -81,7 +82,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{poolid}" } ],
|
||||
links => [{ rel => 'child', href => "{poolid}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -93,7 +94,7 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $res = [];
|
||||
if (my $poolid = $param->{poolid}) {
|
||||
$rpcenv->check($authuser, "/pool/$poolid", [ 'Pool.Audit' ], 1);
|
||||
$rpcenv->check($authuser, "/pool/$poolid", ['Pool.Audit'], 1);
|
||||
|
||||
my $vmlist = PVE::Cluster::get_vmlist() || {};
|
||||
my $idlist = $vmlist->{ids} || {};
|
||||
|
|
@ -105,9 +106,10 @@ __PACKAGE__->register_method ({
|
|||
die "pool '$poolid' does not exist\n" if !$pool_config;
|
||||
|
||||
my $members = [];
|
||||
for my $vmid (sort keys %{$pool_config->{vms}}) {
|
||||
for my $vmid (sort keys %{ $pool_config->{vms} }) {
|
||||
my $vmdata = $idlist->{$vmid};
|
||||
next if !$vmdata || defined($param->{type}) && $param->{type} ne $vmdata->{type};
|
||||
next
|
||||
if !$vmdata || defined($param->{type}) && $param->{type} ne $vmdata->{type};
|
||||
my $entry = PVE::API2Tools::extract_vm_stats($vmid, $vmdata, $rrd);
|
||||
push @$members, $entry;
|
||||
}
|
||||
|
|
@ -115,19 +117,20 @@ __PACKAGE__->register_method ({
|
|||
my $nodename = PVE::INotify::nodename();
|
||||
my $cfg = PVE::Storage::config();
|
||||
if (!defined($param->{type}) || $param->{type} eq 'storage') {
|
||||
for my $storeid (sort keys %{$pool_config->{storage}}) {
|
||||
my $scfg = PVE::Storage::storage_config ($cfg, $storeid, 1);
|
||||
for my $storeid (sort keys %{ $pool_config->{storage} }) {
|
||||
my $scfg = PVE::Storage::storage_config($cfg, $storeid, 1);
|
||||
next if !$scfg;
|
||||
|
||||
my $storage_node = $nodename; # prefer local node
|
||||
if ($scfg->{nodes} && !$scfg->{nodes}->{$storage_node}) {
|
||||
for my $node (sort keys(%{$scfg->{nodes}})) {
|
||||
for my $node (sort keys(%{ $scfg->{nodes} })) {
|
||||
$storage_node = $node;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
my $entry = PVE::API2Tools::extract_storage_stats($storeid, $scfg, $storage_node, $rrd);
|
||||
my $entry =
|
||||
PVE::API2Tools::extract_storage_stats($storeid, $scfg, $storage_node, $rrd);
|
||||
push @$members, $entry;
|
||||
}
|
||||
}
|
||||
|
|
@ -140,8 +143,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
push @$res, $pool_info;
|
||||
} else {
|
||||
for my $pool (sort keys %{$usercfg->{pools}}) {
|
||||
next if !$rpcenv->check($authuser, "/pool/$pool", [ 'Pool.Audit' ], 1);
|
||||
for my $pool (sort keys %{ $usercfg->{pools} }) {
|
||||
next if !$rpcenv->check($authuser, "/pool/$pool", ['Pool.Audit'], 1);
|
||||
|
||||
my $entry = { poolid => $pool };
|
||||
my $pool_config = $usercfg->{pools}->{$pool};
|
||||
|
|
@ -151,9 +154,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'create_pool',
|
||||
protected => 1,
|
||||
path => '',
|
||||
|
|
@ -179,7 +183,8 @@ __PACKAGE__->register_method ({
|
|||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
PVE::AccessControl::lock_user_config(sub {
|
||||
PVE::AccessControl::lock_user_config(
|
||||
sub {
|
||||
my $usercfg = cfs_read_file("user.cfg");
|
||||
my $pool = $param->{poolid};
|
||||
|
||||
|
|
@ -198,21 +203,26 @@ __PACKAGE__->register_method ({
|
|||
$usercfg->{pools}->{$pool}->{comment} = $param->{comment} if $param->{comment};
|
||||
|
||||
cfs_write_file("user.cfg", $usercfg);
|
||||
}, "create pool failed");
|
||||
},
|
||||
"create pool failed",
|
||||
);
|
||||
|
||||
return;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update_pool_deprecated',
|
||||
protected => 1,
|
||||
path => '{poolid}',
|
||||
method => 'PUT',
|
||||
permissions => {
|
||||
description => "You also need the right to modify permissions on any object you add/delete.",
|
||||
description =>
|
||||
"You also need the right to modify permissions on any object you add/delete.",
|
||||
check => ['perm', '/pool/{poolid}', ['Pool.Allocate']],
|
||||
},
|
||||
description => "Update pool data (deprecated, no support for nested pools - use 'PUT /pools/?poolid={poolid}' instead).",
|
||||
description =>
|
||||
"Update pool data (deprecated, no support for nested pools - use 'PUT /pools/?poolid={poolid}' instead).",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
|
|
@ -220,23 +230,26 @@ __PACKAGE__->register_method ({
|
|||
comment => { type => 'string', optional => 1 },
|
||||
vms => {
|
||||
description => 'List of guest VMIDs to add or remove from this pool.',
|
||||
type => 'string', format => 'pve-vmid-list',
|
||||
type => 'string',
|
||||
format => 'pve-vmid-list',
|
||||
optional => 1,
|
||||
},
|
||||
storage => {
|
||||
description => 'List of storage IDs to add or remove from this pool.',
|
||||
type => 'string', format => 'pve-storage-id-list',
|
||||
type => 'string',
|
||||
format => 'pve-storage-id-list',
|
||||
optional => 1,
|
||||
},
|
||||
'allow-move' => {
|
||||
description => 'Allow adding a guest even if already in another pool.'
|
||||
.' The guest will be removed from its current pool and added to this one.',
|
||||
. ' The guest will be removed from its current pool and added to this one.',
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 0,
|
||||
},
|
||||
delete => {
|
||||
description => 'Remove the passed VMIDs and/or storage IDs instead of adding them.',
|
||||
description =>
|
||||
'Remove the passed VMIDs and/or storage IDs instead of adding them.',
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 0,
|
||||
|
|
@ -248,15 +261,17 @@ __PACKAGE__->register_method ({
|
|||
my ($param) = @_;
|
||||
|
||||
return __PACKAGE__->update_pool($param);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update_pool',
|
||||
protected => 1,
|
||||
path => '',
|
||||
method => 'PUT',
|
||||
permissions => {
|
||||
description => "You also need the right to modify permissions on any object you add/delete.",
|
||||
description =>
|
||||
"You also need the right to modify permissions on any object you add/delete.",
|
||||
check => ['perm', '/pool/{poolid}', ['Pool.Allocate']],
|
||||
},
|
||||
description => "Update pool.",
|
||||
|
|
@ -267,23 +282,26 @@ __PACKAGE__->register_method ({
|
|||
comment => { type => 'string', optional => 1 },
|
||||
vms => {
|
||||
description => 'List of guest VMIDs to add or remove from this pool.',
|
||||
type => 'string', format => 'pve-vmid-list',
|
||||
type => 'string',
|
||||
format => 'pve-vmid-list',
|
||||
optional => 1,
|
||||
},
|
||||
storage => {
|
||||
description => 'List of storage IDs to add or remove from this pool.',
|
||||
type => 'string', format => 'pve-storage-id-list',
|
||||
type => 'string',
|
||||
format => 'pve-storage-id-list',
|
||||
optional => 1,
|
||||
},
|
||||
'allow-move' => {
|
||||
description => 'Allow adding a guest even if already in another pool.'
|
||||
.' The guest will be removed from its current pool and added to this one.',
|
||||
. ' The guest will be removed from its current pool and added to this one.',
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 0,
|
||||
},
|
||||
delete => {
|
||||
description => 'Remove the passed VMIDs and/or storage IDs instead of adding them.',
|
||||
description =>
|
||||
'Remove the passed VMIDs and/or storage IDs instead of adding them.',
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 0,
|
||||
|
|
@ -297,7 +315,8 @@ __PACKAGE__->register_method ({
|
|||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
PVE::AccessControl::lock_user_config(sub {
|
||||
PVE::AccessControl::lock_user_config(
|
||||
sub {
|
||||
my $usercfg = cfs_read_file("user.cfg");
|
||||
my $pool = $param->{poolid};
|
||||
my $pool_config = $usercfg->{pools}->{$pool};
|
||||
|
|
@ -311,16 +330,21 @@ __PACKAGE__->register_method ({
|
|||
$rpcenv->check_perm_modify($authuser, "/vms/$vmid");
|
||||
|
||||
if ($param->{delete}) {
|
||||
die "VM $vmid is not a pool member\n" if !$pool_config->{vms}->{$vmid};
|
||||
die "VM $vmid is not a pool member\n"
|
||||
if !$pool_config->{vms}->{$vmid};
|
||||
delete $pool_config->{vms}->{$vmid};
|
||||
delete $usercfg->{vms}->{$vmid};
|
||||
} else {
|
||||
die "VM $vmid is already a pool member\n" if $pool_config->{vms}->{$vmid};
|
||||
die "VM $vmid is already a pool member\n"
|
||||
if $pool_config->{vms}->{$vmid};
|
||||
if (defined(my $existing_pool = $usercfg->{vms}->{$vmid})) {
|
||||
die "VM $vmid belongs already to pool '$existing_pool' and 'allow-move' is not set\n"
|
||||
die
|
||||
"VM $vmid belongs already to pool '$existing_pool' and 'allow-move' is not set\n"
|
||||
if !$param->{'allow-move'};
|
||||
|
||||
$rpcenv->check($authuser, "/pool/$existing_pool", ['Pool.Allocate']);
|
||||
$rpcenv->check(
|
||||
$authuser, "/pool/$existing_pool", ['Pool.Allocate'],
|
||||
);
|
||||
delete $usercfg->{pools}->{$existing_pool}->{vms}->{$vmid};
|
||||
}
|
||||
$pool_config->{vms}->{$vmid} = 1;
|
||||
|
|
@ -347,19 +371,23 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
cfs_write_file("user.cfg", $usercfg);
|
||||
}, "update pools failed");
|
||||
},
|
||||
"update pools failed",
|
||||
);
|
||||
|
||||
return;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'read_pool',
|
||||
path => '{poolid}',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
check => ['perm', '/pool/{poolid}', ['Pool.Audit']],
|
||||
},
|
||||
description => "Get pool configuration (deprecated, no support for nested pools, use 'GET /pools/?poolid={poolid}').",
|
||||
description =>
|
||||
"Get pool configuration (deprecated, no support for nested pools, use 'GET /pools/?poolid={poolid}').",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
|
|
@ -369,7 +397,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
type => {
|
||||
type => 'string',
|
||||
enum => [ 'qemu', 'lxc', 'storage' ],
|
||||
enum => ['qemu', 'lxc', 'storage'],
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
|
|
@ -390,7 +418,7 @@ __PACKAGE__->register_method ({
|
|||
properties => {
|
||||
type => {
|
||||
type => 'string',
|
||||
enum => [ 'qemu', 'lxc', 'openvz', 'storage' ],
|
||||
enum => ['qemu', 'lxc', 'openvz', 'storage'],
|
||||
},
|
||||
id => {
|
||||
type => 'string',
|
||||
|
|
@ -416,10 +444,10 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $pool_info = __PACKAGE__->index($param);
|
||||
return $pool_info->[0];
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete_pool_deprecated',
|
||||
protected => 1,
|
||||
path => '{poolid}',
|
||||
|
|
@ -428,21 +456,23 @@ __PACKAGE__->register_method ({
|
|||
description => "You can only delete empty pools (no members).",
|
||||
check => ['perm', '/pool/{poolid}', ['Pool.Allocate']],
|
||||
},
|
||||
description => "Delete pool (deprecated, no support for nested pools, use 'DELETE /pools/?poolid={poolid}').",
|
||||
description =>
|
||||
"Delete pool (deprecated, no support for nested pools, use 'DELETE /pools/?poolid={poolid}').",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
poolid => { type => 'string', format => 'pve-poolid' },
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
return __PACKAGE__->delete_pool($param);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete_pool',
|
||||
protected => 1,
|
||||
path => '',
|
||||
|
|
@ -456,7 +486,7 @@ __PACKAGE__->register_method ({
|
|||
additionalProperties => 0,
|
||||
properties => {
|
||||
poolid => { type => 'string', format => 'pve-poolid' },
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
|
@ -465,7 +495,8 @@ __PACKAGE__->register_method ({
|
|||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
PVE::AccessControl::lock_user_config(sub {
|
||||
PVE::AccessControl::lock_user_config(
|
||||
sub {
|
||||
my $vmlist = PVE::Cluster::get_vmlist() || {};
|
||||
my $idlist = $vmlist->{ids} || {};
|
||||
|
||||
|
|
@ -476,27 +507,30 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $pool_config = $usercfg->{pools}->{$pool};
|
||||
die "pool '$pool' does not exist\n" if !$pool_config;
|
||||
for my $subpool (sort keys %{$pool_config->{pools}}) {
|
||||
for my $subpool (sort keys %{ $pool_config->{pools} }) {
|
||||
die "pool '$pool' is not empty (contains pool '$subpool')\n";
|
||||
}
|
||||
|
||||
for my $vmid (sort keys %{$pool_config->{vms}}) {
|
||||
for my $vmid (sort keys %{ $pool_config->{vms} }) {
|
||||
next if !$idlist->{$vmid}; # ignore destroyed guests
|
||||
die "pool '$pool' is not empty (contains VM $vmid)\n";
|
||||
}
|
||||
|
||||
for my $storeid (sort keys %{$pool_config->{storage}}) {
|
||||
next if !PVE::Storage::storage_config ($storecfg, $storeid, 1);
|
||||
for my $storeid (sort keys %{ $pool_config->{storage} }) {
|
||||
next if !PVE::Storage::storage_config($storecfg, $storeid, 1);
|
||||
die "pool '$pool' is not empty (contains storage '$storeid')\n";
|
||||
}
|
||||
|
||||
delete ($usercfg->{pools}->{$pool});
|
||||
delete($usercfg->{pools}->{$pool});
|
||||
PVE::AccessControl::delete_pool_acl($pool, $usercfg);
|
||||
|
||||
cfs_write_file("user.cfg", $usercfg);
|
||||
}, "delete pool failed");
|
||||
},
|
||||
"delete pool failed",
|
||||
);
|
||||
|
||||
return;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ sub run_single_job {
|
|||
die $@ if $@;
|
||||
}
|
||||
|
||||
|
||||
# TODO: below two should probably part of the general job framework/plugin system
|
||||
my sub _should_mail_at_failcount {
|
||||
my ($fail_count) = @_;
|
||||
|
|
@ -90,8 +89,7 @@ my sub _should_mail_at_failcount {
|
|||
$i = $i * 2;
|
||||
}
|
||||
return $i * 48 == $fail_count;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
my sub _handle_job_err {
|
||||
my ($job, $err, $mail) = @_;
|
||||
|
|
@ -129,9 +127,7 @@ my sub _handle_job_err {
|
|||
|
||||
eval {
|
||||
PVE::Notify::error(
|
||||
"replication",
|
||||
$template_data,
|
||||
$metadata_fields
|
||||
"replication", $template_data, $metadata_fields,
|
||||
);
|
||||
|
||||
};
|
||||
|
|
@ -153,7 +149,9 @@ sub run_jobs {
|
|||
my $guest_class = $lookup_guest_class->($jobcfg->{vmtype});
|
||||
|
||||
eval {
|
||||
PVE::Replication::run_replication($guest_class, $jobcfg, $iteration, $start_time, $logfunc, $verbose);
|
||||
PVE::Replication::run_replication(
|
||||
$guest_class, $jobcfg, $iteration, $start_time, $logfunc, $verbose,
|
||||
);
|
||||
};
|
||||
if (my $err = $@) {
|
||||
_handle_job_err($jobcfg, $err, $mail);
|
||||
|
|
@ -189,7 +187,7 @@ my $extract_job_status = sub {
|
|||
return $data;
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'status',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -204,10 +202,13 @@ __PACKAGE__->register_method ({
|
|||
additionalProperties => 0,
|
||||
properties => {
|
||||
node => get_standard_option('pve-node'),
|
||||
guest => get_standard_option('pve-vmid', {
|
||||
guest => get_standard_option(
|
||||
'pve-vmid',
|
||||
{
|
||||
optional => 1,
|
||||
description => "Only list replication jobs for this guest.",
|
||||
}),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -218,7 +219,7 @@ __PACKAGE__->register_method ({
|
|||
id => { type => 'string' },
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -233,14 +234,15 @@ __PACKAGE__->register_method ({
|
|||
my $data = $extract_job_status->($jobs->{$id}, $id);
|
||||
my $guest = $data->{guest};
|
||||
next if defined($param->{guest}) && $guest != $param->{guest};
|
||||
next if !$rpcenv->check($authuser, "/vms/$guest", [ 'VM.Audit' ]);
|
||||
next if !$rpcenv->check($authuser, "/vms/$guest", ['VM.Audit']);
|
||||
push @$res, $data;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '{id}',
|
||||
method => 'GET',
|
||||
|
|
@ -259,20 +261,18 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
return [
|
||||
{ name => 'schedule_now' },
|
||||
{ name => 'log' },
|
||||
{ name => 'status' },
|
||||
{ name => 'schedule_now' }, { name => 'log' }, { name => 'status' },
|
||||
];
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'job_status',
|
||||
path => '{id}/status',
|
||||
method => 'GET',
|
||||
|
|
@ -309,17 +309,19 @@ __PACKAGE__->register_method ({
|
|||
my $data = $extract_job_status->($jobcfg, $jobid);
|
||||
my $guest = $data->{guest};
|
||||
|
||||
raise_perm_exc() if !$rpcenv->check($authuser, "/vms/$guest", [ 'VM.Audit' ]);
|
||||
raise_perm_exc() if !$rpcenv->check($authuser, "/vms/$guest", ['VM.Audit']);
|
||||
|
||||
return $data;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'read_job_log',
|
||||
path => '{id}/log',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
description => "Requires the VM.Audit permission on /vms/<vmid>, or 'Sys.Audit' on '/nodes/<node>'",
|
||||
description =>
|
||||
"Requires the VM.Audit permission on /vms/<vmid>, or 'Sys.Audit' on '/nodes/<node>'",
|
||||
user => 'all',
|
||||
},
|
||||
protected => 1,
|
||||
|
|
@ -348,15 +350,15 @@ __PACKAGE__->register_method({
|
|||
type => "object",
|
||||
properties => {
|
||||
n => {
|
||||
description=> "Line number",
|
||||
type=> 'integer',
|
||||
description => "Line number",
|
||||
type => 'integer',
|
||||
},
|
||||
t => {
|
||||
description=> "Line text",
|
||||
description => "Line text",
|
||||
type => 'string',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -375,17 +377,22 @@ __PACKAGE__->register_method({
|
|||
my $node = $param->{node};
|
||||
|
||||
my $vmid = $data->{guest};
|
||||
raise_perm_exc() if (!($rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ]) ||
|
||||
$rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ])));
|
||||
raise_perm_exc()
|
||||
if (!(
|
||||
$rpcenv->check($authuser, "/vms/$vmid", ['VM.Audit'])
|
||||
|| $rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'])
|
||||
));
|
||||
|
||||
my ($count, $lines) = PVE::Tools::dump_logfile($filename, $param->{start}, $param->{limit});
|
||||
my ($count, $lines) =
|
||||
PVE::Tools::dump_logfile($filename, $param->{start}, $param->{limit});
|
||||
|
||||
$rpcenv->set_result_attrib('total', $count);
|
||||
|
||||
return $lines;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'schedule_now',
|
||||
path => '{id}/schedule_now',
|
||||
method => 'POST',
|
||||
|
|
@ -417,6 +424,7 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::ReplicationState::schedule_job_now($jobcfg);
|
||||
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use PVE::RESTHandler;
|
|||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -34,7 +34,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{id}" } ],
|
||||
links => [{ rel => 'child', href => "{id}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -45,18 +45,19 @@ __PACKAGE__->register_method ({
|
|||
my $cfg = PVE::ReplicationConfig->new();
|
||||
|
||||
my $res = [];
|
||||
foreach my $id (sort keys %{$cfg->{ids}}) {
|
||||
foreach my $id (sort keys %{ $cfg->{ids} }) {
|
||||
my $d = $cfg->{ids}->{$id};
|
||||
my $vmid = $d->{guest};
|
||||
next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
|
||||
next if !$rpcenv->check($authuser, "/vms/$vmid", ['VM.Audit'], 1);
|
||||
$d->{id} = $id;
|
||||
push @$res, $d;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'read',
|
||||
path => '{id}',
|
||||
method => 'GET',
|
||||
|
|
@ -80,22 +81,23 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $cfg = PVE::ReplicationConfig->new();
|
||||
|
||||
my $data = $cfg->{ids}->{$param->{id}};
|
||||
my $data = $cfg->{ids}->{ $param->{id} };
|
||||
|
||||
die "no such replication job '$param->{id}'\n" if !defined($data);
|
||||
|
||||
my $vmid = $data->{guest};
|
||||
|
||||
raise_perm_exc() if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ]);
|
||||
raise_perm_exc() if !$rpcenv->check($authuser, "/vms/$vmid", ['VM.Audit']);
|
||||
|
||||
$data->{id} = $param->{id};
|
||||
|
||||
$data->{digest} = $cfg->{digest};
|
||||
|
||||
return $data;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'create',
|
||||
path => '',
|
||||
protected => 1,
|
||||
|
|
@ -124,10 +126,11 @@ __PACKAGE__->register_method ({
|
|||
die "Guest '$guest' does not exist.\n"
|
||||
if !defined($guest_info);
|
||||
die "Target '$param->{target}' does not exist.\n"
|
||||
if !defined($nodelist->{$param->{target}});
|
||||
if !defined($nodelist->{ $param->{target} });
|
||||
|
||||
my $source = $guest_info->{node};
|
||||
die "Source '$param->{source}' does not match current node of guest '$guest' ($source)\n"
|
||||
die
|
||||
"Source '$param->{source}' does not match current node of guest '$guest' ($source)\n"
|
||||
if defined($param->{source}) && $param->{source} ne $source;
|
||||
|
||||
$param->{source} //= $source;
|
||||
|
|
@ -137,7 +140,9 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $guest_class = $PVE::API2::Replication::lookup_guest_class->($guest_info->{type});
|
||||
my $guest_conf = $guest_class->load_config($guest, $source);
|
||||
my $rep_volumes = $guest_class->get_replicatable_volumes(PVE::Storage::config(), $guest, $guest_conf, 0, 0);
|
||||
my $rep_volumes = $guest_class->get_replicatable_volumes(
|
||||
PVE::Storage::config(), $guest, $guest_conf, 0, 0,
|
||||
);
|
||||
die "No replicatable volumes found\n" if !%$rep_volumes;
|
||||
|
||||
my $code = sub {
|
||||
|
|
@ -158,10 +163,10 @@ __PACKAGE__->register_method ({
|
|||
PVE::ReplicationConfig::lock($code);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
|
|
@ -195,10 +200,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
if ($delete) {
|
||||
my $options = $plugin->private()->{options}->{$data->{type}};
|
||||
my $options = $plugin->private()->{options}->{ $data->{type} };
|
||||
foreach my $k (PVE::Tools::split_list($delete)) {
|
||||
my $d = $options->{$k} ||
|
||||
die "no such option '$k'\n";
|
||||
my $d = $options->{$k}
|
||||
|| die "no such option '$k'\n";
|
||||
die "unable to delete required option '$k'\n"
|
||||
if !$d->{optional};
|
||||
die "unable to delete fixed option '$k'\n"
|
||||
|
|
@ -213,9 +218,10 @@ __PACKAGE__->register_method ({
|
|||
PVE::ReplicationConfig::lock($code);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete',
|
||||
protected => 1,
|
||||
path => '{id}',
|
||||
|
|
@ -240,7 +246,7 @@ __PACKAGE__->register_method ({
|
|||
optional => 1,
|
||||
default => 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
|
@ -253,7 +259,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $id = $param->{id};
|
||||
if ($param->{force}) {
|
||||
raise_param_exc({ 'keep' => "conflicts with parameter 'force'" }) if $param->{keep};
|
||||
raise_param_exc({ 'keep' => "conflicts with parameter 'force'" })
|
||||
if $param->{keep};
|
||||
delete $cfg->{ids}->{$id};
|
||||
} else {
|
||||
my $jobcfg = $cfg->{ids}->{$id};
|
||||
|
|
@ -276,6 +283,7 @@ __PACKAGE__->register_method ({
|
|||
PVE::ReplicationConfig::lock($code);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ my $essential_services = {
|
|||
# manage subinstances, of which the default is called "-".
|
||||
# This is where we look for the daemon status
|
||||
my $unit_extra_names = {
|
||||
postfix => 'postfix@-'
|
||||
postfix => 'postfix@-',
|
||||
};
|
||||
|
||||
my $get_full_service_state = sub {
|
||||
|
|
@ -87,7 +87,6 @@ sub get_service_list {
|
|||
return $static_service_list;
|
||||
}
|
||||
|
||||
|
||||
my $service_prop_desc = {
|
||||
description => "Service ID",
|
||||
type => 'string',
|
||||
|
|
@ -99,7 +98,8 @@ my $service_cmd = sub {
|
|||
|
||||
my $initd_cmd;
|
||||
|
||||
die "unknown service command '$cmd'\n" if $cmd !~ m/^(start|stop|restart|reload|try-reload-or-restart)$/;
|
||||
die "unknown service command '$cmd'\n"
|
||||
if $cmd !~ m/^(start|stop|restart|reload|try-reload-or-restart)$/;
|
||||
|
||||
if ($essential_services->{$service} && $cmd eq 'stop') {
|
||||
die "invalid service cmd '$service $cmd': refusing to stop essential service!\n";
|
||||
|
|
@ -134,12 +134,12 @@ my $service_state = sub {
|
|||
return $res;
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||
},
|
||||
description => "Service list.",
|
||||
proxyto => 'node',
|
||||
|
|
@ -156,7 +156,7 @@ __PACKAGE__->register_method ({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{service}" } ],
|
||||
links => [{ rel => 'child', href => "{service}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -166,7 +166,8 @@ __PACKAGE__->register_method ({
|
|||
my $res = [];
|
||||
for my $id (sort keys %{$service_list}) {
|
||||
my $state = $service_state->($id);
|
||||
push @$res, {
|
||||
push @$res,
|
||||
{
|
||||
service => $id,
|
||||
name => $service_list->{$id}->{name},
|
||||
desc => $service_list->{$id}->{desc},
|
||||
|
|
@ -175,7 +176,8 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'srvcmdidx',
|
||||
|
|
@ -183,7 +185,7 @@ __PACKAGE__->register_method({
|
|||
method => 'GET',
|
||||
description => "Directory index",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
|
|
@ -200,7 +202,7 @@ __PACKAGE__->register_method({
|
|||
subdir => { type => 'string' },
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{subdir}" } ],
|
||||
links => [{ rel => 'child', href => "{subdir}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -214,14 +216,15 @@ __PACKAGE__->register_method({
|
|||
];
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'service_state',
|
||||
path => '{service}/state',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||
},
|
||||
description => "Read service properties",
|
||||
proxyto => 'node',
|
||||
|
|
@ -254,15 +257,16 @@ __PACKAGE__->register_method ({
|
|||
desc => $si->{desc},
|
||||
%$state,
|
||||
};
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'service_start',
|
||||
path => '{service}/start',
|
||||
method => 'POST',
|
||||
description => "Start service.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
|
|
@ -293,15 +297,16 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('srvstart', $param->{service}, $user, $realcmd);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'service_stop',
|
||||
path => '{service}/stop',
|
||||
method => 'POST',
|
||||
description => "Stop service.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
|
|
@ -332,15 +337,16 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('srvstop', $param->{service}, $user, $realcmd);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'service_restart',
|
||||
path => '{service}/restart',
|
||||
method => 'POST',
|
||||
description => "Hard restart service. Use reload if you want to reduce interruptions.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
|
|
@ -368,15 +374,16 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('srvrestart', $param->{service}, $user, $realcmd);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'service_reload',
|
||||
path => '{service}/reload',
|
||||
method => 'POST',
|
||||
description => "Reload service. Falls back to restart if service cannot be reloaded.",
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
proxyto => 'node',
|
||||
protected => 1,
|
||||
|
|
@ -405,4 +412,5 @@ __PACKAGE__->register_method ({
|
|||
};
|
||||
|
||||
return $rpcenv->fork_worker('srvreload', $param->{service}, $user, $realcmd);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ my sub cache_is_valid {
|
|||
return if !$info || $info->{status} ne 'active';
|
||||
|
||||
my $checked_info = Proxmox::RS::Subscription::check_age($info, 1);
|
||||
return $checked_info->{status} eq 'active'
|
||||
return $checked_info->{status} eq 'active';
|
||||
}
|
||||
|
||||
sub write_etc_subscription {
|
||||
|
|
@ -92,25 +92,32 @@ sub write_etc_subscription {
|
|||
my $server_id = PVE::API2Tools::get_hwaddress();
|
||||
mkdir "/etc/apt/auth.conf.d";
|
||||
Proxmox::RS::Subscription::write_subscription(
|
||||
$filename, "/etc/apt/auth.conf.d/pve.conf", "enterprise.proxmox.com/debian/pve", $info);
|
||||
$filename,
|
||||
"/etc/apt/auth.conf.d/pve.conf",
|
||||
"enterprise.proxmox.com/debian/pve",
|
||||
$info,
|
||||
);
|
||||
|
||||
if (!(defined($info->{key}) && defined($info->{serverid}))) {
|
||||
unlink "/etc/apt/auth.conf.d/ceph.conf" or $!{ENOENT} or die "failed to remove apt auth ceph.conf - $!";
|
||||
unlink "/etc/apt/auth.conf.d/ceph.conf"
|
||||
or $!{ENOENT}
|
||||
or die "failed to remove apt auth ceph.conf - $!";
|
||||
} else {
|
||||
my $supported_ceph_releases = PVE::Ceph::Releases::get_available_ceph_release_codenames(1);
|
||||
my $ceph_auth = '';
|
||||
for my $ceph_release ($supported_ceph_releases->@*) {
|
||||
$ceph_auth .= "machine enterprise.proxmox.com/debian/ceph-${ceph_release}"
|
||||
." login $info->{key} password $info->{serverid}\n"
|
||||
. " login $info->{key} password $info->{serverid}\n";
|
||||
}
|
||||
# add a generic one to handle the case where a new ceph release was made available while
|
||||
# the subscription info was not yet updated, and as per APT_AUTH.CONF(5) start-with matches.
|
||||
$ceph_auth .= "machine enterprise.proxmox.com/debian/ceph login $info->{key} password $info->{serverid}\n";
|
||||
$ceph_auth .=
|
||||
"machine enterprise.proxmox.com/debian/ceph login $info->{key} password $info->{serverid}\n";
|
||||
PVE::Tools::file_set_contents("/etc/apt/auth.conf.d/ceph.conf", $ceph_auth, 0600);
|
||||
}
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
|
|
@ -217,22 +224,23 @@ __PACKAGE__->register_method ({
|
|||
status => $info->{status},
|
||||
message => $info->{message},
|
||||
url => $url,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$info->{serverid} = $server_id;
|
||||
$info->{sockets} = get_sockets();
|
||||
$info->{url} = $url;
|
||||
|
||||
return $info
|
||||
}});
|
||||
return $info;
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update',
|
||||
path => '',
|
||||
method => 'POST',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Update subscription info.",
|
||||
proxyto => 'node',
|
||||
|
|
@ -245,11 +253,11 @@ __PACKAGE__->register_method ({
|
|||
description => "Always connect to server, even if local cache is still valid.",
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
default => 0
|
||||
}
|
||||
default => 0,
|
||||
},
|
||||
},
|
||||
returns => { type => 'null'},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
|
|
@ -259,7 +267,8 @@ __PACKAGE__->register_method ({
|
|||
my $server_id = PVE::API2Tools::get_hwaddress();
|
||||
my $key = $info->{key};
|
||||
|
||||
die "Updating offline key not possible - please remove and re-add subscription key to switch to online key.\n"
|
||||
die
|
||||
"Updating offline key not possible - please remove and re-add subscription key to switch to online key.\n"
|
||||
if $info->{signature};
|
||||
|
||||
return undef if !$param->{force} && cache_is_valid($info); # key has been recently checked
|
||||
|
|
@ -270,19 +279,22 @@ __PACKAGE__->register_method ({
|
|||
my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
|
||||
my $proxy = $dccfg->{http_proxy};
|
||||
|
||||
$info = Proxmox::RS::Subscription::check_subscription($key, $server_id, "", "Proxmox VE", $proxy);
|
||||
$info = Proxmox::RS::Subscription::check_subscription(
|
||||
$key, $server_id, "", "Proxmox VE", $proxy,
|
||||
);
|
||||
|
||||
write_etc_subscription($info);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'set',
|
||||
path => '',
|
||||
method => 'PUT',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Set subscription key.",
|
||||
proxyto => 'node',
|
||||
|
|
@ -299,7 +311,7 @@ __PACKAGE__->register_method ({
|
|||
},
|
||||
},
|
||||
},
|
||||
returns => { type => 'null'},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
|
|
@ -322,19 +334,21 @@ __PACKAGE__->register_method ({
|
|||
my $proxy = $dccfg->{http_proxy};
|
||||
|
||||
my $checked_info = Proxmox::RS::Subscription::check_subscription(
|
||||
$key, $server_id, "", "Proxmox VE", $proxy);
|
||||
$key, $server_id, "", "Proxmox VE", $proxy,
|
||||
);
|
||||
|
||||
write_etc_subscription($checked_info);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete',
|
||||
path => '',
|
||||
method => 'DELETE',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Modify']],
|
||||
},
|
||||
description => "Delete subscription key of this node.",
|
||||
proxyto => 'node',
|
||||
|
|
@ -345,12 +359,13 @@ __PACKAGE__->register_method ({
|
|||
node => get_standard_option('pve-node'),
|
||||
},
|
||||
},
|
||||
returns => { type => 'null'},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
my $subscription_file = '/etc/subscription';
|
||||
return if ! -e $subscription_file;
|
||||
return if !-e $subscription_file;
|
||||
unlink($subscription_file) or die "cannot delete subscription key: $!";
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@ __PACKAGE__->register_method({
|
|||
path => '',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
description => "List task associated with the current user, or all task the user has 'Sys.Audit' permissions on /nodes/<node> (the <node> the task runs on).",
|
||||
user => 'all'
|
||||
description =>
|
||||
"List task associated with the current user, or all task the user has 'Sys.Audit' permissions on /nodes/<node> (the <node> the task runs on).",
|
||||
user => 'all',
|
||||
},
|
||||
description => "Read task list for one node (finished tasks).",
|
||||
proxyto => 'node',
|
||||
|
|
@ -74,10 +75,13 @@ __PACKAGE__->register_method({
|
|||
optional => 1,
|
||||
description => 'Only list tasks of this type (e.g., vzstart, vzdump).',
|
||||
},
|
||||
vmid => get_standard_option('pve-vmid', {
|
||||
vmid => get_standard_option(
|
||||
'pve-vmid',
|
||||
{
|
||||
description => "Only list tasks for this VM.",
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
errors => {
|
||||
type => 'boolean',
|
||||
default => 0,
|
||||
|
|
@ -114,19 +118,19 @@ __PACKAGE__->register_method({
|
|||
items => {
|
||||
type => "object",
|
||||
properties => {
|
||||
upid => { type => 'string', title => 'UPID', },
|
||||
node => { type => 'string', title => 'Node', },
|
||||
pid => { type => 'integer', title => 'PID', },
|
||||
pstart => { type => 'integer', },
|
||||
starttime => { type => 'integer', title => 'Starttime', },
|
||||
type => { type => 'string', title => 'Type', },
|
||||
id => { type => 'string', title => 'ID', },
|
||||
user => { type => 'string', title => 'User', },
|
||||
endtime => { type => 'integer', optional => 1, title => 'Endtime', },
|
||||
status => { type => 'string', optional => 1, title => 'Status', },
|
||||
upid => { type => 'string', title => 'UPID' },
|
||||
node => { type => 'string', title => 'Node' },
|
||||
pid => { type => 'integer', title => 'PID' },
|
||||
pstart => { type => 'integer' },
|
||||
starttime => { type => 'integer', title => 'Starttime' },
|
||||
type => { type => 'string', title => 'Type' },
|
||||
id => { type => 'string', title => 'ID' },
|
||||
user => { type => 'string', title => 'User' },
|
||||
endtime => { type => 'integer', optional => 1, title => 'Endtime' },
|
||||
status => { type => 'string', optional => 1, title => 'Status' },
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{upid}" } ],
|
||||
links => [{ rel => 'child', href => "{upid}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -162,7 +166,7 @@ __PACKAGE__->register_method({
|
|||
unknown => 0,
|
||||
};
|
||||
for my $filter (PVE::Tools::split_list($param->{statusfilter})) {
|
||||
$statusfilter->{lc($filter)} = 1 ;
|
||||
$statusfilter->{ lc($filter) } = 1;
|
||||
}
|
||||
} elsif ($errors) {
|
||||
$statusfilter->{ok} = 0;
|
||||
|
|
@ -171,7 +175,7 @@ __PACKAGE__->register_method({
|
|||
my $count = 0;
|
||||
my $line;
|
||||
|
||||
my $auditor = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ], 1);
|
||||
my $auditor = $rpcenv->check($user, "/nodes/$node", ['Sys.Audit'], 1);
|
||||
|
||||
my $filter_task = sub {
|
||||
my $task = shift;
|
||||
|
|
@ -229,13 +233,13 @@ __PACKAGE__->register_method({
|
|||
|
||||
if ($source ne 'active') {
|
||||
if (my $bw = File::ReadBackwards->new($filename)) {
|
||||
while (defined ($line = $bw->readline)) {
|
||||
while (defined($line = $bw->readline)) {
|
||||
&$parse_line();
|
||||
}
|
||||
$bw->close();
|
||||
}
|
||||
if (my $bw = File::ReadBackwards->new("$filename.1")) {
|
||||
while (defined ($line = $bw->readline)) {
|
||||
while (defined($line = $bw->readline)) {
|
||||
&$parse_line();
|
||||
}
|
||||
$bw->close();
|
||||
|
|
@ -245,7 +249,8 @@ __PACKAGE__->register_method({
|
|||
$rpcenv->set_result_attrib('total', $count);
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'upid_index',
|
||||
|
|
@ -258,7 +263,7 @@ __PACKAGE__->register_method({
|
|||
properties => {
|
||||
node => get_standard_option('pve-node'),
|
||||
upid => { type => 'string' },
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
type => 'array',
|
||||
|
|
@ -266,16 +271,16 @@ __PACKAGE__->register_method({
|
|||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => "{name}" } ],
|
||||
links => [{ rel => 'child', href => "{name}" }],
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
return [
|
||||
{ name => 'log' },
|
||||
{ name => 'status' }
|
||||
{ name => 'log' }, { name => 'status' },
|
||||
];
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'stop_task',
|
||||
|
|
@ -283,7 +288,8 @@ __PACKAGE__->register_method({
|
|||
method => 'DELETE',
|
||||
description => 'Stop a task.',
|
||||
permissions => {
|
||||
description => "The user needs 'Sys.Modify' permissions on '/nodes/<node>' if they aren't the owner of the task.",
|
||||
description =>
|
||||
"The user needs 'Sys.Modify' permissions on '/nodes/<node>' if they aren't the owner of the task.",
|
||||
user => 'all',
|
||||
},
|
||||
protected => 1,
|
||||
|
|
@ -293,7 +299,7 @@ __PACKAGE__->register_method({
|
|||
properties => {
|
||||
node => get_standard_option('pve-node'),
|
||||
upid => { type => 'string' },
|
||||
}
|
||||
},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
|
@ -301,7 +307,7 @@ __PACKAGE__->register_method({
|
|||
|
||||
my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1);
|
||||
raise_param_exc({ upid => "unable to parse worker upid" }) if !$task;
|
||||
raise_param_exc({ upid => "no such task" }) if ! -f $filename;
|
||||
raise_param_exc({ upid => "no such task" }) if !-f $filename;
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
my $user = $rpcenv->get_user();
|
||||
|
|
@ -310,20 +316,22 @@ __PACKAGE__->register_method({
|
|||
$convert_token_task->($task);
|
||||
|
||||
if (!$check_task_user->($task, $user)) {
|
||||
$rpcenv->check($user, "/nodes/$node", [ 'Sys.Modify' ]);
|
||||
$rpcenv->check($user, "/nodes/$node", ['Sys.Modify']);
|
||||
}
|
||||
|
||||
PVE::RPCEnvironment->check_worker($param->{upid}, 1);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'read_task_log',
|
||||
path => '{upid}/log',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
description => "The user needs 'Sys.Audit' permissions on '/nodes/<node>' if they aren't the owner of the task.",
|
||||
description =>
|
||||
"The user needs 'Sys.Audit' permissions on '/nodes/<node>' if they aren't the owner of the task.",
|
||||
user => 'all',
|
||||
},
|
||||
protected => 1,
|
||||
|
|
@ -355,8 +363,9 @@ __PACKAGE__->register_method({
|
|||
download => {
|
||||
type => 'boolean',
|
||||
optional => 1,
|
||||
description => "Whether the tasklog file should be downloaded. This parameter can't be used in conjunction with other parameters",
|
||||
}
|
||||
description =>
|
||||
"Whether the tasklog file should be downloaded. This parameter can't be used in conjunction with other parameters",
|
||||
},
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -365,15 +374,15 @@ __PACKAGE__->register_method({
|
|||
type => "object",
|
||||
properties => {
|
||||
n => {
|
||||
description=> "Line number",
|
||||
type=> 'integer',
|
||||
description => "Line number",
|
||||
type => 'integer',
|
||||
},
|
||||
t => {
|
||||
description=> "Line text",
|
||||
description => "Line text",
|
||||
type => 'string',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
|
@ -388,7 +397,7 @@ __PACKAGE__->register_method({
|
|||
$convert_token_task->($task);
|
||||
|
||||
if (!$check_task_user->($task, $user)) {
|
||||
$rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]);
|
||||
$rpcenv->check($user, "/nodes/$node", ['Sys.Audit']);
|
||||
}
|
||||
|
||||
if ($param->{download}) {
|
||||
|
|
@ -396,7 +405,7 @@ __PACKAGE__->register_method({
|
|||
die "'download' cannot be used together with 'start' or 'limit' parameters\n";
|
||||
}
|
||||
# 1024 is a practical cutoff for the size distribution of our log files.
|
||||
my $use_compression = ( -s $filename ) > 1024;
|
||||
my $use_compression = (-s $filename) > 1024;
|
||||
|
||||
my $fh;
|
||||
if ($use_compression) {
|
||||
|
|
@ -407,7 +416,8 @@ __PACKAGE__->register_method({
|
|||
}
|
||||
|
||||
my $task_time = strftime('%FT%TZ', gmtime($task->{starttime}));
|
||||
my $download_name = 'task-'.$task->{node}.'-'.$task->{type}.'-'.$task_time.'.log';
|
||||
my $download_name =
|
||||
'task-' . $task->{node} . '-' . $task->{type} . '-' . $task_time . '.log';
|
||||
|
||||
return {
|
||||
download => {
|
||||
|
|
@ -415,9 +425,10 @@ __PACKAGE__->register_method({
|
|||
stream => 1,
|
||||
'content-encoding' => $use_compression ? 'gzip' : undef,
|
||||
'content-type' => "text/plain",
|
||||
'content-disposition' => "attachment; filename=\"".$download_name."\"",
|
||||
'content-disposition' => "attachment; filename=\"" . $download_name . "\"",
|
||||
},
|
||||
},
|
||||
;
|
||||
} else {
|
||||
my $start = $param->{start} // 0;
|
||||
my $limit = $param->{limit} // 50;
|
||||
|
|
@ -428,8 +439,8 @@ __PACKAGE__->register_method({
|
|||
|
||||
return $lines;
|
||||
}
|
||||
}});
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
my $exit_status_cache = {};
|
||||
|
||||
|
|
@ -438,7 +449,8 @@ __PACKAGE__->register_method({
|
|||
path => '{upid}/status',
|
||||
method => 'GET',
|
||||
permissions => {
|
||||
description => "The user needs 'Sys.Audit' permissions on '/nodes/<node>' if they are not the owner of the task.",
|
||||
description =>
|
||||
"The user needs 'Sys.Audit' permissions on '/nodes/<node>' if they are not the owner of the task.",
|
||||
user => 'all',
|
||||
},
|
||||
protected => 1,
|
||||
|
|
@ -458,10 +470,11 @@ __PACKAGE__->register_method({
|
|||
type => "object",
|
||||
properties => {
|
||||
pid => {
|
||||
type => 'integer'
|
||||
type => 'integer',
|
||||
},
|
||||
status => {
|
||||
type => 'string', enum => ['running', 'stopped'],
|
||||
type => 'string',
|
||||
enum => ['running', 'stopped'],
|
||||
},
|
||||
type => {
|
||||
type => 'string',
|
||||
|
|
@ -495,7 +508,7 @@ __PACKAGE__->register_method({
|
|||
|
||||
my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1);
|
||||
raise_param_exc({ upid => "unable to parse worker upid" }) if !$task;
|
||||
raise_param_exc({ upid => "no such task" }) if ! -f $filename;
|
||||
raise_param_exc({ upid => "no such task" }) if !-f $filename;
|
||||
|
||||
my $lines = [];
|
||||
|
||||
|
|
@ -506,24 +519,24 @@ __PACKAGE__->register_method({
|
|||
$convert_token_task->($task);
|
||||
|
||||
if (!$check_task_user->($task, $user)) {
|
||||
$rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]);
|
||||
$rpcenv->check($user, "/nodes/$node", ['Sys.Audit']);
|
||||
}
|
||||
|
||||
my $pstart = PVE::ProcFSTools::read_proc_starttime($task->{pid});
|
||||
$task->{status} = ($pstart && ($pstart == $task->{pstart})) ?
|
||||
'running' : 'stopped';
|
||||
$task->{status} = ($pstart && ($pstart == $task->{pstart})) ? 'running' : 'stopped';
|
||||
|
||||
$task->{upid} = $param->{upid}; # include upid
|
||||
|
||||
if ($task->{status} eq 'stopped') {
|
||||
if (!defined($exit_status_cache->{$task->{upid}})) {
|
||||
$exit_status_cache->{$task->{upid}} =
|
||||
if (!defined($exit_status_cache->{ $task->{upid} })) {
|
||||
$exit_status_cache->{ $task->{upid} } =
|
||||
PVE::Tools::upid_read_status($task->{upid});
|
||||
}
|
||||
$task->{exitstatus} = $exit_status_cache->{$task->{upid}};
|
||||
$task->{exitstatus} = $exit_status_cache->{ $task->{upid} };
|
||||
}
|
||||
|
||||
return $task;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -29,23 +29,23 @@ my sub assert_param_permission_vzdump {
|
|||
|
||||
if (defined($param->{maxfiles}) || defined($param->{'prune-backups'})) {
|
||||
if (my $storeid = PVE::VZDump::get_storage_param($param)) {
|
||||
$rpcenv->check($user, "/storage/$storeid", [ 'Datastore.Allocate' ]);
|
||||
$rpcenv->check($user, "/storage/$storeid", ['Datastore.Allocate']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'vzdump',
|
||||
path => '',
|
||||
method => 'POST',
|
||||
description => "Create backup.",
|
||||
permissions => {
|
||||
description => "The user needs 'VM.Backup' permissions on any VM, and "
|
||||
."'Datastore.AllocateSpace' on the backup storage (and fleecing storage when fleecing "
|
||||
."is used). The 'tmpdir', 'dumpdir', 'script' and 'job-id' parameters are restricted "
|
||||
."to the 'root\@pam' user. The 'maxfiles' and 'prune-backups' settings require "
|
||||
."'Datastore.Allocate' on the backup storage. The 'bwlimit', 'performance' and "
|
||||
."'ionice' parameters require 'Sys.Modify' on '/'.",
|
||||
. "'Datastore.AllocateSpace' on the backup storage (and fleecing storage when fleecing "
|
||||
. "is used). The 'tmpdir', 'dumpdir', 'script' and 'job-id' parameters are restricted "
|
||||
. "to the 'root\@pam' user. The 'maxfiles' and 'prune-backups' settings require "
|
||||
. "'Datastore.Allocate' on the backup storage. The 'bwlimit', 'performance' and "
|
||||
. "'ionice' parameters require 'Sys.Modify' on '/'.",
|
||||
user => 'all',
|
||||
},
|
||||
protected => 1,
|
||||
|
|
@ -53,12 +53,16 @@ __PACKAGE__->register_method ({
|
|||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => PVE::VZDump::Common::json_config_properties({
|
||||
'job-id' => get_standard_option('pve-backup-jobid', {
|
||||
description => "The ID of the backup job. If set, the 'backup-job' metadata field"
|
||||
'job-id' => get_standard_option(
|
||||
'pve-backup-jobid',
|
||||
{
|
||||
description =>
|
||||
"The ID of the backup job. If set, the 'backup-job' metadata field"
|
||||
. " of the backup notification will be set to this value. Only root\@pam"
|
||||
. " can set this parameter.",
|
||||
optional => 1,
|
||||
}),
|
||||
},
|
||||
),
|
||||
stdout => {
|
||||
type => 'boolean',
|
||||
description => "Write tar to stdout, not to a file.",
|
||||
|
|
@ -77,10 +81,11 @@ __PACKAGE__->register_method ({
|
|||
my $nodename = PVE::INotify::nodename();
|
||||
|
||||
if ($rpcenv->{type} ne 'cli') {
|
||||
raise_param_exc({ node => "option is only allowed on the command line interface."})
|
||||
raise_param_exc({ node => "option is only allowed on the command line interface." })
|
||||
if $param->{node} && $param->{node} ne $nodename;
|
||||
|
||||
raise_param_exc({ stdout => "option is only allowed on the command line interface."})
|
||||
raise_param_exc({
|
||||
stdout => "option is only allowed on the command line interface." })
|
||||
if $param->{stdout};
|
||||
}
|
||||
|
||||
|
|
@ -101,9 +106,9 @@ __PACKAGE__->register_method ({
|
|||
my $orphaned_vmids = delete $vmids_per_node->{''} // [];
|
||||
push @{$local_vmids}, @{$orphaned_vmids};
|
||||
|
||||
my $skiplist = [ map { @$_ } values $vmids_per_node->%* ];
|
||||
my $skiplist = [map { @$_ } values $vmids_per_node->%*];
|
||||
|
||||
if($param->{stop}){
|
||||
if ($param->{stop}) {
|
||||
PVE::VZDump::stop_running_backups();
|
||||
return 'OK' if !scalar(@{$local_vmids});
|
||||
}
|
||||
|
|
@ -117,7 +122,7 @@ __PACKAGE__->register_method ({
|
|||
if $param->{stdout} && scalar(@{$local_vmids}) != 1;
|
||||
|
||||
if (my $storeid = PVE::VZDump::get_storage_param($param)) {
|
||||
$rpcenv->check($user, "/storage/$storeid", [ 'Datastore.AllocateSpace' ]);
|
||||
$rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
|
||||
}
|
||||
|
||||
my $worker = sub {
|
||||
|
|
@ -159,8 +164,8 @@ __PACKAGE__->register_method ({
|
|||
open my $saved_stdout, ">&STDOUT"
|
||||
|| die "can't dup STDOUT: $!\n";
|
||||
|
||||
open STDOUT, '>&STDERR' ||
|
||||
die "unable to redirect STDOUT: $!\n";
|
||||
open STDOUT, '>&STDERR'
|
||||
|| die "unable to redirect STDOUT: $!\n";
|
||||
|
||||
$param->{stdout} = $saved_stdout;
|
||||
}
|
||||
|
|
@ -170,17 +175,18 @@ __PACKAGE__->register_method ({
|
|||
$taskid = $local_vmids->[0] if scalar(@{$local_vmids}) == 1;
|
||||
|
||||
return $rpcenv->fork_worker('vzdump', $taskid, $user, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'defaults',
|
||||
path => 'defaults',
|
||||
method => 'GET',
|
||||
description => "Get the currently configured vzdump defaults.",
|
||||
permissions => {
|
||||
description => "The user needs 'Datastore.Audit' or 'Datastore.AllocateSpace' " .
|
||||
"permissions for the specified storage (or default storage if none specified). Some " .
|
||||
"properties are only returned when the user has 'Sys.Audit' permissions for the node.",
|
||||
description => "The user needs 'Datastore.Audit' or 'Datastore.AllocateSpace' "
|
||||
. "permissions for the specified storage (or default storage if none specified). Some "
|
||||
. "properties are only returned when the user has 'Sys.Audit' permissions for the node.",
|
||||
user => 'all',
|
||||
},
|
||||
proxyto => 'node',
|
||||
|
|
@ -228,15 +234,14 @@ __PACKAGE__->register_method ({
|
|||
|
||||
if (defined($res->{'prune-backups'})) {
|
||||
$res->{'prune-backups'} = PVE::JSONSchema::print_property_string(
|
||||
$res->{'prune-backups'},
|
||||
'prune-backups',
|
||||
$res->{'prune-backups'}, 'prune-backups',
|
||||
);
|
||||
}
|
||||
|
||||
$res->{mailto} = join(",", @{$res->{mailto}})
|
||||
$res->{mailto} = join(",", @{ $res->{mailto} })
|
||||
if defined($res->{mailto});
|
||||
|
||||
$res->{'exclude-path'} = join(",", @{$res->{'exclude-path'}})
|
||||
$res->{'exclude-path'} = join(",", @{ $res->{'exclude-path'} })
|
||||
if defined($res->{'exclude-path'});
|
||||
|
||||
# normal backup users don't need to know these
|
||||
|
|
@ -249,21 +254,25 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
my $pool = $res->{pool};
|
||||
if (defined($pool) &&
|
||||
!$rpcenv->check($authuser, "/pool/$pool", ['Pool.Audit'], 1)) {
|
||||
if (
|
||||
defined($pool)
|
||||
&& !$rpcenv->check($authuser, "/pool/$pool", ['Pool.Audit'], 1)
|
||||
) {
|
||||
delete $res->{pool};
|
||||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'extractconfig',
|
||||
path => 'extractconfig',
|
||||
method => 'GET',
|
||||
description => "Extract configuration from vzdump backup archive.",
|
||||
permissions => {
|
||||
description => "The user needs 'VM.Backup' permissions on the backed up guest ID, and 'Datastore.AllocateSpace' on the backup storage.",
|
||||
description =>
|
||||
"The user needs 'VM.Backup' permissions on the backed up guest ID, and 'Datastore.AllocateSpace' on the backup storage.",
|
||||
user => 'all',
|
||||
},
|
||||
protected => 1,
|
||||
|
|
@ -290,12 +299,7 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $storage_cfg = PVE::Storage::config();
|
||||
PVE::Storage::check_volume_access(
|
||||
$rpcenv,
|
||||
$authuser,
|
||||
$storage_cfg,
|
||||
undef,
|
||||
$volume,
|
||||
'backup',
|
||||
$rpcenv, $authuser, $storage_cfg, undef, $volume, 'backup',
|
||||
);
|
||||
|
||||
if (PVE::Storage::parse_volume_id($volume, 1)) {
|
||||
|
|
@ -304,6 +308,7 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return PVE::Storage::extract_vzdump_config($storage_cfg, $volume);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -25,16 +25,18 @@ sub get_hwaddress {
|
|||
my $fn = '/etc/ssh/ssh_host_rsa_key.pub';
|
||||
my $st = stat($fn);
|
||||
|
||||
if (defined($hwaddress)
|
||||
if (
|
||||
defined($hwaddress)
|
||||
&& $hwaddress_st->{mtime} == $st->mtime
|
||||
&& $hwaddress_st->{ino} == $st->ino
|
||||
&& $hwaddress_st->{dev} == $st->dev) {
|
||||
&& $hwaddress_st->{dev} == $st->dev
|
||||
) {
|
||||
return $hwaddress;
|
||||
}
|
||||
|
||||
my $sshkey = PVE::Tools::file_get_contents($fn);
|
||||
$hwaddress = uc(md5_hex($sshkey));
|
||||
$hwaddress_st->@{'mtime', 'ino', 'dev'} = ($st->mtime, $st->ino, $st->dev);
|
||||
$hwaddress_st->@{ 'mtime', 'ino', 'dev' } = ($st->mtime, $st->ino, $st->dev);
|
||||
|
||||
return $hwaddress;
|
||||
}
|
||||
|
|
@ -51,8 +53,10 @@ sub extract_node_stats {
|
|||
|
||||
if (my $d = $rrd->{"pve2-node/$node"}) {
|
||||
|
||||
if (!$members || # no cluster
|
||||
($members->{$node} && $members->{$node}->{online})) {
|
||||
if (
|
||||
!$members || # no cluster
|
||||
($members->{$node} && $members->{$node}->{online})
|
||||
) {
|
||||
if (!$exclude_stats) {
|
||||
$entry->{uptime} = ($d->[0] || 0) + 0;
|
||||
$entry->{cpu} = ($d->[5] || 0) + 0;
|
||||
|
|
@ -69,8 +73,11 @@ sub extract_node_stats {
|
|||
}
|
||||
}
|
||||
|
||||
if ($members && $members->{$node} &&
|
||||
!$members->{$node}->{online}) {
|
||||
if (
|
||||
$members
|
||||
&& $members->{$node}
|
||||
&& !$members->{$node}->{online}
|
||||
) {
|
||||
$entry->{status} = 'offline';
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +130,7 @@ sub extract_vm_stats {
|
|||
$entry->{netout} = ($d->[12] || 0) + 0;
|
||||
$entry->{diskread} = ($d->[13] || 0) + 0;
|
||||
$entry->{diskwrite} = ($d->[14] || 0) + 0;
|
||||
};
|
||||
}
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
|
@ -187,9 +194,19 @@ sub run_spiceterm {
|
|||
|
||||
my $timeout = 40;
|
||||
|
||||
my $cmd = ['/usr/bin/spiceterm', '--port', $port, '--addr', 'localhost',
|
||||
'--timeout', $timeout, '--authpath', $authpath,
|
||||
'--permissions', $permissions];
|
||||
my $cmd = [
|
||||
'/usr/bin/spiceterm',
|
||||
'--port',
|
||||
$port,
|
||||
'--addr',
|
||||
'localhost',
|
||||
'--timeout',
|
||||
$timeout,
|
||||
'--authpath',
|
||||
$authpath,
|
||||
'--permissions',
|
||||
$permissions,
|
||||
];
|
||||
|
||||
my $dcconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
|
||||
push @$cmd, '--keymap', $dcconf->{keyboard} if $dcconf->{keyboard};
|
||||
|
|
@ -199,14 +216,20 @@ sub run_spiceterm {
|
|||
my $realcmd = sub {
|
||||
my $upid = shift;
|
||||
|
||||
syslog ('info', "starting spiceterm $upid - $title\n");
|
||||
syslog('info', "starting spiceterm $upid - $title\n");
|
||||
|
||||
my $cmdstr = join (' ', @$cmd);
|
||||
syslog ('info', "launch command: $cmdstr");
|
||||
my $cmdstr = join(' ', @$cmd);
|
||||
syslog('info', "launch command: $cmdstr");
|
||||
|
||||
eval {
|
||||
foreach my $k (keys %ENV) {
|
||||
next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME' || $k eq 'LANG' || $k eq 'LANGUAGE' ;
|
||||
next
|
||||
if $k eq 'PATH'
|
||||
|| $k eq 'TERM'
|
||||
|| $k eq 'USER'
|
||||
|| $k eq 'HOME'
|
||||
|| $k eq 'LANG'
|
||||
|| $k eq 'LANGUAGE';
|
||||
delete $ENV{$k};
|
||||
}
|
||||
$ENV{PWD} = '/';
|
||||
|
|
@ -215,7 +238,7 @@ sub run_spiceterm {
|
|||
PVE::Tools::run_command($cmd, errmsg => 'spiceterm failed\n', keeplocale => 1);
|
||||
};
|
||||
if (my $err = $@) {
|
||||
syslog ('err', $err);
|
||||
syslog('err', $err);
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -242,7 +265,7 @@ sub resolve_proxyto {
|
|||
if !$node;
|
||||
} else {
|
||||
$node = $uri_param->{$proxyto};
|
||||
raise_param_exc({ $proxyto => "proxyto parameter does not exist"})
|
||||
raise_param_exc({ $proxyto => "proxyto parameter does not exist" })
|
||||
if !$node;
|
||||
}
|
||||
return $node;
|
||||
|
|
@ -260,7 +283,7 @@ sub get_resource_pool_guest_members {
|
|||
|
||||
die "pool '$pool' does not exist\n" if !$data;
|
||||
|
||||
my $pool_members = [ grep { $idlist->{$_} } keys %{$data->{vms}} ];
|
||||
my $pool_members = [grep { $idlist->{$_} } keys %{ $data->{vms} }];
|
||||
|
||||
return $pool_members;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ sub logmsg {
|
|||
|
||||
chomp $msg;
|
||||
|
||||
my $tstr = strftime ("%F %H:%M:%S", localtime);
|
||||
my $tstr = strftime("%F %H:%M:%S", localtime);
|
||||
|
||||
foreach my $line (split (/\n/, $msg)) {
|
||||
foreach my $line (split(/\n/, $msg)) {
|
||||
print $logfd "$tstr $line\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ sub read_aplinfo_from_fh {
|
|||
$update ? die $msg : warn $msg;
|
||||
}
|
||||
} elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) {
|
||||
$res->{lc $1} = $2;
|
||||
$res->{ lc $1 } = $2;
|
||||
} else {
|
||||
my $msg = "unable to parse appliance record: $rec\n";
|
||||
$update ? die $msg : warn $msg;
|
||||
|
|
@ -73,14 +73,19 @@ sub read_aplinfo_from_fh {
|
|||
}
|
||||
|
||||
if ($res->{'package'} eq 'pve-web-news' && $res->{description}) {
|
||||
$list->{'all'}->{$res->{'package'}} = $res;
|
||||
$list->{'all'}->{ $res->{'package'} } = $res;
|
||||
next;
|
||||
}
|
||||
|
||||
$res->{section} = 'unknown' if !$res->{section};
|
||||
|
||||
if ($res->{'package'} && $res->{type} && $res->{os} && $res->{version} &&
|
||||
$res->{infopage}) {
|
||||
if (
|
||||
$res->{'package'}
|
||||
&& $res->{type}
|
||||
&& $res->{os}
|
||||
&& $res->{version}
|
||||
&& $res->{infopage}
|
||||
) {
|
||||
my $template;
|
||||
if ($res->{location}) {
|
||||
$template = $res->{location};
|
||||
|
|
@ -97,7 +102,7 @@ sub read_aplinfo_from_fh {
|
|||
}
|
||||
$res->{source} = $source;
|
||||
$res->{template} = $template;
|
||||
$list->{$res->{section}}->{$template} = $res;
|
||||
$list->{ $res->{section} }->{$template} = $res;
|
||||
$list->{'all'}->{$template} = $res;
|
||||
} else {
|
||||
my $msg = "found incomplete appliance records\n";
|
||||
|
|
@ -109,8 +114,8 @@ sub read_aplinfo_from_fh {
|
|||
sub read_aplinfo {
|
||||
my ($filename, $list, $source, $update) = @_;
|
||||
|
||||
my $fh = IO::File->new("<$filename") ||
|
||||
die "unable to open file '$filename' - $!\n";
|
||||
my $fh = IO::File->new("<$filename")
|
||||
|| die "unable to open file '$filename' - $!\n";
|
||||
|
||||
eval { read_aplinfo_from_fh($fh, $list, $source, $update); };
|
||||
my $err = $@;
|
||||
|
|
@ -127,15 +132,15 @@ sub url_get {
|
|||
|
||||
my $req = HTTP::Request->new(GET => $url);
|
||||
|
||||
logmsg ($logfh, "start download $url");
|
||||
logmsg($logfh, "start download $url");
|
||||
my $res = $ua->request($req, $file);
|
||||
|
||||
if ($res->is_success) {
|
||||
logmsg ($logfh, "download finished: " . $res->status_line);
|
||||
logmsg($logfh, "download finished: " . $res->status_line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
logmsg ($logfh, "download failed: " . $res->status_line);
|
||||
logmsg($logfh, "download failed: " . $res->status_line);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -169,17 +174,15 @@ sub download_aplinfo {
|
|||
my $cmd = "/usr/bin/gpgv -q --keyring $trustedkeyring $sigfn $tmp";
|
||||
|
||||
my $logfunc = sub { logmsg($logfd, "signature verification: $_[0]"); };
|
||||
eval {
|
||||
run_command($cmd, outfunc => $logfunc, errfunc => $logfunc);
|
||||
};
|
||||
eval { run_command($cmd, outfunc => $logfunc, errfunc => $logfunc); };
|
||||
die "unable to verify signature - $@\n" if $@;
|
||||
|
||||
# test syntax
|
||||
eval { read_aplinfo($tmp, {}, $aplinfo->{url}, 1) };
|
||||
die "update failed: $@" if $@;
|
||||
|
||||
rename($tmp, "$APL_INFO_DIRECTORY/$host") or
|
||||
die "update failed: unable to store data: $!\n";
|
||||
rename($tmp, "$APL_INFO_DIRECTORY/$host")
|
||||
or die "update failed: unable to store data: $!\n";
|
||||
|
||||
logmsg($logfd, "update successful");
|
||||
};
|
||||
|
|
@ -217,7 +220,7 @@ sub update {
|
|||
if ($logfile_size > 1024 * 256) {
|
||||
rename($LOGFILE, "$LOGFILE.0") or warn "failed to rotate log file $LOGFILE - $!\n";
|
||||
}
|
||||
my $logfd = IO::File->new (">>$LOGFILE");
|
||||
my $logfd = IO::File->new(">>$LOGFILE");
|
||||
logmsg($logfd, "starting update");
|
||||
|
||||
my $ua = LWP::UserAgent->new;
|
||||
|
|
@ -236,11 +239,9 @@ sub update {
|
|||
|
||||
my @dlerr = ();
|
||||
foreach my $info (@$sources) {
|
||||
eval {
|
||||
download_aplinfo($ua, $info, $logfd);
|
||||
};
|
||||
eval { download_aplinfo($ua, $info, $logfd); };
|
||||
if (my $err = $@) {
|
||||
logmsg ($logfd, $err);
|
||||
logmsg($logfd, $err);
|
||||
push @dlerr, $info->{url};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ sub compute_alg1 {
|
|||
next if defined($done_hash->{$vmid});
|
||||
my $d = $vmstatus->{$vmid};
|
||||
my $shares = $d->{shares} || 1000;
|
||||
my $desired = $d->{balloon_min} + int(($alloc_new/$shares_total)*$shares);
|
||||
my $desired = $d->{balloon_min} + int(($alloc_new / $shares_total) * $shares);
|
||||
|
||||
if ($desired > $d->{maxmem}) {
|
||||
$desired = $d->{maxmem};
|
||||
|
|
@ -74,7 +74,9 @@ sub compute_alg1 {
|
|||
$progress += $absdiff;
|
||||
$repeat = 1;
|
||||
}
|
||||
&$log("change request for $vmid ($balloon, $diff, $desired, $new, $changes, $progress)\n");
|
||||
&$log(
|
||||
"change request for $vmid ($balloon, $diff, $desired, $new, $changes, $progress)\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +86,6 @@ sub compute_alg1 {
|
|||
return $rest;
|
||||
};
|
||||
|
||||
|
||||
my $idlist = []; # list of VMs with working balloon river
|
||||
my $idlist1 = []; # list of VMs with memory pressure
|
||||
my $idlist2 = []; # list of VMs with enough free memory
|
||||
|
|
@ -94,14 +95,17 @@ sub compute_alg1 {
|
|||
next if !$d->{balloon}; # skip if balloon driver not running
|
||||
next if !$d->{balloon_min}; # skip if balloon value not set in config
|
||||
next if $d->{lock} && $d->{lock} eq 'migrate';
|
||||
next if defined($d->{shares}) &&
|
||||
($d->{shares} == 0); # skip if shares set to zero
|
||||
next
|
||||
if defined($d->{shares})
|
||||
&& ($d->{shares} == 0); # skip if shares set to zero
|
||||
|
||||
push @$idlist, $vmid;
|
||||
|
||||
if ($d->{freemem} &&
|
||||
($d->{freemem} > $d->{balloon_min}*0.25) &&
|
||||
($d->{balloon} >= $d->{balloon_min})) {
|
||||
if (
|
||||
$d->{freemem}
|
||||
&& ($d->{freemem} > $d->{balloon_min} * 0.25)
|
||||
&& ($d->{balloon} >= $d->{balloon_min})
|
||||
) {
|
||||
push @$idlist2, $vmid;
|
||||
&$log("idlist2 $vmid $d->{balloon}, $d->{balloon_min}, $d->{freemem}\n");
|
||||
} else {
|
||||
|
|
@ -112,7 +116,7 @@ sub compute_alg1 {
|
|||
|
||||
my $res = {};
|
||||
|
||||
if ($goal > 10*1024*1024) {
|
||||
if ($goal > 10 * 1024 * 1024) {
|
||||
&$log("grow request start $goal\n");
|
||||
# priorize VMs with memory pressure
|
||||
my $rest = &$change_func($res, $idlist1, $goal);
|
||||
|
|
@ -122,7 +126,7 @@ sub compute_alg1 {
|
|||
}
|
||||
&$log("grow request end $rest\n");
|
||||
|
||||
} elsif ($goal < -10*1024*1024) {
|
||||
} elsif ($goal < -10 * 1024 * 1024) {
|
||||
&$log("shrink request $goal\n");
|
||||
# priorize VMs with enough free memory
|
||||
my $rest = &$change_func($res, $idlist2, $goal);
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ sub log_warn { $log_line->('warn', @_); }
|
|||
sub log_fail { $log_line->('fail', @_); }
|
||||
|
||||
my $print_header_first = 1;
|
||||
|
||||
sub print_header {
|
||||
my ($h) = @_;
|
||||
print "\n" if !$print_header_first;
|
||||
|
|
@ -123,7 +124,8 @@ my $get_systemd_unit_state = sub {
|
|||
};
|
||||
|
||||
my %extra = (outfunc => $filter_output, noerr => 1);
|
||||
$extra{errfunc} = sub { } if $suppress_stderr;
|
||||
$extra{errfunc} = sub { }
|
||||
if $suppress_stderr;
|
||||
|
||||
eval {
|
||||
run_command(['systemctl', 'is-enabled', "$unit"], %extra);
|
||||
|
|
@ -163,7 +165,7 @@ my $get_pkg = sub {
|
|||
return undef;
|
||||
}
|
||||
|
||||
my $pkgs = [ grep { $_->{Package} eq $pkg } @$versions ];
|
||||
my $pkgs = [grep { $_->{Package} eq $pkg } @$versions];
|
||||
if (!defined $pkgs || $pkgs == 0) {
|
||||
log_fail("unable to determine installed $pkg version.");
|
||||
return undef;
|
||||
|
|
@ -235,7 +237,9 @@ sub check_pve_packages {
|
|||
}
|
||||
} elsif ($get_pkg->($kinstalled)) {
|
||||
# with 6.2 kernel being available in both we might want to fine-tune the check?
|
||||
log_warn("a suitable kernel ($kinstalled) is installed, but an unsuitable ($kernel_ver) is booted, missing reboot?!");
|
||||
log_warn(
|
||||
"a suitable kernel ($kinstalled) is installed, but an unsuitable ($kernel_ver) is booted, missing reboot?!"
|
||||
);
|
||||
} else {
|
||||
log_warn("unexpected running and installed kernel '$kernel_ver'.");
|
||||
}
|
||||
|
|
@ -251,9 +255,9 @@ sub check_pve_packages {
|
|||
if (scalar(@$outdated_kernel_meta_pkgs) > 0) {
|
||||
log_info(
|
||||
"Found outdated kernel meta-packages, taking up extra space on boot partitions.\n"
|
||||
." After a successful upgrade, you can remove them using this command:\n"
|
||||
." apt remove " . join(' ', $outdated_kernel_meta_pkgs->@*)
|
||||
);
|
||||
. " After a successful upgrade, you can remove them using this command:\n"
|
||||
. " apt remove "
|
||||
. join(' ', $outdated_kernel_meta_pkgs->@*));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -261,7 +265,6 @@ sub check_pve_packages {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
sub check_storage_health {
|
||||
print_header("CHECKING CONFIGURED STORAGES");
|
||||
my $cfg = PVE::Storage::config();
|
||||
|
|
@ -315,8 +318,9 @@ sub check_cluster_corosync {
|
|||
} else {
|
||||
if (grep { $conf_nodelist->{$_}->{quorum_votes} != 1 } keys %$conf_nodelist) {
|
||||
log_warn("non-default quorum_votes distribution detected!");
|
||||
}
|
||||
map { $node_votes += $conf_nodelist->{$_}->{quorum_votes} // 0 } keys %$conf_nodelist;
|
||||
} map {
|
||||
$node_votes += $conf_nodelist->{$_}->{quorum_votes} // 0
|
||||
} keys %$conf_nodelist;
|
||||
}
|
||||
|
||||
my ($expected_votes, $total_votes);
|
||||
|
|
@ -327,9 +331,7 @@ sub check_cluster_corosync {
|
|||
($total_votes) = $line =~ /^Total votes:\s*(\d+)\s*$/
|
||||
if !defined($total_votes);
|
||||
};
|
||||
eval {
|
||||
run_command(['corosync-quorumtool', '-s'], outfunc => $filter_output, noerr => 1);
|
||||
};
|
||||
eval { run_command(['corosync-quorumtool', '-s'], outfunc => $filter_output, noerr => 1); };
|
||||
|
||||
if (!defined($expected_votes)) {
|
||||
log_fail("unable to get expected number of votes, assuming 0.");
|
||||
|
|
@ -366,8 +368,9 @@ sub check_cluster_corosync {
|
|||
log_warn("cluster consists of less than three quorum-providing nodes!")
|
||||
if $conf_nodelist_count < 3 && $conf_nodelist_count + $qdevice_votes < 3;
|
||||
|
||||
log_fail("corosync.conf ($conf_nodelist_count) and pmxcfs ($cfs_nodelist_count) don't agree about size of nodelist.")
|
||||
if $conf_nodelist_count != $cfs_nodelist_count;
|
||||
log_fail(
|
||||
"corosync.conf ($conf_nodelist_count) and pmxcfs ($cfs_nodelist_count) don't agree about size of nodelist."
|
||||
) if $conf_nodelist_count != $cfs_nodelist_count;
|
||||
|
||||
print "\nChecking nodelist entries..\n";
|
||||
my $nodelist_pass = 1;
|
||||
|
|
@ -382,7 +385,7 @@ sub check_cluster_corosync {
|
|||
log_fail("$cs_node: no nodeid configured in corosync.conf.");
|
||||
}
|
||||
my $gotLinks = 0;
|
||||
for my $link (0..7) {
|
||||
for my $link (0 .. 7) {
|
||||
$gotLinks++ if defined($entry->{"ring${link}_addr"});
|
||||
}
|
||||
if ($gotLinks <= 0) {
|
||||
|
|
@ -393,25 +396,24 @@ sub check_cluster_corosync {
|
|||
my $verify_ring_ip = sub {
|
||||
my $key = shift;
|
||||
if (defined(my $ring = $entry->{$key})) {
|
||||
my ($resolved_ip, undef) = PVE::Corosync::resolve_hostname_like_corosync($ring, $conf);
|
||||
my ($resolved_ip, undef) =
|
||||
PVE::Corosync::resolve_hostname_like_corosync($ring, $conf);
|
||||
if (defined($resolved_ip)) {
|
||||
if ($resolved_ip ne $ring) {
|
||||
$nodelist_pass = 0;
|
||||
log_warn(
|
||||
"$cs_node: $key '$ring' resolves to '$resolved_ip'.\n"
|
||||
." Consider replacing it with the currently resolved IP address."
|
||||
);
|
||||
log_warn("$cs_node: $key '$ring' resolves to '$resolved_ip'.\n"
|
||||
. " Consider replacing it with the currently resolved IP address.");
|
||||
}
|
||||
} else {
|
||||
$nodelist_pass = 0;
|
||||
log_fail(
|
||||
"$cs_node: unable to resolve $key '$ring' to an IP address according to Corosync's"
|
||||
." resolve strategy - cluster will potentially fail with Corosync 3.x/kronosnet!"
|
||||
. " resolve strategy - cluster will potentially fail with Corosync 3.x/kronosnet!"
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
for my $link (0..7) {
|
||||
for my $link (0 .. 7) {
|
||||
$verify_ring_ip->("ring${link}_addr");
|
||||
}
|
||||
}
|
||||
|
|
@ -425,17 +427,24 @@ sub check_cluster_corosync {
|
|||
if (defined($transport)) {
|
||||
if ($transport ne 'knet') {
|
||||
$totem_pass = 0;
|
||||
log_fail("Corosync transport explicitly set to '$transport' instead of implicit default!");
|
||||
log_fail(
|
||||
"Corosync transport explicitly set to '$transport' instead of implicit default!");
|
||||
}
|
||||
}
|
||||
|
||||
# TODO: are those values still up-to-date?
|
||||
if ((!defined($totem->{secauth}) || $totem->{secauth} ne 'on') && (!defined($totem->{crypto_cipher}) || $totem->{crypto_cipher} eq 'none')) {
|
||||
if (
|
||||
(!defined($totem->{secauth}) || $totem->{secauth} ne 'on')
|
||||
&& (!defined($totem->{crypto_cipher}) || $totem->{crypto_cipher} eq 'none')
|
||||
) {
|
||||
$totem_pass = 0;
|
||||
log_fail("Corosync authentication/encryption is not explicitly enabled (secauth / crypto_cipher / crypto_hash)!");
|
||||
log_fail(
|
||||
"Corosync authentication/encryption is not explicitly enabled (secauth / crypto_cipher / crypto_hash)!"
|
||||
);
|
||||
} elsif (defined($totem->{crypto_cipher}) && $totem->{crypto_cipher} eq '3des') {
|
||||
$totem_pass = 0;
|
||||
log_fail("Corosync encryption cipher set to '3des', no longer supported in Corosync 3.x!"); # FIXME: can be removed?
|
||||
log_fail("Corosync encryption cipher set to '3des', no longer supported in Corosync 3.x!")
|
||||
; # FIXME: can be removed?
|
||||
}
|
||||
|
||||
log_pass("totem settings OK") if $totem_pass;
|
||||
|
|
@ -478,13 +487,18 @@ sub check_ceph {
|
|||
log_fail("unable to determine Ceph health!");
|
||||
} elsif ($ceph_health eq 'HEALTH_OK') {
|
||||
log_pass("Ceph health reported as 'HEALTH_OK'.");
|
||||
} elsif ($ceph_health eq 'HEALTH_WARN' && $noout && (keys %{$ceph_status->{health}->{checks}} == 1)) {
|
||||
log_pass("Ceph health reported as 'HEALTH_WARN' with a single failing check and 'noout' flag set.");
|
||||
} elsif (
|
||||
$ceph_health eq 'HEALTH_WARN'
|
||||
&& $noout
|
||||
&& (keys %{ $ceph_status->{health}->{checks} } == 1)
|
||||
) {
|
||||
log_pass(
|
||||
"Ceph health reported as 'HEALTH_WARN' with a single failing check and 'noout' flag set."
|
||||
);
|
||||
} else {
|
||||
log_warn(
|
||||
"Ceph health reported as '$ceph_health'.\n Use the PVE dashboard or 'ceph -s'"
|
||||
." to determine the specific issues and try to resolve them."
|
||||
);
|
||||
. " to determine the specific issues and try to resolve them.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -494,18 +508,16 @@ sub check_ceph {
|
|||
if (my $release = eval { PVE::Ceph::Tools::get_local_version(1) }) {
|
||||
my $code_name = $ceph_release2code->{"$release"} || 'unknown';
|
||||
if ($release == $ceph_supported_release) {
|
||||
log_pass("found expected Ceph $ceph_supported_release $ceph_supported_code_name release.")
|
||||
log_pass(
|
||||
"found expected Ceph $ceph_supported_release $ceph_supported_code_name release.");
|
||||
} elsif ($release > $ceph_supported_release) {
|
||||
log_warn(
|
||||
"found newer Ceph release $release $code_name as the expected $ceph_supported_release"
|
||||
." $ceph_supported_code_name, installed third party repos?!"
|
||||
)
|
||||
. " $ceph_supported_code_name, installed third party repos?!");
|
||||
} else {
|
||||
log_fail(
|
||||
"Hyper-converged Ceph $release $code_name is to old for upgrade!\n"
|
||||
." Upgrade Ceph first to $ceph_supported_code_name following our how-to:\n"
|
||||
." <https://pve.proxmox.com/wiki/Category:Ceph_Upgrade>"
|
||||
);
|
||||
log_fail("Hyper-converged Ceph $release $code_name is to old for upgrade!\n"
|
||||
. " Upgrade Ceph first to $ceph_supported_code_name following our how-to:\n"
|
||||
. " <https://pve.proxmox.com/wiki/Category:Ceph_Upgrade>");
|
||||
}
|
||||
} else {
|
||||
log_fail("unable to determine local Ceph version!");
|
||||
|
|
@ -539,7 +551,7 @@ sub check_ceph {
|
|||
}
|
||||
|
||||
for my $service (@$services) {
|
||||
my ($name, $key) = $service->@{'name', 'key'};
|
||||
my ($name, $key) = $service->@{ 'name', 'key' };
|
||||
if (my $service_versions = $ceph_versions_simple->{$key}) {
|
||||
if (keys %$service_versions == 0) {
|
||||
log_skip("no running instances detected for daemon type $name.");
|
||||
|
|
@ -552,8 +564,9 @@ sub check_ceph {
|
|||
log_skip("unable to determine versions of running Ceph $name instances.");
|
||||
}
|
||||
my $service_commits = $ceph_versions_commits->{$key};
|
||||
log_info("different builds of same version detected for an $name. Are you in the middle of the upgrade?")
|
||||
if $service_commits && keys %$service_commits > 1;
|
||||
log_info(
|
||||
"different builds of same version detected for an $name. Are you in the middle of the upgrade?"
|
||||
) if $service_commits && keys %$service_commits > 1;
|
||||
}
|
||||
|
||||
my $overall_versions = $ceph_versions->{overall};
|
||||
|
|
@ -563,7 +576,8 @@ sub check_ceph {
|
|||
log_pass("single running overall version detected for all Ceph daemon types.");
|
||||
$noout_wanted = !$upgraded; # off post-upgrade, on pre-upgrade
|
||||
} elsif (keys $ceph_versions_simple->{overall}->%* != 1) {
|
||||
log_warn("overall version mismatch detected, check 'ceph versions' output for details!");
|
||||
log_warn(
|
||||
"overall version mismatch detected, check 'ceph versions' output for details!");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -586,17 +600,18 @@ sub check_ceph {
|
|||
if (!defined($global_monhost)) {
|
||||
log_warn(
|
||||
"No 'mon_host' entry found in ceph config.\n It's recommended to add mon_host with"
|
||||
." all monitor addresses (without ports) to the global section."
|
||||
);
|
||||
. " all monitor addresses (without ports) to the global section.");
|
||||
}
|
||||
|
||||
my $ipv6 = $global->{ms_bind_ipv6} // $global->{"ms bind ipv6"} // $global->{"ms-bind-ipv6"};
|
||||
my $ipv6 = $global->{ms_bind_ipv6} // $global->{"ms bind ipv6"}
|
||||
// $global->{"ms-bind-ipv6"};
|
||||
if ($ipv6) {
|
||||
my $ipv4 = $global->{ms_bind_ipv4} // $global->{"ms bind ipv4"} // $global->{"ms-bind-ipv4"};
|
||||
my $ipv4 = $global->{ms_bind_ipv4} // $global->{"ms bind ipv4"}
|
||||
// $global->{"ms-bind-ipv4"};
|
||||
if ($ipv6 eq 'true' && (!defined($ipv4) || $ipv4 ne 'false')) {
|
||||
log_warn(
|
||||
"'ms_bind_ipv6' is enabled but 'ms_bind_ipv4' is not disabled.\n Make sure to"
|
||||
." disable 'ms_bind_ipv4' for ipv6 only clusters, or add an ipv4 network to public/cluster network."
|
||||
. " disable 'ms_bind_ipv4' for ipv6 only clusters, or add an ipv4 network to public/cluster network."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -604,7 +619,7 @@ sub check_ceph {
|
|||
if (defined($global->{keyring})) {
|
||||
log_warn(
|
||||
"[global] config section contains 'keyring' option, which will prevent services from"
|
||||
." starting with Nautilus.\n Move 'keyring' option to [client] section instead."
|
||||
. " starting with Nautilus.\n Move 'keyring' option to [client] section instead."
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -627,8 +642,8 @@ sub check_backup_retention_settings {
|
|||
|
||||
my $pass = 1;
|
||||
|
||||
my $maxfiles_msg = "parameter 'maxfiles' is deprecated with PVE 7.x and will be removed in a " .
|
||||
"future version, use 'prune-backups' instead.";
|
||||
my $maxfiles_msg = "parameter 'maxfiles' is deprecated with PVE 7.x and will be removed in a "
|
||||
. "future version, use 'prune-backups' instead.";
|
||||
|
||||
eval {
|
||||
my $confdesc = PVE::VZDump::Common::get_confdesc();
|
||||
|
|
@ -638,7 +653,7 @@ sub check_backup_retention_settings {
|
|||
my $fn = "/etc/vzdump.conf";
|
||||
my $raw = PVE::Tools::file_get_contents($fn);
|
||||
|
||||
my $conf_schema = { type => 'object', properties => $confdesc, };
|
||||
my $conf_schema = { type => 'object', properties => $confdesc };
|
||||
my $param = PVE::JSONSchema::parse_config($conf_schema, $fn, $raw);
|
||||
|
||||
if (defined($param->{maxfiles})) {
|
||||
|
|
@ -686,18 +701,20 @@ sub check_cifs_credential_location {
|
|||
|
||||
my $found;
|
||||
|
||||
PVE::Tools::dir_glob_foreach('/etc/pve/priv/', $regex, sub {
|
||||
PVE::Tools::dir_glob_foreach(
|
||||
'/etc/pve/priv/',
|
||||
$regex,
|
||||
sub {
|
||||
my ($filename) = @_;
|
||||
|
||||
my ($basename) = $filename =~ $regex;
|
||||
|
||||
log_warn(
|
||||
"CIFS credentials '/etc/pve/priv/$filename' will be moved to"
|
||||
." '/etc/pve/priv/storage/$basename.pw' during the update"
|
||||
);
|
||||
log_warn("CIFS credentials '/etc/pve/priv/$filename' will be moved to"
|
||||
. " '/etc/pve/priv/storage/$basename.pw' during the update");
|
||||
|
||||
$found = 1;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
log_pass("no CIFS credentials at outdated location found.") if !$found;
|
||||
}
|
||||
|
|
@ -705,7 +722,7 @@ sub check_cifs_credential_location {
|
|||
sub check_custom_pool_roles {
|
||||
log_info("Checking permission system changes..");
|
||||
|
||||
if (! -f "/etc/pve/user.cfg") {
|
||||
if (!-f "/etc/pve/user.cfg") {
|
||||
log_skip("user.cfg does not exist");
|
||||
return;
|
||||
}
|
||||
|
|
@ -721,10 +738,10 @@ sub check_custom_pool_roles {
|
|||
my $line = $1;
|
||||
my @data;
|
||||
|
||||
for my $d (split (/:/, $line)) {
|
||||
for my $d (split(/:/, $line)) {
|
||||
$d =~ s/^\s+//;
|
||||
$d =~ s/\s+$//;
|
||||
push @data, $d
|
||||
push @data, $d;
|
||||
}
|
||||
|
||||
my $et = shift @data;
|
||||
|
|
@ -745,8 +762,7 @@ sub check_custom_pool_roles {
|
|||
if ($role eq 'PVESysAdmin' || $role eq 'PVEAdmin') {
|
||||
log_warn(
|
||||
"found ACL entry on '$pathtxt' for '$uglist' with role '$role' - this role"
|
||||
." will no longer have 'Permissions.Modify' after the upgrade!"
|
||||
);
|
||||
. " will no longer have 'Permissions.Modify' after the upgrade!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -764,11 +780,17 @@ sub check_custom_pool_roles {
|
|||
}
|
||||
}
|
||||
if ($pve_namespace_clashes > 0) {
|
||||
log_fail("$pve_namespace_clashes custom role(s) will clash with 'PVE' namespace for built-in roles enforced in Proxmox VE 8");
|
||||
log_fail(
|
||||
"$pve_namespace_clashes custom role(s) will clash with 'PVE' namespace for built-in roles enforced in Proxmox VE 8"
|
||||
);
|
||||
} elsif ($custom_roles > 0) {
|
||||
log_pass("none of the $custom_roles custom roles will clash with newly enforced 'PVE' namespace")
|
||||
log_pass(
|
||||
"none of the $custom_roles custom roles will clash with newly enforced 'PVE' namespace"
|
||||
);
|
||||
} else {
|
||||
log_pass("no custom roles defined, so no clash with 'PVE' role ID namespace enforced in Proxmox VE 8")
|
||||
log_pass(
|
||||
"no custom roles defined, so no clash with 'PVE' role ID namespace enforced in Proxmox VE 8"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -786,7 +808,8 @@ sub check_node_and_guest_configurations {
|
|||
} PVE::Cluster::get_nodelist();
|
||||
|
||||
if (scalar(@affected_nodes) > 0) {
|
||||
log_warn("Node config description of the following nodes too long for new limit of 64 KiB:\n "
|
||||
log_warn(
|
||||
"Node config description of the following nodes too long for new limit of 64 KiB:\n "
|
||||
. join(', ', @affected_nodes));
|
||||
} else {
|
||||
log_pass("All node config descriptions fit in the new limit of 64 KiB");
|
||||
|
|
@ -803,7 +826,8 @@ sub check_node_and_guest_configurations {
|
|||
push @$affected_guests_long_desc, "CT $vmid" if defined($desc) && length($desc) > 8 * 1024;
|
||||
|
||||
my $lxc_raw_conf = $conf->{lxc};
|
||||
push @$affected_cts_cgroup_keys, "CT $vmid" if (grep (@$_[0] =~ /^lxc\.cgroup\./, @$lxc_raw_conf));
|
||||
push @$affected_cts_cgroup_keys, "CT $vmid"
|
||||
if (grep (@$_[0] =~ /^lxc\.cgroup\./, @$lxc_raw_conf));
|
||||
}
|
||||
my $vms = PVE::QemuServer::config_list();
|
||||
for my $vmid (sort { $a <=> $b } keys %$vms) {
|
||||
|
|
@ -811,8 +835,10 @@ sub check_node_and_guest_configurations {
|
|||
push @$affected_guests_long_desc, "VM $vmid" if defined($desc) && length($desc) > 8 * 1024;
|
||||
}
|
||||
if (scalar($affected_guests_long_desc->@*) > 0) {
|
||||
log_warn("Guest config description of the following virtual-guests too long for new limit of 64 KiB:\n"
|
||||
." " . join(", ", $affected_guests_long_desc->@*));
|
||||
log_warn(
|
||||
"Guest config description of the following virtual-guests too long for new limit of 64 KiB:\n"
|
||||
. " "
|
||||
. join(", ", $affected_guests_long_desc->@*));
|
||||
} else {
|
||||
log_pass("All guest config descriptions fit in the new limit of 8 KiB");
|
||||
}
|
||||
|
|
@ -821,11 +847,16 @@ sub check_node_and_guest_configurations {
|
|||
|
||||
if (scalar($affected_cts_cgroup_keys->@*) > 0) {
|
||||
if ($forced_legacy_cgroup) {
|
||||
log_pass("Found legacy 'lxc.cgroup' keys, but system explicitly configured for legacy hybrid cgroup hierarchy.");
|
||||
log_pass(
|
||||
"Found legacy 'lxc.cgroup' keys, but system explicitly configured for legacy hybrid cgroup hierarchy."
|
||||
);
|
||||
} else {
|
||||
log_warn("The following CTs have 'lxc.cgroup' keys configured, which will be ignored in the new default unified cgroupv2:\n"
|
||||
." " . join(", ", $affected_cts_cgroup_keys->@*) ."\n"
|
||||
." Often it can be enough to change to the new 'lxc.cgroup2' prefix after the upgrade to Proxmox VE 7.x");
|
||||
log_warn(
|
||||
"The following CTs have 'lxc.cgroup' keys configured, which will be ignored in the new default unified cgroupv2:\n"
|
||||
. " "
|
||||
. join(", ", $affected_cts_cgroup_keys->@*) . "\n"
|
||||
. " Often it can be enough to change to the new 'lxc.cgroup2' prefix after the upgrade to Proxmox VE 7.x"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
log_pass("No legacy 'lxc.cgroup' keys found.");
|
||||
|
|
@ -880,8 +911,7 @@ sub check_storage_content {
|
|||
if ($number > 0) {
|
||||
log_info(
|
||||
"storage '$storeid' - neither content type 'images' nor 'rootdir' configured, but"
|
||||
."found $number guest volume(s)"
|
||||
);
|
||||
. "found $number guest volume(s)");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -974,8 +1004,8 @@ sub check_storage_content {
|
|||
}
|
||||
|
||||
if ($found) {
|
||||
log_warn("Proxmox VE enforces stricter content type checks since 7.0. The guests above " .
|
||||
"might not work until the storage configuration is fixed.");
|
||||
log_warn("Proxmox VE enforces stricter content type checks since 7.0. The guests above "
|
||||
. "might not work until the storage configuration is fixed.");
|
||||
}
|
||||
|
||||
if ($pass) {
|
||||
|
|
@ -1010,22 +1040,26 @@ sub check_storage_content_dirs {
|
|||
for my $subdir (keys $resolved_subdirs->%*) {
|
||||
if (scalar($resolved_subdirs->{$subdir}->@*) > 1) {
|
||||
my $types = join(", ", $resolved_subdirs->{$subdir}->@*);
|
||||
log_warn("storage '$storeid' uses directory $subdir for multiple content types ($types).");
|
||||
log_warn(
|
||||
"storage '$storeid' uses directory $subdir for multiple content types ($types)."
|
||||
);
|
||||
$any_problematic = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($any_problematic) {
|
||||
log_fail("re-using directory for multiple content types (see above) is no longer supported in Proxmox VE 8!")
|
||||
log_fail(
|
||||
"re-using directory for multiple content types (see above) is no longer supported in Proxmox VE 8!"
|
||||
);
|
||||
} else {
|
||||
log_pass("no storage re-uses a directory for multiple content types.")
|
||||
log_pass("no storage re-uses a directory for multiple content types.");
|
||||
}
|
||||
}
|
||||
|
||||
sub check_containers_cgroup_compat {
|
||||
if ($forced_legacy_cgroup) {
|
||||
log_warn("System explicitly configured for legacy hybrid cgroup hierarchy.\n"
|
||||
." NOTE: support for the hybrid cgroup hierarchy will be removed in future Proxmox VE 9 (~ 2025)."
|
||||
. " NOTE: support for the hybrid cgroup hierarchy will be removed in future Proxmox VE 9 (~ 2025)."
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1039,14 +1073,16 @@ sub check_containers_cgroup_compat {
|
|||
'/lib/systemd',
|
||||
'/usr/lib/systemd',
|
||||
'/usr/lib/x86_64-linux-gnu/systemd',
|
||||
'/usr/lib64/systemd'
|
||||
'/usr/lib64/systemd',
|
||||
);
|
||||
my $libsd;
|
||||
for my $dir (@dirs) {
|
||||
$libsd = PVE::Tools::dir_glob_regex($dir, "libsystemd-shared-.+\.so");
|
||||
last if defined($libsd);
|
||||
}
|
||||
if (defined($libsd) && $libsd =~ /libsystemd-shared-(\d+)(\.\d-\d)?(\.fc\d\d)?\.so/) {
|
||||
if (
|
||||
defined($libsd) && $libsd =~ /libsystemd-shared-(\d+)(\.\d-\d)?(\.fc\d\d)?\.so/
|
||||
) {
|
||||
return $1;
|
||||
}
|
||||
|
||||
|
|
@ -1087,10 +1123,13 @@ sub check_containers_cgroup_compat {
|
|||
|
||||
my $log_problem = sub {
|
||||
my ($ctid) = @_;
|
||||
my $extra = $forced_legacy_cgroup ? '' : " or set systemd.unified_cgroup_hierarchy=0 in the Proxmox VE hosts' kernel cmdline";
|
||||
my $extra =
|
||||
$forced_legacy_cgroup
|
||||
? ''
|
||||
: " or set systemd.unified_cgroup_hierarchy=0 in the Proxmox VE hosts' kernel cmdline";
|
||||
log_warn(
|
||||
"Found at least one CT ($ctid) which does not support running in a unified cgroup v2 layout\n"
|
||||
." Consider upgrading the Containers distro${extra}! Skipping further CT compat checks."
|
||||
. " Consider upgrading the Containers distro${extra}! Skipping further CT compat checks."
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -1151,7 +1190,7 @@ sub check_containers_cgroup_compat {
|
|||
|
||||
eval { PVE::LXC::umount_all($ctid, $storage_cfg, $conf) };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
sub check_lxcfs_fuse_version {
|
||||
log_info("Checking if LXCFS is running with FUSE3 library, if already upgraded..");
|
||||
|
|
@ -1174,9 +1213,11 @@ sub check_lxcfs_fuse_version {
|
|||
}
|
||||
|
||||
if ($lxcfs_maps =~ /\/libfuse.so.2/s) {
|
||||
log_warn("systems seems to be upgraded but LXCFS is still running with FUSE 2 library, not yet rebooted?")
|
||||
log_warn(
|
||||
"systems seems to be upgraded but LXCFS is still running with FUSE 2 library, not yet rebooted?"
|
||||
);
|
||||
} elsif ($lxcfs_maps =~ /\/libfuse3.so.3/s) {
|
||||
log_pass("systems seems to be upgraded and LXCFS is running with FUSE 3 library")
|
||||
log_pass("systems seems to be upgraded and LXCFS is running with FUSE 3 library");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -1234,10 +1275,10 @@ sub check_apt_repos {
|
|||
if (!defined($mismatches)) {
|
||||
$mismatches = [];
|
||||
push $mismatches->@*,
|
||||
{ suite => $found_suite, where => $found_suite_where},
|
||||
{ suite => $suite, where => $where};
|
||||
{ suite => $found_suite, where => $found_suite_where },
|
||||
{ suite => $suite, where => $where };
|
||||
} else {
|
||||
push $mismatches->@*, { suite => $suite, where => $where};
|
||||
push $mismatches->@*, { suite => $suite, where => $where };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1253,8 +1294,9 @@ sub check_apt_repos {
|
|||
my @strange_list = map { "found suite $_->{suite} at $_->{where}" } $strange_suites->@*;
|
||||
log_notice(
|
||||
"found unusual suites that are neither old '$old_suite' nor new '$new_suite':"
|
||||
."\n " . join("\n ", @strange_list)
|
||||
."\n Please ensure these repositories are shipping compatible packages for the upgrade!"
|
||||
. "\n "
|
||||
. join("\n ", @strange_list)
|
||||
. "\n Please ensure these repositories are shipping compatible packages for the upgrade!"
|
||||
);
|
||||
}
|
||||
if (defined($mismatches)) {
|
||||
|
|
@ -1262,11 +1304,13 @@ sub check_apt_repos {
|
|||
|
||||
log_fail(
|
||||
"Found mixed old and new package repository suites, fix before upgrading! Mismatches:"
|
||||
."\n " . join("\n ", @mismatch_list)
|
||||
."\n Configure the same base-suite for all Proxmox and Debian provided repos and ask"
|
||||
." original vendor for any third-party repos."
|
||||
."\n E.g., for the upgrade to Proxmox VE ".($min_pve_major + 1)." use the '$new_suite' suite."
|
||||
);
|
||||
. "\n "
|
||||
. join("\n ", @mismatch_list)
|
||||
. "\n Configure the same base-suite for all Proxmox and Debian provided repos and ask"
|
||||
. " original vendor for any third-party repos."
|
||||
. "\n E.g., for the upgrade to Proxmox VE "
|
||||
. ($min_pve_major + 1)
|
||||
. " use the '$new_suite' suite.");
|
||||
} elsif (defined($strange_suites)) {
|
||||
log_notice("found no suite mismatches, but found at least one strange suite");
|
||||
} else {
|
||||
|
|
@ -1278,7 +1322,7 @@ sub check_nvidia_vgpu_service {
|
|||
log_info("Checking for existence of NVIDIA vGPU Manager..");
|
||||
|
||||
my $msg = "NVIDIA vGPU Service found, possibly not compatible with newer kernel versions, check"
|
||||
." with their documentation and https://pve.proxmox.com/wiki/Upgrade_from_7_to_8#Known_upgrade_issues.";
|
||||
. " with their documentation and https://pve.proxmox.com/wiki/Upgrade_from_7_to_8#Known_upgrade_issues.";
|
||||
|
||||
my $state = $get_systemd_unit_state->("nvidia-vgpu-mgr.service", 1);
|
||||
if ($state && $state eq 'active') {
|
||||
|
|
@ -1291,57 +1335,59 @@ sub check_nvidia_vgpu_service {
|
|||
}
|
||||
|
||||
sub check_time_sync {
|
||||
my $unit_active = sub { return $get_systemd_unit_state->($_[0], 1) eq 'active' ? $_[0] : undef };
|
||||
my $unit_active =
|
||||
sub { return $get_systemd_unit_state->($_[0], 1) eq 'active' ? $_[0] : undef };
|
||||
|
||||
log_info("Checking for supported & active NTP service..");
|
||||
if ($unit_active->('systemd-timesyncd.service')) {
|
||||
log_warn(
|
||||
"systemd-timesyncd is not the best choice for time-keeping on servers, due to only applying"
|
||||
." updates on boot.\n While not necessary for the upgrade it's recommended to use one of:\n"
|
||||
." * chrony (Default in new Proxmox VE installations)\n * ntpsec\n * openntpd\n"
|
||||
. " updates on boot.\n While not necessary for the upgrade it's recommended to use one of:\n"
|
||||
. " * chrony (Default in new Proxmox VE installations)\n * ntpsec\n * openntpd\n"
|
||||
);
|
||||
} elsif ($unit_active->('ntp.service')) {
|
||||
log_info("Debian deprecated and removed the ntp package for Bookworm, but the system"
|
||||
." will automatically migrate to the 'ntpsec' replacement package on upgrade.");
|
||||
} elsif (my $active_ntp = ($unit_active->('chrony.service') || $unit_active->('openntpd.service') || $unit_active->('ntpsec.service'))) {
|
||||
. " will automatically migrate to the 'ntpsec' replacement package on upgrade.");
|
||||
} elsif (
|
||||
my $active_ntp = (
|
||||
$unit_active->('chrony.service')
|
||||
|| $unit_active->('openntpd.service')
|
||||
|| $unit_active->('ntpsec.service')
|
||||
)
|
||||
) {
|
||||
log_pass("Detected active time synchronisation unit '$active_ntp'");
|
||||
} else {
|
||||
log_warn(
|
||||
"No (active) time synchronisation daemon (NTP) detected, but synchronized systems are important,"
|
||||
." especially for cluster and/or ceph!"
|
||||
);
|
||||
. " especially for cluster and/or ceph!");
|
||||
}
|
||||
}
|
||||
|
||||
sub check_bootloader {
|
||||
log_info("Checking bootloader configuration...");
|
||||
|
||||
if (! -d '/sys/firmware/efi') {
|
||||
if (!-d '/sys/firmware/efi') {
|
||||
log_skip("System booted in legacy-mode - no need for additional packages");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( -f "/etc/kernel/proxmox-boot-uuids") {
|
||||
if (-f "/etc/kernel/proxmox-boot-uuids") {
|
||||
if (!$upgraded) {
|
||||
log_skip("not yet upgraded, no need to check the presence of systemd-boot");
|
||||
return;
|
||||
}
|
||||
if ( -f "/usr/share/doc/systemd-boot/changelog.Debian.gz") {
|
||||
if (-f "/usr/share/doc/systemd-boot/changelog.Debian.gz") {
|
||||
log_pass("bootloader packages installed correctly");
|
||||
return;
|
||||
}
|
||||
log_warn(
|
||||
"proxmox-boot-tool is used for bootloader configuration in uefi mode"
|
||||
log_warn("proxmox-boot-tool is used for bootloader configuration in uefi mode"
|
||||
. " but the separate systemd-boot package is not installed,"
|
||||
. " initializing new ESPs will not work until the package is installed"
|
||||
);
|
||||
. " initializing new ESPs will not work until the package is installed");
|
||||
return;
|
||||
} elsif ( ! -f "/usr/share/doc/grub-efi-amd64/changelog.Debian.gz" ) {
|
||||
log_warn(
|
||||
"System booted in uefi mode but grub-efi-amd64 meta-package not installed,"
|
||||
} elsif (!-f "/usr/share/doc/grub-efi-amd64/changelog.Debian.gz") {
|
||||
log_warn("System booted in uefi mode but grub-efi-amd64 meta-package not installed,"
|
||||
. " new grub versions will not be installed to /boot/efi!"
|
||||
. " Install grub-efi-amd64."
|
||||
);
|
||||
. " Install grub-efi-amd64.");
|
||||
return;
|
||||
} else {
|
||||
log_pass("bootloader packages installed correctly");
|
||||
|
|
@ -1359,7 +1405,7 @@ sub check_dkms_modules {
|
|||
my $sig_pipe = $SIG{PIPE};
|
||||
$SIG{PIPE} = "DEFAULT";
|
||||
my $exit_code = eval {
|
||||
run_command(['dkms', 'status', '-k', '`uname -r`'], outfunc => $set_count, noerr => 1)
|
||||
run_command(['dkms', 'status', '-k', '`uname -r`'], outfunc => $set_count, noerr => 1);
|
||||
};
|
||||
$SIG{PIPE} = $sig_pipe;
|
||||
|
||||
|
|
@ -1392,7 +1438,7 @@ sub check_misc {
|
|||
|
||||
my $root_free = PVE::Tools::df('/', 10);
|
||||
log_warn("Less than 5 GB free space on root file system.")
|
||||
if defined($root_free) && $root_free->{avail} < 5 * 1000*1000*1000;
|
||||
if defined($root_free) && $root_free->{avail} < 5 * 1000 * 1000 * 1000;
|
||||
|
||||
log_info("Checking for running guests..");
|
||||
my $running_guests = 0;
|
||||
|
|
@ -1406,9 +1452,10 @@ sub check_misc {
|
|||
$running_guests += grep { $_->{status} eq 'running' } @$cts if defined($cts);
|
||||
|
||||
if ($running_guests > 0) {
|
||||
log_warn("$running_guests running guest(s) detected - consider migrating or stopping them.")
|
||||
log_warn(
|
||||
"$running_guests running guest(s) detected - consider migrating or stopping them.");
|
||||
} else {
|
||||
log_pass("no running guest detected.")
|
||||
log_pass("no running guest detected.");
|
||||
}
|
||||
|
||||
log_info("Checking if the local node's hostname '$nodename' is resolvable..");
|
||||
|
|
@ -1461,7 +1508,9 @@ sub check_misc {
|
|||
log_fail("'$fn', certificate's $check->{name} public key size is less than 2048 bit");
|
||||
$certs_check_failed = 1;
|
||||
} else {
|
||||
log_pass("Certificate '$fn' passed Debian Busters (and newer) security level for TLS connections ($size >= 2048)");
|
||||
log_pass(
|
||||
"Certificate '$fn' passed Debian Busters (and newer) security level for TLS connections ($size >= 2048)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1478,10 +1527,10 @@ sub check_misc {
|
|||
|
||||
my sub colored_if {
|
||||
my ($str, $color, $condition) = @_;
|
||||
return "". ($condition ? colored($str, $color) : $str);
|
||||
return "" . ($condition ? colored($str, $color) : $str);
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'checklist',
|
||||
path => 'checklist',
|
||||
method => 'GET',
|
||||
|
|
@ -1502,7 +1551,7 @@ __PACKAGE__->register_method ({
|
|||
my ($param) = @_;
|
||||
|
||||
my $kernel_cli = PVE::Tools::file_get_contents('/proc/cmdline');
|
||||
if ($kernel_cli =~ /systemd.unified_cgroup_hierarchy=0/){
|
||||
if ($kernel_cli =~ /systemd.unified_cgroup_hierarchy=0/) {
|
||||
$forced_legacy_cgroup = 1;
|
||||
}
|
||||
|
||||
|
|
@ -1515,7 +1564,9 @@ __PACKAGE__->register_method ({
|
|||
if ($param->{full}) {
|
||||
check_containers_cgroup_compat();
|
||||
} else {
|
||||
log_skip("NOTE: Expensive checks, like CT cgroupv2 compat, not performed without '--full' parameter");
|
||||
log_skip(
|
||||
"NOTE: Expensive checks, like CT cgroupv2 compat, not performed without '--full' parameter"
|
||||
);
|
||||
}
|
||||
|
||||
print_header("SUMMARY");
|
||||
|
|
@ -1531,13 +1582,18 @@ __PACKAGE__->register_method ({
|
|||
|
||||
if ($counters->{warn} > 0 || $counters->{fail} > 0) {
|
||||
my $color = $counters->{fail} > 0 ? 'bold red' : 'yellow';
|
||||
print colored("\nATTENTION: Please check the output for detailed information!\n", $color);
|
||||
print colored("Try to solve the problems one at a time and then run this checklist tool again.\n", $color) if $counters->{fail} > 0;
|
||||
print colored("\nATTENTION: Please check the output for detailed information!\n",
|
||||
$color);
|
||||
print colored(
|
||||
"Try to solve the problems one at a time and then run this checklist tool again.\n",
|
||||
$color,
|
||||
) if $counters->{fail} > 0;
|
||||
}
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
our $cmddef = [ __PACKAGE__, 'checklist', [], {}];
|
||||
our $cmddef = [__PACKAGE__, 'checklist', [], {}];
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ sub setup_environment {
|
|||
PVE::RPCEnvironment->setup_default_cli_env();
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update',
|
||||
path => 'update',
|
||||
method => 'PUT',
|
||||
|
|
@ -33,7 +33,7 @@ __PACKAGE__->register_method ({
|
|||
additionalProperties => 0,
|
||||
properties => {},
|
||||
},
|
||||
returns => { type => 'null'},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
|
||||
my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
|
||||
|
|
@ -41,16 +41,17 @@ __PACKAGE__->register_method ({
|
|||
my $res = PVE::APLInfo::update($dccfg->{http_proxy});
|
||||
|
||||
if ($res) {
|
||||
print STDOUT "update successful\n"
|
||||
print STDOUT "update successful\n";
|
||||
} else {
|
||||
print STDERR "update failed - see /var/log/pveam.log for details\n"
|
||||
print STDERR "update failed - see /var/log/pveam.log for details\n";
|
||||
}
|
||||
|
||||
return undef;
|
||||
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'available',
|
||||
path => 'available',
|
||||
method => 'GET',
|
||||
|
|
@ -64,9 +65,9 @@ __PACKAGE__->register_method ({
|
|||
enum => ['system', 'mail', 'turnkeylinux'],
|
||||
optional => 1,
|
||||
},
|
||||
}
|
||||
},
|
||||
returns => { type => 'null'},
|
||||
},
|
||||
returns => { type => 'null' },
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
|
|
@ -75,15 +76,16 @@ __PACKAGE__->register_method ({
|
|||
foreach my $section (sort keys %$list) {
|
||||
next if $section eq 'all';
|
||||
next if $param->{section} && $section ne $param->{section};
|
||||
foreach my $template (sort keys %{$list->{$section}}) {
|
||||
foreach my $template (sort keys %{ $list->{$section} }) {
|
||||
print sprintf("%-15s %s\n", $section, $template);
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'index',
|
||||
path => 'index',
|
||||
method => 'GET',
|
||||
|
|
@ -98,10 +100,13 @@ __PACKAGE__->register_method ({
|
|||
additionalProperties => 0,
|
||||
properties => {
|
||||
node => get_standard_option('pve-node'),
|
||||
storage => get_standard_option('pve-storage-id', {
|
||||
storage => get_standard_option(
|
||||
'pve-storage-id',
|
||||
{
|
||||
description => "Only list templates on specified storage",
|
||||
completion => \&PVE::Storage::complete_storage_enabled,
|
||||
}),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
|
|
@ -124,21 +129,27 @@ __PACKAGE__->register_method ({
|
|||
|
||||
PVE::Storage::storage_check_enabled($cfg, $storeid);
|
||||
|
||||
die "Storage does not support templates!\n" if !$cfg->{ids}->{$storeid}->{content}->{vztmpl};
|
||||
die "Storage does not support templates!\n"
|
||||
if !$cfg->{ids}->{$storeid}->{content}->{vztmpl};
|
||||
|
||||
my $vollist = PVE::Storage::volume_list($cfg, $storeid, undef, 'vztmpl');
|
||||
|
||||
my $res = [];
|
||||
foreach my $item (@$vollist) {
|
||||
eval { PVE::Storage::check_volume_access($rpcenv, $authuser, $cfg, undef, $item->{volid}); };
|
||||
eval {
|
||||
PVE::Storage::check_volume_access(
|
||||
$rpcenv, $authuser, $cfg, undef, $item->{volid},
|
||||
);
|
||||
};
|
||||
next if $@;
|
||||
push @$res, $item;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'remove',
|
||||
path => 'remove',
|
||||
method => 'DELETE',
|
||||
|
|
@ -179,26 +190,30 @@ __PACKAGE__->register_method ({
|
|||
unlink $abs_path;
|
||||
|
||||
return undef;
|
||||
}});
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
my $print_list = sub {
|
||||
my ($list) = @_;
|
||||
|
||||
printf "%-60s %-6s\n",
|
||||
qw(NAME SIZE);
|
||||
printf "%-60s %-6s\n", qw(NAME SIZE);
|
||||
|
||||
foreach my $rec (@$list) {
|
||||
printf "%-60s %-4.2fMB\n", $rec->{volid}, $rec->{size}/(1024*1024);
|
||||
printf "%-60s %-4.2fMB\n", $rec->{volid}, $rec->{size} / (1024 * 1024);
|
||||
}
|
||||
};
|
||||
|
||||
our $cmddef = {
|
||||
update => [ __PACKAGE__, 'update', []],
|
||||
download => [ 'PVE::API2::Nodes::Nodeinfo', 'apl_download', [ 'storage', 'template'], { node => $nodename } ],
|
||||
available => [ __PACKAGE__, 'available', []],
|
||||
list => [ __PACKAGE__, 'index', [ 'storage' ], { node => $nodename }, $print_list ],
|
||||
remove => [ __PACKAGE__, 'remove', [ 'template_path' ], { node => $nodename }]
|
||||
update => [__PACKAGE__, 'update', []],
|
||||
download => [
|
||||
'PVE::API2::Nodes::Nodeinfo',
|
||||
'apl_download',
|
||||
['storage', 'template'],
|
||||
{ node => $nodename },
|
||||
],
|
||||
available => [__PACKAGE__, 'available', []],
|
||||
list => [__PACKAGE__, 'index', ['storage'], { node => $nodename }, $print_list],
|
||||
remove => [__PACKAGE__, 'remove', ['template_path'], { node => $nodename }],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ sub setup_environment {
|
|||
PVE::RPCEnvironment->setup_default_cli_env();
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'purge',
|
||||
path => 'purge',
|
||||
method => 'POST',
|
||||
|
|
@ -107,7 +107,8 @@ __PACKAGE__->register_method ({
|
|||
PVE::Ceph::Tools::purge_all_ceph_files($services);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my sub has_valid_subscription {
|
||||
my $info = eval { Proxmox::RS::Subscription::read_subscription('/etc/subscription') } // {};
|
||||
|
|
@ -118,7 +119,7 @@ my sub has_valid_subscription {
|
|||
my $available_ceph_release_codenames = PVE::Ceph::Releases::get_available_ceph_release_codenames(1);
|
||||
my $default_ceph_version = PVE::Ceph::Releases::get_default_ceph_release_codename();
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'install',
|
||||
path => 'install',
|
||||
method => 'POST',
|
||||
|
|
@ -156,27 +157,33 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $repo = $param->{'repository'} // 'enterprise';
|
||||
my $enterprise_repo = $repo eq 'enterprise';
|
||||
my $cdn = $enterprise_repo ? 'https://enterprise.proxmox.com' : 'http://download.proxmox.com';
|
||||
my $cdn =
|
||||
$enterprise_repo ? 'https://enterprise.proxmox.com' : 'http://download.proxmox.com';
|
||||
|
||||
if (has_valid_subscription()) {
|
||||
warn "\nNOTE: The node has an active subscription but a non-production Ceph repository selected.\n\n"
|
||||
warn
|
||||
"\nNOTE: The node has an active subscription but a non-production Ceph repository selected.\n\n"
|
||||
if !$enterprise_repo;
|
||||
} elsif ($enterprise_repo) {
|
||||
warn "\nWARN: Enterprise repository selected, but no active subscription!\n\n";
|
||||
} elsif ($repo eq 'no-subscription') {
|
||||
warn "\nHINT: The no-subscription repository is not the best choice for production setups.\n"
|
||||
."Proxmox recommends using the enterprise repository with a valid subscription.\n";
|
||||
warn
|
||||
"\nHINT: The no-subscription repository is not the best choice for production setups.\n"
|
||||
. "Proxmox recommends using the enterprise repository with a valid subscription.\n";
|
||||
} else {
|
||||
warn "\nWARN: The test repository should only be used for test setups or after consulting"
|
||||
." the official Proxmox support!\n\n"
|
||||
warn
|
||||
"\nWARN: The test repository should only be used for test setups or after consulting"
|
||||
. " the official Proxmox support!\n\n";
|
||||
}
|
||||
|
||||
my $available_ceph_releases = PVE::Ceph::Releases::get_all_available_ceph_releases();
|
||||
die "unsupported ceph version: $cephver" if !exists($available_ceph_releases->{$cephver});
|
||||
die "unsupported ceph version: $cephver"
|
||||
if !exists($available_ceph_releases->{$cephver});
|
||||
|
||||
my $repolist = "deb ${cdn}/debian/ceph-${cephver} bookworm $repo\n";
|
||||
|
||||
my $rendered_release = $available_ceph_releases->{$cephver}->{release} . ' ' . ucfirst($cephver);
|
||||
my $rendered_release =
|
||||
$available_ceph_releases->{$cephver}->{release} . ' ' . ucfirst($cephver);
|
||||
if (-t STDOUT && !$param->{version}) {
|
||||
print "This will install Ceph ${rendered_release} - continue (y/N)? ";
|
||||
|
||||
|
|
@ -190,15 +197,18 @@ __PACKAGE__->register_method ({
|
|||
|
||||
if ($available_ceph_releases->{$cephver}->{unsupported}) {
|
||||
if ($param->{'allow-experimental'}) {
|
||||
warn "NOTE: installing experimental/tech-preview Ceph release ${rendered_release}!\n";
|
||||
warn
|
||||
"NOTE: installing experimental/tech-preview Ceph release ${rendered_release}!\n";
|
||||
} elsif (-t STDOUT) {
|
||||
print "Ceph ${rendered_release} is currently considered a technology preview for Proxmox VE - continue (y/N)? ";
|
||||
print
|
||||
"Ceph ${rendered_release} is currently considered a technology preview for Proxmox VE - continue (y/N)? ";
|
||||
my $answer = <STDIN>;
|
||||
my $continue = defined($answer) && $answer =~ m/^\s*y(?:es)?\s*$/i;
|
||||
|
||||
die "Aborting installation as requested\n" if !$continue;
|
||||
} else {
|
||||
die "refusing to install tech-preview Ceph release ${rendered_release} without 'allow-experimental' parameter!\n";
|
||||
die
|
||||
"refusing to install tech-preview Ceph release ${rendered_release} without 'allow-experimental' parameter!\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,12 +217,13 @@ __PACKAGE__->register_method ({
|
|||
eval {
|
||||
run_command(
|
||||
['apt-get', '-q', 'update'],
|
||||
outfunc => sub {},
|
||||
outfunc => sub { },
|
||||
errfunc => sub { print STDERR "$_[0]\n" },
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
my @apt_install = qw(apt-get --no-install-recommends -o Dpkg::Options::=--force-confnew install --);
|
||||
my @apt_install =
|
||||
qw(apt-get --no-install-recommends -o Dpkg::Options::=--force-confnew install --);
|
||||
my @ceph_packages = qw(
|
||||
ceph
|
||||
ceph-common
|
||||
|
|
@ -227,7 +238,8 @@ __PACKAGE__->register_method ({
|
|||
|
||||
# this flag helps to determine when apt is actually done installing (vs. partial extracting)
|
||||
my $install_flag_fn = PVE::Ceph::Tools::ceph_install_flag_file();
|
||||
open(my $install_flag, '>', $install_flag_fn) or die "could not create install flag - $!\n";
|
||||
open(my $install_flag, '>', $install_flag_fn)
|
||||
or die "could not create install flag - $!\n";
|
||||
close $install_flag;
|
||||
|
||||
if (system(@apt_install, @ceph_packages) != 0) {
|
||||
|
|
@ -241,13 +253,14 @@ __PACKAGE__->register_method ({
|
|||
|
||||
print "\nreloading API to load new Ceph RADOS library...\n";
|
||||
run_command([
|
||||
'systemctl', 'try-reload-or-restart', 'pvedaemon.service', 'pveproxy.service'
|
||||
'systemctl', 'try-reload-or-restart', 'pvedaemon.service', 'pveproxy.service',
|
||||
]);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'status',
|
||||
path => 'status',
|
||||
method => 'GET',
|
||||
|
|
@ -266,7 +279,8 @@ __PACKAGE__->register_method ({
|
|||
timeout => 15,
|
||||
);
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $get_storages = sub {
|
||||
my ($fs, $is_default) = @_;
|
||||
|
|
@ -286,7 +300,7 @@ my $get_storages = sub {
|
|||
return $res;
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'destroyfs',
|
||||
path => 'destroyfs',
|
||||
method => 'DELETE',
|
||||
|
|
@ -350,8 +364,8 @@ __PACKAGE__->register_method ({
|
|||
for my $storeid (keys %$storages) {
|
||||
my $store = $storages->{$storeid};
|
||||
if (!$store->{disable}) {
|
||||
die "storage '$storeid' is not disabled, make sure to disable ".
|
||||
"and unmount the storage first\n";
|
||||
die "storage '$storeid' is not disabled, make sure to disable "
|
||||
. "and unmount the storage first\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -359,7 +373,7 @@ __PACKAGE__->register_method ({
|
|||
for my $storeid (keys %$storages) {
|
||||
# skip external clusters, not managed by pveceph
|
||||
next if $storages->{$storeid}->{monhost};
|
||||
eval { PVE::API2::Storage::Config->delete({storage => $storeid}) };
|
||||
eval { PVE::API2::Storage::Config->delete({ storage => $storeid }) };
|
||||
if ($@) {
|
||||
warn "failed to remove storage '$storeid': $@\n";
|
||||
$err = 1;
|
||||
|
|
@ -385,9 +399,10 @@ __PACKAGE__->register_method ({
|
|||
|
||||
};
|
||||
return $rpcenv->fork_worker('cephdestroyfs', $fs_name, $user, $worker);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'osd-details',
|
||||
path => 'osd-details',
|
||||
method => 'GET',
|
||||
|
|
@ -428,7 +443,8 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
$res->{verbose} = 1 if $param->{verbose};
|
||||
return $res;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $format_osddetails = sub {
|
||||
my ($data, $schema, $options) = @_;
|
||||
|
|
@ -442,10 +458,11 @@ my $format_osddetails = sub {
|
|||
|
||||
if ($options->{"output-format"} eq "text") {
|
||||
for my $dev ($data->{devices}->@*) {
|
||||
my ($disk, $type, $device) = $dev->@{'physical_device', 'type', 'device'};
|
||||
my ($lv_size, $lv_ctime) = $dev->{'lv-info'}->@{'lv_size', 'creation_time'};
|
||||
my ($disk, $type, $device) = $dev->@{ 'physical_device', 'type', 'device' };
|
||||
my ($lv_size, $lv_ctime) = $dev->{'lv-info'}->@{ 'lv_size', 'creation_time' };
|
||||
|
||||
$data->{osd}->{$device} = "Disk: $disk, Type: $type, LV Size: $lv_size, LV Creation Time: $lv_ctime";
|
||||
$data->{osd}->{$device} =
|
||||
"Disk: $disk, Type: $type, LV Size: $lv_size, LV Creation Time: $lv_ctime";
|
||||
}
|
||||
PVE::CLIFormatter::print_api_result($data->{osd}, $schema, undef, $options);
|
||||
} else {
|
||||
|
|
@ -454,11 +471,18 @@ my $format_osddetails = sub {
|
|||
};
|
||||
|
||||
our $cmddef = {
|
||||
init => [ 'PVE::API2::Ceph', 'init', [], { node => $nodename } ],
|
||||
init => ['PVE::API2::Ceph', 'init', [], { node => $nodename }],
|
||||
pool => {
|
||||
ls => [ 'PVE::API2::Ceph::Pool', 'lspools', [], { node => $nodename }, sub {
|
||||
ls => [
|
||||
'PVE::API2::Ceph::Pool',
|
||||
'lspools',
|
||||
[],
|
||||
{ node => $nodename },
|
||||
sub {
|
||||
my ($data, $schema, $options) = @_;
|
||||
PVE::CLIFormatter::print_api_result($data, $schema,
|
||||
PVE::CLIFormatter::print_api_result(
|
||||
$data,
|
||||
$schema,
|
||||
[
|
||||
'pool_name',
|
||||
'size',
|
||||
|
|
@ -473,54 +497,73 @@ our $cmddef = {
|
|||
'percent_used',
|
||||
'bytes_used',
|
||||
],
|
||||
$options);
|
||||
}, $PVE::RESTHandler::standard_output_options],
|
||||
create => [ 'PVE::API2::Ceph::Pool', 'createpool', ['name'], { node => $nodename }],
|
||||
destroy => [ 'PVE::API2::Ceph::Pool', 'destroypool', ['name'], { node => $nodename } ],
|
||||
set => [ 'PVE::API2::Ceph::Pool', 'setpool', ['name'], { node => $nodename } ],
|
||||
get => [ 'PVE::API2::Ceph::Pool', 'getpool', ['name'], { node => $nodename }, sub {
|
||||
$options,
|
||||
);
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
create => ['PVE::API2::Ceph::Pool', 'createpool', ['name'], { node => $nodename }],
|
||||
destroy => ['PVE::API2::Ceph::Pool', 'destroypool', ['name'], { node => $nodename }],
|
||||
set => ['PVE::API2::Ceph::Pool', 'setpool', ['name'], { node => $nodename }],
|
||||
get => [
|
||||
'PVE::API2::Ceph::Pool',
|
||||
'getpool',
|
||||
['name'],
|
||||
{ node => $nodename },
|
||||
sub {
|
||||
my ($data, $schema, $options) = @_;
|
||||
PVE::CLIFormatter::print_api_result($data, $schema, undef, $options);
|
||||
}, $PVE::RESTHandler::standard_output_options],
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
},
|
||||
lspools => { alias => 'pool ls' },
|
||||
createpool => { alias => 'pool create' },
|
||||
destroypool => { alias => 'pool destroy' },
|
||||
fs => {
|
||||
create => [ 'PVE::API2::Ceph::FS', 'createfs', [], { node => $nodename }],
|
||||
destroy => [ __PACKAGE__, 'destroyfs', ['name'], { node => $nodename }],
|
||||
create => ['PVE::API2::Ceph::FS', 'createfs', [], { node => $nodename }],
|
||||
destroy => [__PACKAGE__, 'destroyfs', ['name'], { node => $nodename }],
|
||||
},
|
||||
osd => {
|
||||
create => [ 'PVE::API2::Ceph::OSD', 'createosd', ['dev'], { node => $nodename }, $upid_exit],
|
||||
destroy => [ 'PVE::API2::Ceph::OSD', 'destroyosd', ['osdid'], { node => $nodename }, $upid_exit],
|
||||
create =>
|
||||
['PVE::API2::Ceph::OSD', 'createosd', ['dev'], { node => $nodename }, $upid_exit],
|
||||
destroy =>
|
||||
['PVE::API2::Ceph::OSD', 'destroyosd', ['osdid'], { node => $nodename }, $upid_exit],
|
||||
details => [
|
||||
__PACKAGE__, 'osd-details', ['osdid'], { node => $nodename }, $format_osddetails,
|
||||
__PACKAGE__,
|
||||
'osd-details',
|
||||
['osdid'],
|
||||
{ node => $nodename },
|
||||
$format_osddetails,
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
},
|
||||
createosd => { alias => 'osd create' },
|
||||
destroyosd => { alias => 'osd destroy' },
|
||||
mon => {
|
||||
create => [ 'PVE::API2::Ceph::MON', 'createmon', [], { node => $nodename }, $upid_exit],
|
||||
destroy => [ 'PVE::API2::Ceph::MON', 'destroymon', ['monid'], { node => $nodename }, $upid_exit],
|
||||
create => ['PVE::API2::Ceph::MON', 'createmon', [], { node => $nodename }, $upid_exit],
|
||||
destroy =>
|
||||
['PVE::API2::Ceph::MON', 'destroymon', ['monid'], { node => $nodename }, $upid_exit],
|
||||
},
|
||||
createmon => { alias => 'mon create' },
|
||||
destroymon => { alias => 'mon destroy' },
|
||||
mgr => {
|
||||
create => [ 'PVE::API2::Ceph::MGR', 'createmgr', [], { node => $nodename }, $upid_exit],
|
||||
destroy => [ 'PVE::API2::Ceph::MGR', 'destroymgr', ['id'], { node => $nodename }, $upid_exit],
|
||||
create => ['PVE::API2::Ceph::MGR', 'createmgr', [], { node => $nodename }, $upid_exit],
|
||||
destroy =>
|
||||
['PVE::API2::Ceph::MGR', 'destroymgr', ['id'], { node => $nodename }, $upid_exit],
|
||||
},
|
||||
createmgr => { alias => 'mgr create' },
|
||||
destroymgr => { alias => 'mgr destroy' },
|
||||
mds => {
|
||||
create => [ 'PVE::API2::Ceph::MDS', 'createmds', [], { node => $nodename }, $upid_exit],
|
||||
destroy => [ 'PVE::API2::Ceph::MDS', 'destroymds', ['name'], { node => $nodename }, $upid_exit],
|
||||
create => ['PVE::API2::Ceph::MDS', 'createmds', [], { node => $nodename }, $upid_exit],
|
||||
destroy =>
|
||||
['PVE::API2::Ceph::MDS', 'destroymds', ['name'], { node => $nodename }, $upid_exit],
|
||||
},
|
||||
start => [ 'PVE::API2::Ceph', 'start', [], { node => $nodename }, $upid_exit],
|
||||
stop => [ 'PVE::API2::Ceph', 'stop', [], { node => $nodename }, $upid_exit],
|
||||
install => [ __PACKAGE__, 'install', [] ],
|
||||
purge => [ __PACKAGE__, 'purge', [] ],
|
||||
status => [ __PACKAGE__, 'status', []],
|
||||
start => ['PVE::API2::Ceph', 'start', [], { node => $nodename }, $upid_exit],
|
||||
stop => ['PVE::API2::Ceph', 'stop', [], { node => $nodename }, $upid_exit],
|
||||
install => [__PACKAGE__, 'install', []],
|
||||
purge => [__PACKAGE__, 'purge', []],
|
||||
status => [__PACKAGE__, 'status', []],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -45,19 +45,32 @@ sub param_mapping {
|
|||
my $load_file_and_encode = sub {
|
||||
my ($filename) = @_;
|
||||
|
||||
return PVE::ACME::Challenge->encode_value('string', 'data', PVE::Tools::file_get_contents($filename));
|
||||
return PVE::ACME::Challenge->encode_value(
|
||||
'string',
|
||||
'data',
|
||||
PVE::Tools::file_get_contents($filename),
|
||||
);
|
||||
};
|
||||
|
||||
my $mapping = {
|
||||
'upload_custom_cert' => [
|
||||
'certificates',
|
||||
'key',
|
||||
'certificates', 'key',
|
||||
],
|
||||
'add_plugin' => [
|
||||
['data', $load_file_and_encode, "File with one key-value pair per line, will be base64url encode for storage in plugin config.", 0],
|
||||
[
|
||||
'data',
|
||||
$load_file_and_encode,
|
||||
"File with one key-value pair per line, will be base64url encode for storage in plugin config.",
|
||||
0,
|
||||
],
|
||||
],
|
||||
'update_plugin' => [
|
||||
['data', $load_file_and_encode, "File with one key-value pair per line, will be base64url encode for storage in plugin config.", 0],
|
||||
[
|
||||
'data',
|
||||
$load_file_and_encode,
|
||||
"File with one key-value pair per line, will be base64url encode for storage in plugin config.",
|
||||
0,
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -89,7 +102,8 @@ __PACKAGE__->register_method({
|
|||
print "Directory endpoints:\n";
|
||||
my $i = 0;
|
||||
while ($i < @$directories) {
|
||||
print $i, ") ", $directories->[$i]->{name}, " (", $directories->[$i]->{url}, ")\n";
|
||||
print $i, ") ", $directories->[$i]->{name}, " (", $directories->[$i]->{url},
|
||||
")\n";
|
||||
$i++;
|
||||
}
|
||||
print $i, ") Custom\n";
|
||||
|
|
@ -136,7 +150,8 @@ __PACKAGE__->register_method({
|
|||
my $eab_enabled = $meta->{externalAccountRequired};
|
||||
if (!$eab_enabled && $custom_directory) {
|
||||
my $term = Term::ReadLine->new('pvenode');
|
||||
my $agreed = $term->readline('Do you want to use external account binding? [y|N]: ');
|
||||
my $agreed =
|
||||
$term->readline('Do you want to use external account binding? [y|N]: ');
|
||||
$eab_enabled = ($agreed =~ /^y$/i);
|
||||
} elsif ($eab_enabled) {
|
||||
print "The CA requires external account binding.\n";
|
||||
|
|
@ -154,31 +169,50 @@ __PACKAGE__->register_method({
|
|||
print "\nAttempting to register account with '$param->{directory}'..\n";
|
||||
|
||||
$upid_exit->(PVE::API2::ACMEAccount->register_account($param));
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $print_cert_info = sub {
|
||||
my ($schema, $cert, $options) = @_;
|
||||
|
||||
my $order = [qw(filename fingerprint subject issuer notbefore notafter public-key-type public-key-bits san)];
|
||||
my $order = [
|
||||
qw(filename fingerprint subject issuer notbefore notafter public-key-type public-key-bits san)
|
||||
];
|
||||
PVE::CLIFormatter::print_api_result(
|
||||
$cert, $schema, $order, { %$options, noheader => 1, sort_key => 0 });
|
||||
$cert,
|
||||
$schema,
|
||||
$order,
|
||||
{ %$options, noheader => 1, sort_key => 0 },
|
||||
);
|
||||
};
|
||||
|
||||
our $cmddef = {
|
||||
config => {
|
||||
get => [ 'PVE::API2::NodeConfig', 'get_config', [], { node => $nodename }, sub {
|
||||
get => [
|
||||
'PVE::API2::NodeConfig',
|
||||
'get_config',
|
||||
[],
|
||||
{ node => $nodename },
|
||||
sub {
|
||||
my ($res) = @_;
|
||||
print PVE::NodeConfig::write_node_config($res);
|
||||
}],
|
||||
set => [ 'PVE::API2::NodeConfig', 'set_options', [], { node => $nodename } ],
|
||||
},
|
||||
],
|
||||
set => ['PVE::API2::NodeConfig', 'set_options', [], { node => $nodename }],
|
||||
},
|
||||
|
||||
startall => [ 'PVE::API2::Nodes::Nodeinfo', 'startall', [], { node => $nodename } ],
|
||||
stopall => [ 'PVE::API2::Nodes::Nodeinfo', 'stopall', [], { node => $nodename } ],
|
||||
migrateall => [ 'PVE::API2::Nodes::Nodeinfo', 'migrateall', [ 'target' ], { node => $nodename } ],
|
||||
startall => ['PVE::API2::Nodes::Nodeinfo', 'startall', [], { node => $nodename }],
|
||||
stopall => ['PVE::API2::Nodes::Nodeinfo', 'stopall', [], { node => $nodename }],
|
||||
migrateall =>
|
||||
['PVE::API2::Nodes::Nodeinfo', 'migrateall', ['target'], { node => $nodename }],
|
||||
|
||||
cert => {
|
||||
info => [ 'PVE::API2::Certificates', 'info', [], { node => $nodename }, sub {
|
||||
info => [
|
||||
'PVE::API2::Certificates',
|
||||
'info',
|
||||
[],
|
||||
{ node => $nodename },
|
||||
sub {
|
||||
my ($res, $schema, $options) = @_;
|
||||
|
||||
if (!$options->{'output-format'} || $options->{'output-format'} eq 'text') {
|
||||
|
|
@ -189,84 +223,157 @@ our $cmddef = {
|
|||
PVE::CLIFormatter::print_api_result($res, $schema, undef, $options);
|
||||
}
|
||||
|
||||
}, $PVE::RESTHandler::standard_output_options],
|
||||
set => [ 'PVE::API2::Certificates', 'upload_custom_cert', ['certificates', 'key'], { node => $nodename }, sub {
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
set => [
|
||||
'PVE::API2::Certificates',
|
||||
'upload_custom_cert',
|
||||
['certificates', 'key'],
|
||||
{ node => $nodename },
|
||||
sub {
|
||||
my ($res, $schema, $options) = @_;
|
||||
$print_cert_info->($schema, $res, $options);
|
||||
}, $PVE::RESTHandler::standard_output_options],
|
||||
delete => [ 'PVE::API2::Certificates', 'remove_custom_cert', ['restart'], { node => $nodename } ],
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
delete =>
|
||||
['PVE::API2::Certificates', 'remove_custom_cert', ['restart'], { node => $nodename }],
|
||||
},
|
||||
|
||||
task => {
|
||||
list => [ 'PVE::API2::Tasks', 'node_tasks', [], { node => $nodename }, sub {
|
||||
list => [
|
||||
'PVE::API2::Tasks',
|
||||
'node_tasks',
|
||||
[],
|
||||
{ node => $nodename },
|
||||
sub {
|
||||
my ($data, $schema, $options) = @_;
|
||||
foreach my $task (@$data) {
|
||||
if (!defined($task->{status})) {
|
||||
$task->{status} = 'UNKNOWN';
|
||||
# RUNNING is set by the API call and needs to be checked explicitly
|
||||
} elsif (PVE::Tools::upid_status_is_error($task->{status}) &&
|
||||
$task->{status} ne 'RUNNING')
|
||||
{
|
||||
} elsif (
|
||||
PVE::Tools::upid_status_is_error($task->{status})
|
||||
&& $task->{status} ne 'RUNNING'
|
||||
) {
|
||||
$task->{status} = 'ERROR';
|
||||
}
|
||||
}
|
||||
PVE::CLIFormatter::print_api_result($data, $schema, ['upid', 'type', 'id', 'user', 'starttime', 'endtime', 'status' ], $options);
|
||||
}, $PVE::RESTHandler::standard_output_options],
|
||||
status => [ 'PVE::API2::Tasks', 'read_task_status', [ 'upid' ], { node => $nodename }, sub {
|
||||
PVE::CLIFormatter::print_api_result(
|
||||
$data,
|
||||
$schema,
|
||||
['upid', 'type', 'id', 'user', 'starttime', 'endtime', 'status'],
|
||||
$options,
|
||||
);
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
status => [
|
||||
'PVE::API2::Tasks',
|
||||
'read_task_status',
|
||||
['upid'],
|
||||
{ node => $nodename },
|
||||
sub {
|
||||
my ($data, $schema, $options) = @_;
|
||||
PVE::CLIFormatter::print_api_result($data, $schema, undef, $options);
|
||||
}, $PVE::RESTHandler::standard_output_options],
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
# set limit to 1000000, so we see the whole log, not only the first 50 lines by default
|
||||
log => [ 'PVE::API2::Tasks', 'read_task_log', [ 'upid' ], { node => $nodename, limit => 1000000 }, sub {
|
||||
log => [
|
||||
'PVE::API2::Tasks',
|
||||
'read_task_log',
|
||||
['upid'],
|
||||
{ node => $nodename, limit => 1000000 },
|
||||
sub {
|
||||
my ($data, $resultprops) = @_;
|
||||
foreach my $line (@$data) {
|
||||
print $line->{t} . "\n";
|
||||
}
|
||||
}],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
acme => {
|
||||
account => {
|
||||
list => [ 'PVE::API2::ACMEAccount', 'account_index', [], {}, sub {
|
||||
list => [
|
||||
'PVE::API2::ACMEAccount',
|
||||
'account_index',
|
||||
[],
|
||||
{},
|
||||
sub {
|
||||
my ($res) = @_;
|
||||
for my $acc (@$res) {
|
||||
print "$acc->{name}\n";
|
||||
}
|
||||
}],
|
||||
register => [ __PACKAGE__, 'acme_register', ['name', 'contact'], {}, $upid_exit ],
|
||||
deactivate => [ 'PVE::API2::ACMEAccount', 'deactivate_account', ['name'], {}, $upid_exit ],
|
||||
info => [ 'PVE::API2::ACMEAccount', 'get_account', ['name'], {}, sub {
|
||||
},
|
||||
],
|
||||
register => [__PACKAGE__, 'acme_register', ['name', 'contact'], {}, $upid_exit],
|
||||
deactivate =>
|
||||
['PVE::API2::ACMEAccount', 'deactivate_account', ['name'], {}, $upid_exit],
|
||||
info => [
|
||||
'PVE::API2::ACMEAccount',
|
||||
'get_account',
|
||||
['name'],
|
||||
{},
|
||||
sub {
|
||||
my ($data, $schema, $options) = @_;
|
||||
PVE::CLIFormatter::print_api_result($data, $schema, undef, $options);
|
||||
}, $PVE::RESTHandler::standard_output_options],
|
||||
update => [ 'PVE::API2::ACMEAccount', 'update_account', ['name'], {}, $upid_exit ],
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
update => ['PVE::API2::ACMEAccount', 'update_account', ['name'], {}, $upid_exit],
|
||||
},
|
||||
cert => {
|
||||
order => [ 'PVE::API2::ACME', 'new_certificate', [], { node => $nodename }, $upid_exit ],
|
||||
renew => [ 'PVE::API2::ACME', 'renew_certificate', [], { node => $nodename }, $upid_exit ],
|
||||
revoke => [ 'PVE::API2::ACME', 'revoke_certificate', [], { node => $nodename }, $upid_exit ],
|
||||
order =>
|
||||
['PVE::API2::ACME', 'new_certificate', [], { node => $nodename }, $upid_exit],
|
||||
renew =>
|
||||
['PVE::API2::ACME', 'renew_certificate', [], { node => $nodename }, $upid_exit],
|
||||
revoke =>
|
||||
['PVE::API2::ACME', 'revoke_certificate', [], { node => $nodename }, $upid_exit],
|
||||
},
|
||||
plugin => {
|
||||
list => [ 'PVE::API2::ACMEPlugin', 'index', [], {}, sub {
|
||||
list => [
|
||||
'PVE::API2::ACMEPlugin',
|
||||
'index',
|
||||
[],
|
||||
{},
|
||||
sub {
|
||||
my ($data, $schema, $options) = @_;
|
||||
PVE::CLIFormatter::print_api_result($data, $schema, undef, $options);
|
||||
}, $PVE::RESTHandler::standard_output_options ],
|
||||
config => [ 'PVE::API2::ACMEPlugin', 'get_plugin_config', ['id'], {}, sub {
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
config => [
|
||||
'PVE::API2::ACMEPlugin',
|
||||
'get_plugin_config',
|
||||
['id'],
|
||||
{},
|
||||
sub {
|
||||
my ($data, $schema, $options) = @_;
|
||||
PVE::CLIFormatter::print_api_result($data, $schema, undef, $options);
|
||||
}, $PVE::RESTHandler::standard_output_options ],
|
||||
add => [ 'PVE::API2::ACMEPlugin', 'add_plugin', ['type', 'id'] ],
|
||||
set => [ 'PVE::API2::ACMEPlugin', 'update_plugin', ['id'] ],
|
||||
remove => [ 'PVE::API2::ACMEPlugin', 'delete_plugin', ['id'] ],
|
||||
},
|
||||
$PVE::RESTHandler::standard_output_options,
|
||||
],
|
||||
add => ['PVE::API2::ACMEPlugin', 'add_plugin', ['type', 'id']],
|
||||
set => ['PVE::API2::ACMEPlugin', 'update_plugin', ['id']],
|
||||
remove => ['PVE::API2::ACMEPlugin', 'delete_plugin', ['id']],
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
wakeonlan => [ 'PVE::API2::Nodes::Nodeinfo', 'wakeonlan', [ 'node' ], {}, sub {
|
||||
wakeonlan => [
|
||||
'PVE::API2::Nodes::Nodeinfo',
|
||||
'wakeonlan',
|
||||
['node'],
|
||||
{},
|
||||
sub {
|
||||
my ($mac_addr) = @_;
|
||||
|
||||
print "Wake on LAN packet send for '$mac_addr'\n";
|
||||
} ],
|
||||
},
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
105
PVE/CLI/pvesh.pm
105
PVE/CLI/pvesh.pm
|
|
@ -46,7 +46,7 @@ sub setup_environment {
|
|||
}
|
||||
|
||||
sub complete_api_path {
|
||||
my($text) = @_;
|
||||
my ($text) = @_;
|
||||
|
||||
my ($dir, undef, $rest) = $text =~ m|^(.*/)?(([^/]*))?$|;
|
||||
|
||||
|
|
@ -91,7 +91,11 @@ sub check_proxyto {
|
|||
|
||||
if ($info->{proxyto} || $info->{proxyto_callback}) {
|
||||
my $node = PVE::API2Tools::resolve_proxyto(
|
||||
$rpcenv, $info->{proxyto_callback}, $info->{proxyto}, $all_params);
|
||||
$rpcenv,
|
||||
$info->{proxyto_callback},
|
||||
$info->{proxyto},
|
||||
$all_params,
|
||||
);
|
||||
|
||||
if ($node ne 'localhost' && ($node ne PVE::INotify::nodename())) {
|
||||
die "proxy loop detected - aborting\n" if $disable_proxy;
|
||||
|
|
@ -120,13 +124,13 @@ sub proxy_handler {
|
|||
|
||||
my @pvesh_cmd = ('pvesh', '--noproxy', $cmd, $path, '--output-format', 'json');
|
||||
if (scalar(@$args)) {
|
||||
my $cmdargs = [ String::ShellQuote::shell_quote(@$args) ];
|
||||
my $cmdargs = [String::ShellQuote::shell_quote(@$args)];
|
||||
push @pvesh_cmd, @$cmdargs;
|
||||
}
|
||||
|
||||
my $res = '';
|
||||
PVE::Tools::run_command(
|
||||
[ $ssh_tunnel_cmd->@*, '--', @pvesh_cmd ],
|
||||
[$ssh_tunnel_cmd->@*, '--', @pvesh_cmd],
|
||||
errmsg => "proxy handler failed",
|
||||
outfunc => sub { $res .= shift },
|
||||
);
|
||||
|
|
@ -149,7 +153,7 @@ sub extract_children {
|
|||
if ($href =~ m/^\{(\S+)\}$/) {
|
||||
my $prop = $1;
|
||||
|
||||
foreach my $elem (sort {$a->{$prop} cmp $b->{$prop}} @$data) {
|
||||
foreach my $elem (sort { $a->{$prop} cmp $b->{$prop} } @$data) {
|
||||
next if !ref($elem);
|
||||
my $value = $elem->{$prop};
|
||||
push @$res, $value;
|
||||
|
|
@ -253,7 +257,6 @@ sub extract_path_info {
|
|||
return $info;
|
||||
}
|
||||
|
||||
|
||||
my $path_properties = {};
|
||||
|
||||
my $api_path_property = {
|
||||
|
|
@ -267,7 +270,7 @@ my $api_path_property = {
|
|||
|
||||
my $uri_param = {};
|
||||
if (my $info = extract_path_info($uri_param)) {
|
||||
foreach my $key (keys %{$info->{parameters}->{properties}}) {
|
||||
foreach my $key (keys %{ $info->{parameters}->{properties} }) {
|
||||
next if defined($uri_param->{$key});
|
||||
$path_properties->{$key} = $info->{parameters}->{properties}->{$key};
|
||||
}
|
||||
|
|
@ -285,7 +288,8 @@ my $extract_std_options = 1;
|
|||
my $cond_add_standard_output_properties = sub {
|
||||
my ($props) = @_;
|
||||
|
||||
my $keys = [ grep { !defined($props->{$_}) } keys %$PVE::RESTHandler::standard_output_options ];
|
||||
my $keys =
|
||||
[grep { !defined($props->{$_}) } keys %$PVE::RESTHandler::standard_output_options];
|
||||
|
||||
return PVE::RESTHandler::add_standard_output_properties($props, $keys);
|
||||
};
|
||||
|
|
@ -293,12 +297,14 @@ my $cond_add_standard_output_properties = sub {
|
|||
my $handle_streamed_response = sub {
|
||||
my ($download) = @_;
|
||||
my ($fh, $path, $encoding, $type) =
|
||||
$download->@{'fh', 'path', 'content-encoding', 'content-type'};
|
||||
$download->@{ 'fh', 'path', 'content-encoding', 'content-type' };
|
||||
|
||||
die "{download} returned but neither fh nor path given\n" if !defined($fh) && !defined($path);
|
||||
die "{download} returned but neither fh nor path given\n"
|
||||
if !defined($fh) && !defined($path);
|
||||
|
||||
die "unknown 'content-encoding' $encoding\n" if defined($encoding) && $encoding ne 'gzip';
|
||||
die "unknown 'content-type' $type\n" if defined($type) && $type !~ qw!^(?:text/plain|application/json)$!;
|
||||
die "unknown 'content-type' $type\n"
|
||||
if defined($type) && $type !~ qw!^(?:text/plain|application/json)$!;
|
||||
|
||||
if (defined($path)) {
|
||||
open($fh, '<', $path) or die "open stream path '$path' for reading failed - $!\n";
|
||||
|
|
@ -328,7 +334,8 @@ sub call_api_method {
|
|||
my $path = PVE::Tools::extract_param($param, 'api_path');
|
||||
die "missing API path\n" if !defined($path);
|
||||
|
||||
my $stdopts = $extract_std_options
|
||||
my $stdopts =
|
||||
$extract_std_options
|
||||
? PVE::RESTHandler::extract_standard_output_properties($param)
|
||||
: {};
|
||||
|
||||
|
|
@ -357,7 +364,7 @@ sub call_api_method {
|
|||
&& ref($data->{download}) eq 'HASH'
|
||||
&& ($info->{download_allowed} || $info->{download})
|
||||
) {
|
||||
$data = $handle_streamed_response->($data->{download})
|
||||
$data = $handle_streamed_response->($data->{download});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +373,7 @@ sub call_api_method {
|
|||
PVE::CLIFormatter::print_api_result($data, $info->{returns}, undef, $stdopts);
|
||||
}
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'ls',
|
||||
path => 'ls',
|
||||
method => 'GET',
|
||||
|
|
@ -408,21 +415,22 @@ __PACKAGE__->register_method ({
|
|||
|
||||
$res = [];
|
||||
foreach my $c (@$children) {
|
||||
my $item = { name => $c, capabilities => resource_cap("$path/$c")};
|
||||
my $item = { name => $c, capabilities => resource_cap("$path/$c") };
|
||||
push @$res, $item;
|
||||
}
|
||||
}
|
||||
|
||||
my $schema = { type => 'array', items => { type => 'object' }};
|
||||
my $schema = { type => 'array', items => { type => 'object' } };
|
||||
$stdopts->{sort_key} = 'name';
|
||||
$stdopts->{noborder} //= 1;
|
||||
$stdopts->{noheader} //= 1;
|
||||
PVE::CLIFormatter::print_api_result($res, $schema, ['capabilities', 'name'], $stdopts);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'get',
|
||||
path => 'get',
|
||||
method => 'GET',
|
||||
|
|
@ -438,9 +446,10 @@ __PACKAGE__->register_method ({
|
|||
call_api_method('get', $param);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'set',
|
||||
path => 'set',
|
||||
method => 'PUT',
|
||||
|
|
@ -456,9 +465,10 @@ __PACKAGE__->register_method ({
|
|||
call_api_method('set', $param);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'create',
|
||||
path => 'create',
|
||||
method => 'POST',
|
||||
|
|
@ -474,9 +484,10 @@ __PACKAGE__->register_method ({
|
|||
call_api_method('create', $param);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'delete',
|
||||
path => 'delete',
|
||||
method => 'DELETE',
|
||||
|
|
@ -492,9 +503,10 @@ __PACKAGE__->register_method ({
|
|||
call_api_method('delete', $param);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'usage',
|
||||
path => 'usage',
|
||||
method => 'GET',
|
||||
|
|
@ -516,7 +528,7 @@ __PACKAGE__->register_method ({
|
|||
command => {
|
||||
description => "API command.",
|
||||
type => 'string',
|
||||
enum => [ keys %$method_map ],
|
||||
enum => [keys %$method_map],
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
|
|
@ -538,14 +550,26 @@ __PACKAGE__->register_method ({
|
|||
|
||||
if ($param->{verbose}) {
|
||||
print $handler->usage_str(
|
||||
$info->{name}, "pvesh $cmd $path", undef, $uri_param, 'full');
|
||||
$info->{name},
|
||||
"pvesh $cmd $path",
|
||||
undef,
|
||||
$uri_param,
|
||||
'full',
|
||||
);
|
||||
|
||||
} else {
|
||||
print "USAGE: " . $handler->usage_str(
|
||||
$info->{name}, "pvesh $cmd $path", undef, $uri_param, 'short');
|
||||
print "USAGE: "
|
||||
. $handler->usage_str(
|
||||
$info->{name},
|
||||
"pvesh $cmd $path",
|
||||
undef,
|
||||
$uri_param,
|
||||
'short',
|
||||
);
|
||||
}
|
||||
if ($param-> {returns}) {
|
||||
my $schema = to_json($info->{returns}, {utf8 => 1, canonical => 1, pretty => 1 });
|
||||
if ($param->{returns}) {
|
||||
my $schema =
|
||||
to_json($info->{returns}, { utf8 => 1, canonical => 1, pretty => 1 });
|
||||
print "RETURNS: $schema\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -554,20 +578,21 @@ __PACKAGE__->register_method ({
|
|||
if ($param->{command}) {
|
||||
die "no '$param->{command}' handler for '$path'\n";
|
||||
} else {
|
||||
die "no such resource '$path'\n"
|
||||
die "no such resource '$path'\n";
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
our $cmddef = {
|
||||
usage => [ __PACKAGE__, 'usage', ['api_path']],
|
||||
get => [ __PACKAGE__, 'get', ['api_path']],
|
||||
ls => [ __PACKAGE__, 'ls', ['api_path']],
|
||||
set => [ __PACKAGE__, 'set', ['api_path']],
|
||||
create => [ __PACKAGE__, 'create', ['api_path']],
|
||||
delete => [ __PACKAGE__, 'delete', ['api_path']],
|
||||
usage => [__PACKAGE__, 'usage', ['api_path']],
|
||||
get => [__PACKAGE__, 'get', ['api_path']],
|
||||
ls => [__PACKAGE__, 'ls', ['api_path']],
|
||||
set => [__PACKAGE__, 'set', ['api_path']],
|
||||
create => [__PACKAGE__, 'create', ['api_path']],
|
||||
delete => [__PACKAGE__, 'delete', ['api_path']],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
156
PVE/CLI/pvesr.pm
156
PVE/CLI/pvesr.pm
|
|
@ -36,7 +36,7 @@ my $check_wanted_volid = sub {
|
|||
my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
|
||||
my $scfg = PVE::Storage::storage_check_enabled($storecfg, $storeid, $local_node);
|
||||
die "storage '$storeid' is not replicatable\n"
|
||||
if !$replicatable_storage_types->{$scfg->{type}};
|
||||
if !$replicatable_storage_types->{ $scfg->{type} };
|
||||
|
||||
my ($vtype, undef, $ownervm) = PVE::Storage::parse_volname($storecfg, $volid);
|
||||
die "volume '$volid' has wrong vtype ($vtype != 'images')\n"
|
||||
|
|
@ -47,20 +47,24 @@ my $check_wanted_volid = sub {
|
|||
return $storeid;
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'prepare_local_job',
|
||||
path => 'prepare_local_job',
|
||||
method => 'POST',
|
||||
description => "Prepare for starting a replication job. This is called on the target node before replication starts. This call is for internal use, and return a JSON object on stdout. The method first test if VM <vmid> reside on the local node. If so, stop immediately. After that the method scans all volume IDs for snapshots, and removes all replications snapshots with timestamps different than <last_sync>. It also removes any unused volumes. Returns a hash with boolean markers for all volumes with existing replication snapshots.",
|
||||
description =>
|
||||
"Prepare for starting a replication job. This is called on the target node before replication starts. This call is for internal use, and return a JSON object on stdout. The method first test if VM <vmid> reside on the local node. If so, stop immediately. After that the method scans all volume IDs for snapshots, and removes all replications snapshots with timestamps different than <last_sync>. It also removes any unused volumes. Returns a hash with boolean markers for all volumes with existing replication snapshots.",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
id => get_standard_option('pve-replication-id'),
|
||||
'extra-args' => get_standard_option('extra-args', {
|
||||
description => "The list of volume IDs to consider." }),
|
||||
'extra-args' => get_standard_option(
|
||||
'extra-args',
|
||||
{ description => "The list of volume IDs to consider." },
|
||||
),
|
||||
scan => {
|
||||
description => "List of storage IDs to scan for stale volumes.",
|
||||
type => 'string', format => 'pve-storage-id-list',
|
||||
type => 'string',
|
||||
format => 'pve-storage-id-list',
|
||||
optional => 1,
|
||||
},
|
||||
force => {
|
||||
|
|
@ -70,7 +74,8 @@ __PACKAGE__->register_method ({
|
|||
default => 0,
|
||||
},
|
||||
last_sync => {
|
||||
description => "Time (UNIX epoch) of last successful sync. If not specified, all replication snapshots get removed.",
|
||||
description =>
|
||||
"Time (UNIX epoch) of last successful sync. If not specified, all replication snapshots get removed.",
|
||||
type => 'integer',
|
||||
minimum => 0,
|
||||
optional => 1,
|
||||
|
|
@ -92,9 +97,10 @@ __PACKAGE__->register_method ({
|
|||
my $local_node = PVE::INotify::nodename();
|
||||
|
||||
die "no volumes specified\n"
|
||||
if !$param->{force} && !scalar(@{$param->{'extra-args'}});
|
||||
if !$param->{force} && !scalar(@{ $param->{'extra-args'} });
|
||||
|
||||
my ($vmid, undef, $jobid) = PVE::ReplicationConfig::parse_replication_job_id($param->{id});
|
||||
my ($vmid, undef, $jobid) =
|
||||
PVE::ReplicationConfig::parse_replication_job_id($param->{id});
|
||||
|
||||
my $vms = PVE::Cluster::get_vmlist();
|
||||
die "guest '$vmid' is on local node\n"
|
||||
|
|
@ -110,17 +116,18 @@ __PACKAGE__->register_method ({
|
|||
foreach my $storeid (PVE::Tools::split_list($param->{scan})) {
|
||||
my $scfg = PVE::Storage::storage_check_enabled($storecfg, $storeid, $local_node, 1);
|
||||
next if !$scfg; # simply ignore unavailable storages here
|
||||
die "storage '$storeid' is not replicatable\n" if !$replicatable_storage_types->{$scfg->{type}};
|
||||
die "storage '$storeid' is not replicatable\n"
|
||||
if !$replicatable_storage_types->{ $scfg->{type} };
|
||||
$storage_hash->{$storeid} = 1;
|
||||
}
|
||||
|
||||
my $wanted_volids = {};
|
||||
foreach my $volid (@{$param->{'extra-args'}}) {
|
||||
foreach my $volid (@{ $param->{'extra-args'} }) {
|
||||
my $storeid = $check_wanted_volid->($storecfg, $vmid, $volid, $local_node);
|
||||
$wanted_volids->{$volid} = 1;
|
||||
$storage_hash->{$storeid} = 1;
|
||||
}
|
||||
my $storage_list = [ sort keys %$storage_hash ];
|
||||
my $storage_list = [sort keys %$storage_hash];
|
||||
|
||||
# activate all used storage
|
||||
my $cache = {};
|
||||
|
|
@ -136,7 +143,9 @@ __PACKAGE__->register_method ({
|
|||
my $images = $plugin->list_images($storeid, $scfg, $vmid, undef, $cache);
|
||||
push @$volids, map { $_->{volid} } @$images;
|
||||
}
|
||||
my ($local_snapshots, $cleaned_replicated_volumes) = PVE::Replication::prepare($storecfg, $volids, $jobid, $last_sync, $parent_snapname, $logfunc);
|
||||
my ($local_snapshots, $cleaned_replicated_volumes) = PVE::Replication::prepare(
|
||||
$storecfg, $volids, $jobid, $last_sync, $parent_snapname, $logfunc,
|
||||
);
|
||||
for my $volid ($volids->@*) {
|
||||
next if $wanted_volids->{$volid};
|
||||
|
||||
|
|
@ -146,7 +155,7 @@ __PACKAGE__->register_method ({
|
|||
# it to be missed later, because the relevant storage might not get scanned anymore.
|
||||
$stale ||= grep {
|
||||
PVE::Replication::is_replication_snapshot($_, $jobid)
|
||||
} keys %{$local_snapshots->{$volid} // {}};
|
||||
} keys %{ $local_snapshots->{$volid} // {} };
|
||||
|
||||
if ($stale) {
|
||||
$logfunc->("$jobid: delete stale volume '$volid'");
|
||||
|
|
@ -158,21 +167,26 @@ __PACKAGE__->register_method ({
|
|||
print to_json($local_snapshots) . "\n";
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'finalize_local_job',
|
||||
path => 'finalize_local_job',
|
||||
method => 'POST',
|
||||
description => "Finalize a replication job. This removes all replications snapshots with timestamps different than <last_sync>.",
|
||||
description =>
|
||||
"Finalize a replication job. This removes all replications snapshots with timestamps different than <last_sync>.",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
id => get_standard_option('pve-replication-id'),
|
||||
'extra-args' => get_standard_option('extra-args', {
|
||||
description => "The list of volume IDs to consider." }),
|
||||
'extra-args' => get_standard_option(
|
||||
'extra-args',
|
||||
{ description => "The list of volume IDs to consider." },
|
||||
),
|
||||
last_sync => {
|
||||
description => "Time (UNIX epoch) of last successful sync. If not specified, all replication snapshots gets removed.",
|
||||
description =>
|
||||
"Time (UNIX epoch) of last successful sync. If not specified, all replication snapshots gets removed.",
|
||||
type => 'integer',
|
||||
minimum => 0,
|
||||
optional => 1,
|
||||
|
|
@ -183,7 +197,8 @@ __PACKAGE__->register_method ({
|
|||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my ($vmid, undef, $jobid) = PVE::ReplicationConfig::parse_replication_job_id($param->{id});
|
||||
my ($vmid, undef, $jobid) =
|
||||
PVE::ReplicationConfig::parse_replication_job_id($param->{id});
|
||||
my $last_sync = $param->{last_sync} // 0;
|
||||
|
||||
my $local_node = PVE::INotify::nodename();
|
||||
|
|
@ -196,14 +211,14 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $volids = [];
|
||||
|
||||
die "no volumes specified\n" if !scalar(@{$param->{'extra-args'}});
|
||||
die "no volumes specified\n" if !scalar(@{ $param->{'extra-args'} });
|
||||
|
||||
foreach my $volid (@{$param->{'extra-args'}}) {
|
||||
foreach my $volid (@{ $param->{'extra-args'} }) {
|
||||
$check_wanted_volid->($storecfg, $vmid, $volid, $local_node);
|
||||
push @$volids, $volid;
|
||||
}
|
||||
|
||||
$volids = [ sort @$volids ];
|
||||
$volids = [sort @$volids];
|
||||
|
||||
my $logfunc = sub {
|
||||
my ($msg) = @_;
|
||||
|
|
@ -213,13 +228,15 @@ __PACKAGE__->register_method ({
|
|||
PVE::Replication::prepare($storecfg, $volids, $jobid, $last_sync, undef, $logfunc);
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'run',
|
||||
path => 'run',
|
||||
method => 'POST',
|
||||
description => "This method is called by the systemd-timer and executes all (or a specific) sync jobs.",
|
||||
description =>
|
||||
"This method is called by the systemd-timer and executes all (or a specific) sync jobs.",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
|
|
@ -264,9 +281,10 @@ __PACKAGE__->register_method ({
|
|||
}
|
||||
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'enable',
|
||||
path => 'enable',
|
||||
method => 'POST',
|
||||
|
|
@ -284,9 +302,10 @@ __PACKAGE__->register_method ({
|
|||
$param->{disable} = 0;
|
||||
|
||||
return PVE::API2::ReplicationConfig->update($param);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'disable',
|
||||
path => 'disable',
|
||||
method => 'POST',
|
||||
|
|
@ -304,21 +323,24 @@ __PACKAGE__->register_method ({
|
|||
$param->{disable} = 1;
|
||||
|
||||
return PVE::API2::ReplicationConfig->update($param);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
__PACKAGE__->register_method({
|
||||
name => 'set_state',
|
||||
path => '',
|
||||
protected => 1,
|
||||
method => 'POST',
|
||||
description => "Set the job replication state on migration. This call is for internal use. It will accept the job state as ja JSON obj.",
|
||||
description =>
|
||||
"Set the job replication state on migration. This call is for internal use. It will accept the job state as ja JSON obj.",
|
||||
permissions => {
|
||||
check => ['perm', '/storage', ['Datastore.Allocate']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
vmid => get_standard_option('pve-vmid', { completion => \&PVE::Cluster::complete_vmid }),
|
||||
vmid =>
|
||||
get_standard_option('pve-vmid', { completion => \&PVE::Cluster::complete_vmid }),
|
||||
state => {
|
||||
description => "Job state as JSON decoded string.",
|
||||
type => 'string',
|
||||
|
|
@ -331,11 +353,12 @@ __PACKAGE__->register_method ({
|
|||
|
||||
my $vmid = extract_param($param, 'vmid');
|
||||
my $json_string = extract_param($param, 'state');
|
||||
my $remote_job_state= decode_json $json_string;
|
||||
my $remote_job_state = decode_json $json_string;
|
||||
|
||||
PVE::ReplicationState::write_vmid_job_states($remote_job_state, $vmid);
|
||||
return undef;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
my $print_job_list = sub {
|
||||
my ($list) = @_;
|
||||
|
|
@ -348,10 +371,12 @@ my $print_job_list = sub {
|
|||
my $plugin = PVE::ReplicationConfig->lookup($job->{type});
|
||||
my $tid = $plugin->get_unique_target_id($job);
|
||||
|
||||
printf($format, $job->{id}, $tid,
|
||||
printf(
|
||||
$format,
|
||||
$job->{id}, $tid,
|
||||
defined($job->{schedule}) ? $job->{schedule} : '*/15',
|
||||
defined($job->{rate}) ? $job->{rate} : '-',
|
||||
$job->{disable} ? 'no' : 'yes'
|
||||
$job->{disable} ? 'no' : 'yes',
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
@ -361,7 +386,15 @@ my $print_job_status = sub {
|
|||
|
||||
my $format = "%-10s %-10s %-20s %20s %20s %10s %10s %s\n";
|
||||
|
||||
printf($format, "JobID", "Enabled", "Target", "LastSync", "NextSync", "Duration", "FailCount", "State");
|
||||
printf($format,
|
||||
"JobID",
|
||||
"Enabled",
|
||||
"Target",
|
||||
"LastSync",
|
||||
"NextSync",
|
||||
"Duration",
|
||||
"FailCount",
|
||||
"State");
|
||||
|
||||
foreach my $job (sort { $a->{guest} <=> $b->{guest} } @$list) {
|
||||
my $plugin = PVE::ReplicationConfig->lookup($job->{type});
|
||||
|
|
@ -385,32 +418,41 @@ my $print_job_status = sub {
|
|||
my $state = $job->{pid} ? "SYNCING" : $job->{error} // 'OK';
|
||||
my $enabled = $job->{disable} ? 'No' : 'Yes';
|
||||
|
||||
printf($format, $job->{id}, $enabled, $tid,
|
||||
$timestr, $nextstr, $job->{duration} // '-',
|
||||
printf($format,
|
||||
$job->{id}, $enabled, $tid, $timestr, $nextstr, $job->{duration} // '-',
|
||||
$job->{fail_count}, $state);
|
||||
}
|
||||
};
|
||||
|
||||
our $cmddef = {
|
||||
status => [ 'PVE::API2::Replication', 'status', [], { node => $nodename }, $print_job_status ],
|
||||
'schedule-now' => [ 'PVE::API2::Replication', 'schedule_now', ['id'], { node => $nodename }],
|
||||
status =>
|
||||
['PVE::API2::Replication', 'status', [], { node => $nodename }, $print_job_status],
|
||||
'schedule-now' => ['PVE::API2::Replication', 'schedule_now', ['id'], { node => $nodename }],
|
||||
|
||||
list => [ 'PVE::API2::ReplicationConfig', 'index' , [], {}, $print_job_list ],
|
||||
read => [ 'PVE::API2::ReplicationConfig', 'read' , ['id'], {},
|
||||
sub { my $res = shift; print to_json($res, { utf8 => 1, pretty => 1, canonical => 1}); }],
|
||||
update => [ 'PVE::API2::ReplicationConfig', 'update' , ['id'], {} ],
|
||||
delete => [ 'PVE::API2::ReplicationConfig', 'delete' , ['id'], {} ],
|
||||
'create-local-job' => [ 'PVE::API2::ReplicationConfig', 'create' , ['id', 'target'],
|
||||
{ type => 'local' } ],
|
||||
list => ['PVE::API2::ReplicationConfig', 'index', [], {}, $print_job_list],
|
||||
read => [
|
||||
'PVE::API2::ReplicationConfig',
|
||||
'read',
|
||||
['id'],
|
||||
{},
|
||||
sub {
|
||||
my $res = shift;
|
||||
print to_json($res, { utf8 => 1, pretty => 1, canonical => 1 });
|
||||
},
|
||||
],
|
||||
update => ['PVE::API2::ReplicationConfig', 'update', ['id'], {}],
|
||||
delete => ['PVE::API2::ReplicationConfig', 'delete', ['id'], {}],
|
||||
'create-local-job' =>
|
||||
['PVE::API2::ReplicationConfig', 'create', ['id', 'target'], { type => 'local' }],
|
||||
|
||||
enable => [ __PACKAGE__, 'enable', ['id'], {}],
|
||||
disable => [ __PACKAGE__, 'disable', ['id'], {}],
|
||||
enable => [__PACKAGE__, 'enable', ['id'], {}],
|
||||
disable => [__PACKAGE__, 'disable', ['id'], {}],
|
||||
|
||||
'prepare-local-job' => [ __PACKAGE__, 'prepare_local_job', ['id', 'extra-args'], {} ],
|
||||
'finalize-local-job' => [ __PACKAGE__, 'finalize_local_job', ['id', 'extra-args'], {} ],
|
||||
'prepare-local-job' => [__PACKAGE__, 'prepare_local_job', ['id', 'extra-args'], {}],
|
||||
'finalize-local-job' => [__PACKAGE__, 'finalize_local_job', ['id', 'extra-args'], {}],
|
||||
|
||||
run => [ __PACKAGE__ , 'run'],
|
||||
'set-state' => [ __PACKAGE__ , 'set_state', ['vmid', 'state']],
|
||||
run => [__PACKAGE__, 'run'],
|
||||
'set-state' => [__PACKAGE__, 'set_state', ['vmid', 'state']],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ __PACKAGE__->register_method({
|
|||
name => 'set_offline_key',
|
||||
path => 'set_offline_key',
|
||||
method => 'POST',
|
||||
description => "Internal use only! To set an offline key, use the package proxmox-offline-mirror-helper instead.",
|
||||
description =>
|
||||
"Internal use only! To set an offline key, use the package proxmox-offline-mirror-helper instead.",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
|
|
@ -45,22 +46,30 @@ __PACKAGE__->register_method({
|
|||
$info = Proxmox::RS::Subscription::check_server_id($info);
|
||||
$info = Proxmox::RS::Subscription::check_age($info, 0);
|
||||
|
||||
PVE::API2::Subscription::check_key($info->{key}, PVE::API2::Subscription::get_sockets());
|
||||
PVE::API2::Subscription::check_key($info->{key},
|
||||
PVE::API2::Subscription::get_sockets());
|
||||
|
||||
PVE::API2::Subscription::write_etc_subscription($info);
|
||||
|
||||
return;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
||||
our $cmddef = {
|
||||
update => [ 'PVE::API2::Subscription', 'update', undef, { node => $nodename } ],
|
||||
get => [ 'PVE::API2::Subscription', 'get', undef, { node => $nodename }, sub {
|
||||
update => ['PVE::API2::Subscription', 'update', undef, { node => $nodename }],
|
||||
get => [
|
||||
'PVE::API2::Subscription',
|
||||
'get',
|
||||
undef,
|
||||
{ node => $nodename },
|
||||
sub {
|
||||
my $info = shift;
|
||||
print "$_: $info->{$_}\n" for sort keys %$info;
|
||||
}],
|
||||
set => [ 'PVE::API2::Subscription', 'set', ['key'], { node => $nodename } ],
|
||||
"set-offline-key" => [ __PACKAGE__, 'set_offline_key', ['data'] ],
|
||||
delete => [ 'PVE::API2::Subscription', 'delete', undef, { node => $nodename } ],
|
||||
},
|
||||
],
|
||||
set => ['PVE::API2::Subscription', 'set', ['key'], { node => $nodename }],
|
||||
"set-offline-key" => [__PACKAGE__, 'set_offline_key', ['data']],
|
||||
delete => ['PVE::API2::Subscription', 'delete', undef, { node => $nodename }],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -14,12 +14,17 @@ sub setup_environment {
|
|||
}
|
||||
|
||||
# Note: use string 'vmid' as $arg_param option, to allow vmid lists
|
||||
our $cmddef = [ 'PVE::API2::VZDump', 'vzdump', 'vmid', undef,
|
||||
our $cmddef = [
|
||||
'PVE::API2::VZDump',
|
||||
'vzdump',
|
||||
'vmid',
|
||||
undef,
|
||||
sub {
|
||||
my $upid = shift;
|
||||
exit(0) if $upid eq 'OK';
|
||||
my $status = PVE::Tools::upid_read_status($upid);
|
||||
exit(PVE::Tools::upid_status_is_error($status) ? -1 : 0);
|
||||
}];
|
||||
},
|
||||
];
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use v5.36;
|
|||
use PVE::pvecfg;
|
||||
|
||||
my sub get_current_pve_major_release {
|
||||
my $release_tuples = [ split(/\./, PVE::pvecfg::release()) ];
|
||||
my $release_tuples = [split(/\./, PVE::pvecfg::release())];
|
||||
return $release_tuples->[0];
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +83,8 @@ my sub get_ceph_release_def {
|
|||
for my $codename (sort keys $ceph_release_info->%*) {
|
||||
my $ceph_release = $ceph_release_info->{$codename};
|
||||
$ceph_release->{codename} = $codename;
|
||||
$ceph_release->{'available-for-current-pve-release'} = $ceph_release->{'available-for-pve-release'}->{$current_pve_major_release};
|
||||
$ceph_release->{'available-for-current-pve-release'} =
|
||||
$ceph_release->{'available-for-pve-release'}->{$current_pve_major_release};
|
||||
}
|
||||
|
||||
$_ceph_release_info = $ceph_release_info;
|
||||
|
|
@ -95,6 +96,7 @@ sub get_ceph_release_info($codename) {
|
|||
}
|
||||
|
||||
my $_available_ceph_releases;
|
||||
|
||||
sub get_all_available_ceph_releases {
|
||||
if (!defined($_available_ceph_releases)) {
|
||||
my $ceph_releases = get_ceph_release_def();
|
||||
|
|
@ -112,16 +114,17 @@ sub get_available_ceph_release_codenames($include_unstable_releases = 0) {
|
|||
my $available_releases = get_all_available_ceph_releases();
|
||||
|
||||
return $include_unstable_releases
|
||||
? [ sort keys $available_releases->%* ]
|
||||
: [ grep { !$available_releases->{$_}->{unsupported} } sort keys $available_releases->%* ]
|
||||
;
|
||||
? [sort keys $available_releases->%*]
|
||||
: [grep { !$available_releases->{$_}->{unsupported} } sort keys $available_releases->%*];
|
||||
}
|
||||
|
||||
my $_default_ceph_release_codename;
|
||||
|
||||
sub get_default_ceph_release_codename {
|
||||
if (!defined($_default_ceph_release_codename)) {
|
||||
my $ceph_releases = get_all_available_ceph_releases();
|
||||
my @default_release = grep { $ceph_releases->{$_}->{'current-backend-default'} } keys $ceph_releases->%*;
|
||||
my @default_release =
|
||||
grep { $ceph_releases->{$_}->{'current-backend-default'} } keys $ceph_releases->%*;
|
||||
die "internal error: got multiple ceph releases with 'current-backend-default' set\n"
|
||||
if scalar(@default_release) > 1;
|
||||
die "internal error: got no ceph releases with 'current-backend-default' set\n"
|
||||
|
|
|
|||
|
|
@ -23,17 +23,25 @@ sub get_local_services {
|
|||
|
||||
my $path = "/etc/systemd/system/ceph-$type.target.wants";
|
||||
my $regex = "ceph-$type\@(.*)\.service";
|
||||
PVE::Tools::dir_glob_foreach($path, $regex, sub {
|
||||
PVE::Tools::dir_glob_foreach(
|
||||
$path,
|
||||
$regex,
|
||||
sub {
|
||||
my (undef, $id) = @_;
|
||||
$res->{$type}->{$id}->{service} = 1;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
$path = "/var/lib/ceph/$type";
|
||||
$regex = "([^-]+)-(.*)";
|
||||
PVE::Tools::dir_glob_foreach($path, $regex, sub {
|
||||
PVE::Tools::dir_glob_foreach(
|
||||
$path,
|
||||
$regex,
|
||||
sub {
|
||||
my (undef, $clustername, $id) = @_;
|
||||
$res->{$type}->{$id}->{direxists} = 1;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
|
@ -57,7 +65,11 @@ sub broadcast_ceph_versions {
|
|||
if (length(my $old_version_raw = $old_versions->{$nodename})) {
|
||||
my $old = eval { decode_json($old_version_raw) };
|
||||
warn "failed to parse ceph-versions '$old_version_raw' as JSON - $@" if $@; # should not happen
|
||||
if (defined($old) && $old->{buildcommit} eq $buildcommit && $old->{version}->{str} eq $version) {
|
||||
if (
|
||||
defined($old)
|
||||
&& $old->{buildcommit} eq $buildcommit
|
||||
&& $old->{version}->{str} eq $version
|
||||
) {
|
||||
return; # up to date, nothing to do so avoid (not exactly cheap) broadcast
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +89,9 @@ sub get_ceph_versions {
|
|||
|
||||
if (defined(my $versions = PVE::Cluster::get_node_kv("ceph-versions"))) {
|
||||
$res = {
|
||||
map { eval { $_ => decode_json($versions->{$_}) } } keys %$versions
|
||||
map {
|
||||
eval { $_ => decode_json($versions->{$_}) }
|
||||
} keys %$versions
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +103,9 @@ sub get_cluster_service {
|
|||
|
||||
my $raw = PVE::Cluster::get_node_kv("ceph-$type");
|
||||
my $res = {
|
||||
map { $_ => eval { decode_json($raw->{$_}) } } keys $raw->%*
|
||||
map {
|
||||
$_ => eval { decode_json($raw->{$_}) }
|
||||
} keys $raw->%*
|
||||
};
|
||||
|
||||
return $res;
|
||||
|
|
@ -114,7 +130,7 @@ sub get_services_info {
|
|||
my $services = get_cluster_service($type);
|
||||
|
||||
foreach my $host (sort keys %$services) {
|
||||
foreach my $id (sort keys %{$services->{$host}}) {
|
||||
foreach my $id (sort keys %{ $services->{$host} }) {
|
||||
my $service = $result->{$id} = $services->{$host}->{$id};
|
||||
$service->{host} = $host;
|
||||
$service->{name} = $id;
|
||||
|
|
@ -166,10 +182,14 @@ sub list_local_mds_ids {
|
|||
my $ceph_mds_data_dir = PVE::Ceph::Tools::get_config('ceph_mds_data_dir');
|
||||
my $ccname = PVE::Ceph::Tools::get_config('ccname');
|
||||
|
||||
PVE::Tools::dir_glob_foreach($ceph_mds_data_dir, qr/$ccname-(\S+)/, sub {
|
||||
PVE::Tools::dir_glob_foreach(
|
||||
$ceph_mds_data_dir,
|
||||
qr/$ccname-(\S+)/,
|
||||
sub {
|
||||
my (undef, $mds_id) = @_;
|
||||
push @$mds_list, $mds_id;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return $mds_list;
|
||||
}
|
||||
|
|
@ -193,18 +213,17 @@ sub get_cluster_mds_state {
|
|||
$state->{state} = $mds->{state};
|
||||
$state->{fs_name} = $fsname;
|
||||
|
||||
$mds_state->{$mds->{name}} = $state;
|
||||
$mds_state->{ $mds->{name} } = $state;
|
||||
};
|
||||
|
||||
my $mds_dump = $rados->mon_command({ prefix => 'mds stat' });
|
||||
my $fsmap = $mds_dump->{fsmap};
|
||||
|
||||
|
||||
foreach my $mds (@{$fsmap->{standbys}}) {
|
||||
foreach my $mds (@{ $fsmap->{standbys} }) {
|
||||
$add_state->($mds);
|
||||
}
|
||||
|
||||
for my $fs_info (@{$fsmap->{filesystems}}) {
|
||||
for my $fs_info (@{ $fsmap->{filesystems} }) {
|
||||
my $active_mds = $fs_info->{mdsmap}->{info};
|
||||
|
||||
# normally there's only one active MDS, but we can have multiple active for
|
||||
|
|
@ -294,7 +313,7 @@ sub create_mds {
|
|||
broadcast_ceph_services();
|
||||
|
||||
return undef;
|
||||
};
|
||||
}
|
||||
|
||||
sub destroy_mds {
|
||||
my ($id, $rados) = @_;
|
||||
|
|
@ -317,20 +336,20 @@ sub destroy_mds {
|
|||
print "removing ceph-mds directory '$service_dir'\n";
|
||||
File::Path::remove_tree($service_dir);
|
||||
} else {
|
||||
warn "cannot cleanup MDS $id directory, '$service_dir' not found\n"
|
||||
warn "cannot cleanup MDS $id directory, '$service_dir' not found\n";
|
||||
}
|
||||
|
||||
print "removing ceph auth for '$service_name'\n";
|
||||
$rados->mon_command({
|
||||
prefix => 'auth del',
|
||||
entity => $service_name,
|
||||
format => 'plain'
|
||||
format => 'plain',
|
||||
});
|
||||
|
||||
broadcast_ceph_services();
|
||||
|
||||
return undef;
|
||||
};
|
||||
}
|
||||
|
||||
# MGR
|
||||
|
||||
|
|
@ -355,7 +374,7 @@ sub create_mgr {
|
|||
osd => 'allow *',
|
||||
mds => 'allow *',
|
||||
],
|
||||
format => 'plain'
|
||||
format => 'plain',
|
||||
});
|
||||
PVE::Tools::file_set_contents($mgrkeyring, $output);
|
||||
|
||||
|
|
@ -380,7 +399,7 @@ sub destroy_mgr {
|
|||
my $mgrdir = "/var/lib/ceph/mgr/$clustername-$mgrid";
|
||||
|
||||
die "ceph manager directory '$mgrdir' not found\n"
|
||||
if ! -d $mgrdir;
|
||||
if !-d $mgrdir;
|
||||
|
||||
print "disabling service 'ceph-mgr\@$mgrid.service'\n";
|
||||
ceph_service_cmd('disable', $mgrname);
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ sub get_local_version {
|
|||
|
||||
my $ceph_version;
|
||||
run_command(
|
||||
[ $ceph_service->{ceph_bin}, '--version' ],
|
||||
[$ceph_service->{ceph_bin}, '--version'],
|
||||
noerr => $noerr,
|
||||
outfunc => sub { $ceph_version = shift if !defined $ceph_version },
|
||||
);
|
||||
|
|
@ -92,7 +92,7 @@ sub parse_ceph_version : prototype($) {
|
|||
|
||||
if ($ceph_version =~ /$re_ceph_version/) {
|
||||
my ($version, $buildcommit) = ($1, $2);
|
||||
my $subversions = [ split(/\.|-/, $version) ];
|
||||
my $subversions = [split(/\.|-/, $version)];
|
||||
|
||||
# return (version, buildid, [major, minor, ...]) : major;
|
||||
return wantarray
|
||||
|
|
@ -116,7 +116,7 @@ sub get_config {
|
|||
|
||||
my $value = $config_values->{$key} // $config_files->{$key};
|
||||
|
||||
die "no such ceph config '$key'" if ! defined($value);
|
||||
die "no such ceph config '$key'" if !defined($value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
|
@ -124,7 +124,7 @@ sub get_config {
|
|||
sub purge_all_ceph_files {
|
||||
my ($services) = @_;
|
||||
my $is_local_mon;
|
||||
my $monlist = [ split(',', PVE::CephConfig::get_monaddr_list($pve_ceph_cfgpath)) ];
|
||||
my $monlist = [split(',', PVE::CephConfig::get_monaddr_list($pve_ceph_cfgpath))];
|
||||
|
||||
foreach my $service (keys %$services) {
|
||||
my $type = $services->{$service};
|
||||
|
|
@ -140,17 +140,20 @@ sub purge_all_ceph_files {
|
|||
$path = '/var/log/ceph' if $service eq 'logs';
|
||||
if ($dir_exists) {
|
||||
my $err;
|
||||
File::Path::remove_tree($path, {
|
||||
File::Path::remove_tree(
|
||||
$path,
|
||||
{
|
||||
keep_root => 1,
|
||||
error => \$err,
|
||||
});
|
||||
},
|
||||
);
|
||||
warn "Error removing path, '$path'\n" if @$err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar @$monlist > 0 && !$is_local_mon) {
|
||||
warn "Foreign MON address in ceph.conf. Keeping config & keyrings\n"
|
||||
warn "Foreign MON address in ceph.conf. Keeping config & keyrings\n";
|
||||
} else {
|
||||
print "Removing config & keyring files\n";
|
||||
for my $file (%$config_files) {
|
||||
|
|
@ -180,7 +183,7 @@ sub purge_all_ceph_services {
|
|||
}
|
||||
}
|
||||
|
||||
sub ceph_install_flag_file { return '/run/pve-ceph-install-flag' };
|
||||
sub ceph_install_flag_file { return '/run/pve-ceph-install-flag' }
|
||||
|
||||
sub check_ceph_installed {
|
||||
my ($service, $noerr) = @_;
|
||||
|
|
@ -189,7 +192,7 @@ sub check_ceph_installed {
|
|||
|
||||
# NOTE: the flag file is checked as on a new installation, the binary gets
|
||||
# extracted by dpkg before the installation is finished
|
||||
if (! -x $ceph_service->{$service} || -f ceph_install_flag_file()) {
|
||||
if (!-x $ceph_service->{$service} || -f ceph_install_flag_file()) {
|
||||
die "binary not installed: $ceph_service->{$service}\n" if !$noerr;
|
||||
return undef;
|
||||
}
|
||||
|
|
@ -197,13 +200,12 @@ sub check_ceph_installed {
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
sub check_ceph_configured {
|
||||
|
||||
check_ceph_inited();
|
||||
|
||||
die "ceph not fully configured - missing '$pve_ckeyring_path'\n"
|
||||
if ! -f $pve_ckeyring_path;
|
||||
if !-f $pve_ckeyring_path;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -215,8 +217,8 @@ sub check_ceph_inited {
|
|||
|
||||
my @errors;
|
||||
|
||||
push(@errors, "missing '$pve_ceph_cfgpath'") if ! -f $pve_ceph_cfgpath;
|
||||
push(@errors, "missing '$pve_ceph_cfgdir'") if ! -d $pve_ceph_cfgdir;
|
||||
push(@errors, "missing '$pve_ceph_cfgpath'") if !-f $pve_ceph_cfgpath;
|
||||
push(@errors, "missing '$pve_ceph_cfgdir'") if !-d $pve_ceph_cfgdir;
|
||||
|
||||
if (@errors) {
|
||||
my $err = 'pveceph configuration not initialized - ' . join(', ', @errors) . "\n";
|
||||
|
|
@ -232,7 +234,7 @@ sub check_ceph_enabled {
|
|||
|
||||
return undef if !check_ceph_inited($noerr);
|
||||
|
||||
if (! -f $ceph_cfgpath) {
|
||||
if (!-f $ceph_cfgpath) {
|
||||
die "pveceph configuration not enabled\n" if !$noerr;
|
||||
return undef;
|
||||
}
|
||||
|
|
@ -282,7 +284,7 @@ sub set_pool {
|
|||
}
|
||||
# by default, pool size always resets min_size, so set it as first item
|
||||
# https://tracker.ceph.com/issues/44862
|
||||
my $keys = [ grep { $_ ne 'size' } sort keys %$param ];
|
||||
my $keys = [grep { $_ ne 'size' } sort keys %$param];
|
||||
unshift @$keys, 'size' if exists $param->{size};
|
||||
|
||||
my $current_properties = get_pool_properties($pool, $rados);
|
||||
|
|
@ -305,7 +307,7 @@ sub set_pool {
|
|||
}
|
||||
|
||||
if (scalar(keys %$param) > 0) {
|
||||
my $missing = join(', ', sort keys %$param );
|
||||
my $missing = join(', ', sort keys %$param);
|
||||
die "Could not set: $missing\n";
|
||||
}
|
||||
|
||||
|
|
@ -428,8 +430,8 @@ sub setup_pve_symlinks {
|
|||
if !$lnk || $lnk ne $pve_ceph_cfgpath;
|
||||
} else {
|
||||
mkdir $ceph_cfgdir;
|
||||
symlink($pve_ceph_cfgpath, $ceph_cfgpath) ||
|
||||
die "unable to create symlink '$ceph_cfgpath' - $!\n";
|
||||
symlink($pve_ceph_cfgpath, $ceph_cfgpath)
|
||||
|| die "unable to create symlink '$ceph_cfgpath' - $!\n";
|
||||
}
|
||||
my $ceph_uid = getpwnam('ceph');
|
||||
my $ceph_gid = getgrnam('ceph');
|
||||
|
|
@ -437,15 +439,15 @@ sub setup_pve_symlinks {
|
|||
}
|
||||
|
||||
sub get_or_create_admin_keyring {
|
||||
if (! -f $pve_ckeyring_path) {
|
||||
run_command("ceph-authtool --create-keyring $pve_ckeyring_path " .
|
||||
"--gen-key -n client.admin " .
|
||||
"--cap mon 'allow *' " .
|
||||
"--cap osd 'allow *' " .
|
||||
"--cap mds 'allow *' " .
|
||||
"--cap mgr 'allow *' ");
|
||||
if (!-f $pve_ckeyring_path) {
|
||||
run_command("ceph-authtool --create-keyring $pve_ckeyring_path "
|
||||
. "--gen-key -n client.admin "
|
||||
. "--cap mon 'allow *' "
|
||||
. "--cap osd 'allow *' "
|
||||
. "--cap mds 'allow *' "
|
||||
. "--cap mgr 'allow *' ");
|
||||
# we do not want to overwrite it
|
||||
if (! -f $ckeyring_path) {
|
||||
if (!-f $ckeyring_path) {
|
||||
run_command("cp $pve_ckeyring_path $ckeyring_path");
|
||||
run_command("chown ceph:ceph $ckeyring_path");
|
||||
}
|
||||
|
|
@ -495,7 +497,7 @@ sub ceph_volume_list {
|
|||
}
|
||||
|
||||
my $output = '';
|
||||
my $cmd = [ $ceph_service->{ceph_volume}, 'lvm', 'list', '--format', 'json' ];
|
||||
my $cmd = [$ceph_service->{ceph_volume}, 'lvm', 'list', '--format', 'json'];
|
||||
run_command($cmd, outfunc => sub { $output .= shift });
|
||||
|
||||
$result = eval { decode_json($output) };
|
||||
|
|
@ -508,7 +510,7 @@ sub ceph_volume_zap {
|
|||
|
||||
die "no osdid given\n" if !defined($osdid);
|
||||
|
||||
my $cmd = [ $ceph_service->{ceph_volume}, 'lvm', 'zap', '--osd-id', $osdid ];
|
||||
my $cmd = [$ceph_service->{ceph_volume}, 'lvm', 'zap', '--osd-id', $osdid];
|
||||
push @$cmd, '--destroy' if $destroy;
|
||||
|
||||
run_command($cmd);
|
||||
|
|
@ -520,83 +522,87 @@ sub get_db_wal_sizes {
|
|||
my $rados = PVE::RADOS->new();
|
||||
my $db_config = $rados->mon_command({ prefix => 'config-key dump', key => 'config/' });
|
||||
|
||||
$res->{db} = $db_config->{"config/osd/bluestore_block_db_size"} //
|
||||
$db_config->{"config/global/bluestore_block_db_size"};
|
||||
$res->{db} = $db_config->{"config/osd/bluestore_block_db_size"}
|
||||
// $db_config->{"config/global/bluestore_block_db_size"};
|
||||
|
||||
$res->{wal} = $db_config->{"config/osd/bluestore_block_wal_size"} //
|
||||
$db_config->{"config/global/bluestore_block_wal_size"};
|
||||
$res->{wal} = $db_config->{"config/osd/bluestore_block_wal_size"}
|
||||
// $db_config->{"config/global/bluestore_block_wal_size"};
|
||||
|
||||
if (!$res->{db} || !$res->{wal}) {
|
||||
my $cfg = cfs_read_file('ceph.conf');
|
||||
if (!$res->{db}) {
|
||||
$res->{db} = $cfg->{osd}->{bluestore_block_db_size} //
|
||||
$cfg->{global}->{bluestore_block_db_size};
|
||||
$res->{db} = $cfg->{osd}->{bluestore_block_db_size}
|
||||
// $cfg->{global}->{bluestore_block_db_size};
|
||||
}
|
||||
|
||||
if (!$res->{wal}) {
|
||||
$res->{wal} = $cfg->{osd}->{bluestore_block_wal_size} //
|
||||
$cfg->{global}->{bluestore_block_wal_size};
|
||||
$res->{wal} = $cfg->{osd}->{bluestore_block_wal_size}
|
||||
// $cfg->{global}->{bluestore_block_wal_size};
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub get_possible_osd_flags {
|
||||
my $possible_flags = {
|
||||
pause => {
|
||||
description => 'Pauses read and writes.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
noup => {
|
||||
description => 'OSDs are not allowed to start.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
nodown => {
|
||||
description => 'OSD failure reports are being ignored, such that the monitors will not mark OSDs down.',
|
||||
description =>
|
||||
'OSD failure reports are being ignored, such that the monitors will not mark OSDs down.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
noout => {
|
||||
description => 'OSDs will not automatically be marked out after the configured interval.',
|
||||
description =>
|
||||
'OSDs will not automatically be marked out after the configured interval.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
noin => {
|
||||
description => 'OSDs that were previously marked out will not be marked back in when they start.',
|
||||
description =>
|
||||
'OSDs that were previously marked out will not be marked back in when they start.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
nobackfill => {
|
||||
description => 'Backfilling of PGs is suspended.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
norebalance => {
|
||||
description => 'Rebalancing of PGs is suspended.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
norecover => {
|
||||
description => 'Recovery of PGs is suspended.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
noscrub => {
|
||||
description => 'Scrubbing is disabled.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
'nodeep-scrub' => {
|
||||
description => 'Deep Scrubbing is disabled.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
notieragent => {
|
||||
description => 'Cache tiering activity is suspended.',
|
||||
type => 'boolean',
|
||||
optional=> 1,
|
||||
optional => 1,
|
||||
},
|
||||
};
|
||||
return $possible_flags;
|
||||
|
|
@ -646,9 +652,7 @@ sub create_ecprofile {
|
|||
$failure_domain = 'host' if !$failure_domain;
|
||||
|
||||
my $profile = [
|
||||
"crush-failure-domain=${failure_domain}",
|
||||
"k=${k}",
|
||||
"m=${m}",
|
||||
"crush-failure-domain=${failure_domain}", "k=${k}", "m=${m}",
|
||||
];
|
||||
|
||||
push(@$profile, "crush-device-class=${device_class}") if $device_class;
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ sub update_cert_cache {
|
|||
|
||||
$cert_cache_timestamp = time() if !defined($update_node);
|
||||
|
||||
my $node_list = defined($update_node) ?
|
||||
[ $update_node ] : [ keys %$cert_cache_nodes ];
|
||||
my $node_list = defined($update_node) ? [$update_node] : [keys %$cert_cache_nodes];
|
||||
|
||||
foreach my $node (@$node_list) {
|
||||
my $clear_old = sub {
|
||||
|
|
@ -64,7 +63,7 @@ sub check_cert_fingerprint {
|
|||
my ($cert) = @_;
|
||||
|
||||
# clear cache every 30 minutes at least
|
||||
update_cert_cache(undef, 1) if time() - $cert_cache_timestamp >= 60*30;
|
||||
update_cert_cache(undef, 1) if time() - $cert_cache_timestamp >= 60 * 30;
|
||||
|
||||
# get fingerprint of server certificate
|
||||
my $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
|
||||
|
|
@ -81,7 +80,10 @@ sub check_cert_fingerprint {
|
|||
|
||||
# clear cache and retry at most once every minute
|
||||
if (time() - $cert_cache_timestamp >= 60) {
|
||||
syslog ('info', "Could not verify remote node certificate '$fp' with list of pinned certificates, refreshing cache");
|
||||
syslog(
|
||||
'info',
|
||||
"Could not verify remote node certificate '$fp' with list of pinned certificates, refreshing cache",
|
||||
);
|
||||
update_cert_cache();
|
||||
return &$check();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,26 +9,35 @@ use PVE::Tools;
|
|||
|
||||
my $account_prefix = '/etc/pve/priv/acme';
|
||||
|
||||
PVE::JSONSchema::register_standard_option('pve-acme-account-name', {
|
||||
PVE::JSONSchema::register_standard_option(
|
||||
'pve-acme-account-name',
|
||||
{
|
||||
description => 'ACME account config file name.',
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
format_description => 'name',
|
||||
optional => 1,
|
||||
default => 'default',
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
PVE::JSONSchema::register_standard_option('pve-acme-account-contact', {
|
||||
PVE::JSONSchema::register_standard_option(
|
||||
'pve-acme-account-contact',
|
||||
{
|
||||
type => 'string',
|
||||
format => 'email-list',
|
||||
description => 'Contact email addresses.',
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
PVE::JSONSchema::register_standard_option('pve-acme-directory-url', {
|
||||
PVE::JSONSchema::register_standard_option(
|
||||
'pve-acme-directory-url',
|
||||
{
|
||||
type => 'string',
|
||||
description => 'URL of ACME CA directory endpoint.',
|
||||
pattern => '^https?://.*',
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
my $local_cert_lock = '/var/lock/pve-certs.lock';
|
||||
|
||||
|
|
@ -84,7 +93,7 @@ sub set_cert_files {
|
|||
};
|
||||
warn "$@\n" if $@;
|
||||
}
|
||||
die "Setting certificate files failed - $err\n"
|
||||
die "Setting certificate files failed - $err\n";
|
||||
}
|
||||
|
||||
unlink $cert_path_tmp;
|
||||
|
|
@ -100,14 +109,18 @@ sub acme_account_dir {
|
|||
sub list_acme_accounts {
|
||||
my $accounts = [];
|
||||
|
||||
return $accounts if ! -d $account_prefix;
|
||||
return $accounts if !-d $account_prefix;
|
||||
|
||||
PVE::Tools::dir_glob_foreach($account_prefix, qr/[^.]+.*/, sub {
|
||||
PVE::Tools::dir_glob_foreach(
|
||||
$account_prefix,
|
||||
qr/[^.]+.*/,
|
||||
sub {
|
||||
my ($name) = @_;
|
||||
|
||||
push @$accounts, $name
|
||||
if PVE::JSONSchema::pve_verify_configid($name, 1);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ PVE::Status::Plugin->init();
|
|||
sub foreach_plug($&) {
|
||||
my ($status_cfg, $code) = @_;
|
||||
|
||||
for my $id (sort keys %{$status_cfg->{ids}}) {
|
||||
for my $id (sort keys %{ $status_cfg->{ids} }) {
|
||||
my $plugin_config = $status_cfg->{ids}->{$id};
|
||||
next if $plugin_config->{disable};
|
||||
|
||||
|
|
@ -46,18 +46,22 @@ sub transactions_start {
|
|||
|
||||
my $transactions = [];
|
||||
|
||||
foreach_plug($cfg, sub {
|
||||
foreach_plug(
|
||||
$cfg,
|
||||
sub {
|
||||
my ($plugin, $id, $plugin_config) = @_;
|
||||
|
||||
my $connection = $plugin->_connect($plugin_config, $id);
|
||||
|
||||
push @$transactions, {
|
||||
push @$transactions,
|
||||
{
|
||||
connection => $connection,
|
||||
cfg => $plugin_config,
|
||||
id => $id,
|
||||
data => '',
|
||||
};
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,10 +67,12 @@ sub auth_handler {
|
|||
my $require_auth = 1;
|
||||
|
||||
# explicitly allow some calls without auth
|
||||
if (($rel_uri eq '/access/domains' && $method eq 'GET') ||
|
||||
($rel_uri eq '/access/ticket' && ($method eq 'GET' || $method eq 'POST')) ||
|
||||
($rel_uri eq '/access/openid/login' && $method eq 'POST') ||
|
||||
($rel_uri eq '/access/openid/auth-url' && $method eq 'POST')) {
|
||||
if (
|
||||
($rel_uri eq '/access/domains' && $method eq 'GET')
|
||||
|| ($rel_uri eq '/access/ticket' && ($method eq 'GET' || $method eq 'POST'))
|
||||
|| ($rel_uri eq '/access/openid/login' && $method eq 'POST')
|
||||
|| ($rel_uri eq '/access/openid/auth-url' && $method eq 'POST')
|
||||
) {
|
||||
$require_auth = 0;
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +148,8 @@ sub rest_handler {
|
|||
for my $p (sort keys %{$params}) {
|
||||
if (defined($uri_param->{$p}) && $uri_param->{$p} ne $params->{$p}) {
|
||||
raise_param_exc({
|
||||
$p => "duplicate parameter (already defined in URI) with conflicting values!"
|
||||
$p =>
|
||||
"duplicate parameter (already defined in URI) with conflicting values!",
|
||||
});
|
||||
}
|
||||
$uri_param->{$p} = $params->{$p};
|
||||
|
|
@ -160,7 +163,11 @@ sub rest_handler {
|
|||
|
||||
if ($info->{proxyto} || $info->{proxyto_callback}) {
|
||||
my $node = PVE::API2Tools::resolve_proxyto(
|
||||
$rpcenv, $info->{proxyto_callback}, $info->{proxyto}, $uri_param);
|
||||
$rpcenv,
|
||||
$info->{proxyto_callback},
|
||||
$info->{proxyto},
|
||||
$uri_param,
|
||||
);
|
||||
|
||||
if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
|
||||
die "unable to proxy file uploads" if $auth->{isUpload};
|
||||
|
|
@ -172,7 +179,7 @@ sub rest_handler {
|
|||
|
||||
my $euid = $>;
|
||||
if ($info->{protected} && ($euid != 0)) {
|
||||
$resp = { proxy => 'localhost' , proxy_params => $params };
|
||||
$resp = { proxy => 'localhost', proxy_params => $params };
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
70
PVE/Jobs.pm
70
PVE/Jobs.pm
|
|
@ -41,7 +41,10 @@ my $saved_config_props = [qw(enabled schedule)];
|
|||
sub detect_changed_runtime_props {
|
||||
my ($jobid, $type, $cfg) = @_;
|
||||
|
||||
lock_job_state($jobid, $type, sub {
|
||||
lock_job_state(
|
||||
$jobid,
|
||||
$type,
|
||||
sub {
|
||||
my $old_state = read_job_state($jobid, $type) // $default_state;
|
||||
|
||||
my $updated = 0;
|
||||
|
|
@ -64,14 +67,15 @@ sub detect_changed_runtime_props {
|
|||
|
||||
my $path = $get_state_file->($jobid, $type);
|
||||
PVE::Tools::file_set_contents($path, encode_json($old_state));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# lockless, since we use file_get_contents, which is atomic
|
||||
sub read_job_state {
|
||||
my ($jobid, $type) = @_;
|
||||
my $path = $get_state_file->($jobid, $type);
|
||||
return if ! -e $path;
|
||||
return if !-e $path;
|
||||
|
||||
my $raw = PVE::Tools::file_get_contents($path);
|
||||
|
||||
|
|
@ -105,7 +109,7 @@ my $get_job_task_status = sub {
|
|||
|
||||
my ($task, $filename) = PVE::Tools::upid_decode($state->{upid}, 1);
|
||||
die "unable to parse worker upid - $state->{upid}\n" if !$task;
|
||||
die "no such task\n" if ! -f $filename;
|
||||
die "no such task\n" if !-f $filename;
|
||||
|
||||
my $pstart = PVE::ProcFSTools::read_proc_starttime($task->{pid});
|
||||
if ($pstart && $pstart == $task->{pstart}) {
|
||||
|
|
@ -125,7 +129,10 @@ sub update_job_stopped {
|
|||
return if !defined($state) || $state->{state} ne 'started'; # removed or not started
|
||||
|
||||
if (defined($get_job_task_status->($state))) {
|
||||
lock_job_state($jobid, $type, sub {
|
||||
lock_job_state(
|
||||
$jobid,
|
||||
$type,
|
||||
sub {
|
||||
my $state = read_job_state($jobid, $type);
|
||||
return if !defined($state) || $state->{state} ne 'started'; # removed or not started
|
||||
|
||||
|
|
@ -142,7 +149,8 @@ sub update_job_stopped {
|
|||
|
||||
my $path = $get_state_file->($jobid, $type);
|
||||
PVE::Tools::file_set_contents($path, encode_json($new_state));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +158,10 @@ sub update_job_stopped {
|
|||
sub create_job {
|
||||
my ($jobid, $type, $cfg) = @_;
|
||||
|
||||
lock_job_state($jobid, $type, sub {
|
||||
lock_job_state(
|
||||
$jobid,
|
||||
$type,
|
||||
sub {
|
||||
my $state = read_job_state($jobid, $type) // $default_state;
|
||||
|
||||
if ($state->{state} ne 'created') {
|
||||
|
|
@ -166,7 +177,8 @@ sub create_job {
|
|||
|
||||
my $path = $get_state_file->($jobid, $type);
|
||||
PVE::Tools::file_set_contents($path, encode_json($state));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# to be called when the job is removed
|
||||
|
|
@ -185,7 +197,10 @@ sub starting_job {
|
|||
my $state = read_job_state($jobid, $type);
|
||||
return 0 if !defined($state) || $state->{state} eq 'started'; # removed or already started
|
||||
|
||||
lock_job_state($jobid, $type, sub {
|
||||
lock_job_state(
|
||||
$jobid,
|
||||
$type,
|
||||
sub {
|
||||
my $state = read_job_state($jobid, $type);
|
||||
return 0 if !defined($state) || $state->{state} eq 'started'; # removed or already started
|
||||
|
||||
|
|
@ -197,14 +212,18 @@ sub starting_job {
|
|||
|
||||
my $path = $get_state_file->($jobid, $type);
|
||||
PVE::Tools::file_set_contents($path, encode_json($new_state));
|
||||
});
|
||||
},
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub started_job {
|
||||
my ($jobid, $type, $upid, $msg) = @_;
|
||||
|
||||
lock_job_state($jobid, $type, sub {
|
||||
lock_job_state(
|
||||
$jobid,
|
||||
$type,
|
||||
sub {
|
||||
my $state = read_job_state($jobid, $type);
|
||||
return if !defined($state); # job was removed, do not update
|
||||
die "unexpected state '$state->{state}'\n" if $state->{state} ne 'starting';
|
||||
|
|
@ -226,20 +245,25 @@ sub started_job {
|
|||
|
||||
my $path = $get_state_file->($jobid, $type);
|
||||
PVE::Tools::file_set_contents($path, encode_json($new_state));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# will be called when the job schedule is updated
|
||||
sub update_last_runtime {
|
||||
my ($jobid, $type) = @_;
|
||||
lock_job_state($jobid, $type, sub {
|
||||
lock_job_state(
|
||||
$jobid,
|
||||
$type,
|
||||
sub {
|
||||
my $old_state = read_job_state($jobid, $type) // $default_state;
|
||||
|
||||
$old_state->{updated} = time();
|
||||
|
||||
my $path = $get_state_file->($jobid, $type);
|
||||
PVE::Tools::file_set_contents($path, encode_json($old_state));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
sub get_last_runtime {
|
||||
|
|
@ -266,7 +290,7 @@ sub run_jobs {
|
|||
my $jobs_cfg = cfs_read_file('jobs.cfg');
|
||||
my $nodename = PVE::INotify::nodename();
|
||||
|
||||
foreach my $id (sort keys %{$jobs_cfg->{ids}}) {
|
||||
foreach my $id (sort keys %{ $jobs_cfg->{ids} }) {
|
||||
my $cfg = $jobs_cfg->{ids}->{$id};
|
||||
my $type = $cfg->{type};
|
||||
my $schedule = delete $cfg->{schedule};
|
||||
|
|
@ -311,7 +335,10 @@ sub run_jobs {
|
|||
|
||||
# creates and removes statefiles for job configs
|
||||
sub synchronize_job_states_with_config {
|
||||
cfs_lock_file('jobs.cfg', undef, sub {
|
||||
cfs_lock_file(
|
||||
'jobs.cfg',
|
||||
undef,
|
||||
sub {
|
||||
my $data = cfs_read_file('jobs.cfg');
|
||||
|
||||
for my $id (keys $data->{ids}->%*) {
|
||||
|
|
@ -329,14 +356,19 @@ sub synchronize_job_states_with_config {
|
|||
my $valid_types = PVE::Job::Registry->lookup_types();
|
||||
my $type_regex = join("|", $valid_types->@*);
|
||||
|
||||
PVE::Tools::dir_glob_foreach($state_dir, "(${type_regex})-(.*).json", sub {
|
||||
PVE::Tools::dir_glob_foreach(
|
||||
$state_dir,
|
||||
"(${type_regex})-(.*).json",
|
||||
sub {
|
||||
my ($path, $type, $id) = @_;
|
||||
|
||||
if (!defined($data->{ids}->{$id})) {
|
||||
remove_job($id, $type);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ my $MAXDOMAINS = 5;
|
|||
|
||||
my $node_config_lock = '/var/lock/pvenode.lock';
|
||||
|
||||
PVE::JSONSchema::register_format('pve-acme-domain', sub {
|
||||
PVE::JSONSchema::register_format(
|
||||
'pve-acme-domain',
|
||||
sub {
|
||||
my ($domain, $noerr) = @_;
|
||||
|
||||
my $label = qr/[a-z0-9][a-z0-9_-]*/i;
|
||||
|
|
@ -23,9 +25,12 @@ PVE::JSONSchema::register_format('pve-acme-domain', sub {
|
|||
return $domain if $domain =~ /^$label(?:\.$label)+$/;
|
||||
return undef if $noerr;
|
||||
die "value '$domain' does not look like a valid domain name!\n";
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
PVE::JSONSchema::register_format('pve-acme-alias', sub {
|
||||
PVE::JSONSchema::register_format(
|
||||
'pve-acme-alias',
|
||||
sub {
|
||||
my ($alias, $noerr) = @_;
|
||||
|
||||
my $label = qr/[a-z0-9_][a-z0-9_-]*/i;
|
||||
|
|
@ -33,7 +38,8 @@ PVE::JSONSchema::register_format('pve-acme-alias', sub {
|
|||
return $alias if $alias =~ /^$label(?:\.$label)+$/;
|
||||
return undef if $noerr;
|
||||
die "value '$alias' does not look like a valid alias name!\n";
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
sub config_file {
|
||||
my ($node) = @_;
|
||||
|
|
@ -81,12 +87,13 @@ my $confdesc = {
|
|||
description => {
|
||||
type => 'string',
|
||||
description => "Description for the Node. Shown in the web-interface node notes panel."
|
||||
." This is saved as comment inside the configuration file.",
|
||||
. " This is saved as comment inside the configuration file.",
|
||||
maxLength => 64 * 1024,
|
||||
optional => 1,
|
||||
},
|
||||
'startall-onboot-delay' => {
|
||||
description => 'Initial delay in seconds, before starting all the Virtual Guests with on-boot enabled.',
|
||||
description =>
|
||||
'Initial delay in seconds, before starting all the Virtual Guests with on-boot enabled.',
|
||||
type => 'integer',
|
||||
minimum => 0,
|
||||
maximum => 300,
|
||||
|
|
@ -179,14 +186,14 @@ $confdesc->{acme} = {
|
|||
optional => 1,
|
||||
};
|
||||
|
||||
for my $i (0..$MAXDOMAINS) {
|
||||
for my $i (0 .. $MAXDOMAINS) {
|
||||
$confdesc->{"acmedomain$i"} = {
|
||||
type => 'string',
|
||||
description => 'ACME domain and validation plugin',
|
||||
format => $acme_domain_desc,
|
||||
optional => 1,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
my $conf_schema = {
|
||||
type => 'object',
|
||||
|
|
@ -236,7 +243,7 @@ sub get_wakeonlan_config {
|
|||
my $res = {};
|
||||
if (defined($node_conf->{wakeonlan})) {
|
||||
$res = eval {
|
||||
PVE::JSONSchema::parse_property_string($wakeonlan_desc, $node_conf->{wakeonlan})
|
||||
PVE::JSONSchema::parse_property_string($wakeonlan_desc, $node_conf->{wakeonlan});
|
||||
};
|
||||
die $@ if $@;
|
||||
}
|
||||
|
|
@ -254,9 +261,7 @@ sub get_acme_conf {
|
|||
|
||||
my $res = {};
|
||||
if (defined($node_conf->{acme})) {
|
||||
$res = eval {
|
||||
PVE::JSONSchema::parse_property_string($acmedesc, $node_conf->{acme})
|
||||
};
|
||||
$res = eval { PVE::JSONSchema::parse_property_string($acmedesc, $node_conf->{acme}) };
|
||||
if (my $err = $@) {
|
||||
return undef if $noerr;
|
||||
die $err;
|
||||
|
|
@ -275,13 +280,12 @@ sub get_acme_conf {
|
|||
|
||||
$res->{account} //= 'default';
|
||||
|
||||
for my $index (0..$MAXDOMAINS) {
|
||||
for my $index (0 .. $MAXDOMAINS) {
|
||||
my $domain_rec = $node_conf->{"acmedomain$index"};
|
||||
next if !defined($domain_rec);
|
||||
|
||||
my $parsed = eval {
|
||||
PVE::JSONSchema::parse_property_string($acme_domain_desc, $domain_rec)
|
||||
};
|
||||
my $parsed =
|
||||
eval { PVE::JSONSchema::parse_property_string($acme_domain_desc, $domain_rec) };
|
||||
if (my $err = $@) {
|
||||
return undef if $noerr;
|
||||
die $err;
|
||||
|
|
@ -290,7 +294,7 @@ sub get_acme_conf {
|
|||
if (my $exists = $res->{domains}->{$domain}) {
|
||||
return undef if $noerr;
|
||||
die "duplicate domain '$domain' in ACME config properties"
|
||||
." 'acmedomain$index' and '$exists->{_configkey}'\n";
|
||||
. " 'acmedomain$index' and '$exists->{_configkey}'\n";
|
||||
}
|
||||
$parsed->{plugin} //= 'standalone';
|
||||
|
||||
|
|
|
|||
|
|
@ -15,13 +15,14 @@ my $get_cache = sub {
|
|||
my $uid = getpwnam('root');
|
||||
my $gid = getgrnam('www-data');
|
||||
|
||||
$cache = Proxmox::RS::SharedCache->new({
|
||||
$cache = Proxmox::RS::SharedCache->new(
|
||||
{
|
||||
path => "/run/pve/metrics",
|
||||
owner => $uid,
|
||||
group => $gid,
|
||||
entry_mode => 0640, # Entry permissions
|
||||
keep_old => OLD_GENERATIONS,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ my sub gauge {
|
|||
value => $value + 0,
|
||||
timestamp => $timestamp + 0,
|
||||
type => 'gauge',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
my sub derive {
|
||||
|
|
@ -72,7 +73,7 @@ my sub derive {
|
|||
value => $value + 0,
|
||||
timestamp => $timestamp + 0,
|
||||
type => 'derive',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
my $nodename = PVE::INotify::nodename();
|
||||
|
|
|
|||
|
|
@ -11,12 +11,16 @@ my sub dir2text {
|
|||
|
||||
print STDERR "dir2text '${target_dir}${regexp}'...";
|
||||
my $text = "# output '${target_dir}${regexp}' file(s)\n";
|
||||
PVE::Tools::dir_glob_foreach($target_dir, $regexp, sub {
|
||||
PVE::Tools::dir_glob_foreach(
|
||||
$target_dir,
|
||||
$regexp,
|
||||
sub {
|
||||
my ($file) = @_;
|
||||
return if $file eq '.' || $file eq '..';
|
||||
$text .= "\n# cat $target_dir$file\n";
|
||||
$text .= PVE::Tools::file_get_contents($target_dir.$file)."\n";
|
||||
});
|
||||
$text .= PVE::Tools::file_get_contents($target_dir . $file) . "\n";
|
||||
},
|
||||
);
|
||||
return $text;
|
||||
}
|
||||
|
||||
|
|
@ -48,8 +52,7 @@ my $init_report_cmds = sub {
|
|||
title => 'overall system load info',
|
||||
order => 20,
|
||||
cmds => [
|
||||
'top -b -c -w512 -n 1 -o TIME | head -n 30',
|
||||
'head /proc/pressure/*',
|
||||
'top -b -c -w512 -n 1 -o TIME | head -n 30', 'head /proc/pressure/*',
|
||||
],
|
||||
},
|
||||
storage => {
|
||||
|
|
@ -113,8 +116,7 @@ my $init_report_cmds = sub {
|
|||
hardware => {
|
||||
order => 70,
|
||||
cmds => [
|
||||
'dmidecode -t bios',
|
||||
'lspci -nnk',
|
||||
'dmidecode -t bios', 'lspci -nnk',
|
||||
],
|
||||
},
|
||||
'block devices' => {
|
||||
|
|
@ -129,15 +131,13 @@ my $init_report_cmds = sub {
|
|||
volumes => {
|
||||
order => 90,
|
||||
cmds => [
|
||||
'pvs',
|
||||
'lvs',
|
||||
'vgs',
|
||||
'pvs', 'lvs', 'vgs',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
if (cmd_exists('zfs')) {
|
||||
push @{$report_def->{volumes}->{cmds}},
|
||||
push @{ $report_def->{volumes}->{cmds} },
|
||||
'zpool status',
|
||||
'zpool list -v',
|
||||
'zfs list',
|
||||
|
|
@ -146,7 +146,7 @@ my $init_report_cmds = sub {
|
|||
}
|
||||
|
||||
if (-e '/etc/ceph/ceph.conf') {
|
||||
push @{$report_def->{volumes}->{cmds}},
|
||||
push @{ $report_def->{volumes}->{cmds} },
|
||||
'pveceph status',
|
||||
'ceph osd status',
|
||||
'ceph df',
|
||||
|
|
@ -161,7 +161,7 @@ my $init_report_cmds = sub {
|
|||
}
|
||||
|
||||
if (cmd_exists('multipath')) {
|
||||
push @{$report_def->{disks}->{cmds}},
|
||||
push @{ $report_def->{disks}->{cmds} },
|
||||
'cat /etc/multipath.conf',
|
||||
'cat /etc/multipath/wwids',
|
||||
'multipath -ll',
|
||||
|
|
@ -189,19 +189,20 @@ sub generate {
|
|||
noerr => 1, # avoid checking programs exit code
|
||||
};
|
||||
|
||||
my $sorter = sub { ($def->{$_[0]}->{order} // 1<<30) <=> ($def->{$_[1]}->{order} // 1<<30) };
|
||||
my $sorter =
|
||||
sub { ($def->{ $_[0] }->{order} // 1 << 30) <=> ($def->{ $_[1] }->{order} // 1 << 30) };
|
||||
|
||||
for my $section ( sort { $sorter->($a, $b) } keys %$def) {
|
||||
for my $section (sort { $sorter->($a, $b) } keys %$def) {
|
||||
my $s = $def->{$section};
|
||||
my $title = $s->{title} // "info about $section";
|
||||
|
||||
$report .= "\n==== $title ====\n";
|
||||
for my $command (@{$s->{cmds}}) {
|
||||
for my $command (@{ $s->{cmds} }) {
|
||||
eval {
|
||||
if (ref $command eq 'CODE') {
|
||||
$report .= PVE::Tools::run_with_timeout($cmd_timeout, $command);
|
||||
} else {
|
||||
print STDERR "Process ".$command."...";
|
||||
print STDERR "Process " . $command . "...";
|
||||
$report .= "\n# $command\n";
|
||||
PVE::Tools::run_command($command, %$run_cmd_params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ sub init {
|
|||
|
||||
my $accept_lock_fn = "/var/lock/pvedaemon.lck";
|
||||
|
||||
my $lockfh = IO::File->new(">>${accept_lock_fn}") ||
|
||||
die "unable to open lock file '${accept_lock_fn}' - $!\n";
|
||||
my $lockfh = IO::File->new(">>${accept_lock_fn}")
|
||||
|| die "unable to open lock file '${accept_lock_fn}' - $!\n";
|
||||
|
||||
my $socket = $self->create_reusable_socket(85, '127.0.0.1');
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ sub init {
|
|||
sub run {
|
||||
my ($self) = @_;
|
||||
|
||||
my $server = PVE::HTTPServer->new(%{$self->{server_config}});
|
||||
my $server = PVE::HTTPServer->new(%{ $self->{server_config} });
|
||||
$server->run();
|
||||
}
|
||||
|
||||
|
|
@ -58,10 +58,10 @@ $daemon->register_stop_command();
|
|||
$daemon->register_status_command();
|
||||
|
||||
our $cmddef = {
|
||||
start => [ __PACKAGE__, 'start', []],
|
||||
restart => [ __PACKAGE__, 'restart', []],
|
||||
stop => [ __PACKAGE__, 'stop', []],
|
||||
status => [ __PACKAGE__, 'status', [], undef, sub { print shift . "\n";} ],
|
||||
start => [__PACKAGE__, 'start', []],
|
||||
restart => [__PACKAGE__, 'restart', []],
|
||||
stop => [__PACKAGE__, 'stop', []],
|
||||
status => [__PACKAGE__, 'status', [], undef, sub { print shift . "\n"; }],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ sub init {
|
|||
|
||||
my $accept_lock_fn = "/var/lock/pveproxy.lck";
|
||||
|
||||
my $lockfh = IO::File->new(">>${accept_lock_fn}") ||
|
||||
die "unable to open lock file '${accept_lock_fn}' - $!\n";
|
||||
my $lockfh = IO::File->new(">>${accept_lock_fn}")
|
||||
|| die "unable to open lock file '${accept_lock_fn}' - $!\n";
|
||||
|
||||
my $listen_ip = $proxyconf->{LISTEN_IP};
|
||||
my $socket = $self->create_reusable_socket(8006, $listen_ip);
|
||||
|
|
@ -150,14 +150,17 @@ sub init {
|
|||
if (-f '/etc/pve/local/pveproxy-ssl.pem' && -f $custom_key_path) {
|
||||
$self->{server_config}->{ssl}->{cert_file} = '/etc/pve/local/pveproxy-ssl.pem';
|
||||
$self->{server_config}->{ssl}->{key_file} = $custom_key_path;
|
||||
syslog('info', 'Using \'/etc/pve/local/pveproxy-ssl.pem\' as certificate for the web interface.');
|
||||
syslog(
|
||||
'info',
|
||||
'Using \'/etc/pve/local/pveproxy-ssl.pem\' as certificate for the web interface.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub run {
|
||||
my ($self) = @_;
|
||||
|
||||
my $server = PVE::HTTPServer->new(%{$self->{server_config}});
|
||||
my $server = PVE::HTTPServer->new(%{ $self->{server_config} });
|
||||
$server->run();
|
||||
}
|
||||
|
||||
|
|
@ -167,10 +170,10 @@ $daemon->register_stop_command();
|
|||
$daemon->register_status_command();
|
||||
|
||||
our $cmddef = {
|
||||
start => [ __PACKAGE__, 'start', []],
|
||||
restart => [ __PACKAGE__, 'restart', []],
|
||||
stop => [ __PACKAGE__, 'stop', []],
|
||||
status => [ __PACKAGE__, 'status', [], undef, sub { print shift . "\n";} ],
|
||||
start => [__PACKAGE__, 'start', []],
|
||||
restart => [__PACKAGE__, 'restart', []],
|
||||
stop => [__PACKAGE__, 'stop', []],
|
||||
status => [__PACKAGE__, 'status', [], undef, sub { print shift . "\n"; }],
|
||||
};
|
||||
|
||||
sub is_phone {
|
||||
|
|
@ -231,7 +234,9 @@ sub get_index {
|
|||
};
|
||||
warn "$@\n" if $@;
|
||||
|
||||
my $mobile = (is_phone($r->header('User-Agent')) && (!defined($args->{mobile}) || $args->{mobile})) || $args->{mobile};
|
||||
my $mobile =
|
||||
(is_phone($r->header('User-Agent')) && (!defined($args->{mobile}) || $args->{mobile}))
|
||||
|| $args->{mobile};
|
||||
|
||||
my $novnc = defined($args->{console}) && $args->{novnc};
|
||||
my $xtermjs = defined($args->{console}) && $args->{xtermjs};
|
||||
|
|
@ -259,7 +264,7 @@ sub get_index {
|
|||
version => "$version",
|
||||
wtversion => $wtversion,
|
||||
theme => $theme,
|
||||
consenttext => $consent_text
|
||||
consenttext => $consent_text,
|
||||
};
|
||||
|
||||
# by default, load the normal index
|
||||
|
|
@ -274,7 +279,7 @@ sub get_index {
|
|||
}
|
||||
|
||||
my $page = '';
|
||||
my $template = Template->new({ABSOLUTE => 1});
|
||||
my $template = Template->new({ ABSOLUTE => 1 });
|
||||
|
||||
$template->process("$dir/index.html.tpl", $vars, \$page) || die $template->error(), "\n";
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ my @JOB_TYPES = qw(replication jobs);
|
|||
|
||||
my sub running_job_pids : prototype($) {
|
||||
my ($self) = @_;
|
||||
my $pids = [ map { keys $_->%* } values $self->{jobs}->%* ];
|
||||
my $pids = [map { keys $_->%* } values $self->{jobs}->%*];
|
||||
return scalar($pids->@*) ? $pids : undef;
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ my sub finish_jobs : prototype($) {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
sub hup {
|
||||
my ($self) = @_;
|
||||
|
|
@ -85,9 +85,7 @@ sub run {
|
|||
die "fork failed: $!\n";
|
||||
} elsif ($child == 0) {
|
||||
$self->after_fork_cleanup();
|
||||
eval {
|
||||
$sub->();
|
||||
};
|
||||
eval { $sub->(); };
|
||||
if (my $err = $@) {
|
||||
syslog('err', "$type: $err");
|
||||
}
|
||||
|
|
@ -106,20 +104,26 @@ sub run {
|
|||
# my $scheduled_jobs = PVE::Jobs::get_pending() or return;
|
||||
# forked { PVE::Jobs::run_jobs($scheduled_jobs) }
|
||||
|
||||
$fork->('replication', sub {
|
||||
PVE::API2::Replication::run_jobs(undef, sub {}, 0, 1);
|
||||
});
|
||||
$fork->(
|
||||
'replication',
|
||||
sub {
|
||||
PVE::API2::Replication::run_jobs(undef, sub { }, 0, 1);
|
||||
},
|
||||
);
|
||||
|
||||
$fork->('jobs', sub {
|
||||
$fork->(
|
||||
'jobs',
|
||||
sub {
|
||||
PVE::Jobs::run_jobs($first_run);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
$first_run = 0;
|
||||
};
|
||||
|
||||
PVE::Jobs::setup_dirs();
|
||||
|
||||
for (my $count = 1000;;$count++) {
|
||||
for (my $count = 1000;; $count++) {
|
||||
return if $self->{got_hup_signal}; # keep workers running, PVE::Daemon re-execs us on return
|
||||
last if $self->{shutdown_request}; # exit main-run loop for shutdown
|
||||
|
||||
|
|
@ -144,7 +148,7 @@ sub run {
|
|||
|
||||
# NOTE: we only get here on shutdown_request, so we already sent a TERM to all job-types
|
||||
my $timeout = 0;
|
||||
while(my $pids = running_job_pids($self)) {
|
||||
while (my $pids = running_job_pids($self)) {
|
||||
kill 'TERM', $pids->@*; # send TERM to all workers at once, possible thundering herd - FIXME?
|
||||
|
||||
finish_jobs($self);
|
||||
|
|
@ -177,10 +181,10 @@ $daemon->register_restart_command(1);
|
|||
$daemon->register_status_command();
|
||||
|
||||
our $cmddef = {
|
||||
start => [ __PACKAGE__, 'start', []],
|
||||
stop => [ __PACKAGE__, 'stop', []],
|
||||
restart => [ __PACKAGE__, 'restart', []],
|
||||
status => [ __PACKAGE__, 'status', [], undef, sub { print shift . "\n";} ],
|
||||
start => [__PACKAGE__, 'start', []],
|
||||
stop => [__PACKAGE__, 'stop', []],
|
||||
restart => [__PACKAGE__, 'restart', []],
|
||||
status => [__PACKAGE__, 'status', [], undef, sub { print shift . "\n"; }],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ sub init {
|
|||
sub shutdown {
|
||||
my ($self) = @_;
|
||||
|
||||
syslog('info' , "server closing");
|
||||
syslog('info', "server closing");
|
||||
|
||||
# wait for children
|
||||
1 while (waitpid(-1, POSIX::WNOHANG()) > 0);
|
||||
|
|
@ -97,8 +97,10 @@ sub update_supported_cpuflags {
|
|||
my $supported_cpuflags = eval { PVE::QemuServer::query_supported_cpu_flags() };
|
||||
warn $@ if $@;
|
||||
|
||||
if (!$supported_cpuflags ||
|
||||
(!$supported_cpuflags->{tcg} && !$supported_cpuflags->{kvm})) {
|
||||
if (
|
||||
!$supported_cpuflags
|
||||
|| (!$supported_cpuflags->{tcg} && !$supported_cpuflags->{kvm})
|
||||
) {
|
||||
# something went wrong, clear broadcast flags and set try-again delay
|
||||
warn "CPU flag detection failed, will try again after delay\n";
|
||||
$next_flag_update_time = time() + $failed_flag_update_delay_sec;
|
||||
|
|
@ -111,7 +113,10 @@ sub update_supported_cpuflags {
|
|||
|
||||
for my $accel ("tcg", "kvm") {
|
||||
if ($supported_cpuflags->{$accel}) {
|
||||
PVE::Cluster::broadcast_node_kv("cpuflags-$accel", join(' ', @{$supported_cpuflags->{$accel}}));
|
||||
PVE::Cluster::broadcast_node_kv(
|
||||
"cpuflags-$accel",
|
||||
join(' ', @{ $supported_cpuflags->{$accel} }),
|
||||
);
|
||||
} else {
|
||||
# clear potentially invalid data
|
||||
PVE::Cluster::broadcast_node_kv("cpuflags-$accel", '');
|
||||
|
|
@ -135,8 +140,10 @@ my sub broadcast_static_node_info {
|
|||
$old = eval { decode_json($old->{$nodename}) } if defined($old->{$nodename});
|
||||
|
||||
if (
|
||||
!defined($old->{cpus}) || $old->{cpus} != $cpus
|
||||
|| !defined($old->{memory}) || $old->{memory} != $memory
|
||||
!defined($old->{cpus})
|
||||
|| $old->{cpus} != $cpus
|
||||
|| !defined($old->{memory})
|
||||
|| $old->{memory} != $memory
|
||||
|| ($old->{'cgroup-mode'} // -1) != ($cgroup_mode // -1)
|
||||
) {
|
||||
my $info = {
|
||||
|
|
@ -180,10 +187,23 @@ sub update_node_status {
|
|||
my $ctime = time();
|
||||
|
||||
my $data = $generate_rrd_string->(
|
||||
[$uptime, $sublevel, $ctime, $avg1, $maxcpu, $stat->{cpu}, $stat->{wait},
|
||||
$meminfo->{memtotal}, $meminfo->{memused},
|
||||
$meminfo->{swaptotal}, $meminfo->{swapused},
|
||||
$dinfo->{blocks}, $dused, $netin, $netout]
|
||||
[
|
||||
$uptime,
|
||||
$sublevel,
|
||||
$ctime,
|
||||
$avg1,
|
||||
$maxcpu,
|
||||
$stat->{cpu},
|
||||
$stat->{wait},
|
||||
$meminfo->{memtotal},
|
||||
$meminfo->{memused},
|
||||
$meminfo->{swaptotal},
|
||||
$meminfo->{swapused},
|
||||
$dinfo->{blocks},
|
||||
$dused,
|
||||
$netin,
|
||||
$netout,
|
||||
],
|
||||
);
|
||||
PVE::Cluster::broadcast_rrd("pve2-node/$nodename", $data);
|
||||
|
||||
|
|
@ -221,9 +241,10 @@ sub auto_balloning {
|
|||
my $target = int($config->{'ballooning-target'} // 80);
|
||||
# goal is the change amount required to achieve that
|
||||
my $goal = int($hostmeminfo->{memtotal} * $target / 100 - $hostmeminfo->{memused});
|
||||
$log->("target: $target%% host goal: $goal free: $hostfreemem total: $hostmeminfo->{memtotal}\n");
|
||||
$log->(
|
||||
"target: $target%% host goal: $goal free: $hostfreemem total: $hostmeminfo->{memtotal}\n");
|
||||
|
||||
my $maxchange = 100*1024*1024;
|
||||
my $maxchange = 100 * 1024 * 1024;
|
||||
my $res = PVE::AutoBalloon::compute_alg1($vmstatus, $goal, $maxchange);
|
||||
|
||||
for my $vmid (sort keys %$res) {
|
||||
|
|
@ -253,14 +274,41 @@ sub update_qemu_status {
|
|||
my $status = $d->{qmpstatus} || $d->{status} || 'stopped';
|
||||
my $template = $d->{template} ? $d->{template} : "0";
|
||||
if ($d->{pid}) { # running
|
||||
$data = $generate_rrd_string->(
|
||||
[$d->{uptime}, $d->{name}, $status, $template, $ctime, $d->{cpus}, $d->{cpu},
|
||||
$d->{maxmem}, $d->{mem}, $d->{maxdisk}, $d->{disk},
|
||||
$d->{netin}, $d->{netout}, $d->{diskread}, $d->{diskwrite}]);
|
||||
$data = $generate_rrd_string->([
|
||||
$d->{uptime},
|
||||
$d->{name},
|
||||
$status,
|
||||
$template,
|
||||
$ctime,
|
||||
$d->{cpus},
|
||||
$d->{cpu},
|
||||
$d->{maxmem},
|
||||
$d->{mem},
|
||||
$d->{maxdisk},
|
||||
$d->{disk},
|
||||
$d->{netin},
|
||||
$d->{netout},
|
||||
$d->{diskread},
|
||||
$d->{diskwrite},
|
||||
]);
|
||||
} else {
|
||||
$data = $generate_rrd_string->(
|
||||
[0, $d->{name}, $status, $template, $ctime, $d->{cpus}, undef,
|
||||
$d->{maxmem}, undef, $d->{maxdisk}, $d->{disk}, undef, undef, undef, undef]);
|
||||
$data = $generate_rrd_string->([
|
||||
0,
|
||||
$d->{name},
|
||||
$status,
|
||||
$template,
|
||||
$ctime,
|
||||
$d->{cpus},
|
||||
undef,
|
||||
$d->{maxmem},
|
||||
undef,
|
||||
$d->{maxdisk},
|
||||
$d->{disk},
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
]);
|
||||
}
|
||||
PVE::Cluster::broadcast_rrd("pve2.3-vm/$vmid", $data);
|
||||
|
||||
|
|
@ -280,7 +328,7 @@ sub remove_stale_lxc_consoles {
|
|||
foreach my $vmid (keys %$pidhash) {
|
||||
next if defined($vmstatus->{$vmid});
|
||||
syslog('info', "remove stale lxc-console for CT $vmid");
|
||||
foreach my $pid (@{$pidhash->{$vmid}}) {
|
||||
foreach my $pid (@{ $pidhash->{$vmid} }) {
|
||||
kill(9, $pid);
|
||||
}
|
||||
}
|
||||
|
|
@ -289,6 +337,7 @@ sub remove_stale_lxc_consoles {
|
|||
my $rebalance_error_count = {};
|
||||
|
||||
my $NO_REBALANCE;
|
||||
|
||||
sub rebalance_lxc_containers {
|
||||
# Make sure we can find the cpuset controller path:
|
||||
return if $NO_REBALANCE;
|
||||
|
|
@ -308,7 +357,7 @@ sub rebalance_lxc_containers {
|
|||
my $cpucount = scalar(@allowed_cpus);
|
||||
my $max_cpuid = $allowed_cpus[-1];
|
||||
|
||||
my @cpu_ctcount = (0) x ($max_cpuid+1);
|
||||
my @cpu_ctcount = (0) x ($max_cpuid + 1);
|
||||
my @balanced_cts;
|
||||
|
||||
# A mapping { vmid => cgroup_payload_path } for containers where namespace
|
||||
|
|
@ -328,9 +377,7 @@ sub rebalance_lxc_containers {
|
|||
if (defined($cgbase)) {
|
||||
# allow all, so that we can set new cpuset in /ns
|
||||
$all_cpus->write_to_path($cgbase);
|
||||
eval {
|
||||
$newset->write_to_path("$cgbase/ns");
|
||||
};
|
||||
eval { $newset->write_to_path("$cgbase/ns"); };
|
||||
if (my $err = $@) {
|
||||
warn $err if !$rebalance_error_count->{$vmid}++;
|
||||
# restore original
|
||||
|
|
@ -361,10 +408,8 @@ sub rebalance_lxc_containers {
|
|||
next; # old style container
|
||||
}
|
||||
|
||||
my ($conf, $cpuset) = eval {(
|
||||
PVE::LXC::Config->load_config($vmid),
|
||||
PVE::CpuSet->new_from_path($cgpath),
|
||||
)};
|
||||
my ($conf, $cpuset) =
|
||||
eval { (PVE::LXC::Config->load_config($vmid), PVE::CpuSet->new_from_path($cgpath)) };
|
||||
if (my $err = $@) {
|
||||
warn $err;
|
||||
next;
|
||||
|
|
@ -372,7 +417,8 @@ sub rebalance_lxc_containers {
|
|||
|
||||
my @cpuset_members = $cpuset->members();
|
||||
|
||||
if (!PVE::LXC::Config->has_lxc_entry($conf, 'lxc.cgroup.cpuset.cpus')
|
||||
if (
|
||||
!PVE::LXC::Config->has_lxc_entry($conf, 'lxc.cgroup.cpuset.cpus')
|
||||
&& !PVE::LXC::Config->has_lxc_entry($conf, 'lxc.cgroup2.cpuset.cpus')
|
||||
) {
|
||||
my $cores = $conf->{cores} || $cpucount;
|
||||
|
|
@ -430,7 +476,7 @@ sub rebalance_lxc_containers {
|
|||
foreach my $bct (@balanced_cts) {
|
||||
my ($vmid, $cores, $cpuset) = @$bct;
|
||||
|
||||
my $rest = [ grep { !$cpuset->has($_) } @allowed_cpus ];
|
||||
my $rest = [grep { !$cpuset->has($_) } @allowed_cpus];
|
||||
|
||||
my $newset = PVE::CpuSet->new();
|
||||
for my $cpu ($cpuset->members()) {
|
||||
|
|
@ -461,17 +507,41 @@ sub update_lxc_status {
|
|||
my $template = $d->{template} ? $d->{template} : "0";
|
||||
my $data;
|
||||
if ($d->{status} eq 'running') { # running
|
||||
$data = $generate_rrd_string->(
|
||||
[$d->{uptime}, $d->{name}, $d->{status}, $template,
|
||||
$ctime, $d->{cpus}, $d->{cpu},
|
||||
$d->{maxmem}, $d->{mem},
|
||||
$d->{maxdisk}, $d->{disk},
|
||||
$d->{netin}, $d->{netout},
|
||||
$d->{diskread}, $d->{diskwrite}]);
|
||||
$data = $generate_rrd_string->([
|
||||
$d->{uptime},
|
||||
$d->{name},
|
||||
$d->{status},
|
||||
$template,
|
||||
$ctime,
|
||||
$d->{cpus},
|
||||
$d->{cpu},
|
||||
$d->{maxmem},
|
||||
$d->{mem},
|
||||
$d->{maxdisk},
|
||||
$d->{disk},
|
||||
$d->{netin},
|
||||
$d->{netout},
|
||||
$d->{diskread},
|
||||
$d->{diskwrite},
|
||||
]);
|
||||
} else {
|
||||
$data = $generate_rrd_string->(
|
||||
[0, $d->{name}, $d->{status}, $template, $ctime, $d->{cpus}, undef,
|
||||
$d->{maxmem}, undef, $d->{maxdisk}, $d->{disk}, undef, undef, undef, undef]);
|
||||
$data = $generate_rrd_string->([
|
||||
0,
|
||||
$d->{name},
|
||||
$d->{status},
|
||||
$template,
|
||||
$ctime,
|
||||
$d->{cpus},
|
||||
undef,
|
||||
$d->{maxmem},
|
||||
undef,
|
||||
$d->{maxdisk},
|
||||
$d->{disk},
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
undef,
|
||||
]);
|
||||
}
|
||||
PVE::Cluster::broadcast_rrd("pve2.3-vm/$vmid", $data);
|
||||
|
||||
|
|
@ -521,7 +591,7 @@ sub update_ceph_metadata {
|
|||
|
||||
sub update_sdn_status {
|
||||
|
||||
if($have_sdn) {
|
||||
if ($have_sdn) {
|
||||
my ($transport_status, $vnet_status) = PVE::Network::SDN::status();
|
||||
|
||||
my $status = $transport_status ? encode_json($transport_status) : undef;
|
||||
|
|
@ -533,8 +603,7 @@ my $broadcast_version_info_done = 0;
|
|||
my sub broadcast_version_info : prototype() {
|
||||
if (!$broadcast_version_info_done) {
|
||||
PVE::Cluster::broadcast_node_kv(
|
||||
'version-info',
|
||||
encode_json(PVE::pvecfg::version_info()),
|
||||
'version-info', encode_json(PVE::pvecfg::version_info()),
|
||||
);
|
||||
$broadcast_version_info_done = 1;
|
||||
}
|
||||
|
|
@ -556,69 +625,47 @@ sub update_status {
|
|||
|
||||
my $status_cfg = PVE::Cluster::cfs_read_file('status.cfg');
|
||||
|
||||
eval {
|
||||
update_node_status($status_cfg, $pull_txn);
|
||||
};
|
||||
eval { update_node_status($status_cfg, $pull_txn); };
|
||||
$err = $@;
|
||||
syslog('err', "node status update error: $err") if $err;
|
||||
|
||||
eval {
|
||||
update_qemu_status($status_cfg, $pull_txn);
|
||||
};
|
||||
eval { update_qemu_status($status_cfg, $pull_txn); };
|
||||
$err = $@;
|
||||
syslog('err', "qemu status update error: $err") if $err;
|
||||
|
||||
eval {
|
||||
update_lxc_status($status_cfg, $pull_txn);
|
||||
};
|
||||
eval { update_lxc_status($status_cfg, $pull_txn); };
|
||||
$err = $@;
|
||||
syslog('err', "lxc status update error: $err") if $err;
|
||||
|
||||
eval {
|
||||
rebalance_lxc_containers();
|
||||
};
|
||||
eval { rebalance_lxc_containers(); };
|
||||
$err = $@;
|
||||
syslog('err', "lxc cpuset rebalance error: $err") if $err;
|
||||
|
||||
eval {
|
||||
update_storage_status($status_cfg, $pull_txn);
|
||||
};
|
||||
eval { update_storage_status($status_cfg, $pull_txn); };
|
||||
$err = $@;
|
||||
syslog('err', "storage status update error: $err") if $err;
|
||||
|
||||
eval {
|
||||
remove_stale_lxc_consoles();
|
||||
};
|
||||
eval { remove_stale_lxc_consoles(); };
|
||||
$err = $@;
|
||||
syslog('err', "lxc console cleanup error: $err") if $err;
|
||||
|
||||
eval {
|
||||
rotate_authkeys();
|
||||
};
|
||||
eval { rotate_authkeys(); };
|
||||
$err = $@;
|
||||
syslog('err', "authkey rotation error: $err") if $err;
|
||||
|
||||
eval {
|
||||
update_ceph_metadata();
|
||||
};
|
||||
eval { update_ceph_metadata(); };
|
||||
$err = $@;
|
||||
syslog('err', "ceph metadata update error: $err") if $err;
|
||||
|
||||
eval {
|
||||
update_sdn_status();
|
||||
};
|
||||
eval { update_sdn_status(); };
|
||||
$err = $@;
|
||||
syslog('err', "sdn status update error: $err") if $err;
|
||||
|
||||
eval {
|
||||
broadcast_version_info();
|
||||
};
|
||||
eval { broadcast_version_info(); };
|
||||
$err = $@;
|
||||
syslog('err', "version info update error: $err") if $err;
|
||||
|
||||
eval {
|
||||
PVE::PullMetric::transaction_finish($pull_txn);
|
||||
};
|
||||
eval { PVE::PullMetric::transaction_finish($pull_txn); };
|
||||
$err = $@;
|
||||
syslog('err', "could not populate metric data cache: $err") if $err;
|
||||
}
|
||||
|
|
@ -640,7 +687,7 @@ sub run {
|
|||
$next_update = time() + $updatetime;
|
||||
|
||||
if ($cycle) {
|
||||
my ($ccsec, $cusec) = gettimeofday ();
|
||||
my ($ccsec, $cusec) = gettimeofday();
|
||||
eval {
|
||||
# syslog('info', "start status update");
|
||||
PVE::Cluster::cfs_update();
|
||||
|
|
@ -652,8 +699,8 @@ sub run {
|
|||
syslog('err', "status update error: $err");
|
||||
}
|
||||
|
||||
my ($ccsec_end, $cusec_end) = gettimeofday ();
|
||||
my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
|
||||
my ($ccsec_end, $cusec_end) = gettimeofday();
|
||||
my $cptime = ($ccsec_end - $ccsec) + ($cusec_end - $cusec) / 1000000;
|
||||
|
||||
syslog('info', sprintf("status update time (%.3f seconds)", $cptime))
|
||||
if ($cptime > 5);
|
||||
|
|
@ -669,16 +716,25 @@ sub run {
|
|||
} else {
|
||||
my $diff = $resident_kb - $initial_memory_usage;
|
||||
if ($diff > 15 * 1024) {
|
||||
syslog ('info', "restarting server after $cycle cycles to " .
|
||||
"reduce memory usage (free $resident_kb ($diff) KB)");
|
||||
syslog(
|
||||
'info',
|
||||
"restarting server after $cycle cycles to "
|
||||
. "reduce memory usage (free $resident_kb ($diff) KB)",
|
||||
);
|
||||
$self->restart_daemon();
|
||||
}
|
||||
}
|
||||
|
||||
my $wcount = 0;
|
||||
while ((time() < $next_update) &&
|
||||
($wcount < $updatetime) && # protect against time wrap
|
||||
!$restart_request) { $wcount++; sleep (1); };
|
||||
while (
|
||||
(time() < $next_update)
|
||||
&& ($wcount < $updatetime)
|
||||
&& # protect against time wrap
|
||||
!$restart_request
|
||||
) {
|
||||
$wcount++;
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
$self->restart_daemon() if $restart_request;
|
||||
}
|
||||
|
|
@ -690,10 +746,10 @@ $daemon->register_stop_command();
|
|||
$daemon->register_status_command();
|
||||
|
||||
our $cmddef = {
|
||||
start => [ __PACKAGE__, 'start', []],
|
||||
restart => [ __PACKAGE__, 'restart', []],
|
||||
stop => [ __PACKAGE__, 'stop', []],
|
||||
status => [ __PACKAGE__, 'status', [], undef, sub { print shift . "\n";} ],
|
||||
start => [__PACKAGE__, 'start', []],
|
||||
restart => [__PACKAGE__, 'restart', []],
|
||||
stop => [__PACKAGE__, 'stop', []],
|
||||
status => [__PACKAGE__, 'status', [], undef, sub { print shift . "\n"; }],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ my %daemon_options = (
|
|||
setuid => 'www-data',
|
||||
setgid => 'www-data',
|
||||
pidfile => '/var/run/pveproxy/spiceproxy.pid',
|
||||
);
|
||||
);
|
||||
|
||||
my $daemon = __PACKAGE__->new('spiceproxy', $cmdline, %daemon_options);
|
||||
|
||||
|
|
@ -36,8 +36,8 @@ sub init {
|
|||
|
||||
my $accept_lock_fn = "/var/lock/spiceproxy.lck";
|
||||
|
||||
my $lockfh = IO::File->new(">>${accept_lock_fn}") ||
|
||||
die "unable to open lock file '${accept_lock_fn}' - $!\n";
|
||||
my $lockfh = IO::File->new(">>${accept_lock_fn}")
|
||||
|| die "unable to open lock file '${accept_lock_fn}' - $!\n";
|
||||
|
||||
my $listen_ip = $proxyconf->{LISTEN_IP};
|
||||
my $socket = $self->create_reusable_socket(3128, $listen_ip);
|
||||
|
|
@ -61,7 +61,7 @@ sub init {
|
|||
sub run {
|
||||
my ($self) = @_;
|
||||
|
||||
my $server = PVE::HTTPServer->new(%{$self->{server_config}});
|
||||
my $server = PVE::HTTPServer->new(%{ $self->{server_config} });
|
||||
$server->run();
|
||||
}
|
||||
|
||||
|
|
@ -71,10 +71,10 @@ $daemon->register_stop_command();
|
|||
$daemon->register_status_command();
|
||||
|
||||
our $cmddef = {
|
||||
start => [ __PACKAGE__, 'start', []],
|
||||
restart => [ __PACKAGE__, 'restart', []],
|
||||
stop => [ __PACKAGE__, 'stop', []],
|
||||
status => [ __PACKAGE__, 'status', [], undef, sub { print shift . "\n";} ],
|
||||
start => [__PACKAGE__, 'start', []],
|
||||
restart => [__PACKAGE__, 'restart', []],
|
||||
stop => [__PACKAGE__, 'stop', []],
|
||||
status => [__PACKAGE__, 'status', [], undef, sub { print shift . "\n"; }],
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ sub type {
|
|||
sub properties {
|
||||
return {
|
||||
path => {
|
||||
type => 'string', format => 'graphite-path',
|
||||
type => 'string',
|
||||
format => 'graphite-path',
|
||||
description => "root graphite path (ex: proxmox.mycluster.mykey)",
|
||||
},
|
||||
timeout => {
|
||||
|
|
@ -35,7 +36,7 @@ sub properties {
|
|||
description => "graphite TCP socket timeout (default=1)",
|
||||
minimum => 0,
|
||||
default => 1,
|
||||
optional => 1
|
||||
optional => 1,
|
||||
},
|
||||
proto => {
|
||||
type => 'string',
|
||||
|
|
@ -110,7 +111,7 @@ sub _connect {
|
|||
|
||||
if ($proto eq 'tcp') {
|
||||
# seconds and µs
|
||||
my $timeout_struct = pack( 'l!l!', $timeout, 0);
|
||||
my $timeout_struct = pack('l!l!', $timeout, 0);
|
||||
setsockopt($carbon_socket, SOL_SOCKET, SO_SNDTIMEO, $timeout_struct);
|
||||
setsockopt($carbon_socket, SOL_SOCKET, SO_RCVTIMEO, $timeout_struct);
|
||||
}
|
||||
|
|
@ -156,6 +157,7 @@ sub assemble {
|
|||
}
|
||||
|
||||
PVE::JSONSchema::register_format('graphite-path', \&pve_verify_graphite_path);
|
||||
|
||||
sub pve_verify_graphite_path {
|
||||
my ($path, $noerr) = @_;
|
||||
|
||||
|
|
@ -169,5 +171,4 @@ sub pve_verify_graphite_path {
|
|||
return $path;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ sub type {
|
|||
sub properties {
|
||||
return {
|
||||
organization => {
|
||||
description => "The InfluxDB organization. Only necessary when using the http v2 api."
|
||||
." Has no meaning when using v2 compatibility api.",
|
||||
description =>
|
||||
"The InfluxDB organization. Only necessary when using the http v2 api."
|
||||
. " Has no meaning when using v2 compatibility api.",
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
|
|
@ -33,14 +34,15 @@ sub properties {
|
|||
optional => 1,
|
||||
},
|
||||
token => {
|
||||
description => "The InfluxDB access token. Only necessary when using the http v2 api."
|
||||
." If the v2 compatibility api is used, use 'user:password' instead.",
|
||||
description =>
|
||||
"The InfluxDB access token. Only necessary when using the http v2 api."
|
||||
. " If the v2 compatibility api is used, use 'user:password' instead.",
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
'api-path-prefix' => {
|
||||
description => "An API path prefix inserted between '<host>:<port>/' and '/api2/'."
|
||||
." Can be useful if the InfluxDB service runs behind a reverse proxy.",
|
||||
. " Can be useful if the InfluxDB service runs behind a reverse proxy.",
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
|
|
@ -51,7 +53,8 @@ sub properties {
|
|||
optional => 1,
|
||||
},
|
||||
'max-body-size' => {
|
||||
description => "InfluxDB max-body-size in bytes. Requests are batched up to this size.",
|
||||
description =>
|
||||
"InfluxDB max-body-size in bytes. Requests are batched up to this size.",
|
||||
type => 'integer',
|
||||
minimum => 1,
|
||||
default => 25_000_000,
|
||||
|
|
@ -64,17 +67,18 @@ sub properties {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
sub options {
|
||||
return {
|
||||
server => {},
|
||||
port => {},
|
||||
mtu => { optional => 1 },
|
||||
disable => { optional => 1 },
|
||||
organization => { optional => 1},
|
||||
bucket => { optional => 1},
|
||||
token => { optional => 1},
|
||||
influxdbproto => { optional => 1},
|
||||
timeout => { optional => 1},
|
||||
organization => { optional => 1 },
|
||||
bucket => { optional => 1 },
|
||||
token => { optional => 1 },
|
||||
influxdbproto => { optional => 1 },
|
||||
timeout => { optional => 1 },
|
||||
'max-body-size' => { optional => 1 },
|
||||
'api-path-prefix' => { optional => 1 },
|
||||
'verify-certificate' => { optional => 1 },
|
||||
|
|
@ -110,7 +114,7 @@ sub update_qemu_status {
|
|||
$ctime *= 1000000000;
|
||||
|
||||
my $object = "object=qemu,vmid=$vmid,nodename=$nodename";
|
||||
if($data->{name} && $data->{name} ne '') {
|
||||
if ($data->{name} && $data->{name} ne '') {
|
||||
$object .= ",host=$data->{name}";
|
||||
}
|
||||
$object =~ s/\s/\\ /g;
|
||||
|
|
@ -125,7 +129,7 @@ sub update_lxc_status {
|
|||
$ctime *= 1000000000;
|
||||
|
||||
my $object = "object=lxc,vmid=$vmid,nodename=$nodename";
|
||||
if($data->{name} && $data->{name} ne '') {
|
||||
if ($data->{name} && $data->{name} ne '') {
|
||||
$object .= ",host=$data->{name}";
|
||||
}
|
||||
$object =~ s/\s/\\ /g;
|
||||
|
|
@ -140,7 +144,7 @@ sub update_storage_status {
|
|||
$ctime *= 1000000000;
|
||||
|
||||
my $object = "object=storages,nodename=$nodename,host=$storeid";
|
||||
if($data->{type} && $data->{type} ne '') {
|
||||
if ($data->{type} && $data->{type} ne '') {
|
||||
$object .= ",type=$data->{type}";
|
||||
}
|
||||
$object =~ s/\s/\\ /g;
|
||||
|
|
@ -232,7 +236,7 @@ sub _connect {
|
|||
|
||||
my $req = HTTP::Request->new(POST => $url);
|
||||
if (defined($token)) {
|
||||
$req->header( "Authorization", "Token $token");
|
||||
$req->header("Authorization", "Token $token");
|
||||
}
|
||||
|
||||
return $req;
|
||||
|
|
@ -277,7 +281,7 @@ sub build_influxdb_payload {
|
|||
my ($class, $txn, $data, $ctime, $tags, $excluded, $measurement, $instance) = @_;
|
||||
|
||||
# 'abc' and '123' are both valid hostnames/tags, that confuses influx's type detection
|
||||
my $to_quote = { name => 1, tags => 1, };
|
||||
my $to_quote = { name => 1, tags => 1 };
|
||||
|
||||
my @values = ();
|
||||
|
||||
|
|
@ -297,8 +301,10 @@ sub build_influxdb_payload {
|
|||
|
||||
if (!defined($measurement)) {
|
||||
build_influxdb_payload($class, $txn, $value, $ctime, $tags, $excluded, $key);
|
||||
} elsif(!defined($instance)) {
|
||||
build_influxdb_payload($class, $txn, $value, $ctime, $tags, $excluded, $measurement, $key);
|
||||
} elsif (!defined($instance)) {
|
||||
build_influxdb_payload(
|
||||
$class, $txn, $value, $ctime, $tags, $excluded, $measurement, $key,
|
||||
);
|
||||
} else {
|
||||
push @values, get_recursive_values($value);
|
||||
}
|
||||
|
|
@ -321,7 +327,7 @@ sub get_recursive_values {
|
|||
|
||||
foreach my $key (keys %$hash) {
|
||||
my $value = $hash->{$key};
|
||||
if(ref($value) eq 'HASH') {
|
||||
if (ref($value) eq 'HASH') {
|
||||
push(@values, get_recursive_values($value));
|
||||
} elsif (!ref($value) && $value ne '') {
|
||||
if (defined(my $v = prepare_value($value))) {
|
||||
|
|
@ -429,5 +435,4 @@ sub on_delete_hook {
|
|||
return undef;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -9,20 +9,23 @@ use PVE::SectionConfig;
|
|||
|
||||
use base qw(PVE::SectionConfig);
|
||||
|
||||
cfs_register_file('status.cfg',
|
||||
cfs_register_file(
|
||||
'status.cfg',
|
||||
sub { __PACKAGE__->parse_config(@_); },
|
||||
sub { __PACKAGE__->write_config(@_); }
|
||||
sub { __PACKAGE__->write_config(@_); },
|
||||
);
|
||||
|
||||
my $defaultData = {
|
||||
propertyList => {
|
||||
id => {
|
||||
description => "The ID of the entry.",
|
||||
type => 'string', format => 'pve-configid',
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
type => {
|
||||
description => "Plugin type.",
|
||||
type => 'string', format => 'pve-configid',
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
disable => {
|
||||
description => "Flag to disable the plugin.",
|
||||
|
|
@ -30,21 +33,22 @@ my $defaultData = {
|
|||
optional => 1,
|
||||
},
|
||||
server => {
|
||||
type => 'string', format => 'address',
|
||||
type => 'string',
|
||||
format => 'address',
|
||||
description => "server dns name or IP address",
|
||||
},
|
||||
port => {
|
||||
type => 'integer',
|
||||
description => "server network port",
|
||||
minimum => 1,
|
||||
maximum => 64*1024,
|
||||
maximum => 64 * 1024,
|
||||
},
|
||||
mtu => {
|
||||
type => 'integer',
|
||||
description => "MTU for metrics transmission over UDP",
|
||||
default => 1500,
|
||||
minimum => 512,
|
||||
maximum => 64*1024,
|
||||
maximum => 64 * 1024,
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
478
PVE/VZDump.pm
478
PVE/VZDump.pm
File diff suppressed because it is too large
Load diff
|
|
@ -10,7 +10,7 @@ my $pkglist = PVE::APLInfo::load_data();
|
|||
|
||||
my $err = 0;
|
||||
|
||||
foreach my $k (keys %{$pkglist->{'all'}}) {
|
||||
foreach my $k (keys %{ $pkglist->{'all'} }) {
|
||||
next if $k eq 'pve-web-news';
|
||||
my $res = $pkglist->{all}->{$k};
|
||||
|
||||
|
|
@ -24,5 +24,5 @@ foreach my $k (keys %{$pkglist->{'all'}}) {
|
|||
}
|
||||
}
|
||||
|
||||
$err ? exit (-11) : exit (0);
|
||||
$err ? exit(-11) : exit(0);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ use PVE::Tools;
|
|||
my $country = {};
|
||||
|
||||
my $line;
|
||||
open (TMP, "</usr/share/zoneinfo/iso3166.tab");
|
||||
while (defined ($line = <TMP>)) {
|
||||
open(TMP, "</usr/share/zoneinfo/iso3166.tab");
|
||||
while (defined($line = <TMP>)) {
|
||||
if ($line =~ m/^([A-Z][A-Z])\s+(.*\S)\s*$/) {
|
||||
$country->{lc($1)} = $2;
|
||||
$country->{ lc($1) } = $2;
|
||||
}
|
||||
}
|
||||
close (TMP);
|
||||
close(TMP);
|
||||
|
||||
# we need mappings for X11, console, and kvm vnc
|
||||
|
||||
|
|
@ -27,15 +27,15 @@ close (TMP);
|
|||
my $keymaps = PVE::Tools::kvmkeymaps();
|
||||
|
||||
foreach my $km (sort keys %$keymaps) {
|
||||
my ($desc, $kvm, $console, $x11, $x11var) = @{$keymaps->{$km}};
|
||||
my ($desc, $kvm, $console, $x11, $x11var) = @{ $keymaps->{$km} };
|
||||
|
||||
if ($km =~m/^([a-z][a-z])-([a-z][a-z])$/i) {
|
||||
defined ($country->{$2}) || die "undefined country code '$2'";
|
||||
if ($km =~ m/^([a-z][a-z])-([a-z][a-z])$/i) {
|
||||
defined($country->{$2}) || die "undefined country code '$2'";
|
||||
} else {
|
||||
defined ($country->{$km}) || die "undefined country code '$km'";
|
||||
defined($country->{$km}) || die "undefined country code '$km'";
|
||||
}
|
||||
|
||||
$x11var = '' if !defined ($x11var);
|
||||
$x11var = '' if !defined($x11var);
|
||||
print "map:$km:$desc:$kvm:$console:$x11:$x11var:\n";
|
||||
}
|
||||
|
||||
|
|
@ -69,10 +69,9 @@ my $defmap = {
|
|||
'li' => 'de-ch',
|
||||
};
|
||||
|
||||
|
||||
my $mirrors = PVE::Tools::debmirrors();
|
||||
foreach my $cc (keys %$mirrors) {
|
||||
die "undefined country code '$cc'" if !defined ($country->{$cc});
|
||||
die "undefined country code '$cc'" if !defined($country->{$cc});
|
||||
}
|
||||
|
||||
foreach my $cc (sort keys %$country) {
|
||||
|
|
|
|||
|
|
@ -17,44 +17,42 @@ my $tree = {
|
|||
{
|
||||
id => -3,
|
||||
name => 'pveA',
|
||||
children => [ 0,1,2,3 ],
|
||||
children => [0, 1, 2, 3],
|
||||
type => 'host',
|
||||
},
|
||||
{
|
||||
id => -5,
|
||||
name => 'pveB',
|
||||
children => [ 4,5,6,7 ],
|
||||
children => [4, 5, 6, 7],
|
||||
type => 'host',
|
||||
},
|
||||
{
|
||||
id => -7,
|
||||
name => 'pveC',
|
||||
children => [ 8,9,10,11 ],
|
||||
children => [8, 9, 10, 11],
|
||||
type => 'host',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
# Check if all the grep and casts are correct
|
||||
my @belong_to_B = ( 4,5 );
|
||||
my @not_belong_to_B = ( -1,1,10,15 );
|
||||
my @belong_to_B = (4, 5);
|
||||
my @not_belong_to_B = (-1, 1, 10, 15);
|
||||
foreach (@belong_to_B) {
|
||||
is (
|
||||
is(
|
||||
PVE::API2::Ceph::OSD::osd_belongs_to_node($tree, 'pveB', $_),
|
||||
1,
|
||||
"OSD $_ belongs to node pveB",
|
||||
);
|
||||
}
|
||||
foreach (@not_belong_to_B) {
|
||||
is (
|
||||
is(
|
||||
PVE::API2::Ceph::OSD::osd_belongs_to_node($tree, 'pveB', $_),
|
||||
0,
|
||||
"OSD $_ does not belong to node pveB",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
my $double_nodes_tree = {
|
||||
nodes => [
|
||||
{
|
||||
|
|
@ -64,17 +62,16 @@ my $double_nodes_tree = {
|
|||
{
|
||||
name => 'pveA',
|
||||
type => 'host',
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
};
|
||||
eval { PVE::API2::Ceph::OSD::osd_belongs_to_node($double_nodes_tree, 'pveA') };
|
||||
like($@, qr/duplicate host name found/, "Die if node occurs too often");
|
||||
|
||||
is (
|
||||
is(
|
||||
PVE::API2::Ceph::OSD::osd_belongs_to_node(undef),
|
||||
0,
|
||||
"Early-return false if there's no/empty node tree",
|
||||
);
|
||||
|
||||
|
||||
done_testing(@belong_to_B + @not_belong_to_B + 2);
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ my $mocked_storage_config = {
|
|||
shared => 0,
|
||||
content => {
|
||||
'images' => 1,
|
||||
'rootdir' => 1
|
||||
'rootdir' => 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -169,8 +169,8 @@ sub register_mocked_volid {
|
|||
my ($volid, $snapname) = @_;
|
||||
|
||||
my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
|
||||
my $scfg = $mocked_storage_config->{ids}->{$storeid} ||
|
||||
die "no such storage '$storeid'\n";
|
||||
my $scfg = $mocked_storage_config->{ids}->{$storeid}
|
||||
|| die "no such storage '$storeid'\n";
|
||||
|
||||
my $d = $mocked_storage_content->{$storeid}->{$volname} //= {};
|
||||
|
||||
|
|
@ -278,13 +278,13 @@ sub setup {
|
|||
$pve_cluster_module->mock(
|
||||
get_vmlist => sub { return $mocked_vmlist->(); },
|
||||
get_members => $mocked_get_members,
|
||||
cfs_update => sub {},
|
||||
cfs_update => sub { },
|
||||
cfs_lock_file => $mocked_cfs_lock_file,
|
||||
cfs_write_file => $mocked_cfs_write_file,
|
||||
cfs_read_file => $mocked_cfs_read_file,
|
||||
);
|
||||
$pve_inotify_module->mock('nodename' => sub { return $mocked_nodename; });
|
||||
};
|
||||
}
|
||||
|
||||
# code to generate/conpare test logs
|
||||
|
||||
|
|
@ -306,8 +306,8 @@ sub openlog {
|
|||
|
||||
die "log already open" if defined($logname);
|
||||
|
||||
open (my $fh, ">", "$filename.tmp") ||
|
||||
die "unable to open log - $!";
|
||||
open(my $fh, ">", "$filename.tmp")
|
||||
|| die "unable to open log - $!";
|
||||
|
||||
$logname = $filename;
|
||||
$logfh = $fh;
|
||||
|
|
@ -416,5 +416,4 @@ sub track_jobs {
|
|||
$status = $new;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ my $test_status3 = {
|
|||
101 => {
|
||||
maxmem => GB(2),
|
||||
shares => 1000,
|
||||
balloon => GB(1)+MB(7),
|
||||
balloon => GB(1) + MB(7),
|
||||
balloon_min => GB(1),
|
||||
freemem => MB(0),
|
||||
},
|
||||
|
|
@ -88,7 +88,7 @@ my $status = absim($test_status3, MB(593), 101 => MB(1300), 102 => MB(1300));
|
|||
absim($status, -MB(200), 101 => MB(1200), 102 => MB(1200));
|
||||
absim($status, -MB(400), 101 => MB(1200), 102 => GB(1));
|
||||
absim($status, -MB(593), 101 => MB(1007), 102 => GB(1));
|
||||
exit (0);
|
||||
exit(0);
|
||||
|
||||
sub abapply {
|
||||
my ($vmstatus, $res, $sum) = @_;
|
||||
|
|
@ -112,6 +112,7 @@ sub abapply {
|
|||
}
|
||||
|
||||
my $tcount = 0;
|
||||
|
||||
sub absim {
|
||||
my ($vmstatus, $goal, %expect) = @_;
|
||||
|
||||
|
|
@ -144,7 +145,7 @@ sub abcheck {
|
|||
|
||||
foreach my $vmid (keys %expect) {
|
||||
my $ev = $expect{$vmid};
|
||||
if (defined ($res->{$vmid})) {
|
||||
if (defined($res->{$vmid})) {
|
||||
die "T$tcount: wrong value for VM $vmid ($ev != $res->{$vmid})\n"
|
||||
if $ev != $res->{$vmid};
|
||||
} else {
|
||||
|
|
@ -154,8 +155,8 @@ sub abcheck {
|
|||
|
||||
foreach my $vmid (keys %$res) {
|
||||
die "T$tcount: got unexpected result for $vmid\n"
|
||||
if (defined($res->{$vmid}) &&
|
||||
!defined($expect{$vmid}));
|
||||
if (defined($res->{$vmid})
|
||||
&& !defined($expect{$vmid}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,9 +178,10 @@ sub abtest {
|
|||
|
||||
sub MB {
|
||||
my $mb = shift;
|
||||
return $mb*1000*1000;
|
||||
};
|
||||
return $mb * 1000 * 1000;
|
||||
}
|
||||
|
||||
sub GB {
|
||||
my $gb = shift;
|
||||
return $gb*1000*1000*1000;
|
||||
};
|
||||
return $gb * 1000 * 1000 * 1000;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,12 @@ sub test_rpc {
|
|||
|
||||
for (my $i = 0; $i < $qcount; $i++) {
|
||||
eval {
|
||||
my ($page, $response, %reply_headers)
|
||||
= get_https($host, 8006, '/api2/json',
|
||||
make_headers(Cookie => "PVEAuthCookie=$ticket"));
|
||||
my ($page, $response, %reply_headers) = get_https(
|
||||
$host,
|
||||
8006,
|
||||
'/api2/json',
|
||||
make_headers(Cookie => "PVEAuthCookie=$ticket"),
|
||||
);
|
||||
die "$response\n" if $response !~ m/200 OK/;
|
||||
};
|
||||
|
||||
|
|
@ -48,20 +51,20 @@ sub run_tests {
|
|||
my $starttime = [gettimeofday];
|
||||
|
||||
for (my $i = 0; $i < $wcount; $i++) {
|
||||
if (my $pid = fork ()) {
|
||||
if (my $pid = fork()) {
|
||||
$workers->{$pid} = 1;
|
||||
} else {
|
||||
test_rpc ($host);
|
||||
exit (0);
|
||||
test_rpc($host);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
# wait for children
|
||||
1 while (wait > 0);
|
||||
|
||||
my $elapsed = int(tv_interval ($starttime) * 1000);
|
||||
my $elapsed = int(tv_interval($starttime) * 1000);
|
||||
|
||||
my $tpq = $elapsed / ($wcount*$qcount);
|
||||
my $tpq = $elapsed / ($wcount * $qcount);
|
||||
|
||||
print "$host: $tpq ms per query\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ ReplicationTestEnv::setup();
|
|||
ok(PVE::INotify::nodename() eq 'node1');
|
||||
|
||||
my $list = PVE::Cluster::get_vmlist();
|
||||
is_deeply($list, { ids => {900 => { node => 'node1', type => 'qemu', version => 1}}});
|
||||
is_deeply($list, { ids => { 900 => { node => 'node1', type => 'qemu', version => 1 } } });
|
||||
my $cfg = PVE::ReplicationConfig->new();
|
||||
is_deeply($cfg, { ids => { job_900_to_node1 => $testjob }});
|
||||
is_deeply($cfg, { ids => { job_900_to_node1 => $testjob } });
|
||||
|
||||
exit(0);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ my $schedule = [];
|
|||
my $mocked_replicate = sub {
|
||||
my ($guest_class, $jobcfg, $state, $start_time, $logfunc) = @_;
|
||||
|
||||
push @$schedule, {
|
||||
push @$schedule,
|
||||
{
|
||||
id => $jobcfg->{id},
|
||||
guest => $jobcfg->{guest},
|
||||
vmtype => $jobcfg->{vmtype},
|
||||
|
|
@ -60,7 +61,7 @@ $ReplicationTestEnv::mocked_vm_configs = {
|
|||
ReplicationTestEnv::setup();
|
||||
|
||||
for (my $i = 0; $i < 61; $i++) {
|
||||
PVE::API2::Replication::run_jobs($i*60);
|
||||
PVE::API2::Replication::run_jobs($i * 60);
|
||||
}
|
||||
|
||||
my $exptected_schedule = [
|
||||
|
|
@ -70,7 +71,7 @@ my $exptected_schedule = [
|
|||
vmtype => 'qemu',
|
||||
guest_class => 'PVE::QemuConfig',
|
||||
id => '900-1_to_node2',
|
||||
guest => 900
|
||||
guest => 900,
|
||||
},
|
||||
{
|
||||
last_sync => 900,
|
||||
|
|
@ -86,7 +87,7 @@ my $exptected_schedule = [
|
|||
vmtype => 'qemu',
|
||||
guest_class => 'PVE::QemuConfig',
|
||||
id => '900-1_to_node2',
|
||||
guest => 900
|
||||
guest => 900,
|
||||
},
|
||||
{
|
||||
last_sync => 2700,
|
||||
|
|
@ -94,8 +95,8 @@ my $exptected_schedule = [
|
|||
vmtype => 'qemu',
|
||||
guest_class => 'PVE::QemuConfig',
|
||||
id => '900-1_to_node2',
|
||||
guest => 900
|
||||
}
|
||||
guest => 900,
|
||||
},
|
||||
];
|
||||
|
||||
is_deeply($schedule, $exptected_schedule);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ use PVE::Tools;
|
|||
$ReplicationTestEnv::mocked_nodename = 'node1';
|
||||
|
||||
my $pve_replication_module = Test::MockModule->new('PVE::Replication');
|
||||
$pve_replication_module->mock(
|
||||
replicate => sub { die "faked replication error\n"; });
|
||||
$pve_replication_module->mock(replicate => sub { die "faked replication error\n"; });
|
||||
|
||||
my $testjob = {
|
||||
'type' => 'local',
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ use PVE::Storage;
|
|||
my $replicated_volume_status = {};
|
||||
|
||||
my $mocked_remote_prepare_local_job = sub {
|
||||
my ($ssh_info, $jobid, $vmid, $volumes, $storeid_list, $last_sync, $parent_snapname, $force) = @_;
|
||||
my ($ssh_info, $jobid, $vmid, $volumes, $storeid_list, $last_sync, $parent_snapname, $force)
|
||||
= @_;
|
||||
|
||||
my $target = $ssh_info->{node};
|
||||
|
||||
|
|
@ -38,9 +39,10 @@ my $mocked_remote_prepare_local_job = sub {
|
|||
|
||||
return $last_snapshots if !defined($replicated_volume_status->{$target});
|
||||
|
||||
my $last_sync_snapname = PVE::ReplicationState::replication_snapshot_name($jobid, $last_sync);
|
||||
my $last_sync_snapname =
|
||||
PVE::ReplicationState::replication_snapshot_name($jobid, $last_sync);
|
||||
|
||||
foreach my $volid (keys %{$replicated_volume_status->{$target}}) {
|
||||
foreach my $volid (keys %{ $replicated_volume_status->{$target} }) {
|
||||
if (!grep { $_ eq $volid } @$volumes) {
|
||||
delete $replicated_volume_status->{$target}->{$volid};
|
||||
next;
|
||||
|
|
@ -80,7 +82,8 @@ my $pve_replication_module = Test::MockModule->new('PVE::Replication');
|
|||
$pve_replication_module->mock(
|
||||
remote_prepare_local_job => $mocked_remote_prepare_local_job,
|
||||
remote_finalize_local_job => $mocked_remote_finalize_local_job,
|
||||
replicate_volume => $mocked_replicate_volume);
|
||||
replicate_volume => $mocked_replicate_volume,
|
||||
);
|
||||
|
||||
my $testjob = {
|
||||
'type' => 'local',
|
||||
|
|
@ -142,8 +145,6 @@ for (my $i = 0; $i < 15; $i++) {
|
|||
$ctime += 60;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ReplicationTestEnv::commit_log();
|
||||
|
||||
exit(0);
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ my $mocked_delete_job = sub {
|
|||
};
|
||||
|
||||
my $pve_replication_config_module = Test::MockModule->new('PVE::ReplicationConfig');
|
||||
$pve_replication_config_module->mock(
|
||||
delete_job => $mocked_delete_job);
|
||||
$pve_replication_config_module->mock(delete_job => $mocked_delete_job);
|
||||
|
||||
my $testjob = {
|
||||
'type' => 'local',
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ my $vmlist = {
|
|||
'type' => 'lxc',
|
||||
'node' => 'node2',
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
my $pools = {
|
||||
|
|
@ -57,134 +57,163 @@ my $pve_cluster_module = Test::MockModule->new('PVE::Cluster');
|
|||
$pve_cluster_module->mock(
|
||||
get_vmlist => sub {
|
||||
return $vmlist;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
my $pve_inotify = Test::MockModule->new('PVE::INotify');
|
||||
$pve_inotify->mock(
|
||||
nodename => sub {
|
||||
return 'node1';
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
my $pve_api2tools = Test::MockModule->new('PVE::API2Tools');
|
||||
$pve_api2tools->mock(
|
||||
get_resource_pool_guest_members => sub {
|
||||
return $pools->{testpool};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
my $tests = [];
|
||||
my $addtest = sub {
|
||||
my ($name, $test) = @_;
|
||||
push @$tests, {
|
||||
push @$tests,
|
||||
{
|
||||
name => $name,
|
||||
test => $test,
|
||||
};
|
||||
};
|
||||
|
||||
$addtest->('Test all guests', {
|
||||
$addtest->(
|
||||
'Test all guests',
|
||||
{
|
||||
expected => {
|
||||
node1 => [ 100, 101, 112, 113 ],
|
||||
node2 => [ 200, 201, 212, 213 ],
|
||||
node1 => [100, 101, 112, 113],
|
||||
node2 => [200, 201, 212, 213],
|
||||
},
|
||||
param => {
|
||||
all => 1,
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->('Test all guests with node limit', {
|
||||
$addtest->(
|
||||
'Test all guests with node limit',
|
||||
{
|
||||
expected => {
|
||||
node2 => [ 200, 201, 212, 213 ],
|
||||
node2 => [200, 201, 212, 213],
|
||||
},
|
||||
param => {
|
||||
all => 1,
|
||||
node => 'node2',
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->('Test exclude', {
|
||||
$addtest->(
|
||||
'Test exclude',
|
||||
{
|
||||
expected => {
|
||||
node1 =>[ 101, 112, 113 ],
|
||||
node2 => [ 201, 212, 213 ],
|
||||
node1 => [101, 112, 113],
|
||||
node2 => [201, 212, 213],
|
||||
},
|
||||
param => {
|
||||
all => 1,
|
||||
exclude => '100, 102, 200, 202',
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->('Test exclude with node limit', {
|
||||
$addtest->(
|
||||
'Test exclude with node limit',
|
||||
{
|
||||
expected => {
|
||||
node1 =>[ 101, 112, 113 ],
|
||||
node1 => [101, 112, 113],
|
||||
},
|
||||
param => {
|
||||
all => 1,
|
||||
exclude => '100, 102, 200, 202',
|
||||
node => 'node1',
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->('Test pool members', {
|
||||
$addtest->(
|
||||
'Test pool members',
|
||||
{
|
||||
expected => {
|
||||
node1 =>[ 100, 101 ],
|
||||
node2 => [ 200, 201 ],
|
||||
node1 => [100, 101],
|
||||
node2 => [200, 201],
|
||||
},
|
||||
param => {
|
||||
pool => 'testpool',
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->('Test pool members with node limit', {
|
||||
$addtest->(
|
||||
'Test pool members with node limit',
|
||||
{
|
||||
expected => {
|
||||
node2 => [ 200, 201 ],
|
||||
node2 => [200, 201],
|
||||
},
|
||||
param => {
|
||||
pool => 'testpool',
|
||||
node => 'node2',
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->('Test selected VMIDs', {
|
||||
$addtest->(
|
||||
'Test selected VMIDs',
|
||||
{
|
||||
expected => {
|
||||
node1 =>[ 100 ],
|
||||
node2 => [ 201, 212 ],
|
||||
node1 => [100],
|
||||
node2 => [201, 212],
|
||||
},
|
||||
param => {
|
||||
vmid => '100, 201, 212',
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->('Test selected VMIDs with node limit', {
|
||||
$addtest->(
|
||||
'Test selected VMIDs with node limit',
|
||||
{
|
||||
expected => {
|
||||
node1 =>[ 100 ],
|
||||
node1 => [100],
|
||||
},
|
||||
param => {
|
||||
vmid => '100, 201, 212',
|
||||
node => 'node1',
|
||||
}
|
||||
});
|
||||
|
||||
$addtest->('Test selected VMIDs on other nodes', {
|
||||
expected => {
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->(
|
||||
'Test selected VMIDs on other nodes',
|
||||
{
|
||||
expected => {},
|
||||
param => {
|
||||
vmid => '201, 212',
|
||||
node => 'node1',
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
$addtest->('Test VMID not present in vmlist', {
|
||||
$addtest->(
|
||||
'Test VMID not present in vmlist',
|
||||
{
|
||||
expected => {
|
||||
node1 => [ 100 ],
|
||||
node2 => [ 201, 212 ],
|
||||
'' => [ 7654 ],
|
||||
node1 => [100],
|
||||
node2 => [201, 212],
|
||||
'' => [7654],
|
||||
},
|
||||
param => {
|
||||
vmid => '100, 201, 212, 7654',
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
for my $test (@{$tests}) {
|
||||
my $testname = $test->{name};
|
||||
|
|
|
|||
|
|
@ -180,8 +180,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups vzdump 1',
|
||||
vzdump_param => {
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,' .
|
||||
'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,'
|
||||
. 'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -235,8 +235,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups storage 1',
|
||||
storage_param => {
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,' .
|
||||
'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,'
|
||||
. 'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -253,8 +253,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups storage 2',
|
||||
storage_param => {
|
||||
'prune-backups' => 'keep-last=0,keep-hourly=0,keep-daily=0,' .
|
||||
'keep-weekly=0,keep-monthly=0,keep-yearly=0',
|
||||
'prune-backups' => 'keep-last=0,keep-hourly=0,keep-daily=0,'
|
||||
. 'keep-weekly=0,keep-monthly=0,keep-yearly=0',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -293,8 +293,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups CLI 1',
|
||||
cli_param => {
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,' .
|
||||
'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,'
|
||||
. 'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -311,8 +311,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups CLI 2',
|
||||
cli_param => {
|
||||
'prune-backups' => 'keep-last=0,keep-hourly=0,keep-daily=0,' .
|
||||
'keep-weekly=0,keep-monthly=0,keep-yearly=0',
|
||||
'prune-backups' => 'keep-last=0,keep-hourly=0,keep-daily=0,'
|
||||
. 'keep-weekly=0,keep-monthly=0,keep-yearly=0',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -326,8 +326,8 @@ my @tests = (
|
|||
cli_param => {
|
||||
'prune-backups' => 'foo=bar',
|
||||
},
|
||||
expected => "format error\n" .
|
||||
"foo: property is not defined in schema and the schema does not allow additional properties\n",
|
||||
expected => "format error\n"
|
||||
. "foo: property is not defined in schema and the schema does not allow additional properties\n",
|
||||
},
|
||||
{
|
||||
description => 'both CLI 1',
|
||||
|
|
@ -335,8 +335,8 @@ my @tests = (
|
|||
'prune-backups' => 'keep-hourly=1,keep-monthly=2,keep-yearly=3',
|
||||
maxfiles => 4,
|
||||
},
|
||||
expected => "400 Parameter verification failed.\n" .
|
||||
"prune-backups: option conflicts with option 'maxfiles'\n",
|
||||
expected => "400 Parameter verification failed.\n"
|
||||
. "prune-backups: option conflicts with option 'maxfiles'\n",
|
||||
},
|
||||
{
|
||||
description => 'mixed 1',
|
||||
|
|
@ -499,8 +499,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -511,8 +510,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -523,8 +521,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -535,8 +532,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -567,8 +563,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -579,8 +574,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -591,8 +585,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -603,8 +596,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,17 +4,18 @@
|
|||
# This can also be added as a line in /etc/vzdump.conf
|
||||
# e.g. 'script: /usr/local/bin/vzdump-hook-script.pl'
|
||||
|
||||
|
||||
use strict;
|
||||
|
||||
print "HOOK: " . join (' ', @ARGV) . "\n";
|
||||
print "HOOK: " . join(' ', @ARGV) . "\n";
|
||||
|
||||
my $phase = shift;
|
||||
|
||||
if ($phase eq 'job-init' ||
|
||||
$phase eq 'job-start' ||
|
||||
$phase eq 'job-end' ||
|
||||
$phase eq 'job-abort') {
|
||||
if (
|
||||
$phase eq 'job-init'
|
||||
|| $phase eq 'job-start'
|
||||
|| $phase eq 'job-end'
|
||||
|| $phase eq 'job-abort'
|
||||
) {
|
||||
|
||||
# undef for Proxmox Backup Server storages
|
||||
# undef in phase 'job-init' except when --dumpdir is used directly
|
||||
|
|
@ -38,13 +39,15 @@ if ($phase eq 'job-init' ||
|
|||
|
||||
# do what you want
|
||||
|
||||
} elsif ($phase eq 'backup-start' ||
|
||||
$phase eq 'backup-end' ||
|
||||
$phase eq 'backup-abort' ||
|
||||
$phase eq 'log-end' ||
|
||||
$phase eq 'pre-stop' ||
|
||||
$phase eq 'pre-restart' ||
|
||||
$phase eq 'post-restart') {
|
||||
} elsif (
|
||||
$phase eq 'backup-start'
|
||||
|| $phase eq 'backup-end'
|
||||
|| $phase eq 'backup-abort'
|
||||
|| $phase eq 'log-end'
|
||||
|| $phase eq 'pre-stop'
|
||||
|| $phase eq 'pre-restart'
|
||||
|| $phase eq 'post-restart'
|
||||
) {
|
||||
|
||||
my $mode = shift; # stop/suspend/snapshot
|
||||
|
||||
|
|
@ -69,7 +72,7 @@ if ($phase eq 'job-init' ||
|
|||
|
||||
print "HOOK-ENV: ";
|
||||
for my $var (qw(vmtype dumpdir storeid hostname target logfile)) {
|
||||
print "$var=$ENV{uc($var)};" if defined($ENV{uc($var)});
|
||||
print "$var=$ENV{uc($var)};" if defined($ENV{ uc($var) });
|
||||
}
|
||||
print "\n";
|
||||
|
||||
|
|
@ -91,4 +94,4 @@ if ($phase eq 'job-init' ||
|
|||
|
||||
}
|
||||
|
||||
exit (0);
|
||||
exit(0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue