auto-format code using perltidy with Proxmox style guide
using the new top-level `make tidy` target, which calls perltidy via our wrapper to enforce the desired style as closely as possible. Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
parent
a750c44c42
commit
138cae897a
88 changed files with 19180 additions and 17920 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
139
PVE/API2/Cluster/BulkActions.pm
Normal file
139
PVE/API2/Cluster/BulkActions.pm
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package PVE::API2::Cluster::BulkActions;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use Storable qw(dclone);
|
||||
use JSON;
|
||||
|
||||
use PVE::Exception qw(raise_param_exc);
|
||||
use PVE::Tools qw(extract_param);
|
||||
use PVE::JSONSchema qw(get_standard_option);
|
||||
use PVE::RESTHandler;
|
||||
|
||||
use base qw(PVE::RESTHandler);
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
name => 'index',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
description => 'Index for cluster-wide bulk-action API endpoints.',
|
||||
permissions => { user => 'all' },
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {},
|
||||
},
|
||||
returns => {
|
||||
type => 'array',
|
||||
items => {
|
||||
type => 'object',
|
||||
properties => {},
|
||||
},
|
||||
links => [ { rel => 'child', href => '{name}' } ],
|
||||
},
|
||||
code => sub {
|
||||
my $result = [
|
||||
{ name => 'migrate' },
|
||||
{ name => 'start' },
|
||||
{ name => 'stop' },
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
});
|
||||
|
||||
my $guest_format = {
|
||||
vmid => {
|
||||
defau
|
||||
},
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method ({
|
||||
name => 'migrate',
|
||||
path => 'migrate',
|
||||
method => 'POST',
|
||||
description => 'Returns a list of all entities that can be used as notification targets' .
|
||||
' (endpoints and groups).',
|
||||
permissions => {
|
||||
description => "The 'VM.Migrate' permission is required on '/' or on '/vms/<ID>' for each "
|
||||
."ID passed via the 'vms' parameter.",
|
||||
user => 'all',
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
'guests' => {
|
||||
type => 'array',
|
||||
description => '',
|
||||
items => {
|
||||
type => 'string',
|
||||
format => $guest_format,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
type => 'string',
|
||||
items => {
|
||||
type => 'object',
|
||||
properties => {
|
||||
name => {
|
||||
description => 'Name of the endpoint/group.',
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
},
|
||||
'type' => {
|
||||
description => 'Type of the endpoint or group.',
|
||||
type => 'string',
|
||||
enum => [qw(sendmail gotify group)],
|
||||
},
|
||||
'comment' => {
|
||||
description => 'Comment',
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
links => [ { rel => 'child', href => '{name}' } ],
|
||||
},
|
||||
code => sub {
|
||||
my $config = PVE::Notify::read_config();
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $targets = eval {
|
||||
my $result = [];
|
||||
|
||||
for my $target (@{$config->get_sendmail_endpoints()}) {
|
||||
push @$result, {
|
||||
name => $target->{name},
|
||||
comment => $target->{comment},
|
||||
type => 'sendmail',
|
||||
};
|
||||
}
|
||||
|
||||
for my $target (@{$config->get_gotify_endpoints()}) {
|
||||
push @$result, {
|
||||
name => $target->{name},
|
||||
comment => $target->{comment},
|
||||
type => 'gotify',
|
||||
};
|
||||
}
|
||||
|
||||
for my $target (@{$config->get_groups()}) {
|
||||
push @$result, {
|
||||
name => $target->{name},
|
||||
comment => $target->{comment},
|
||||
type => 'group',
|
||||
};
|
||||
}
|
||||
|
||||
$result
|
||||
};
|
||||
|
||||
raise_api_error($@) if $@;
|
||||
|
||||
return filter_entities_by_privs($rpcenv, $targets);
|
||||
}
|
||||
});
|
||||
|
||||
1;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) };
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -47,11 +47,10 @@ __PACKAGE__->register_method ({
|
|||
my ($param) = @_;
|
||||
|
||||
my $res = [
|
||||
{ type => 'pci' },
|
||||
{ type => 'usb' },
|
||||
{ type => 'pci' }, { type => 'usb' },
|
||||
];
|
||||
|
||||
return $res;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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->%*)];
|
||||
}
|
||||
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@ __PACKAGE__->register_method({
|
|||
my ($param) = @_;
|
||||
|
||||
return PVE::SysFSTools::scan_usb();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
102
PVE/API2/Pool.pm
102
PVE/API2/Pool.pm
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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} = '/';
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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', [], {}];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
} ],
|
||||
},
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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']],
|
||||
|
|
|
|||
106
PVE/CLI/pvesr.pm
106
PVE/CLI/pvesr.pm
|
|
@ -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'], {}],
|
||||
|
|
|
|||
|
|
@ -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 }],
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,18 +46,22 @@ sub transactions_start {
|
|||
|
||||
my $transactions = [];
|
||||
|
||||
foreach_plug($cfg, sub {
|
||||
foreach_plug(
|
||||
$cfg,
|
||||
sub {
|
||||
my ($plugin, $id, $plugin_config) = @_;
|
||||
|
||||
my $connection = $plugin->_connect($plugin_config, $id);
|
||||
|
||||
push @$transactions, {
|
||||
push @$transactions,
|
||||
{
|
||||
connection => $connection,
|
||||
cfg => $plugin_config,
|
||||
id => $id,
|
||||
data => '',
|
||||
};
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,10 +67,12 @@ sub auth_handler {
|
|||
my $require_auth = 1;
|
||||
|
||||
# explicitly allow some calls without auth
|
||||
if (($rel_uri eq '/access/domains' && $method eq 'GET') ||
|
||||
($rel_uri eq '/access/ticket' && ($method eq 'GET' || $method eq 'POST')) ||
|
||||
($rel_uri eq '/access/openid/login' && $method eq 'POST') ||
|
||||
($rel_uri eq '/access/openid/auth-url' && $method eq 'POST')) {
|
||||
if (
|
||||
($rel_uri eq '/access/domains' && $method eq 'GET')
|
||||
|| ($rel_uri eq '/access/ticket' && ($method eq 'GET' || $method eq 'POST'))
|
||||
|| ($rel_uri eq '/access/openid/login' && $method eq 'POST')
|
||||
|| ($rel_uri eq '/access/openid/auth-url' && $method eq 'POST')
|
||||
) {
|
||||
$require_auth = 0;
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +148,8 @@ sub rest_handler {
|
|||
for my $p (sort keys %{$params}) {
|
||||
if (defined($uri_param->{$p}) && $uri_param->{$p} ne $params->{$p}) {
|
||||
raise_param_exc({
|
||||
$p => "duplicate parameter (already defined in URI) with conflicting values!"
|
||||
$p =>
|
||||
"duplicate parameter (already defined in URI) with conflicting values!",
|
||||
});
|
||||
}
|
||||
$uri_param->{$p} = $params->{$p};
|
||||
|
|
@ -160,7 +163,11 @@ sub rest_handler {
|
|||
|
||||
if ($info->{proxyto} || $info->{proxyto_callback}) {
|
||||
my $node = PVE::API2Tools::resolve_proxyto(
|
||||
$rpcenv, $info->{proxyto_callback}, $info->{proxyto}, $uri_param);
|
||||
$rpcenv,
|
||||
$info->{proxyto_callback},
|
||||
$info->{proxyto},
|
||||
$uri_param,
|
||||
);
|
||||
|
||||
if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
|
||||
die "unable to proxy file uploads" if $auth->{isUpload};
|
||||
|
|
|
|||
64
PVE/Jobs.pm
64
PVE/Jobs.pm
|
|
@ -41,7 +41,10 @@ my $saved_config_props = [qw(enabled schedule)];
|
|||
sub detect_changed_runtime_props {
|
||||
my ($jobid, $type, $cfg) = @_;
|
||||
|
||||
lock_job_state($jobid, $type, sub {
|
||||
lock_job_state(
|
||||
$jobid,
|
||||
$type,
|
||||
sub {
|
||||
my $old_state = read_job_state($jobid, $type) // $default_state;
|
||||
|
||||
my $updated = 0;
|
||||
|
|
@ -64,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 $@;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -15,13 +15,14 @@ my $get_cache = sub {
|
|||
my $uid = getpwnam('root');
|
||||
my $gid = getgrnam('www-data');
|
||||
|
||||
$cache = Proxmox::RS::SharedCache->new({
|
||||
$cache = Proxmox::RS::SharedCache->new(
|
||||
{
|
||||
path => "/run/pve/metrics",
|
||||
owner => $uid,
|
||||
group => $gid,
|
||||
entry_mode => 0640, # Entry permissions
|
||||
keep_old => OLD_GENERATIONS,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ my sub gauge {
|
|||
value => $value + 0,
|
||||
timestamp => $timestamp + 0,
|
||||
type => 'gauge',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
my sub derive {
|
||||
|
|
@ -72,7 +73,7 @@ my sub derive {
|
|||
value => $value + 0,
|
||||
timestamp => $timestamp + 0,
|
||||
type => 'derive',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
my $nodename = PVE::INotify::nodename();
|
||||
|
|
|
|||
|
|
@ -11,12 +11,16 @@ my sub dir2text {
|
|||
|
||||
print STDERR "dir2text '${target_dir}${regexp}'...";
|
||||
my $text = "# output '${target_dir}${regexp}' file(s)\n";
|
||||
PVE::Tools::dir_glob_foreach($target_dir, $regexp, sub {
|
||||
PVE::Tools::dir_glob_foreach(
|
||||
$target_dir,
|
||||
$regexp,
|
||||
sub {
|
||||
my ($file) = @_;
|
||||
return if $file eq '.' || $file eq '..';
|
||||
$text .= "\n# cat $target_dir$file\n";
|
||||
$text .= PVE::Tools::file_get_contents($target_dir . $file) . "\n";
|
||||
});
|
||||
},
|
||||
);
|
||||
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};
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
174
PVE/VZDump.pm
174
PVE/VZDump.pm
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ use PVE::Tools;
|
|||
$ReplicationTestEnv::mocked_nodename = 'node1';
|
||||
|
||||
my $pve_replication_module = Test::MockModule->new('PVE::Replication');
|
||||
$pve_replication_module->mock(
|
||||
replicate => sub { die "faked replication error\n"; });
|
||||
$pve_replication_module->mock(replicate => sub { die "faked replication error\n"; });
|
||||
|
||||
my $testjob = {
|
||||
'type' => 'local',
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ use PVE::Storage;
|
|||
my $replicated_volume_status = {};
|
||||
|
||||
my $mocked_remote_prepare_local_job = sub {
|
||||
my ($ssh_info, $jobid, $vmid, $volumes, $storeid_list, $last_sync, $parent_snapname, $force) = @_;
|
||||
my ($ssh_info, $jobid, $vmid, $volumes, $storeid_list, $last_sync, $parent_snapname, $force)
|
||||
= @_;
|
||||
|
||||
my $target = $ssh_info->{node};
|
||||
|
||||
|
|
@ -38,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);
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ my $mocked_delete_job = sub {
|
|||
};
|
||||
|
||||
my $pve_replication_config_module = Test::MockModule->new('PVE::ReplicationConfig');
|
||||
$pve_replication_config_module->mock(
|
||||
delete_job => $mocked_delete_job);
|
||||
$pve_replication_config_module->mock(delete_job => $mocked_delete_job);
|
||||
|
||||
my $testjob = {
|
||||
'type' => 'local',
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ my $vmlist = {
|
|||
'type' => 'lxc',
|
||||
'node' => 'node2',
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
my $pools = {
|
||||
|
|
@ -57,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};
|
||||
|
|
|
|||
|
|
@ -180,8 +180,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups vzdump 1',
|
||||
vzdump_param => {
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,' .
|
||||
'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,'
|
||||
. 'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -235,8 +235,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups storage 1',
|
||||
storage_param => {
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,' .
|
||||
'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,'
|
||||
. 'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -253,8 +253,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups storage 2',
|
||||
storage_param => {
|
||||
'prune-backups' => 'keep-last=0,keep-hourly=0,keep-daily=0,' .
|
||||
'keep-weekly=0,keep-monthly=0,keep-yearly=0',
|
||||
'prune-backups' => 'keep-last=0,keep-hourly=0,keep-daily=0,'
|
||||
. 'keep-weekly=0,keep-monthly=0,keep-yearly=0',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -293,8 +293,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups CLI 1',
|
||||
cli_param => {
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,' .
|
||||
'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
'prune-backups' => 'keep-last=1,keep-hourly=2,keep-daily=3,'
|
||||
. 'keep-weekly=4,keep-monthly=5,keep-yearly=6',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -311,8 +311,8 @@ my @tests = (
|
|||
{
|
||||
description => 'prune-backups CLI 2',
|
||||
cli_param => {
|
||||
'prune-backups' => 'keep-last=0,keep-hourly=0,keep-daily=0,' .
|
||||
'keep-weekly=0,keep-monthly=0,keep-yearly=0',
|
||||
'prune-backups' => 'keep-last=0,keep-hourly=0,keep-daily=0,'
|
||||
. 'keep-weekly=0,keep-monthly=0,keep-yearly=0',
|
||||
},
|
||||
expected => {
|
||||
'prune-backups' => {
|
||||
|
|
@ -326,8 +326,8 @@ my @tests = (
|
|||
cli_param => {
|
||||
'prune-backups' => 'foo=bar',
|
||||
},
|
||||
expected => "format error\n" .
|
||||
"foo: property is not defined in schema and the schema does not allow additional properties\n",
|
||||
expected => "format error\n"
|
||||
. "foo: property is not defined in schema and the schema does not allow additional properties\n",
|
||||
},
|
||||
{
|
||||
description => 'both CLI 1',
|
||||
|
|
@ -335,8 +335,8 @@ my @tests = (
|
|||
'prune-backups' => 'keep-hourly=1,keep-monthly=2,keep-yearly=3',
|
||||
maxfiles => 4,
|
||||
},
|
||||
expected => "400 Parameter verification failed.\n" .
|
||||
"prune-backups: option conflicts with option 'maxfiles'\n",
|
||||
expected => "400 Parameter verification failed.\n"
|
||||
. "prune-backups: option conflicts with option 'maxfiles'\n",
|
||||
},
|
||||
{
|
||||
description => 'mixed 1',
|
||||
|
|
@ -499,8 +499,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -511,8 +510,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -523,8 +521,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -535,8 +532,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -567,8 +563,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -579,8 +574,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -591,8 +585,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -603,8 +596,7 @@ my @tests = (
|
|||
},
|
||||
expected => {
|
||||
'mailto' => [
|
||||
'developer@proxmox.com',
|
||||
'admin@proxmox.com',
|
||||
'developer@proxmox.com', 'admin@proxmox.com',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,17 +4,18 @@
|
|||
# This can also be added as a line in /etc/vzdump.conf
|
||||
# e.g. 'script: /usr/local/bin/vzdump-hook-script.pl'
|
||||
|
||||
|
||||
use strict;
|
||||
|
||||
print "HOOK: " . join(' ', @ARGV) . "\n";
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue