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:
Thomas Lamprecht 2025-06-17 18:22:29 +02:00
parent a750c44c42
commit 138cae897a
88 changed files with 19180 additions and 17920 deletions

View file

@ -79,7 +79,8 @@ __PACKAGE__->register_method ({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'version',
@ -132,5 +133,6 @@ __PACKAGE__->register_method ({
$res->{$_} = $version_info->{$_} for qw(version release repoid);
return $res;
}});
},
});
1;

View file

@ -43,7 +43,8 @@ __PACKAGE__->register_method ({
return [
{ name => 'certificate' },
];
}});
},
});
my $order_certificate = sub {
my ($acme, $acme_node_config) = @_;
@ -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;
@ -225,7 +224,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker("acmenewcert", undef, $authuser, $realcmd);
}});
},
});
__PACKAGE__->register_method({
name => 'renew_certificate',
@ -261,9 +261,14 @@ __PACKAGE__->register_method ({
raise("No current (custom) certificate found, please order a new certificate!\n")
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);
@ -306,7 +311,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker("acmerenew", undef, $authuser, $realcmd);
}});
},
});
__PACKAGE__->register_method({
name => 'revoke_certificate',
@ -377,6 +383,7 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker("acmerevoke", undef, $authuser, $realcmd);
}});
},
});
1;

View file

@ -45,8 +45,7 @@ __PACKAGE__->register_method ({
description => "ACMEAccount index.",
parameters => {
additionalProperties => 0,
properties => {
},
properties => {},
},
returns => {
type => 'array',
@ -67,7 +66,8 @@ __PACKAGE__->register_method ({
{ name => 'plugins' },
{ name => 'challenge-schema' },
];
}});
},
});
__PACKAGE__->register_method({
name => 'account_index',
@ -78,8 +78,7 @@ __PACKAGE__->register_method ({
protected => 1,
parameters => {
additionalProperties => 0,
properties => {
},
properties => {},
},
returns => {
type => 'array',
@ -94,7 +93,8 @@ __PACKAGE__->register_method ({
my $accounts = PVE::CertHelpers::list_acme_accounts();
return [map { { name => $_ } } @$accounts];
}});
},
});
__PACKAGE__->register_method({
name => 'register_account',
@ -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',
@ -146,14 +149,18 @@ __PACKAGE__->register_method ({
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) = @_;
@ -193,12 +202,14 @@ my $update_account = sub {
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;
@ -215,14 +226,17 @@ my $update_account = sub {
}
}
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 $@;
};
@ -233,7 +247,8 @@ __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,7 +271,8 @@ __PACKAGE__->register_method ({
} else {
return $update_account->($param, 'refresh');
}
}});
},
});
__PACKAGE__->register_method({
name => 'get_account',
@ -298,7 +314,8 @@ __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."})
raise_param_exc({
'name' => "ACME account config file '${account_name}' does not exist." })
if !-e $account_file;
my $acme = PVE::ACME->new($account_file);
@ -311,7 +328,8 @@ __PACKAGE__->register_method ({
$res->{tos} = $acme->{tos};
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'deactivate_account',
@ -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({
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,7 +387,8 @@ __PACKAGE__->register_method ({
my $meta = $acme->get_meta();
return $meta ? $meta->{termsOfService} : undef;
}});
},
});
__PACKAGE__->register_method({
name => 'get_meta',
@ -377,10 +401,13 @@ __PACKAGE__->register_method ({
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,7 +448,8 @@ __PACKAGE__->register_method ({
my $meta = $acme->get_meta();
return $meta;
}});
},
});
__PACKAGE__->register_method({
name => 'get_directories',
@ -450,7 +478,8 @@ __PACKAGE__->register_method ({
my ($param) = @_;
return $acme_directories;
}});
},
});
__PACKAGE__->register_method({
name => 'challengeschema',
@ -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;

View file

@ -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();
@ -89,7 +93,7 @@ __PACKAGE__->register_method ({
}
return $res;
}
},
});
__PACKAGE__->register_method({
@ -115,7 +119,7 @@ __PACKAGE__->register_method({
my $cfg = load_config();
return $modify_cfg_for_api->($cfg, $param->{id});
}
},
});
__PACKAGE__->register_method({
@ -129,7 +133,7 @@ __PACKAGE__->register_method({
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({
@ -166,7 +174,7 @@ __PACKAGE__->register_method({
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({
@ -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 {

View file

@ -75,7 +75,8 @@ __PACKAGE__->register_method({
];
return $res;
}});
},
});
my $get_pkgfile = sub {
my ($veriter) = @_;
@ -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;
};
@ -236,13 +239,15 @@ __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']],
},
@ -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,
},
@ -316,10 +322,10 @@ __PACKAGE__->register_method({
{
label => "Available Version",
id => "available-version",
}
]
},
data => []
],
},
data => [],
};
my $count = 0;
@ -327,10 +333,11 @@ __PACKAGE__->register_method({
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',
@ -422,7 +426,8 @@ __PACKAGE__->register_method({
$output .= "RC: $rc" if $rc != 0;
return $output;
}});
},
});
__PACKAGE__->register_method({
name => 'repositories',
@ -525,7 +530,8 @@ __PACKAGE__->register_method({
},
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',
@ -654,14 +662,18 @@ __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']],
},
@ -707,9 +719,10 @@ __PACKAGE__->register_method({
$param->{path},
int($param->{index}),
$options,
$param->{digest}
$param->{digest},
);
}});
},
});
__PACKAGE__->register_method({
name => 'versions',
@ -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};
@ -853,6 +876,7 @@ __PACKAGE__->register_method({
}
return $pkglist;
}});
},
});
1;

View file

@ -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) = @_;
@ -73,7 +74,7 @@ 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']);
}
@ -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',
@ -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).',
},
},
},
@ -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,7 +748,8 @@ __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},
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;

View file

@ -20,7 +20,6 @@ __PACKAGE__->register_method ({
path => 'qemu/machines',
});
__PACKAGE__->register_method({
name => 'index',
path => '',
@ -49,10 +48,9 @@ __PACKAGE__->register_method ({
];
return $result;
}
},
});
__PACKAGE__->register_method({
name => 'qemu_caps_index',
path => 'qemu',
@ -77,12 +75,11 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $result = [
{ name => 'cpu' },
{ name => 'machines' },
{ name => 'cpu' }, { name => 'machines' },
];
return $result;
}
},
});
1;

View file

@ -112,7 +112,8 @@ __PACKAGE__->register_method ({
];
return $result;
}});
},
});
__PACKAGE__->register_method({
name => 'init',
@ -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,
@ -200,7 +203,10 @@ __PACKAGE__->register_method ({
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,11 +251,13 @@ __PACKAGE__->register_method ({
PVE::Ceph::Tools::get_or_create_admin_keyring();
}
PVE::Ceph::Tools::setup_pve_symlinks();
});
},
);
die $@ if $@;
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'stop',
@ -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,9 +307,9 @@ __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({
name => 'start',
@ -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,7 +361,8 @@ __PACKAGE__->register_method ({
return $rpcenv->fork_worker('srvstart', $param->{service} || 'ceph',
$authuser, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'restart',
@ -402,7 +413,8 @@ __PACKAGE__->register_method ({
return $rpcenv->fork_worker('srvrestart', $param->{service} || 'ceph',
$authuser, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'status',
@ -427,8 +439,8 @@ __PACKAGE__->register_method ({
PVE::Ceph::Tools::check_ceph_inited();
return PVE::Ceph::Tools::ceph_cluster_status();
}});
},
});
__PACKAGE__->register_method({
name => 'crush',
@ -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',
@ -516,9 +530,9 @@ __PACKAGE__->register_method({
t => {
description => "Line text",
type => 'string',
}
}
}
},
},
},
},
code => sub {
my ($param) = @_;
@ -530,12 +544,14 @@ __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({
name => 'rules',
@ -561,7 +577,7 @@ __PACKAGE__->register_method ({
name => {
description => "Name of the CRUSH rule.",
type => "string",
}
},
},
},
links => [{ rel => 'child', href => "{name}" }],
@ -582,7 +598,8 @@ __PACKAGE__->register_method ({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'cmd_safety',
@ -624,7 +641,7 @@ __PACKAGE__->register_method ({
status => {
type => 'string',
optional => 1,
description => 'Status message given by Ceph.'
description => 'Status message given by Ceph.',
},
},
},
@ -678,6 +695,7 @@ __PACKAGE__->register_method ({
$result->{status} = $result->{status_message};
return $result;
}});
},
});
1;

View file

@ -35,13 +35,12 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $result = [
{ name => 'raw' },
{ name => 'db' },
{ name => 'value' },
{ name => 'raw' }, { name => 'db' }, { name => 'value' },
];
return $result;
}});
},
});
__PACKAGE__->register_method({
name => 'raw',
@ -67,7 +66,8 @@ __PACKAGE__->register_method ({
my $path = PVE::Ceph::Tools::get_config('pve_ceph_cfgpath');
return file_get_contents($path);
}});
},
});
__PACKAGE__->register_method({
name => 'db',
@ -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" },
},
},
@ -111,8 +111,8 @@ __PACKAGE__->register_method ({
}
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})*$/;
@ -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 => {
@ -191,4 +191,5 @@ __PACKAGE__->register_method ({
}
return $config;
}});
},
});

View file

@ -70,7 +70,7 @@ __PACKAGE__->register_method ({
];
return $res;
}
},
});
__PACKAGE__->register_method({
@ -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;

View file

@ -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',
@ -83,7 +84,7 @@ __PACKAGE__->register_method ({
}
return PVE::RESTHandler::hash_to_array($mds_hash, 'name');
}
},
});
__PACKAGE__->register_method({
@ -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,7 +180,7 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephcreatemds', "mds.$mds_id", $authuser, $worker);
}
},
});
__PACKAGE__->register_method({
@ -229,7 +231,7 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephdestroymds', "mds.$mds_id", $authuser, $worker);
}
},
});
1;

View file

@ -77,7 +77,7 @@ __PACKAGE__->register_method ({
}
return PVE::RESTHandler::hash_to_array($mgr_hash, 'name');
}
},
});
__PACKAGE__->register_method({
@ -126,7 +126,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephcreatemgr', "mgr.$mgrid", $authuser, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'destroymgr',
@ -168,4 +169,5 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephdestroymgr', "mgr.$mgrid", $authuser, $worker);
}});
},
});

View file

@ -28,8 +28,12 @@ my $find_mon_ips = sub {
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+)$/;
@ -59,14 +63,16 @@ 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
@ -148,10 +154,12 @@ 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;
@ -260,7 +268,8 @@ __PACKAGE__->register_method ({
}
return PVE::RESTHandler::hash_to_array($monhash, 'name');
}});
},
});
__PACKAGE__->register_method({
name => 'createmon',
@ -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);
@ -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"
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,19 +486,21 @@ __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({
name => 'destroymon',
@ -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;

View file

@ -87,7 +87,6 @@ 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({
name => 'index',
path => '',
@ -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});
@ -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)) {
@ -222,14 +221,15 @@ __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({
name => 'createosd',
@ -253,7 +253,8 @@ __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"
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."
@ -271,7 +272,8 @@ __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"
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."
@ -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.',
},
},
@ -323,7 +326,8 @@ __PACKAGE__->register_method ({
# '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}'" })
raise_param_exc(
{ $type => "cannot use 'osds-per-device' parameter with '${type}'" })
if $param->{$type};
}
}
@ -339,8 +343,9 @@ __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";
}
}
@ -376,7 +381,8 @@ __PACKAGE__->register_method ({
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,7 +393,6 @@ __PACKAGE__->register_method ({
}
};
# test disk requirements early
my $devlist = [map { $_->{name} } values %$devs];
my $disklist = PVE::Diskmanage::get_disks($devlist, 1, 1);
@ -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 = ();
@ -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 => {
@ -624,12 +635,12 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $result = [
{ name => 'metadata' },
{ name => 'lv-info' },
{ name => 'metadata' }, { name => 'lv-info' },
];
return $result;
}});
},
});
__PACKAGE__->register_method({
name => 'osddetails',
@ -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 $@;
@ -775,7 +786,7 @@ __PACKAGE__->register_method ({
support_discard => int($metadata->{"${prefix}_${dev}_support_discard"}),
type => $metadata->{"${prefix}_${dev}_type"},
device => $device,
}
},
);
};
@ -784,7 +795,8 @@ __PACKAGE__->register_method ({
$get_data->("wal", "bluefs", "wal") if $metadata->{bluefs_dedicated_wal};
return $data;
}});
},
});
__PACKAGE__->register_method({
name => 'osdvolume',
@ -867,7 +879,8 @@ __PACKAGE__->register_method ({
}
my $lv_data = { map { $_->{type} => $_ } @{ $result->{$osdid} } };
my $volume = $lv_data->{$type} || die "volume type '${type}' not found for OSD ${osdid}\n";
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)
@ -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";
@ -1023,7 +1053,9 @@ __PACKAGE__->register_method ({
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,7 +1108,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'in',
@ -1114,7 +1148,8 @@ __PACKAGE__->register_method ({
$rados->mon_command({ prefix => "osd in", ids => [$osdsection], format => 'plain' });
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'out',
@ -1153,7 +1188,8 @@ __PACKAGE__->register_method ({
$rados->mon_command({ prefix => "osd out", ids => [$osdsection], format => 'plain' });
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'scrub',
@ -1198,6 +1234,7 @@ __PACKAGE__->register_method ({
$rados->mon_command({ prefix => $prefix, who => $osdid });
return undef;
}});
},
});
1;

View file

@ -21,8 +21,7 @@ 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) {
@ -32,12 +31,12 @@ my $get_autoscale_status = sub {
return $data;
};
__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 => {
@ -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};
}
@ -389,7 +386,6 @@ sub ec_parse_and_check {
return $ec;
}
__PACKAGE__->register_method({
name => 'createpool',
path => '',
@ -459,7 +455,8 @@ __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}) {
@ -503,8 +500,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'destroypool',
@ -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,
@ -609,8 +607,8 @@ __PACKAGE__->register_method ({
}
};
return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'setpool',
@ -652,7 +650,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephsetpool', $pool, $authuser, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'poolindex',
@ -688,8 +687,8 @@ __PACKAGE__->register_method ({
];
return $result;
}});
},
});
__PACKAGE__->register_method({
name => 'getpool',
@ -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->() },
},
},
@ -791,12 +790,13 @@ __PACKAGE__->register_method ({
$data->{statistics} = $d->{stats};
}
my $apps = $rados->mon_command({ prefix => "osd pool application get", pool => "$pool", });
my $apps =
$rados->mon_command({ prefix => "osd pool application get", pool => "$pool" });
$data->{application_list} = [keys %$apps];
}
return $data;
}});
},
});
1;

View file

@ -5,14 +5,13 @@ 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({
subclass => "PVE::API2::ACME",
path => 'acme',
@ -42,9 +41,7 @@ __PACKAGE__->register_method ({
my ($param) = @_;
return [
{ name => 'acme' },
{ name => 'custom' },
{ name => 'info' },
{ name => 'acme' }, { name => 'custom' }, { name => 'info' },
];
},
});
@ -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 {
@ -140,7 +135,8 @@ __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."})
raise_param_exc(
{ 'key' => "Attempted to upload custom certificate without (existing) key." })
if !-e "${cert_prefix}.key";
}
@ -148,7 +144,8 @@ __PACKAGE__->register_method ({
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,7 +157,8 @@ __PACKAGE__->register_method ({
die "$@\n" if $@;
return $info;
}});
},
});
__PACKAGE__->register_method({
name => 'remove_custom_cert',
@ -208,6 +206,7 @@ __PACKAGE__->register_method ({
die "$@\n" if $@;
return undef;
}});
},
});
1;

View file

@ -113,10 +113,11 @@ 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} }) {
$dc_properties->{$opt} = $dc_schema->{properties}->{$opt};
@ -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 => {
@ -214,7 +216,8 @@ __PACKAGE__->register_method({
}
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').",
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,
@ -299,7 +309,8 @@ __PACKAGE__->register_method({
renderer => 'bytes',
},
netin => {
description => "The amount of traffic in bytes that was sent to the guest over"
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,
@ -318,7 +329,8 @@ __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,
},
@ -351,7 +363,8 @@ __PACKAGE__->register_method({
minimum => 0,
},
diskread => {
description => "The amount of bytes the guest read from its block devices since"
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',
@ -359,7 +372,8 @@ __PACKAGE__->register_method({
renderer => 'bytes',
},
diskwrite => {
description => "The amount of bytes the guest wrote to its block devices since"
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',
@ -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',
@ -422,7 +439,7 @@ __PACKAGE__->register_method({
my $haresources = PVE::HA::Config::read_resources_config();
my $hatypemap = {
'qemu' => 'vm',
'lxc' => 'ct'
'lxc' => 'ct',
};
my $pooldata = {};
@ -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,7 +488,9 @@ __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};
}
}
@ -485,8 +505,10 @@ __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};
}
@ -510,7 +532,8 @@ __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 $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'})) {
@ -537,7 +560,8 @@ __PACKAGE__->register_method({
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;
}
}
@ -581,7 +605,8 @@ __PACKAGE__->register_method({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'tasks',
@ -616,19 +641,22 @@ __PACKAGE__->register_method({
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']],
@ -663,7 +691,8 @@ __PACKAGE__->register_method({
$res->{'allowed-tags'} = [sort keys $tags->%*];
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'set_options',
@ -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',
@ -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;

View file

@ -59,7 +59,8 @@ __PACKAGE__->register_method({
return [
{ subdir => 'not-backed-up' },
];
}});
},
});
__PACKAGE__->register_method({
name => 'get_guests_not_in_backup',
@ -105,7 +106,8 @@ __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) {
@ -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;

View 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;

View file

@ -39,13 +39,11 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $result = [
{ name => 'metadata' },
{ name => 'status' },
{ name => 'flags' },
{ name => 'metadata' }, { name => 'status' }, { name => 'flags' },
];
return $result;
}
},
});
my $metadata_common_props = {
@ -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",
@ -279,7 +280,7 @@ __PACKAGE__->register_method ({
$res->{osd} = $rados->mon_command({ prefix => "osd metadata" });
return $res;
}
},
});
__PACKAGE__->register_method({
@ -302,7 +303,7 @@ __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();
@ -329,8 +330,7 @@ __PACKAGE__->register_method ({
},
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.",
@ -378,7 +379,7 @@ __PACKAGE__->register_method ({
}
return $res;
}
},
});
__PACKAGE__->register_method({
@ -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,8 +433,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephsetflags', undef, $user, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'get_flag',
@ -449,7 +450,8 @@ __PACKAGE__->register_method ({
properties => {
flag => {
description => "The name of the flag name to get.",
type => 'string', enum => $possible_flags_list,
type => 'string',
enum => $possible_flags_list,
},
},
},
@ -469,7 +471,8 @@ __PACKAGE__->register_method ({
}
return 0;
}});
},
});
__PACKAGE__->register_method({
name => 'update_flag',
@ -509,7 +512,7 @@ __PACKAGE__->register_method ({
});
return undef;
}});
},
});
1;

View file

@ -41,10 +41,10 @@ __PACKAGE__->register_method({
},
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;

View file

@ -47,12 +47,11 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $result = [
{ name => 'dir' },
{ name => 'pci' },
{ name => 'usb' },
{ name => 'dir' }, { name => 'pci' }, { name => 'usb' },
];
return $result;
}});
},
});
1;

View file

@ -32,11 +32,14 @@ __PACKAGE__->register_method ({
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.",
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,7 +80,7 @@ __PACKAGE__->register_method ({
description => "The message of the error",
},
},
}
},
},
},
},
@ -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",
};
@ -143,7 +151,8 @@ __PACKAGE__->register_method ({
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,7 +182,8 @@ __PACKAGE__->register_method ({
$data->{digest} = $cfg->{digest};
return $data;
}});
},
});
__PACKAGE__->register_method({
name => 'create',
@ -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,7 +219,9 @@ __PACKAGE__->register_method ({
PVE::Mapping::Dir::write_dir_config($cfg);
}, "create directory mapping failed");
},
"create directory mapping failed",
);
return;
},
@ -238,7 +251,8 @@ __PACKAGE__->register_method ({
$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,7 +274,9 @@ __PACKAGE__->register_method ({
PVE::Mapping::Dir::write_dir_config($cfg);
}, "update directory mapping failed");
},
"update directory mapping failed",
);
return;
},
@ -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;

View file

@ -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,7 +77,7 @@ __PACKAGE__->register_method ({
description => "The message of the error",
},
},
}
},
},
},
},
@ -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",
};
@ -140,7 +148,8 @@ __PACKAGE__->register_method ({
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,7 +179,8 @@ __PACKAGE__->register_method ({
$data->{digest} = $cfg->{digest};
return $data;
}});
},
});
__PACKAGE__->register_method({
name => 'create',
@ -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,7 +213,9 @@ __PACKAGE__->register_method ({
PVE::Mapping::PCI::write_pci_config($cfg);
}, "create hardware mapping failed");
},
"create hardware mapping failed",
);
return;
},
@ -232,7 +245,8 @@ __PACKAGE__->register_method ({
$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,7 +265,9 @@ __PACKAGE__->register_method ({
PVE::Mapping::PCI::write_pci_config($cfg);
}, "update hardware mapping failed");
},
"update hardware mapping failed",
);
return;
},
@ -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;

View file

@ -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,7 +74,7 @@ __PACKAGE__->register_method ({
description => "The message of the error",
},
},
}
},
},
},
},
@ -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",
};
@ -138,7 +141,8 @@ __PACKAGE__->register_method ({
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,7 +172,8 @@ __PACKAGE__->register_method ({
$data->{digest} = $cfg->{digest};
return $data;
}});
},
});
__PACKAGE__->register_method({
name => 'create',
@ -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,7 +206,9 @@ __PACKAGE__->register_method ({
PVE::Mapping::USB::write_usb_config($cfg);
}, "create hardware mapping failed");
},
"create hardware mapping failed",
);
return;
},
@ -230,7 +238,8 @@ __PACKAGE__->register_method ({
$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,7 +258,9 @@ __PACKAGE__->register_method ({
PVE::Mapping::USB::write_usb_config($cfg);
}, "update hardware mapping failed");
},
"update hardware mapping failed",
);
return;
},
@ -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;

View file

