This was missing, but it mostly well defined since we're using the rust bindings here. I copied most descriptions over from the PBS api, except the ones only existing here (like sockets and level) Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
354 lines
8.6 KiB
Perl
354 lines
8.6 KiB
Perl
package PVE::API2::Subscription;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Digest::MD5 qw(md5_hex md5_base64);
|
|
use HTTP::Request;
|
|
use JSON;
|
|
use LWP::UserAgent;
|
|
use MIME::Base64;
|
|
|
|
use Proxmox::RS::Subscription;
|
|
|
|
use PVE::AccessControl;
|
|
use PVE::Cluster qw (cfs_read_file cfs_write_file);
|
|
use PVE::DataCenterConfig;
|
|
use PVE::Exception qw(raise_param_exc);
|
|
use PVE::INotify;
|
|
use PVE::JSONSchema qw(get_standard_option);
|
|
use PVE::ProcFSTools;
|
|
use PVE::SafeSyslog;
|
|
use PVE::Storage;
|
|
use PVE::Tools;
|
|
|
|
use PVE::Ceph::Releases;
|
|
use PVE::API2Tools;
|
|
|
|
use base qw(PVE::RESTHandler);
|
|
|
|
my $subscription_pattern = 'pve([1248])([cbsp])-[0-9a-f]{10}';
|
|
my $filename = "/etc/subscription";
|
|
|
|
sub get_sockets {
|
|
my $info = PVE::ProcFSTools::read_cpuinfo();
|
|
return $info->{sockets};
|
|
}
|
|
|
|
sub parse_key {
|
|
my ($key, $noerr) = @_;
|
|
|
|
if ($key =~ m/^${subscription_pattern}$/) {
|
|
return wantarray ? ($1, $2) : $1; # number of sockets, level
|
|
}
|
|
return undef if $noerr;
|
|
|
|
die "Wrong subscription key format\n";
|
|
}
|
|
|
|
sub check_key {
|
|
my ($key, $req_sockets) = @_;
|
|
|
|
my ($sockets, $level) = parse_key($key);
|
|
if ($sockets < $req_sockets) {
|
|
die "wrong number of sockets ($sockets < $req_sockets)\n";
|
|
}
|
|
return ($sockets, $level);
|
|
}
|
|
|
|
sub read_etc_subscription {
|
|
my $req_sockets = get_sockets();
|
|
my $server_id = PVE::API2Tools::get_hwaddress();
|
|
|
|
my $info = Proxmox::RS::Subscription::read_subscription($filename);
|
|
|
|
return $info if !$info || $info->{status} ne 'active';
|
|
|
|
my ($sockets, $level);
|
|
eval { ($sockets, $level) = check_key($info->{key}, $req_sockets); };
|
|
if (my $err = $@) {
|
|
chomp $err;
|
|
$info->{status} = 'invalid';
|
|
$info->{message} = $err;
|
|
} else {
|
|
$info->{level} = $level;
|
|
}
|
|
|
|
return $info;
|
|
}
|
|
|
|
my sub cache_is_valid {
|
|
my ($info) = @_;
|
|
|
|
return if !$info || $info->{status} ne 'active';
|
|
|
|
my $checked_info = Proxmox::RS::Subscription::check_age($info, 1);
|
|
return $checked_info->{status} eq 'active'
|
|
}
|
|
|
|
sub write_etc_subscription {
|
|
my ($info) = @_;
|
|
|
|
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);
|
|
|
|
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 - $!";
|
|
} 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"
|
|
}
|
|
PVE::Tools::file_set_contents("/etc/apt/auth.conf.d/ceph.conf", $ceph_auth);
|
|
}
|
|
}
|
|
|
|
__PACKAGE__->register_method ({
|
|
name => 'get',
|
|
path => '',
|
|
method => 'GET',
|
|
description => "Read subscription info.",
|
|
proxyto => 'node',
|
|
permissions => { user => 'all' },
|
|
parameters => {
|
|
additionalProperties => 0,
|
|
properties => {
|
|
node => get_standard_option('pve-node'),
|
|
},
|
|
},
|
|
returns => {
|
|
type => 'object',
|
|
additionalProperties => 0,
|
|
properties => {
|
|
status => {
|
|
type => 'string',
|
|
enum => [qw(new notfound active invalid expired suspended)],
|
|
description => "The current subscription status.",
|
|
},
|
|
checktime => {
|
|
type => 'integer',
|
|
description => 'Timestamp of the last check done.',
|
|
optional => 1,
|
|
},
|
|
key => {
|
|
type => 'string',
|
|
description => 'The subscription key, if set and permitted to access.',
|
|
optional => 1,
|
|
},
|
|
level => {
|
|
type => 'string',
|
|
description => 'A short code for the subscription level.',
|
|
optional => 1,
|
|
},
|
|
message => {
|
|
type => 'string',
|
|
description => 'A more human readable status message.',
|
|
optional => 1,
|
|
},
|
|
nextduedate => {
|
|
type => 'string',
|
|
description => 'Next due date of the set subscription.',
|
|
optional => 1,
|
|
},
|
|
productname => {
|
|
type => 'string',
|
|
description => 'Human readable productname of the set subscription.',
|
|
optional => 1,
|
|
},
|
|
regdate => {
|
|
type => 'string',
|
|
description => 'Register date of the set subscription.',
|
|
optional => 1,
|
|
},
|
|
serverid => {
|
|
type => 'string',
|
|
description => 'The server ID, if permitted to access.',
|
|
optional => 1,
|
|
},
|
|
signature => {
|
|
type => 'string',
|
|
description => 'Signature for offline keys',
|
|
optional => 1,
|
|
},
|
|
sockets => {
|
|
type => 'integer',
|
|
description => 'The number of sockets for this host.',
|
|
optional => 1,
|
|
},
|
|
url => {
|
|
type => 'string',
|
|
description => 'URL to the web shop.',
|
|
optional => 1,
|
|
},
|
|
},
|
|
},
|
|
code => sub {
|
|
my ($param) = @_;
|
|
|
|
my $node = $param->{node};
|
|
|
|
my $rpcenv = PVE::RPCEnvironment::get();
|
|
my $authuser = $rpcenv->get_user();
|
|
my $has_permission = $rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1);
|
|
|
|
my $server_id = PVE::API2Tools::get_hwaddress();
|
|
my $url = "https://www.proxmox.com/en/proxmox-virtual-environment/pricing";
|
|
|
|
my $info = read_etc_subscription();
|
|
if (!$info) {
|
|
my $no_subscription_info = {
|
|
status => "notfound",
|
|
message => "There is no subscription key",
|
|
url => $url,
|
|
};
|
|
$no_subscription_info->{serverid} = $server_id if $has_permission;
|
|
return $no_subscription_info;
|
|
}
|
|
|
|
if (!$has_permission) {
|
|
return {
|
|
status => $info->{status},
|
|
message => $info->{message},
|
|
url => $url,
|
|
}
|
|
}
|
|
|
|
$info->{serverid} = $server_id;
|
|
$info->{sockets} = get_sockets();
|
|
$info->{url} = $url;
|
|
|
|
return $info
|
|
}});
|
|
|
|
__PACKAGE__->register_method ({
|
|
name => 'update',
|
|
path => '',
|
|
method => 'POST',
|
|
permissions => {
|
|
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
|
},
|
|
description => "Update subscription info.",
|
|
proxyto => 'node',
|
|
protected => 1,
|
|
parameters => {
|
|
additionalProperties => 0,
|
|
properties => {
|
|
node => get_standard_option('pve-node'),
|
|
force => {
|
|
description => "Always connect to server, even if local cache is still valid.",
|
|
type => 'boolean',
|
|
optional => 1,
|
|
default => 0
|
|
}
|
|
},
|
|
},
|
|
returns => { type => 'null'},
|
|
code => sub {
|
|
my ($param) = @_;
|
|
|
|
my $info = read_etc_subscription();
|
|
return undef if !$info;
|
|
|
|
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"
|
|
if $info->{signature};
|
|
|
|
return undef if !$param->{force} && cache_is_valid($info); # key has been recently checked
|
|
|
|
my $req_sockets = get_sockets();
|
|
check_key($key, $req_sockets);
|
|
|
|
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);
|
|
|
|
write_etc_subscription($info);
|
|
|
|
return undef;
|
|
}});
|
|
|
|
__PACKAGE__->register_method ({
|
|
name => 'set',
|
|
path => '',
|
|
method => 'PUT',
|
|
permissions => {
|
|
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
|
},
|
|
description => "Set subscription key.",
|
|
proxyto => 'node',
|
|
protected => 1,
|
|
parameters => {
|
|
additionalProperties => 0,
|
|
properties => {
|
|
node => get_standard_option('pve-node'),
|
|
key => {
|
|
description => "Proxmox VE subscription key",
|
|
type => 'string',
|
|
pattern => "\\s*${subscription_pattern}\\s*",
|
|
maxLength => 32,
|
|
},
|
|
},
|
|
},
|
|
returns => { type => 'null'},
|
|
code => sub {
|
|
my ($param) = @_;
|
|
|
|
my $key = PVE::Tools::trim($param->{key});
|
|
|
|
my $new_info = {
|
|
status => 'New',
|
|
key => $key,
|
|
checktime => time(),
|
|
};
|
|
|
|
my $req_sockets = get_sockets();
|
|
my $server_id = PVE::API2Tools::get_hwaddress();
|
|
|
|
check_key($key, $req_sockets);
|
|
|
|
write_etc_subscription($new_info);
|
|
|
|
my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
|
|
my $proxy = $dccfg->{http_proxy};
|
|
|
|
my $checked_info = Proxmox::RS::Subscription::check_subscription(
|
|
$key, $server_id, "", "Proxmox VE", $proxy);
|
|
|
|
write_etc_subscription($checked_info);
|
|
|
|
return undef;
|
|
}});
|
|
|
|
__PACKAGE__->register_method ({
|
|
name => 'delete',
|
|
path => '',
|
|
method => 'DELETE',
|
|
permissions => {
|
|
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
|
|
},
|
|
description => "Delete subscription key of this node.",
|
|
proxyto => 'node',
|
|
protected => 1,
|
|
parameters => {
|
|
additionalProperties => 0,
|
|
properties => {
|
|
node => get_standard_option('pve-node'),
|
|
},
|
|
},
|
|
returns => { type => 'null'},
|
|
code => sub {
|
|
my $subscription_file = '/etc/subscription';
|
|
return if ! -e $subscription_file;
|
|
unlink($subscription_file) or die "cannot delete subscription key: $!";
|
|
return undef;
|
|
}});
|
|
|
|
1;
|