@ -42,7 +42,7 @@ __PACKAGE__->register_method ({
];
return $result;
}
},
});
__PACKAGE__->register_method({
@ -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.",
@ -94,7 +94,8 @@ __PACKAGE__->register_method ({
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,7 +105,8 @@ __PACKAGE__->register_method ({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'read',
@ -135,7 +137,8 @@ __PACKAGE__->register_method ({
}
return $status_cfg->{ids}->{$id};
}});
},
});
__PACKAGE__->register_method({
name => 'create',
@ -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,12 +184,13 @@ __PACKAGE__->register_method ({
}
PVE::Cluster::cfs_write_file('status.cfg', $cfg);
});
},
);
die $@ if $@;
return;
}});
},
});
__PACKAGE__->register_method({
name => 'update',
@ -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);
@ -244,11 +252,13 @@ __PACKAGE__->register_method ({
$plugin->test_connection($data, $id);
PVE::Cluster::cfs_write_file('status.cfg', $cfg);
});
},
);
die $@ if $@;
return;
}});
},
});
__PACKAGE__->register_method({
name => 'delete',
@ -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,11 +298,13 @@ __PACKAGE__->register_method ({
delete $cfg->{ids}->{$id};
PVE::Cluster::cfs_write_file('status.cfg', $cfg);
});
},
);
die $@ if $@;
return;
}});
},
});
__PACKAGE__->register_method({
name => 'export',
@ -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,7 +402,7 @@ __PACKAGE__->register_method ({
} else {
$generations = 0;
};
}
my @metrics = @{ PVE::PullMetric::get_local_metrics($generations) };

View file

@ -84,7 +84,7 @@ __PACKAGE__->register_method ({
];
return $result;
}
},
});
__PACKAGE__->register_method({
@ -93,7 +93,8 @@ __PACKAGE__->register_method ({
method => 'GET',
description => 'Returns known notification metadata fields',
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -120,13 +121,11 @@ __PACKAGE__->register_method ({
# TODO: Adapt this API handler once we have a 'notification registry'
my $result = [
{ name => 'type' },
{ name => 'hostname' },
{ name => 'job-id' },
{ name => 'type' }, { name => 'hostname' }, { name => 'job-id' },
];
return $result;
}
},
});
__PACKAGE__->register_method({
@ -135,7 +134,8 @@ __PACKAGE__->register_method ({
method => 'GET',
description => 'Returns known notification metadata fields and their known values',
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -151,7 +151,7 @@ __PACKAGE__->register_method ({
properties => {
'value' => {
description => 'Notification metadata value known by the system.',
type => 'string'
type => 'string',
},
'comment' => {
description => 'Additional comment for this value.',
@ -196,32 +196,35 @@ __PACKAGE__->register_method ({
# Here we need a manual permission check.
if ($rpcenv->check($user, "/", ["Sys.Audit"], 1)) {
for my $backup_job (@{ PVE::API2::Backup->index({}) }) {
push @$values, {
push @$values,
{
value => $backup_job->{id},
comment => $backup_job->{comment},
field => 'job-id'
field => 'job-id',
};
}
}
# The API call returns only returns jobs for which the user
# has adequate permissions.
for my $sync_job (@{ PVE::API2::ReplicationConfig->index({}) }) {
push @$values, {
push @$values,
{
value => $sync_job->{id},
comment => $sync_job->{comment},
field => 'job-id'
field => 'job-id',
};
}
for my $node (@{ PVE::Cluster::get_nodelist() }) {
push @$values, {
push @$values,
{
value => $node,
field => 'hostname',
}
};
}
return $values;
}
},
});
__PACKAGE__->register_method({
@ -251,7 +254,7 @@ __PACKAGE__->register_method ({
];
return $result;
}
},
});
__PACKAGE__->register_method({
@ -260,7 +263,8 @@ __PACKAGE__->register_method ({
method => 'GET',
description => 'Returns a list of all entities that can be used as notification targets.',
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
['perm', '/mapping/notifications', ['Mapping.Use']],
@ -309,14 +313,12 @@ __PACKAGE__->register_method ({
code => sub {
my $config = PVE::Notify::read_config();
my $targets = eval {
$config->get_targets();
};
my $targets = eval { $config->get_targets(); };
raise_api_error($@) if $@;
return $targets;
}
},
});
__PACKAGE__->register_method({
@ -326,7 +328,8 @@ __PACKAGE__->register_method ({
method => 'POST',
description => 'Send a test notification to a provided target.',
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
['perm', '/mapping/notifications', ['Mapping.Use']],
@ -338,7 +341,7 @@ __PACKAGE__->register_method ({
name => {
description => 'Name of the target.',
type => 'string',
format => 'pve-configid'
format => 'pve-configid',
},
},
},
@ -355,7 +358,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
my $sendmail_properties = {
@ -411,7 +414,8 @@ __PACKAGE__->register_method ({
method => 'GET',
description => 'Returns a list of all sendmail endpoints',
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -439,13 +443,11 @@ __PACKAGE__->register_method ({
code => sub {
my $config = PVE::Notify::read_config();
my $entities = eval {
$config->get_sendmail_endpoints();
};
my $entities = eval { $config->get_sendmail_endpoints(); };
raise_api_error($@) if $@;
return $entities;
}
},
});
__PACKAGE__->register_method({
@ -454,7 +456,8 @@ __PACKAGE__->register_method ({
method => 'GET',
description => 'Return a specific sendmail endpoint',
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -467,14 +470,13 @@ __PACKAGE__->register_method ({
type => 'string',
format => 'pve-configid',
},
}
},
},
returns => {
type => 'object',
properties => {
%$sendmail_properties,
digest => get_standard_option('pve-config-digest'),
}
%$sendmail_properties, digest => get_standard_option('pve-config-digest'),
},
},
code => sub {
@ -482,15 +484,13 @@ __PACKAGE__->register_method ({
my $name = extract_param($param, 'name');
my $config = PVE::Notify::read_config();
my $endpoint = eval {
$config->get_sendmail_endpoint($name)
};
my $endpoint = eval { $config->get_sendmail_endpoint($name) };
raise_api_error($@) if $@;
$endpoint->{digest} = $config->digest();
return $endpoint;
}
},
});
__PACKAGE__->register_method({
@ -500,9 +500,11 @@ __PACKAGE__->register_method ({
method => 'POST',
description => 'Create a new sendmail endpoint',
permissions => {
check => ['and',
check => [
'and',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['or',
[
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/', ['Sys.AccessNetwork']],
],
@ -529,13 +531,7 @@ __PACKAGE__->register_method ({
my $config = PVE::Notify::read_config();
$config->add_sendmail_endpoint(
$name,
$mailto,
$mailto_user,
$from_address,
$author,
$comment,
$disable,
$name, $mailto, $mailto_user, $from_address, $author, $comment, $disable,
);
PVE::Notify::write_config($config);
@ -544,7 +540,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -554,9 +550,11 @@ __PACKAGE__->register_method ({
method => 'PUT',
description => 'Update existing sendmail endpoint',
permissions => {
check => ['and',
check => [
'and',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['or',
[
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/', ['Sys.AccessNetwork']],
],
@ -577,7 +575,7 @@ __PACKAGE__->register_method ({
},
digest => get_standard_option('pve-config-digest'),
}
},
},
returns => { type => 'null' },
code => sub {
@ -616,7 +614,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -635,7 +633,7 @@ __PACKAGE__->register_method ({
type => 'string',
format => 'pve-configid',
},
}
},
},
returns => { type => 'null' },
code => sub {
@ -652,7 +650,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if ($@);
return;
}
},
});
my $gotify_properties = {
@ -715,13 +713,11 @@ __PACKAGE__->register_method ({
my $config = PVE::Notify::read_config();
my $rpcenv = PVE::RPCEnvironment::get();
my $entities = eval {
$config->get_gotify_endpoints();
};
my $entities = eval { $config->get_gotify_endpoints(); };
raise_api_error($@) if $@;
return $entities;
}
},
});
__PACKAGE__->register_method({
@ -731,7 +727,8 @@ __PACKAGE__->register_method ({
description => 'Return a specific gotify endpoint',
protected => 1,
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -742,31 +739,29 @@ __PACKAGE__->register_method ({
name => {
type => 'string',
format => 'pve-configid',
description => 'Name of the endpoint.'
description => 'Name of the endpoint.',
},
},
}
},
returns => {
type => 'object',
properties => {
%{ remove_protected_properties($gotify_properties, ['token']) },
digest => get_standard_option('pve-config-digest'),
}
},
},
code => sub {
my ($param) = @_;
my $name = extract_param($param, 'name');
my $config = PVE::Notify::read_config();
my $endpoint = eval {
$config->get_gotify_endpoint($name)
};
my $endpoint = eval { $config->get_gotify_endpoint($name) };
raise_api_error($@) if $@;
$endpoint->{digest} = $config->digest();
return $endpoint;
}
},
});
__PACKAGE__->register_method({
@ -776,9 +771,11 @@ __PACKAGE__->register_method ({
method => 'POST',
description => 'Create a new gotify endpoint',
permissions => {
check => ['and',
check => [
'and',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['or',
[
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/', ['Sys.AccessNetwork']],
],
@ -803,11 +800,7 @@ __PACKAGE__->register_method ({
my $config = PVE::Notify::read_config();
$config->add_gotify_endpoint(
$name,
$server,
$token,
$comment,
$disable,
$name, $server, $token, $comment, $disable,
);
PVE::Notify::write_config($config);
@ -816,7 +809,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -826,9 +819,11 @@ __PACKAGE__->register_method ({
method => 'PUT',
description => 'Update existing gotify endpoint',
permissions => {
check => ['and',
check => [
'and',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['or',
[
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/', ['Sys.AccessNetwork']],
],
@ -848,7 +843,7 @@ __PACKAGE__->register_method ({
description => 'A list of settings you want to delete.',
},
digest => get_standard_option('pve-config-digest'),
}
},
},
returns => { type => 'null' },
code => sub {
@ -883,7 +878,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -902,7 +897,7 @@ __PACKAGE__->register_method ({
type => 'string',
format => 'pve-configid',
},
}
},
},
returns => { type => 'null' },
code => sub {
@ -919,7 +914,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
my $smtp_properties = {
@ -1002,7 +997,8 @@ __PACKAGE__->register_method ({
method => 'GET',
description => 'Returns a list of all smtp endpoints',
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -1030,13 +1026,11 @@ __PACKAGE__->register_method ({
code => sub {
my $config = PVE::Notify::read_config();
my $entities = eval {
$config->get_smtp_endpoints();
};
my $entities = eval { $config->get_smtp_endpoints(); };
raise_api_error($@) if $@;
return $entities;
}
},
});
__PACKAGE__->register_method({
@ -1045,7 +1039,8 @@ __PACKAGE__->register_method ({
method => 'GET',
description => 'Return a specific smtp endpoint',
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -1058,14 +1053,14 @@ __PACKAGE__->register_method ({
type => 'string',
format => 'pve-configid',
},
}
},
},
returns => {
type => 'object',
properties => {
%{ remove_protected_properties($smtp_properties, ['password']) },
digest => get_standard_option('pve-config-digest'),
}
},
},
code => sub {
@ -1073,15 +1068,13 @@ __PACKAGE__->register_method ({
my $name = extract_param($param, 'name');
my $config = PVE::Notify::read_config();
my $endpoint = eval {
$config->get_smtp_endpoint($name)
};
my $endpoint = eval { $config->get_smtp_endpoint($name) };
raise_api_error($@) if $@;
$endpoint->{digest} = $config->digest();
return $endpoint;
}
},
});
__PACKAGE__->register_method({
@ -1091,9 +1084,11 @@ __PACKAGE__->register_method ({
method => 'POST',
description => 'Create a new smtp endpoint',
permissions => {
check => ['and',
check => [
'and',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['or',
[
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/', ['Sys.AccessNetwork']],
],
@ -1145,7 +1140,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -1155,9 +1150,11 @@ __PACKAGE__->register_method ({
method => 'PUT',
description => 'Update existing smtp endpoint',
permissions => {
check => ['and',
check => [
'and',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['or',
[
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/', ['Sys.AccessNetwork']],
],
@ -1178,7 +1175,7 @@ __PACKAGE__->register_method ({
},
digest => get_standard_option('pve-config-digest'),
}
},
},
returns => { type => 'null' },
code => sub {
@ -1227,7 +1224,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -1246,7 +1243,7 @@ __PACKAGE__->register_method ({
type => 'string',
format => 'pve-configid',
},
}
},
},
returns => { type => 'null' },
code => sub {
@ -1263,7 +1260,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if ($@);
return;
}
},
});
my $webhook_properties = {
@ -1350,13 +1347,11 @@ __PACKAGE__->register_method ({
my $config = PVE::Notify::read_config();
my $rpcenv = PVE::RPCEnvironment::get();
my $entities = eval {
$config->get_webhook_endpoints();
};
my $entities = eval { $config->get_webhook_endpoints(); };
raise_api_error($@) if $@;
return $entities;
}
},
});
__PACKAGE__->register_method({
@ -1366,7 +1361,8 @@ __PACKAGE__->register_method ({
description => 'Return a specific webhook endpoint',
protected => 1,
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -1377,31 +1373,28 @@ __PACKAGE__->register_method ({
name => {
type => 'string',
format => 'pve-configid',
description => 'Name of the endpoint.'
description => 'Name of the endpoint.',
},
},
}
},
returns => {
type => 'object',
properties => {
%$webhook_properties,
digest => get_standard_option('pve-config-digest'),
}
%$webhook_properties, digest => get_standard_option('pve-config-digest'),
},
},
code => sub {
my ($param) = @_;
my $name = extract_param($param, 'name');
my $config = PVE::Notify::read_config();
my $endpoint = eval {
$config->get_webhook_endpoint($name)
};
my $endpoint = eval { $config->get_webhook_endpoint($name) };
raise_api_error($@) if $@;
$endpoint->{digest} = $config->digest();
return $endpoint;
}
},
});
__PACKAGE__->register_method({
@ -1411,9 +1404,11 @@ __PACKAGE__->register_method ({
method => 'POST',
description => 'Create a new webhook endpoint',
permissions => {
check => ['and',
check => [
'and',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['or',
[
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/', ['Sys.AccessNetwork']],
],
@ -1440,7 +1435,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -1450,9 +1445,11 @@ __PACKAGE__->register_method ({
method => 'PUT',
description => 'Update existing webhook endpoint',
permissions => {
check => ['and',
check => [
'and',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['or',
[
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/', ['Sys.AccessNetwork']],
],
@ -1472,7 +1469,7 @@ __PACKAGE__->register_method ({
description => 'A list of settings you want to delete.',
},
digest => get_standard_option('pve-config-digest'),
}
},
},
returns => { type => 'null' },
code => sub {
@ -1499,7 +1496,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -1518,7 +1515,7 @@ __PACKAGE__->register_method ({
type => 'string',
format => 'pve-configid',
},
}
},
},
returns => { type => 'null' },
code => sub {
@ -1535,7 +1532,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
my $matcher_properties = {
@ -1580,7 +1577,8 @@ my $matcher_properties = {
},
mode => {
type => 'string',
description => "Choose between 'all' and 'any' for when multiple properties are specified",
description =>
"Choose between 'all' and 'any' for when multiple properties are specified",
optional => 1,
enum => [qw(all any)],
default => 'all',
@ -1610,7 +1608,8 @@ __PACKAGE__->register_method ({
description => 'Returns a list of all matchers',
protected => 1,
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
['perm', '/mapping/notifications', ['Mapping.Use']],
@ -1631,20 +1630,18 @@ __PACKAGE__->register_method ({
type => 'string',
enum => [qw(user-created builtin modified-builtin)],
},
}
},
},
links => [{ rel => 'child', href => '{name}' }],
},
code => sub {
my $config = PVE::Notify::read_config();
my $entities = eval {
$config->get_matchers();
};
my $entities = eval { $config->get_matchers(); };
raise_api_error($@) if $@;
return $entities;
}
},
});
__PACKAGE__->register_method({
@ -1654,7 +1651,8 @@ __PACKAGE__->register_method ({
description => 'Return a specific matcher',
protected => 1,
permissions => {
check => ['or',
check => [
'or',
['perm', '/mapping/notifications', ['Mapping.Modify']],
['perm', '/mapping/notifications', ['Mapping.Audit']],
],
@ -1666,13 +1664,12 @@ __PACKAGE__->register_method ({
type => 'string',
format => 'pve-configid',
},
}
},
},
returns => {
type => 'object',
properties => {
%$matcher_properties,
digest => get_standard_option('pve-config-digest'),
%$matcher_properties, digest => get_standard_option('pve-config-digest'),
},
},
code => sub {
@ -1681,15 +1678,13 @@ __PACKAGE__->register_method ({
my $config = PVE::Notify::read_config();
my $matcher = eval {
$config->get_matcher($name)
};
my $matcher = eval { $config->get_matcher($name) };
raise_api_error($@) if $@;
$matcher->{digest} = $config->digest();
return $matcher;
}
},
});
__PACKAGE__->register_method({
@ -1742,7 +1737,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -1810,7 +1805,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
__PACKAGE__->register_method({
@ -1829,7 +1824,7 @@ __PACKAGE__->register_method ({
type => 'string',
format => 'pve-configid',
},
}
},
},
returns => { type => 'null' },
code => sub {
@ -1846,7 +1841,7 @@ __PACKAGE__->register_method ({
raise_api_error($@) if $@;
return;
}
},
});
1;

View file

@ -57,13 +57,11 @@ __PACKAGE__->register_method({
my ($param) = @_;
my $res = [
{ id => 'status' },
{ id => 'resources' },
{ id => 'groups' },
{ id => 'status' }, { id => 'resources' }, { id => 'groups' },
];
return $res;
}});
},
});
1;

View file

@ -47,11 +47,10 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $res = [
{ type => 'pci' },
{ type => 'usb' },
{ type => 'pci' }, { type => 'usb' },
];
return $res;
}
},
});

View file

@ -94,13 +94,15 @@ __PACKAGE__->register_method ({
},
iommugroup => {
type => 'integer',
description => "The IOMMU group in which the device is in. If no IOMMU group is"
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"
description =>
"If set, marks that the device is capable of creating mediated"
. " devices.",
},
},
@ -129,7 +131,8 @@ __PACKAGE__->register_method ({
my $verbose = $param->{verbose} // 1;
return PVE::SysFSTools::lspci($filter, $verbose);
}});
},
});
__PACKAGE__->register_method({
name => 'pci_index',
@ -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_-]+)',
},
},
},
@ -165,7 +169,8 @@ __PACKAGE__->register_method ({
];
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'mdevscan',
@ -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->%*)];
}
}});
},
});

View file

@ -49,5 +49,5 @@ __PACKAGE__->register_method({
my ($param) = @_;
return PVE::SysFSTools::scan_usb();
}
},
});

View file

@ -37,8 +37,18 @@ my $bond_mode_enum = [
'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,32 +109,39 @@ 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'],
@ -128,7 +149,8 @@ my $confdesc = {
'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,
},
};
@ -377,22 +408,27 @@ __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',
@ -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." });
}
};
@ -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') {
@ -592,7 +631,10 @@ __PACKAGE__->register_method({
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,7 +651,8 @@ __PACKAGE__->register_method({
die $@ if $@;
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'update_network',
@ -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,7 +753,8 @@ __PACKAGE__->register_method({
die $@ if $@;
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'network_config',
@ -744,7 +793,8 @@ __PACKAGE__->register_method({
if !$ifaces->{ $param->{iface} };
return $ifaces->{ $param->{iface} };
}});
},
});
sub ifupdown2_version {
my $v;
@ -756,10 +806,12 @@ 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';
my ($v, $pve, $v_str) = ifupdown2_version();
die "incompatible 'ifupdown2' package version '$v_str'! Did you installed from Proxmox repositories?\n"
die
"incompatible 'ifupdown2' package version '$v_str'! Did you installed from Proxmox repositories?\n"
if $v < (1 * 100000 + 2 * 1000 + 8 * 10) || !$pve;
}
@ -815,7 +867,8 @@ __PACKAGE__->register_method({
}
};
return $rpcenv->fork_worker('srvreload', 'networking', $authuser, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'delete_network',
@ -867,4 +920,5 @@ __PACKAGE__->register_method({
die $@ if $@;
return undef;
}});
},
});

View file

@ -14,7 +14,8 @@ 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,
},
@ -67,7 +69,8 @@ __PACKAGE__->register_method({
}
return $config;
}});
},
});
__PACKAGE__->register_method({
name => 'set_options',
@ -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;

View file

@ -91,10 +91,11 @@ my $verify_command_item_desc = {
optional => 1,
},
},
}
},
};
PVE::JSONSchema::register_format('pve-command-batch', \&verify_command_batch);
sub verify_command_batch {
my ($value, $noerr) = @_;
my $commands = eval { decode_json($value); };
@ -195,7 +196,6 @@ __PACKAGE__->register_method ({
path => 'certificates',
});
__PACKAGE__->register_method({
subclass => "PVE::API2::NodeConfig",
path => 'config',
@ -234,7 +234,8 @@ __PACKAGE__->register_method ({
{ name => 'zones' },
];
return $result;
}});
},
});
}
__PACKAGE__->register_method({
@ -305,7 +306,8 @@ __PACKAGE__->register_method ({
push @$result, { name => 'sdn' } if $have_sdn;
return $result;
}});
},
});
__PACKAGE__->register_method({
name => 'version',
@ -341,7 +343,8 @@ __PACKAGE__->register_method ({
my ($resp, $param) = @_;
return PVE::pvecfg::version_info();
}});
},
});
my sub get_current_kernel_info {
my ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
@ -410,14 +413,16 @@ __PACKAGE__->register_method({
enum => [qw(efi legacy-bios)],
},
secureboot => {
description => 'System is booted in secure mode, only applicable for the "efi" mode.',
description =>
'System is booted in secure mode, only applicable for the "efi" mode.',
type => 'boolean',
optional => 1,
},
},
},
'current-kernel' => {
description => "Meta-information about the currently booted kernel of this node.",
description =>
"Meta-information about the currently booted kernel of this node.",
type => 'object',
properties => {
sysname => {
@ -469,7 +474,7 @@ __PACKAGE__->register_method({
items => {
type => 'string',
description => "The value of the load.",
}
},
},
memory => {
type => "object",
@ -512,7 +517,7 @@ __PACKAGE__->register_method({
description => "The available bytes in the root filesystem.",
},
},
}
},
},
},
code => sub {
@ -558,8 +563,7 @@ __PACKAGE__->register_method({
used => $meminfo->{swapused},
};
$res->{pveversion} = PVE::pvecfg::package() . "/" .
PVE::pvecfg::version_text();
$res->{pveversion} = PVE::pvecfg::package() . "/" . PVE::pvecfg::version_text();
my $dinfo = df('/', 1); # output is bytes
@ -571,7 +575,8 @@ __PACKAGE__->register_method({
};
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'netstat',
@ -605,7 +610,8 @@ __PACKAGE__->register_method({
next if $dev !~ m/^(?:tap|veth)([1-9]\d*)i(\d+)$/;
my ($vmid, $netid) = ($1, $2);
push @$res, {
push @$res,
{
vmid => $vmid,
dev => "net$netid",
in => $netdev->{$dev}->{transmit},
@ -614,7 +620,8 @@ __PACKAGE__->register_method({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'execute',
@ -630,10 +637,13 @@ __PACKAGE__->register_method({
commands => {
description => "JSON encoded array of commands.",
type => "string",
verbose_description => "JSON encoded array of commands, where each command is an object with the following properties:\n"
. PVE::RESTHandler::dump_properties($verify_command_item_desc->{items}->{properties}, 'full'),
verbose_description =>
"JSON encoded array of commands, where each command is an object with the following properties:\n"
. PVE::RESTHandler::dump_properties(
$verify_command_item_desc->{items}->{properties}, 'full',
),
format => "pve-command-batch",
}
},
},
},
returns => {
@ -659,20 +669,23 @@ __PACKAGE__->register_method({
my $path = "nodes/$param->{node}/$cmd->{path}";
my $uri_param = {};
my ($handler, $info) = PVE::API2->find_handler($cmd->{method}, $path, $uri_param);
my ($handler, $info) =
PVE::API2->find_handler($cmd->{method}, $path, $uri_param);
if (!$handler || !$info) {
die "no handler for '$path'\n";
}
foreach my $p (keys %{ $cmd->{args} }) {
raise_param_exc({ $p => "duplicate parameter" }) if defined($uri_param->{$p});
raise_param_exc({ $p => "duplicate parameter" })
if defined($uri_param->{$p});
$uri_param->{$p} = $cmd->{args}->{$p};
}
# check access permissions
$rpcenv->check_api2_permissions($info->{permissions}, $user, $uri_param);
push @$res, {
push @$res,
{
status => HTTP_OK,
data => $handler->handle($info, $uri_param),
};
@ -691,8 +704,8 @@ __PACKAGE__->register_method({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'node_cmd',
@ -726,7 +739,8 @@ __PACKAGE__->register_method({
}
return;
}});
},
});
__PACKAGE__->register_method({
name => 'wakeonlan',
@ -740,13 +754,16 @@ __PACKAGE__->register_method({
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node', {
node => get_standard_option(
'pve-node',
{
description => 'target node for wake on LAN packet',
completion => sub {
my $members = PVE::Cluster::get_members();
return [grep { !$members->{$_}->{online} } keys %$members];
}
}),
},
},
),
},
},
returns => {
@ -800,7 +817,8 @@ __PACKAGE__->register_method({
close($sock);
return $wol_config->{mac};
}});
},
});
__PACKAGE__->register_method({
name => 'rrd',
@ -822,7 +840,8 @@ __PACKAGE__->register_method({
},
ds => {
description => "The list of datasources you want to display.",
type => 'string', format => 'pve-configid-list',
type => 'string',
format => 'pve-configid-list',
},
cf => {
description => "The RRD consolidation function",
@ -842,10 +861,11 @@ __PACKAGE__->register_method({
my ($param) = @_;
return PVE::RRD::create_rrd_graph(
"pve2-node/$param->{node}", $param->{timeframe},
$param->{ds}, $param->{cf});
"pve2-node/$param->{node}", $param->{timeframe}, $param->{ds}, $param->{cf},
);
}});
},
});
__PACKAGE__->register_method({
name => 'rrddata',
@ -883,9 +903,10 @@ __PACKAGE__->register_method({
code => sub {
my ($param) = @_;
return PVE::RRD::create_rrd_data(
"pve2-node/$param->{node}", $param->{timeframe}, $param->{cf});
}});
return PVE::RRD::create_rrd_data("pve2-node/$param->{node}", $param->{timeframe},
$param->{cf});
},
});
__PACKAGE__->register_method({
name => 'syslog',
@ -943,9 +964,9 @@ __PACKAGE__->register_method({
t => {
description => "Line text",
type => 'string',
}
}
}
},
},
},
},
code => sub {
my ($param) = @_;
@ -963,13 +984,19 @@ __PACKAGE__->register_method({
$service = $service_aliases->{ $param->{service} } // $param->{service};
}
my ($count, $lines) = PVE::Tools::dump_journal($param->{start}, $param->{limit},
$param->{since}, $param->{until}, $service);
my ($count, $lines) = PVE::Tools::dump_journal(
$param->{start},
$param->{limit},
$param->{since},
$param->{until},
$service,
);
$rpcenv->set_result_attrib('total', $count);
return $lines;
}});
},
});
__PACKAGE__->register_method({
name => 'journal',
@ -989,13 +1016,15 @@ __PACKAGE__->register_method({
since => {
type => 'integer',
minimum => 0,
description => "Display all log since this UNIX epoch. Conflicts with 'startcursor'.",
description =>
"Display all log since this UNIX epoch. Conflicts with 'startcursor'.",
optional => 1,
},
until => {
type => 'integer',
minimum => 0,
description => "Display all log until this UNIX epoch. Conflicts with 'endcursor'.",
description =>
"Display all log until this UNIX epoch. Conflicts with 'endcursor'.",
optional => 1,
},
lastentries => {
@ -1020,7 +1049,7 @@ __PACKAGE__->register_method({
type => 'array',
items => {
type => "string",
}
},
},
code => sub {
my ($param) = @_;
@ -1032,7 +1061,8 @@ __PACKAGE__->register_method({
push @$cmd, '-n', $param->{lastentries} if $param->{lastentries};
push @$cmd, '-b', $param->{since} if $param->{since};
push @$cmd, '-e', $param->{until} if $param->{until};
push @$cmd, '-f', PVE::Tools::shellquote($param->{startcursor}) if $param->{startcursor};
push @$cmd, '-f', PVE::Tools::shellquote($param->{startcursor})
if $param->{startcursor};
push @$cmd, '-t', PVE::Tools::shellquote($param->{endcursor}) if $param->{endcursor};
push @$cmd, ' | gzip ';
@ -1047,7 +1077,9 @@ __PACKAGE__->register_method({
'content-encoding' => 'gzip',
},
},
}});
;
},
});
my $sslcert;
@ -1093,7 +1125,8 @@ my $get_vnc_connection_info = sub {
my ($remip, $family);
if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
($remip, $family) = PVE::Cluster::remote_node_ip($node);
$remote_cmd = PVE::SSHInfo::ssh_info_to_command({ ip => $remip, name => $node }, ('-t'));
$remote_cmd =
PVE::SSHInfo::ssh_info_to_command({ ip => $remip, name => $node }, ('-t'));
push @$remote_cmd, '--';
} else {
$family = PVE::Tools::get_host_address_family($node);
@ -1118,14 +1151,16 @@ __PACKAGE__->register_method ({
node => get_standard_option('pve-node'),
cmd => {
type => 'string',
description => "Run specific command or default to login (requires 'root\@pam')",
description =>
"Run specific command or default to login (requires 'root\@pam')",
enum => [keys %$shell_cmd_map],
optional => 1,
default => 'login',
},
'cmd-opts' => {
type => 'string',
description => "Add parameters to a command. Encoded as null terminated strings.",
description =>
"Add parameters to a command. Encoded as null terminated strings.",
requires => 'cmd',
optional => 1,
default => '',
@ -1167,7 +1202,6 @@ __PACKAGE__->register_method ({
my $rpcenv = PVE::RPCEnvironment::get();
my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
if (defined($param->{cmd}) && $param->{cmd} ne 'login' && $user ne 'root@pam') {
raise_perm_exc('user != root@pam');
}
@ -1186,11 +1220,16 @@ __PACKAGE__->register_method ({
my $timeout = 10;
my $cmd = ['/usr/bin/vncterm',
'-rfbport', $port,
'-timeout', $timeout,
'-authpath', $authpath,
'-perm', 'Sys.Console',
my $cmd = [
'/usr/bin/vncterm',
'-rfbport',
$port,
'-timeout',
$timeout,
'-authpath',
$authpath,
'-perm',
'Sys.Console',
];
push @$cmd, '-width', $param->{width} if $param->{width};
@ -1214,7 +1253,13 @@ __PACKAGE__->register_method ({
eval {
foreach my $k (keys %ENV) {
next if $k eq 'PVE_VNC_TICKET';
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} = '/';
@ -1239,7 +1284,8 @@ __PACKAGE__->register_method ({
upid => $upid,
cert => $sslcert,
};
}});
},
});
__PACKAGE__->register_method({
name => 'termproxy',
@ -1256,14 +1302,16 @@ __PACKAGE__->register_method ({
node => get_standard_option('pve-node'),
cmd => {
type => 'string',
description => "Run specific command or default to login (requires 'root\@pam')",
description =>
"Run specific command or default to login (requires 'root\@pam')",
enum => [keys %$shell_cmd_map],
optional => 1,
default => 'login',
},
'cmd-opts' => {
type => 'string',
description => "Add parameters to a command. Encoded as null terminated strings.",
description =>
"Add parameters to a command. Encoded as null terminated strings.",
requires => 'cmd',
optional => 1,
default => '',
@ -1299,11 +1347,7 @@ __PACKAGE__->register_method ({
syslog('info', "starting termproxy $upid\n");
my $cmd = [
'/usr/bin/termproxy',
$port,
'--path', $authpath,
'--perm', 'Sys.Console',
'--'
'/usr/bin/termproxy', $port, '--path', $authpath, '--perm', 'Sys.Console', '--',
];
push @$cmd, @$remcmd, @$shcmd;
@ -1319,7 +1363,8 @@ __PACKAGE__->register_method ({
port => $port,
upid => $upid,
};
}});
},
});
__PACKAGE__->register_method({
name => 'vncwebsocket',
@ -1367,7 +1412,8 @@ __PACKAGE__->register_method({
my $port = $param->{port};
return { port => $port };
}});
},
});
__PACKAGE__->register_method({
name => 'spiceshell',
@ -1386,14 +1432,16 @@ __PACKAGE__->register_method ({
proxy => get_standard_option('spice-proxy', { optional => 1 }),
cmd => {
type => 'string',
description => "Run specific command or default to login (requires 'root\@pam')",
description =>
"Run specific command or default to login (requires 'root\@pam')",
enum => [keys %$shell_cmd_map],
optional => 1,
default => 'login',
},
'cmd-opts' => {
type => 'string',
description => "Add parameters to a command. Encoded as null terminated strings.",
description =>
"Add parameters to a command. Encoded as null terminated strings.",
requires => 'cmd',
optional => 1,
default => '',
@ -1409,7 +1457,6 @@ __PACKAGE__->register_method ({
my ($user, undef, $realm) = PVE::AccessControl::verify_username($authuser);
if (defined($param->{cmd}) && $param->{cmd} ne 'login' && $user ne 'root@pam') {
raise_perm_exc('user != root@pam');
}
@ -1424,8 +1471,10 @@ __PACKAGE__->register_method ({
my $title = "Shell on '$node'";
return PVE::API2Tools::run_spiceterm($authpath, $permissions, 0, $node, $proxy, $title, $shcmd);
}});
return PVE::API2Tools::run_spiceterm($authpath, $permissions, 0, $node, $proxy, $title,
$shcmd);
},
});
__PACKAGE__->register_method({
name => 'dns',
@ -1474,7 +1523,8 @@ __PACKAGE__->register_method({
my $res = PVE::INotify::read_file('resolvconf');
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'update_dns',
@ -1496,17 +1546,20 @@ __PACKAGE__->register_method({
},
dns1 => {
description => 'First name server IP address.',
type => 'string', format => 'ip',
type => 'string',
format => 'ip',
optional => 1,
},
dns2 => {
description => 'Second name server IP address.',
type => 'string', format => 'ip',
type => 'string',
format => 'ip',
optional => 1,
},
dns3 => {
description => 'Third name server IP address.',
type => 'string', format => 'ip',
type => 'string',
format => 'ip',
optional => 1,
},
},
@ -1518,7 +1571,8 @@ __PACKAGE__->register_method({
PVE::INotify::update_file('resolvconf', $param);
return;
}});
},
});
__PACKAGE__->register_method({
name => 'time',
@ -1569,7 +1623,8 @@ __PACKAGE__->register_method({
};
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'set_timezone',
@ -1586,7 +1641,8 @@ __PACKAGE__->register_method({
properties => {
node => get_standard_option('pve-node'),
timezone => {
description => "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
description =>
"Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
type => 'string',
},
},
@ -1598,7 +1654,8 @@ __PACKAGE__->register_method({
PVE::INotify::write_file('timezone', $param->{timezone});
return;
}});
},
});
__PACKAGE__->register_method({
name => 'aplinfo',
@ -1634,7 +1691,8 @@ __PACKAGE__->register_method({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'apl_download',
@ -1650,10 +1708,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 => "The storage where the template will be stored",
completion => \&PVE::Storage::complete_storage_enabled,
}),
},
),
template => {
type => 'string',
description => "The template which will downloaded",
@ -1690,18 +1751,23 @@ __PACKAGE__->register_method({
my $worker = sub {
my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
PVE::Tools::download_file_from_url("$tmpldir/$template", $appliance->{location}, {
PVE::Tools::download_file_from_url(
"$tmpldir/$template",
$appliance->{location},
{
hash_required => 1,
sha512sum => $appliance->{sha512sum},
md5sum => $appliance->{md5sum},
http_proxy => $dccfg->{http_proxy},
});
},
);
};
my $upid = $rpcenv->fork_worker('download', $template, $user, $worker);
return $upid;
}});
},
});
__PACKAGE__->register_method({
name => 'query_url_metadata',
@ -1710,7 +1776,8 @@ __PACKAGE__->register_method({
description => "Query metadata of an URL: file size, file name and mime type.",
proxyto => 'node',
permissions => {
check => ['or',
check => [
'or',
['perm', '/', ['Sys.Audit', 'Sys.Modify']],
['perm', '/nodes/{node}', ['Sys.AccessNetwork']],
],
@ -1782,7 +1849,10 @@ __PACKAGE__->register_method({
my $filename;
if ($disposition && ($disposition =~ m/filename="([^"]*)"/ || $disposition =~ m/filename=([^;]*)/)) {
if (
$disposition
&& ($disposition =~ m/filename="([^"]*)"/ || $disposition =~ m/filename=([^;]*)/)
) {
$filename = $1;
} elsif ($url =~ m!^[^?]+/([^?/]*)(?:\?.*)?$!) {
$filename = $1;
@ -1799,7 +1869,8 @@ __PACKAGE__->register_method({
$ret->{mimetype} = $type if $type;
return $ret;
}});
},
});
__PACKAGE__->register_method({
name => 'report',
@ -1822,7 +1893,8 @@ __PACKAGE__->register_method({
},
code => sub {
return PVE::Report::generate();
}});
},
});
# returns a list of VMIDs, those can be filtered by
# * current parent node
@ -1884,7 +1956,8 @@ my $get_start_stop_list = sub {
my $conf = $vmlist->{$vmid}->{conf};
next if $autostart && !$conf->{onboot};
my $startup = $conf->{startup} ? PVE::JSONSchema::pve_parse_startup_order($conf->{startup}) : {};
my $startup =
$conf->{startup} ? PVE::JSONSchema::pve_parse_startup_order($conf->{startup}) : {};
my $order = $startup->{order} = $startup->{order} // LONG_MAX;
$resList->{$order}->{$vmid} = $startup;
@ -1910,7 +1983,8 @@ my $remove_locks_on_startup = sub {
warn "$msg\n"; # prints to task log
syslog('warning', $msg);
}
}; warn $@ if $@;
};
warn $@ if $@;
}
};
@ -1925,7 +1999,8 @@ __PACKAGE__->register_method ({
user => 'all',
},
proxyto => 'node',
description => "Start all VMs and containers located on this node (by default only those with onboot=1).",
description =>
"Start all VMs and containers located on this node (by default only those with onboot=1).",
parameters => {
additionalProperties => 0,
properties => {
@ -1934,11 +2009,13 @@ __PACKAGE__->register_method ({
optional => 1,
type => 'boolean',
default => 'off',
description => "Issue start command even if virtual guest have 'onboot' not set or set to off.",
description =>
"Issue start command even if virtual guest have 'onboot' not set or set to off.",
},
vms => {
description => "Only consider guests from this comma separated list of VMIDs.",
type => 'string', format => 'pve-vmid-list',
type => 'string',
format => 'pve-vmid-list',
optional => 1,
},
},
@ -1978,7 +2055,9 @@ __PACKAGE__->register_method ({
}
eval { # remove backup locks, but avoid running into a scheduled backup job
PVE::Tools::lock_file('/var/run/vzdump.lock', 10, $remove_locks_on_startup, $nodename);
PVE::Tools::lock_file(
'/var/run/vzdump.lock', 10, $remove_locks_on_startup, $nodename,
);
};
warn $@ if $@;
@ -2001,12 +2080,14 @@ __PACKAGE__->register_method ({
if ($d->{type} eq 'lxc') {
return if PVE::LXC::check_running($vmid);
print STDERR "Starting CT $vmid\n";
$upid = PVE::API2::LXC::Status->vm_start({node => $nodename, vmid => $vmid });
$upid = PVE::API2::LXC::Status->vm_start(
{ node => $nodename, vmid => $vmid });
} elsif ($d->{type} eq 'qemu') {
$default_delay = 3; # to reduce load
return if PVE::QemuServer::check_running($vmid, 1);
print STDERR "Starting VM $vmid\n";
$upid = PVE::API2::Qemu->vm_start({node => $nodename, vmid => $vmid });
$upid =
PVE::API2::Qemu->vm_start({ node => $nodename, vmid => $vmid });
} else {
die "unknown VM type '$d->{type}'\n";
}
@ -2021,7 +2102,8 @@ __PACKAGE__->register_method ({
# use default delay to reduce load
my $delay = defined($d->{up}) ? int($d->{up}) : $default_delay;
if ($delay > 0) {
print STDERR "Waiting for $delay seconds (startup delay)\n" if $d->{up};
print STDERR "Waiting for $delay seconds (startup delay)\n"
if $d->{up};
for (my $i = 0; $i < $delay; $i++) {
sleep(1);
}
@ -2038,7 +2120,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('startall', undef, $authuser, $code);
}});
},
});
my $create_stop_worker = sub {
my ($nodename, $type, $vmid, $timeout, $force_stop) = @_;
@ -2078,7 +2161,8 @@ __PACKAGE__->register_method ({
node => get_standard_option('pve-node'),
vms => {
description => "Only consider Guests with these IDs.",
type => 'string', format => 'pve-vmid-list',
type => 'string',
format => 'pve-vmid-list',
optional => 1,
},
'force-stop' => {
@ -2088,7 +2172,8 @@ __PACKAGE__->register_method ({
optional => 1,
},
'timeout' => {
description => 'Timeout for each guest shutdown task. Depending on `force-stop`,'
description =>
'Timeout for each guest shutdown task. Depending on `force-stop`,'
. ' the shutdown gets then simply aborted or a hard-stop is forced.',
type => 'integer',
optional => 1,
@ -2146,7 +2231,8 @@ __PACKAGE__->register_method ({
my $timeout = int($d->{down} // $param->{timeout} // 180);
my $upid = eval {
$create_stop_worker->(
$nodename, $d->{type}, $vmid, $timeout, $param->{'force-stop'} // 1)
$nodename, $d->{type}, $vmid, $timeout, $param->{'force-stop'} // 1,
);
};
warn $@ if $@;
next if !$upid;
@ -2182,7 +2268,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('stopall', undef, $authuser, $code);
}});
},
});
my $create_suspend_worker = sub {
my ($nodename, $vmid) = @_;
@ -2191,9 +2278,7 @@ my $create_suspend_worker = sub {
return;
}
print STDERR "Suspending VM $vmid\n";
return PVE::API2::Qemu->vm_suspend(
{ node => $nodename, vmid => $vmid, todisk => 1 }
);
return PVE::API2::Qemu->vm_suspend({ node => $nodename, vmid => $vmid, todisk => 1 });
};
__PACKAGE__->register_method({
@ -2202,7 +2287,8 @@ __PACKAGE__->register_method ({
method => 'POST',
protected => 1,
permissions => {
description => "The 'VM.PowerMgmt' permission is required on '/' or on '/vms/<ID>' for each"
description =>
"The 'VM.PowerMgmt' permission is required on '/' or on '/vms/<ID>' for each"
. " ID passed via the 'vms' parameter. Additionally, you need 'VM.Config.Disk' on the"
. " '/vms/{vmid}' path and 'Datastore.AllocateSpace' for the configured state-storage(s)",
user => 'all',
@ -2215,7 +2301,8 @@ __PACKAGE__->register_method ({
node => get_standard_option('pve-node'),
vms => {
description => "Only consider Guests with these IDs.",
type => 'string', format => 'pve-vmid-list',
type => 'string',
format => 'pve-vmid-list',
optional => 1,
},
},
@ -2270,9 +2357,7 @@ __PACKAGE__->register_method ({
log_warn("skipping $vmid, only VMs can be suspended");
next;
}
my $upid = eval {
$create_suspend_worker->($nodename, $vmid)
};
my $upid = eval { $create_suspend_worker->($nodename, $vmid) };
warn $@ if $@;
next if !$upid;
@ -2307,8 +2392,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('suspendall', undef, $authuser, $code);
}});
},
});
my $create_migrate_worker = sub {
my ($nodename, $type, $vmid, $target, $with_local_disks) = @_;
@ -2328,7 +2413,8 @@ my $create_migrate_worker = sub {
my $invalidConditions = '';
if ($online && !$with_local_disks && scalar @{ $preconditions->{local_disks} }) {
$invalidConditions .= "\n Has local disks: ";
$invalidConditions .= join(', ', map { $_->{volid} } @{$preconditions->{local_disks}});
$invalidConditions .=
join(', ', map { $_->{volid} } @{ $preconditions->{local_disks} });
}
if (@{ $preconditions->{local_resources} }) {
@ -2342,7 +2428,9 @@ my $create_migrate_worker = sub {
$invalidConditions .= join(', ', $unavail_storages->@*);
}
if (my $unavail_resources = $not_allowed_nodes->{$target}->{'unavailable-resources'}) {
if (
my $unavail_resources = $not_allowed_nodes->{$target}->{'unavailable-resources'}
) {
$invalidConditions .= "\n Has unavailable resources: ";
$invalidConditions .= join(', ', $unavail_resources->@*);
}
@ -2380,7 +2468,8 @@ __PACKAGE__->register_method ({
proxyto => 'node',
protected => 1,
permissions => {
description => "The 'VM.Migrate' permission is required on '/' or on '/vms/<ID>' for each "
description =>
"The 'VM.Migrate' permission is required on '/' or on '/vms/<ID>' for each "
. "ID passed via the 'vms' parameter.",
user => 'all',
},
@ -2395,11 +2484,12 @@ __PACKAGE__->register_method ({
. "'max_workers' from datacenter.cfg. One of both must be set!",
optional => 1,
type => 'integer',
minimum => 1
minimum => 1,
},
vms => {
description => "Only consider Guests with these IDs.",
type => 'string', format => 'pve-vmid-list',
type => 'string',
format => 'pve-vmid-list',
optional => 1,
},
"with-local-disks" => {
@ -2440,8 +2530,10 @@ __PACKAGE__->register_method ({
my $datacenterconfig = cfs_read_file('datacenter.cfg');
# prefer parameter over datacenter cfg settings
my $maxWorkers = $param->{maxworkers} || $datacenterconfig->{max_workers} ||
die "either 'maxworkers' parameter or max_workers in datacenter.cfg must be set!\n";
my $maxWorkers =
$param->{maxworkers}
|| $datacenterconfig->{max_workers}
|| die "either 'maxworkers' parameter or max_workers in datacenter.cfg must be set!\n";
my $code = sub {
$rpcenv->{type} = 'priv'; # to start tasks in background
@ -2457,7 +2549,11 @@ __PACKAGE__->register_method ({
foreach my $vmid (sort keys %$vmlist) {
my $d = $vmlist->{$vmid};
my $pid;
eval { $pid = &$create_migrate_worker($nodename, $d->{type}, $vmid, $target, $with_local_disks); };
eval {
$pid = &$create_migrate_worker(
$nodename, $d->{type}, $vmid, $target, $with_local_disks,
);
};
warn $@ if $@;
next if !$pid;
@ -2490,7 +2586,8 @@ __PACKAGE__->register_method ({
return $rpcenv->fork_worker('migrateall', undef, $authuser, $code);
}});
},
});
__PACKAGE__->register_method({
name => 'get_etc_hosts',
@ -2514,7 +2611,7 @@ __PACKAGE__->register_method ({
digest => get_standard_option('pve-config-digest'),
data => {
type => 'string',
description => 'The content of /etc/hosts.'
description => 'The content of /etc/hosts.',
},
},
},
@ -2523,7 +2620,8 @@ __PACKAGE__->register_method ({
return PVE::INotify::read_file('etchosts');
}});
},
});
__PACKAGE__->register_method({
name => 'write_etc_hosts',
@ -2542,7 +2640,7 @@ __PACKAGE__->register_method ({
digest => get_standard_option('pve-config-digest'),
data => {
type => 'string',
description => 'The target content of /etc/hosts.'
description => 'The target content of /etc/hosts.',
},
},
},
@ -2552,17 +2650,22 @@ __PACKAGE__->register_method ({
code => sub {
my ($param) = @_;
PVE::Tools::lock_file('/var/lock/pve-etchosts.lck', undef, sub {
PVE::Tools::lock_file(
'/var/lock/pve-etchosts.lck',
undef,
sub {
if ($param->{digest}) {
my $hosts = PVE::INotify::read_file('etchosts');
PVE::Tools::assert_if_modified($hosts->{digest}, $param->{digest});
}
PVE::INotify::write_file('etchosts', $param->{data});
});
},
);
die $@ if $@;
return;
}});
},
});
# bash completion helper
@ -2686,6 +2789,7 @@ __PACKAGE__->register_method ({
}
return $res;
}});
},
});
1;

View file

@ -22,7 +22,8 @@ __PACKAGE__->register_method ({
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 => {
@ -107,7 +108,8 @@ __PACKAGE__->register_method ({
my $members = [];
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;
}
@ -127,7 +129,8 @@ __PACKAGE__->register_method ({
}
}
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;
}
}
@ -151,7 +154,8 @@ __PACKAGE__->register_method ({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'create_pool',
@ -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,10 +203,13 @@ __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({
name => 'update_pool_deprecated',
@ -209,10 +217,12 @@ __PACKAGE__->register_method ({
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,12 +230,14 @@ __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' => {
@ -236,7 +248,8 @@ __PACKAGE__->register_method ({
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,7 +261,8 @@ __PACKAGE__->register_method ({
my ($param) = @_;
return __PACKAGE__->update_pool($param);
}});
},
});
__PACKAGE__->register_method({
name => 'update_pool',
@ -256,7 +270,8 @@ __PACKAGE__->register_method ({
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,12 +282,14 @@ __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' => {
@ -283,7 +300,8 @@ __PACKAGE__->register_method ({
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,10 +371,13 @@ __PACKAGE__->register_method ({
}
cfs_write_file("user.cfg", $usercfg);
}, "update pools failed");
},
"update pools failed",
);
return;
}});
},
});
__PACKAGE__->register_method({
name => 'read_pool',
@ -359,7 +386,8 @@ __PACKAGE__->register_method ({
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 => {
@ -416,8 +444,8 @@ __PACKAGE__->register_method ({
my $pool_info = __PACKAGE__->index($param);
return $pool_info->[0];
}});
},
});
__PACKAGE__->register_method({
name => 'delete_pool_deprecated',
@ -428,19 +456,21 @@ __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({
name => 'delete_pool',
@ -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} || {};
@ -494,9 +525,12 @@ __PACKAGE__->register_method ({
PVE::AccessControl::delete_pool_acl($pool, $usercfg);
cfs_write_file("user.cfg", $usercfg);
}, "delete pool failed");
},
"delete pool failed",
);
return;
}});
},
});
1;

View file

@ -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);
@ -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 => {
@ -238,7 +239,8 @@ __PACKAGE__->register_method ({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'index',
@ -265,12 +267,10 @@ __PACKAGE__->register_method ({
my ($param) = @_;
return [
{ name => 'schedule_now' },
{ name => 'log' },
{ name => 'status' },
{ name => 'schedule_now' }, { name => 'log' }, { name => 'status' },
];
}});
},
});
__PACKAGE__->register_method({
name => 'job_status',
@ -312,14 +312,16 @@ __PACKAGE__->register_method ({
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,
@ -354,9 +356,9 @@ __PACKAGE__->register_method({
t => {
description => "Line text",
type => 'string',
}
}
}
},
},
},
},
code => sub {
my ($param) = @_;
@ -375,15 +377,20 @@ __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({
name => 'schedule_now',
@ -417,6 +424,7 @@ __PACKAGE__->register_method ({
PVE::ReplicationState::schedule_job_now($jobcfg);
}});
},
});
1;

View file

@ -54,7 +54,8 @@ __PACKAGE__->register_method ({
}
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'read',
@ -93,7 +94,8 @@ __PACKAGE__->register_method ({
$data->{digest} = $cfg->{digest};
return $data;
}});
},
});
__PACKAGE__->register_method({
name => 'create',
@ -127,7 +129,8 @@ __PACKAGE__->register_method ({
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,8 +163,8 @@ __PACKAGE__->register_method ({
PVE::ReplicationConfig::lock($code);
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'update',
@ -197,8 +202,8 @@ __PACKAGE__->register_method ({
if ($delete) {
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,7 +218,8 @@ __PACKAGE__->register_method ({
PVE::ReplicationConfig::lock($code);
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'delete',
@ -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;

View file

@ -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";
@ -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',
@ -214,7 +216,8 @@ __PACKAGE__->register_method({
];
return $res;
}});
},
});
__PACKAGE__->register_method({
name => 'service_state',
@ -254,7 +257,8 @@ __PACKAGE__->register_method ({
desc => $si->{desc},
%$state,
};
}});
},
});
__PACKAGE__->register_method({
name => 'service_start',
@ -293,7 +297,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('srvstart', $param->{service}, $user, $realcmd);
}});
},
});
__PACKAGE__->register_method({
name => 'service_stop',
@ -332,7 +337,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('srvstop', $param->{service}, $user, $realcmd);
}});
},
});
__PACKAGE__->register_method({
name => 'service_restart',
@ -368,7 +374,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('srvrestart', $param->{service}, $user, $realcmd);
}});
},
});
__PACKAGE__->register_method({
name => 'service_reload',
@ -405,4 +412,5 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('srvreload', $param->{service}, $user, $realcmd);
}});
},
});

View file

@ -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,20 +92,27 @@ 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);
}
}
@ -217,15 +224,16 @@ __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({
name => 'update',
@ -245,8 +253,8 @@ __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' },
@ -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,12 +279,15 @@ __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({
name => 'set',
@ -322,12 +334,14 @@ __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({
name => 'delete',
@ -351,6 +365,7 @@ __PACKAGE__->register_method ({
return if !-e $subscription_file;
unlink($subscription_file) or die "cannot delete subscription key: $!";
return undef;
}});
},
});
1;

View file

@ -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,16 +118,16 @@ __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}" }],
@ -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',
@ -272,10 +277,10 @@ __PACKAGE__->register_method({
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 {
@ -316,14 +322,16 @@ __PACKAGE__->register_method({
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 => {
@ -371,9 +380,9 @@ __PACKAGE__->register_method({
t => {
description => "Line text",
type => 'string',
}
}
}
},
},
},
},
code => sub {
my ($param) = @_;
@ -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 => {
@ -418,6 +428,7 @@ __PACKAGE__->register_method({
'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',
@ -510,8 +523,7 @@ __PACKAGE__->register_method({
}
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
@ -524,6 +536,7 @@ __PACKAGE__->register_method({
}
return $task;
}});
},
});
1;

View file

@ -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.",
@ -80,7 +84,8 @@ __PACKAGE__->register_method ({
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};
}
@ -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,7 +175,8 @@ __PACKAGE__->register_method ({
$taskid = $local_vmids->[0] if scalar(@{$local_vmids}) == 1;
return $rpcenv->fork_worker('vzdump', $taskid, $user, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'defaults',
@ -178,9 +184,9 @@ __PACKAGE__->register_method ({
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,8 +234,7 @@ __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',
);
}
@ -249,13 +254,16 @@ __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({
name => 'extractconfig',
@ -263,7 +271,8 @@ __PACKAGE__->register_method ({
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;

View file

@ -25,10 +25,12 @@ 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;
}
@ -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};
@ -206,7 +223,13 @@ sub run_spiceterm {
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} = '/';

View file

@ -79,8 +79,13 @@ sub read_aplinfo_from_fh {
$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};
@ -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 = $@;
@ -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");
};
@ -236,9 +239,7 @@ 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);
push @dlerr, $info->{url};

View file

@ -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 {

View file

@ -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);
@ -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'.");
}
@ -252,8 +256,8 @@ sub check_pve_packages {
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->@*)
);
. " 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;
@ -393,14 +396,13 @@ 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;
@ -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"
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>"
);
. " <https://pve.proxmox.com/wiki/Category:Ceph_Upgrade>");
}
} else {
log_fail("unable to determine local Ceph version!");
@ -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,13 +600,14 @@ 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"
@ -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;
}
@ -724,7 +741,7 @@ sub check_custom_pool_roles {
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,15 +1040,19 @@ 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.");
}
}
@ -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,7 +1123,10 @@ 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."
@ -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;
}
@ -1253,7 +1294,8 @@ 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 "
. join("\n ", @strange_list)
. "\n Please ensure these repositories are shipping compatible packages for the upgrade!"
);
}
@ -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 "
. 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 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 {
@ -1291,7 +1335,8 @@ 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')) {
@ -1303,13 +1348,18 @@ sub check_time_sync {
} 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'))) {
} 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!");
}
}
@ -1330,18 +1380,14 @@ sub check_bootloader {
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,"
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;
@ -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)"
);
}
}
@ -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,12 +1582,17 @@ __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', [], {}];

View file

@ -41,14 +41,15 @@ __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({
name => 'available',
@ -64,7 +65,7 @@ __PACKAGE__->register_method ({
enum => ['system', 'mail', 'turnkeylinux'],
optional => 1,
},
}
},
},
returns => { type => 'null' },
code => sub {
@ -81,7 +82,8 @@ __PACKAGE__->register_method ({
}
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'index',
@ -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,19 +129,25 @@ __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({
name => 'remove',
@ -179,14 +190,13 @@ __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);
@ -195,10 +205,15 @@ my $print_list = sub {
our $cmddef = {
update => [__PACKAGE__, 'update', []],
download => [ 'PVE::API2::Nodes::Nodeinfo', 'apl_download', [ 'storage', 'template'], { node => $nodename } ],
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 }]
remove => [__PACKAGE__, 'remove', ['template_path'], { node => $nodename }],
};
1;

View file

@ -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') } // {};
@ -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"
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";
}
}
@ -209,10 +219,11 @@ __PACKAGE__->register_method ({
['apt-get', '-q', 'update'],
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,11 +253,12 @@ __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({
name => 'status',
@ -266,7 +279,8 @@ __PACKAGE__->register_method ({
timeout => 15,
);
return undef;
}});
},
});
my $get_storages = sub {
my ($fs, $is_default) = @_;
@ -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";
}
}
@ -385,7 +399,8 @@ __PACKAGE__->register_method ({
};
return $rpcenv->fork_worker('cephdestroyfs', $fs_name, $user, $worker);
}});
},
});
__PACKAGE__->register_method({
name => 'osd-details',
@ -428,7 +443,8 @@ __PACKAGE__->register_method ({
}
$res->{verbose} = 1 if $param->{verbose};
return $res;
}});
},
});
my $format_osddetails = sub {
my ($data, $schema, $options) = @_;
@ -445,7 +461,8 @@ my $format_osddetails = sub {
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 {
@ -456,9 +473,16 @@ my $format_osddetails = sub {
our $cmddef = {
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,15 +497,25 @@ our $cmddef = {
'percent_used',
'bytes_used',
],
$options);
}, $PVE::RESTHandler::standard_output_options],
$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 {
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' },
@ -491,10 +525,16 @@ our $cmddef = {
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,
],
},
@ -502,19 +542,22 @@ our $cmddef = {
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],
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],
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],
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],

View file

@ -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 }],
},
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 } ],
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,72 +223,139 @@ 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 {
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],
},
$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 ],
},
$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']],
@ -262,11 +363,17 @@ our $cmddef = {
},
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";
} ],
},
],
};

View file

@ -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;
@ -253,7 +257,6 @@ sub extract_path_info {
return $info;
}
my $path_properties = {};
my $api_path_property = {
@ -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);
};
@ -295,10 +299,12 @@ my $handle_streamed_response = sub {
my ($fh, $path, $encoding, $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});
}
}
@ -420,7 +427,8 @@ __PACKAGE__->register_method ({
PVE::CLIFormatter::print_api_result($res, $schema, ['capabilities', 'name'], $stdopts);
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'get',
@ -438,7 +446,8 @@ __PACKAGE__->register_method ({
call_api_method('get', $param);
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'set',
@ -456,7 +465,8 @@ __PACKAGE__->register_method ({
call_api_method('set', $param);
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'create',
@ -474,7 +484,8 @@ __PACKAGE__->register_method ({
call_api_method('create', $param);
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'delete',
@ -492,7 +503,8 @@ __PACKAGE__->register_method ({
call_api_method('delete', $param);
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'usage',
@ -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 });
my $schema =
to_json($info->{returns}, { utf8 => 1, canonical => 1, pretty => 1 });
print "RETURNS: $schema\n";
}
}
@ -554,12 +578,13 @@ __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']],

View file

@ -51,16 +51,20 @@ __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,
@ -94,7 +99,8 @@ __PACKAGE__->register_method ({
die "no volumes specified\n"
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,7 +116,8 @@ __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;
}
@ -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};
@ -158,21 +167,26 @@ __PACKAGE__->register_method ({
print to_json($local_snapshots) . "\n";
return undef;
}});
},
});
__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();
@ -213,13 +228,15 @@ __PACKAGE__->register_method ({
PVE::Replication::prepare($storecfg, $volids, $jobid, $last_sync, undef, $logfunc);
return undef;
}});
},
});
__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,7 +281,8 @@ __PACKAGE__->register_method ({
}
return undef;
}});
},
});
__PACKAGE__->register_method({
name => 'enable',
@ -284,7 +302,8 @@ __PACKAGE__->register_method ({
$param->{disable} = 0;
return PVE::API2::ReplicationConfig->update($param);
}});
},
});
__PACKAGE__->register_method({
name => 'disable',
@ -304,21 +323,24 @@ __PACKAGE__->register_method ({
$param->{disable} = 1;
return PVE::API2::ReplicationConfig->update($param);
}});
},
});
__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',
@ -335,7 +357,8 @@ __PACKAGE__->register_method ({
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,23 +418,32 @@ 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 ],
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}); }],
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' } ],
'create-local-job' =>
['PVE::API2::ReplicationConfig', 'create', ['id', 'target'], { type => 'local' }],
enable => [__PACKAGE__, 'enable', ['id'], {}],
disable => [__PACKAGE__, 'disable', ['id'], {}],

View file

@ -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,19 +46,27 @@ __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 {
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 }],

View file

@ -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;

View file

@ -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();
@ -113,15 +115,16 @@ sub get_available_ceph_release_codenames($include_unstable_releases = 0) {
return $include_unstable_releases
? [sort keys $available_releases->%*]
: [ grep { !$available_releases->{$_}->{unsupported} } 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"

View file

@ -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;
@ -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;
}
@ -199,7 +219,6 @@ sub get_cluster_mds_state {
my $mds_dump = $rados->mon_command({ prefix => 'mds stat' });
my $fsmap = $mds_dump->{fsmap};
foreach my $mds (@{ $fsmap->{standbys} }) {
$add_state->($mds);
}
@ -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);

View file

@ -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) = @_;
@ -197,7 +200,6 @@ sub check_ceph_installed {
return 1;
}
sub check_ceph_configured {
check_ceph_inited();
@ -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');
@ -438,12 +440,12 @@ 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 *' ");
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) {
run_command("cp $pve_ckeyring_path $ckeyring_path");
@ -520,27 +522,28 @@ 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 => {
@ -554,17 +557,20 @@ sub get_possible_osd_flags {
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,
},
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,
},
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,
},
@ -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;

View file

@ -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 {
@ -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();
}

View file

@ -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;
@ -102,12 +111,16 @@ sub list_acme_accounts {
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;
}

View file

@ -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;
}

View file

@ -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};

View file

@ -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,7 +67,8 @@ 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
@ -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 {
@ -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 $@;
}

View file

@ -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) = @_;
@ -86,7 +92,8 @@ my $confdesc = {
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,
@ -186,7 +193,7 @@ for my $i (0..$MAXDOMAINS) {
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;
@ -279,9 +284,8 @@ sub get_acme_conf {
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;

View file

@ -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();

View file

@ -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";
});
},
);
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,9 +131,7 @@ my $init_report_cmds = sub {
volumes => {
order => 90,
cmds => [
'pvs',
'lvs',
'vgs',
'pvs', 'lvs', 'vgs',
],
},
};
@ -189,7 +189,8 @@ 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) {
my $s = $def->{$section};

View file

@ -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');

View file

@ -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,7 +150,10 @@ 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.',
);
}
}
@ -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

View file

@ -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,13 +104,19 @@ sub run {
# my $scheduled_jobs = PVE::Jobs::get_pending() or return;
# forked { PVE::Jobs::run_jobs($scheduled_jobs) }
$fork->('replication', sub {
$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;
};

View file

@ -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,7 +241,8 @@ 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 $res = PVE::AutoBalloon::compute_alg1($vmstatus, $goal, $maxchange);
@ -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);
@ -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;
@ -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;
@ -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);
@ -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;
}
@ -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;
}

View file

@ -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);

View file

@ -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',
@ -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;

View file

@ -22,7 +22,8 @@ sub type {
sub properties {
return {
organization => {
description => "The InfluxDB organization. Only necessary when using the http v2 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,7 +34,8 @@ sub properties {
optional => 1,
},
token => {
description => "The InfluxDB access token. Only necessary when using the http v2 api."
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,
@ -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,6 +67,7 @@ sub properties {
},
};
}
sub options {
return {
server => {},
@ -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 = ();
@ -298,7 +302,9 @@ 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);
build_influxdb_payload(
$class, $txn, $value, $ctime, $tags, $excluded, $measurement, $key,
);
} else {
push @values, get_recursive_values($value);
}
@ -429,5 +435,4 @@ sub on_delete_hook {
return undef;
}
1;

View file

@ -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,7 +33,8 @@ my $defaultData = {
optional => 1,
},
server => {
type => 'string', format => 'address',
type => 'string',
format => 'address',
description => "server dns name or IP address",
},
port => {

View file

@ -31,12 +31,15 @@ use PVE::API2Tools;
# and `$digest:$counter` values converted from vzdump.cron
# TODO move to a better place once cycle
# Jobs::VZDump -> API2::VZDump -> API2::Backups -> Jobs::VZDump is broken..
PVE::JSONSchema::register_standard_option('pve-backup-jobid', {
PVE::JSONSchema::register_standard_option(
'pve-backup-jobid',
{
type => 'string',
description => "The job ID.",
maxLength => 50,
pattern => '\S+',
});
},
);
my @posix_filesystems = qw(ext3 ext4 nfs nfs4 reiserfs xfs);
@ -180,14 +183,15 @@ my $parse_prune_backups_maxfiles = sub {
my $maxfiles = delete $param->{maxfiles};
my $prune_backups = $param->{'prune-backups'};
debugmsg('warn', "both 'maxfiles' and 'prune-backups' defined as ${kind} - ignoring 'maxfiles'")
if defined($maxfiles) && defined($prune_backups);
debugmsg(
'warn',
"both 'maxfiles' and 'prune-backups' defined as ${kind} - ignoring 'maxfiles'",
) if defined($maxfiles) && defined($prune_backups);
if (defined($prune_backups)) {
return $prune_backups if ref($prune_backups) eq 'HASH'; # already parsed
$param->{'prune-backups'} = PVE::JSONSchema::parse_property_string(
'prune-backups',
$prune_backups
'prune-backups', $prune_backups,
);
} elsif (defined($maxfiles)) {
if ($maxfiles) {
@ -214,7 +218,8 @@ sub storage_info {
scfg => $scfg,
};
$info->{'prune-backups'} = PVE::JSONSchema::parse_property_string('prune-backups', $scfg->{'prune-backups'})
$info->{'prune-backups'} =
PVE::JSONSchema::parse_property_string('prune-backups', $scfg->{'prune-backups'})
if defined($scfg->{'prune-backups'});
if (PVE::Storage::storage_has_feature($cfg, $storage, 'backup-provider')) {
@ -306,7 +311,6 @@ sub check_vmids {
return $res;
}
sub read_vzdump_defaults {
my $fn = "/etc/vzdump.conf";
@ -411,40 +415,41 @@ my sub build_guest_table {
columns => [
{
label => "VMID",
id => "vmid"
id => "vmid",
},
{
label => "Name",
id => "name"
id => "name",
},
{
label => "Status",
id => "status"
id => "status",
},
{
label => "Time",
id => "time",
renderer => "duration"
renderer => "duration",
},
{
label => "Size",
id => "size",
renderer => "human-bytes"
renderer => "human-bytes",
},
{
label => "Filename",
id => "filename"
id => "filename",
},
]
],
},
data => []
data => [],
};
for my $task (@$task_list) {
my $successful = $task->{state} eq 'ok';
my $size = $successful ? $task->{size} : 0;
my $filename = $successful ? $task->{target} : undef;
push @{$table->{data}}, {
push @{ $table->{data} },
{
"vmid" => int($task->{vmid}),
"name" => $task->{hostname},
"status" => $task->{state},
@ -524,12 +529,10 @@ sub send_notification {
$text_log_part .= read_backup_task_logs($tasklist);
$text_log_part .= $detail_post if defined($detail_post);
if (length($text_log_part) > MAX_LOG_SIZE)
{
if (length($text_log_part) > MAX_LOG_SIZE) {
# Let's limit the maximum length of included logs
$text_log_part = "Log output was too long to be sent. ".
"See Task History for details!\n";
};
$text_log_part = "Log output was too long to be sent. " . "See Task History for details!\n";
}
my $template_data = PVE::Notify::common_template_data();
$template_data->{error} = $err;
@ -571,7 +574,7 @@ sub send_notification {
$mailto,
undef,
undef,
"vzdump backup tool"
"vzdump backup tool",
);
my $endpoints = [$endpoint_name];
@ -579,29 +582,21 @@ sub send_notification {
# Add a matcher that matches all notifications, set our
# newly created target as a target.
$notification_config->add_matcher(
"<matcher-$endpoint_name>",
$endpoints,
"<matcher-$endpoint_name>", $endpoints,
);
PVE::Notify::notify(
$severity,
"vzdump",
$template_data,
$fields,
$notification_config
$severity, "vzdump", $template_data, $fields, $notification_config,
);
}
} else {
# We use the 'new' system, or we are set to 'auto' and
# no email addresses were configured.
PVE::Notify::notify(
$severity,
"vzdump",
$template_data,
$fields,
$severity, "vzdump", $template_data, $fields,
);
}
};
}
sub new {
my ($class, $cmdline, $opts, $skiplist) = @_;
@ -629,8 +624,9 @@ sub new {
foreach my $k (keys %$defaults) {
next if $k eq 'exclude-path' || $k eq 'prune-backups'; # dealt with separately
if ($k eq 'dumpdir' || $k eq 'storage') {
$opts->{$k} = $defaults->{$k} if !defined ($opts->{dumpdir}) &&
!defined ($opts->{storage});
$opts->{$k} = $defaults->{$k}
if !defined($opts->{dumpdir})
&& !defined($opts->{storage});
} elsif (!defined($opts->{$k})) {
$opts->{$k} = $defaults->{$k};
} elsif ($k eq 'performance') {
@ -658,11 +654,7 @@ sub new {
}
if ($opts->{stdexcludes}) {
push @$findexcl,
'/tmp/?*',
'/var/tmp/?*',
'/var/run/?*.pid',
;
push @$findexcl, '/tmp/?*', '/var/tmp/?*', '/var/run/?*.pid',;
}
foreach my $p (@plugins) {
@ -755,8 +747,10 @@ sub get_mount_info {
my $parser = sub {
my $line = shift;
if (my ($fsid, $fstype, undef, $mp) = $line =~
m!(\S+.*)\s+(\S+)\s+\d+\s+\-?\d+\s+\d+\s+(\d+%|-)\s+(/.*)$!) {
if (
my ($fsid, $fstype, undef, $mp) =
$line =~ m!(\S+.*)\s+(\S+)\s+\d+\s+\-?\d+\s+\d+\s+(\d+%|-)\s+(/.*)$!
) {
$res = {
device => $fsid,
fstype => $fstype,
@ -765,7 +759,9 @@ sub get_mount_info {
}
};
eval { PVE::Tools::run_command($cmd, errfunc => sub {}, outfunc => $parser); };
eval {
PVE::Tools::run_command($cmd, errfunc => sub { }, outfunc => $parser);
};
warn $@ if $@;
return $res;
@ -952,17 +948,19 @@ sub exec_backup_task {
# for now we deny backups of a running ha managed service in *stop* mode
# as it interferes with the HA stack (started services should not stop).
if ($opts->{mode} eq 'stop' &&
PVE::HA::Config::vm_is_ha_managed($vmid, 'started'))
{
die "Cannot execute a backup with stop mode on a HA managed and".
" enabled Service. Use snapshot mode or disable the Service.\n";
if (
$opts->{mode} eq 'stop'
&& PVE::HA::Config::vm_is_ha_managed($vmid, 'started')
) {
die "Cannot execute a backup with stop mode on a HA managed and"
. " enabled Service. Use snapshot mode or disable the Service.\n";
}
my $tmplog = "$logdir/$vmtype-$vmid.log";
my $bkname = "vzdump-$vmtype-$vmid";
my $basename = $bkname . strftime("-%Y_%m_%d-%H_%M_%S", localtime($task->{backup_time}));
my $basename =
$bkname . strftime("-%Y_%m_%d-%H_%M_%S", localtime($task->{backup_time}));
my $prune_options = $opts->{'prune-backups'};
@ -984,21 +982,23 @@ sub exec_backup_task {
$count = grep { !$_->{protected} } @backups;
$protected_count = scalar(@backups) - $count;
} else {
$count = grep { !$_->{mark} || $_->{mark} ne "protected" } get_backup_file_list($opts->{dumpdir}, $bkname)->@*;
$count = grep { !$_->{mark} || $_->{mark} ne "protected" }
get_backup_file_list($opts->{dumpdir}, $bkname)->@*;
}
if ($opts->{protected}) {
my $max_protected = PVE::Storage::get_max_protected_backups(
$opts->{scfg},
$opts->{storage},
$opts->{scfg}, $opts->{storage},
);
if ($max_protected > -1 && $protected_count >= $max_protected) {
die "The number of protected backups per guest is limited to $max_protected ".
"on storage '$opts->{storage}'\n";
die
"The number of protected backups per guest is limited to $max_protected "
. "on storage '$opts->{storage}'\n";
}
} elsif ($count >= $backup_limit) {
die "There is a max backup limit of $backup_limit enforced by the target storage ".
"or the vzdump parameters. Either increase the limit or delete old backups.\n";
die
"There is a max backup limit of $backup_limit enforced by the target storage "
. "or the vzdump parameters. Either increase the limit or delete old backups.\n";
}
}
@ -1038,23 +1038,29 @@ sub exec_backup_task {
} else {
# dumpdir is posix? then use it as temporary dir
my $info = get_mount_info($opts->{dumpdir});
if ($vmtype eq 'qemu' ||
grep ($_ eq $info->{fstype}, @posix_filesystems)) {
if (
$vmtype eq 'qemu'
|| grep ($_ eq $info->{fstype}, @posix_filesystems)
) {
$task->{tmpdir} = "$opts->{dumpdir}/$basename.tmp";
} else {
$task->{tmpdir} = "/var/tmp/vzdumptmp${pid}_$vmid";
debugmsg ('info', "filesystem type on dumpdir is '$info->{fstype}' -" .
"using $task->{tmpdir} for temporary files", $logfd);
debugmsg(
'info',
"filesystem type on dumpdir is '$info->{fstype}' -"
. "using $task->{tmpdir} for temporary files",
$logfd,
);
}
}
rmtree $task->{tmpdir};
mkdir $task->{tmpdir};
-d $task->{tmpdir} ||
die "unable to create temporary directory '$task->{tmpdir}'";
-d $task->{tmpdir}
|| die "unable to create temporary directory '$task->{tmpdir}'";
$logfd = IO::File->new (">$tmplog") ||
die "unable to create log file '$tmplog'";
$logfd = IO::File->new(">$tmplog")
|| die "unable to create log file '$tmplog'";
$task->{dumpdir} = $opts->{dumpdir};
$task->{storeid} = $opts->{storage};
@ -1129,7 +1135,6 @@ sub exec_backup_task {
$cleanup->{restart} = 1;
}
} elsif ($mode eq 'suspend') {
$plugin->prepare($task, $vmid, $mode);
@ -1205,15 +1210,16 @@ sub exec_backup_task {
my $archive_txt = 'vzdump';
$archive_txt = 'Proxmox Backup Server' if $self->{opts}->{pbs};
$archive_txt = $self->{'backup-provider'}->provider_name() if $self->{'backup-provider'};
$archive_txt = $self->{'backup-provider'}->provider_name()
if $self->{'backup-provider'};
debugmsg('info', "creating $archive_txt archive '$task->{target}'", $logfd);
$plugin->archive($task, $vmid, $task->{tmptar}, $comp);
if ($self->{'backup-provider'} || $self->{opts}->{pbs}) {
# size is added to task struct in guest vzdump plugins
} else {
rename ($task->{tmptar}, $task->{target}) ||
die "unable to rename '$task->{tmptar}' to '$task->{target}'\n";
rename($task->{tmptar}, $task->{target})
|| die "unable to rename '$task->{tmptar}' to '$task->{target}'\n";
# determine size
$task->{size} = (-s $task->{target}) || 0;
@ -1223,7 +1229,9 @@ sub exec_backup_task {
# Mark as protected before pruning.
if (my $storeid = $opts->{storage}) {
my $volname = $opts->{pbs} || $self->{'backup-provider'} ? $task->{target}
my $volname =
$opts->{pbs} || $self->{'backup-provider'}
? $task->{target}
: basename($task->{target});
my $volid = "${storeid}:backup/${volname}";
@ -1233,7 +1241,9 @@ sub exec_backup_task {
if (my $err = $@) {
debugmsg('warn', "unable to add notes - $err", $logfd);
} else {
eval { PVE::Storage::update_volume_attribute($cfg, $volid, 'notes', $notes) };
eval {
PVE::Storage::update_volume_attribute($cfg, $volid, 'notes', $notes);
};
debugmsg('warn', "unable to add notes - $@", $logfd) if $@;
}
}
@ -1246,7 +1256,8 @@ sub exec_backup_task {
}
if ($opts->{remove}) {
my $keepstr = join(', ', map { "$_=$prune_options->{$_}" } sort keys %$prune_options);
my $keepstr =
join(', ', map { "$_=$prune_options->{$_}" } sort keys %$prune_options);
debugmsg('info', "prune older backups with retention: $keepstr", $logfd);
my $pruned = 0;
if (!defined($opts->{storage})) {
@ -1278,7 +1289,8 @@ sub exec_backup_task {
}
if ($self->{'backup-provider'}) {
my $cleanup_result = $self->{'backup-provider'}->backup_cleanup($vmid, $vmtype, 1, {});
my $cleanup_result =
$self->{'backup-provider'}->backup_cleanup($vmid, $vmtype, 1, {});
$task->{size} = $cleanup_result->{stats}->{'archive-size'};
}
$self->run_hook_script('backup-end', $task, $logfd);
@ -1342,8 +1354,8 @@ sub exec_backup_task {
if ($self->{'backup-provider'}) {
eval {
$self->{'backup-provider'}->backup_cleanup(
$vmid, $task->{vmtype}, 0, { error => $err });
$self->{'backup-provider'}
->backup_cleanup($vmid, $task->{vmtype}, 0, { error => $err });
};
debugmsg('warn', "backup cleanup for external provider failed - $@") if $@;
}
@ -1417,7 +1429,8 @@ sub exec_backup {
$plugin = $vzdump_plugins->{$guest_type};
next if !$rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], $opts->{all});
}
push @$tasklist, {
push @$tasklist,
{
mode => $opts->{mode},
plugin => $plugin,
state => 'todo',
@ -1492,7 +1505,6 @@ sub exec_backup {
unlink $pidfile;
}
sub option_exists {
my $key = shift;
return defined($confdesc->{$key});
@ -1565,8 +1577,10 @@ sub stop_running_backups {
my $task = PVE::Tools::upid_decode($upid);
if (PVE::ProcFSTools::check_process_running($task->{pid}, $task->{pstart}) &&
PVE::ProcFSTools::read_proc_starttime($task->{pid}) == $task->{pstart}) {
if (
PVE::ProcFSTools::check_process_running($task->{pid}, $task->{pstart})
&& PVE::ProcFSTools::read_proc_starttime($task->{pid}) == $task->{pstart}
) {
kill(15, $task->{pid});
# wait max 15 seconds to shut down (else, do nothing for now)
my $i;

View file

@ -69,7 +69,6 @@ my $defmap = {
'li' => 'de-ch',
};
my $mirrors = PVE::Tools::debmirrors();
foreach my $cc (keys %$mirrors) {
die "undefined country code '$cc'" if !defined($country->{$cc});

View file

@ -35,7 +35,6 @@ my $tree = {
],
};
# Check if all the grep and casts are correct
my @belong_to_B = (4, 5);
my @not_belong_to_B = (-1, 1, 10, 15);
@ -54,7 +53,6 @@ foreach (@not_belong_to_B) {
);
}
my $double_nodes_tree = {
nodes => [
{
@ -64,8 +62,8 @@ 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");
@ -76,5 +74,4 @@ is (
"Early-return false if there's no/empty node tree",
);
done_testing(@belong_to_B + @not_belong_to_B + 2);

View file

@ -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} //= {};
@ -284,7 +284,7 @@ sub setup {
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;

View file

@ -112,6 +112,7 @@ sub abapply {
}
my $tcount = 0;
sub absim {
my ($vmstatus, $goal, %expect) = @_;
@ -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}));
}
}
@ -178,8 +179,9 @@ sub abtest {
sub MB {
my $mb = shift;
return $mb * 1000 * 1000;
};
}
sub GB {
my $gb = shift;
return $gb * 1000 * 1000 * 1000;
};
}

View file

@ -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/;
};

View file

@ -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},
@ -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);

View file

@ -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',

View file

@ -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,7 +39,8 @@ 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} }) {
if (!grep { $_ eq $volid } @$volumes) {
@ -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);

View file

@ -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',

View file

@ -46,7 +46,7 @@ my $vmlist = {
'type' => 'lxc',
'node' => 'node2',
},
}
},
};
my $pools = {
@ -57,53 +57,62 @@ 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],
},
param => {
all => 1,
}
});
},
},
);
$addtest->('Test all guests with node limit', {
$addtest->(
'Test all guests with node limit',
{
expected => {
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],
@ -111,10 +120,13 @@ $addtest->('Test exclude', {
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],
},
@ -122,59 +134,76 @@ $addtest->('Test exclude with node limit', {
all => 1,
exclude => '100, 102, 200, 202',
node => 'node1',
}
});
},
},
);
$addtest->('Test pool members', {
$addtest->(
'Test pool members',
{
expected => {
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],
},
param => {
pool => 'testpool',
node => 'node2',
}
});
},
},
);
$addtest->('Test selected VMIDs', {
$addtest->(
'Test selected VMIDs',
{
expected => {
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],
},
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],
@ -182,9 +211,9 @@ $addtest->('Test VMID not present in vmlist', {
},
param => {
vmid => '100, 201, 212, 7654',
}
});
},
},
);
for my $test (@{$tests}) {
my $testname = $test->{name};

View file

@ -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',
],
},
},

View file

@ -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";
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