]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Subscription.pm
subscription delete: followup fixes
[pve-manager.git] / PVE / API2 / Subscription.pm
1 package PVE::API2::Subscription;
2
3 use strict;
4 use warnings;
5 use Digest::MD5 qw(md5_hex md5_base64);
6 use MIME::Base64;
7 use HTTP::Request;
8 use LWP::UserAgent;
9 use JSON;
10
11 use PVE::Tools;
12 use PVE::ProcFSTools;
13 use PVE::Exception qw(raise_param_exc);
14 use PVE::INotify;
15 use PVE::Cluster qw (cfs_read_file cfs_write_file);
16 use PVE::DataCenterConfig;
17 use PVE::AccessControl;
18 use PVE::Storage;
19 use PVE::JSONSchema qw(get_standard_option);
20
21 use PVE::SafeSyslog;
22 use PVE::Subscription;
23
24 use PVE::API2Tools;
25 use PVE::RESTHandler;
26
27 use base qw(PVE::RESTHandler);
28
29 PVE::INotify::register_file('subscription', "/etc/subscription",
30 \&read_etc_pve_subscription,
31 \&write_etc_pve_subscription);
32
33 my $subscription_pattern = 'pve([1248])([cbsp])-[0-9a-f]{10}';
34
35 sub get_sockets {
36 my $info = PVE::ProcFSTools::read_cpuinfo();
37 return $info->{sockets};
38 }
39
40 sub parse_key {
41 my ($key, $noerr) = @_;
42
43 if ($key =~ m/^${subscription_pattern}$/) {
44 return wantarray ? ($1, $2) : $1; # number of sockets, level
45 }
46 return undef if $noerr;
47
48 die "Wrong subscription key format\n";
49 }
50
51 sub check_key {
52 my ($key, $req_sockets) = @_;
53
54 my ($sockets, $level) = parse_key($key);
55 if ($sockets < $req_sockets) {
56 die "wrong number of sockets ($sockets < $req_sockets)\n";
57 }
58 return ($sockets, $level);
59 }
60
61 sub read_etc_pve_subscription {
62 my ($filename, $fh) = @_;
63
64 my $req_sockets = get_sockets();
65 my $server_id = PVE::API2Tools::get_hwaddress();
66
67 my $info = PVE::Subscription::read_subscription($server_id, $filename, $fh);
68
69 return $info if $info->{status} ne 'Active';
70
71 my ($sockets, $level);
72 eval { ($sockets, $level) = check_key($info->{key}, $req_sockets); };
73 if (my $err = $@) {
74 chomp $err;
75 $info->{status} = 'Invalid';
76 $info->{message} = $err;
77 } else {
78 $info->{level} = $level;
79 }
80
81 return $info;
82 }
83
84 sub write_etc_pve_subscription {
85 my ($filename, $fh, $info) = @_;
86
87 my $server_id = PVE::API2Tools::get_hwaddress();
88 PVE::Subscription::write_subscription($server_id, $filename, $fh, $info);
89 }
90
91 __PACKAGE__->register_method ({
92 name => 'get',
93 path => '',
94 method => 'GET',
95 description => "Read subscription info.",
96 proxyto => 'node',
97 permissions => { user => 'all' },
98 parameters => {
99 additionalProperties => 0,
100 properties => {
101 node => get_standard_option('pve-node'),
102 },
103 },
104 returns => { type => 'object'},
105 code => sub {
106 my ($param) = @_;
107
108 my $node = $param->{node};
109
110 my $rpcenv = PVE::RPCEnvironment::get();
111 my $authuser = $rpcenv->get_user();
112 my $has_permission = $rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1);
113
114 my $server_id = PVE::API2Tools::get_hwaddress();
115 my $url = "https://www.proxmox.com/proxmox-ve/pricing";
116
117 my $info = PVE::INotify::read_file('subscription');
118 if (!$info) {
119 my $no_subscription_info = {
120 status => "NotFound",
121 message => "There is no subscription key",
122 url => $url,
123 };
124 $no_subscription_info->{serverid} = $server_id if $has_permission;
125 return $no_subscription_info;
126 }
127
128 if (!$has_permission) {
129 return {
130 status => $info->{status},
131 message => $info->{message},
132 url => $url,
133 }
134 }
135
136 $info->{serverid} = $server_id;
137 $info->{sockets} = get_sockets();
138 $info->{url} = $url;
139
140 return $info
141 }});
142
143 __PACKAGE__->register_method ({
144 name => 'update',
145 path => '',
146 method => 'POST',
147 permissions => {
148 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
149 },
150 description => "Update subscription info.",
151 proxyto => 'node',
152 protected => 1,
153 parameters => {
154 additionalProperties => 0,
155 properties => {
156 node => get_standard_option('pve-node'),
157 force => {
158 description => "Always connect to server, even if we have up to date info inside local cache.",
159 type => 'boolean',
160 optional => 1,
161 default => 0
162 }
163 },
164 },
165 returns => { type => 'null'},
166 code => sub {
167 my ($param) = @_;
168
169 my $info = PVE::INotify::read_file('subscription');
170 return undef if !$info;
171
172 my $server_id = PVE::API2Tools::get_hwaddress();
173 my $key = $info->{key};
174
175 if ($key) {
176 PVE::Subscription::update_apt_auth($key, $server_id);
177 }
178
179 if (!$param->{force} && $info->{status} eq 'Active') {
180 my $age = time() - $info->{checktime};
181 return undef if $age < $PVE::Subscription::localkeydays*60*60*24;
182 }
183
184 my $req_sockets = get_sockets();
185 check_key($key, $req_sockets);
186
187 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
188 my $proxy = $dccfg->{http_proxy};
189
190 $info = PVE::Subscription::check_subscription($key, $server_id, $proxy);
191
192 PVE::INotify::write_file('subscription', $info);
193
194 return undef;
195 }});
196
197 __PACKAGE__->register_method ({
198 name => 'set',
199 path => '',
200 method => 'PUT',
201 permissions => {
202 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
203 },
204 description => "Set subscription key.",
205 proxyto => 'node',
206 protected => 1,
207 parameters => {
208 additionalProperties => 0,
209 properties => {
210 node => get_standard_option('pve-node'),
211 key => {
212 description => "Proxmox VE subscription key",
213 type => 'string',
214 pattern => $subscription_pattern,
215 maxLength => 32,
216 },
217 },
218 },
219 returns => { type => 'null'},
220 code => sub {
221 my ($param) = @_;
222
223 my $key = PVE::Tools::trim($param->{key});
224
225 my $info = {
226 status => 'New',
227 key => $key,
228 checktime => time(),
229 };
230
231 my $req_sockets = get_sockets();
232 my $server_id = PVE::API2Tools::get_hwaddress();
233
234 check_key($key, $req_sockets);
235
236 PVE::INotify::write_file('subscription', $info);
237
238 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
239 my $proxy = $dccfg->{http_proxy};
240
241 $info = PVE::Subscription::check_subscription($key, $server_id, $proxy);
242
243 PVE::INotify::write_file('subscription', $info);
244
245 return undef;
246 }});
247
248 __PACKAGE__->register_method ({
249 name => 'delete',
250 path => '',
251 method => 'DELETE',
252 permissions => {
253 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
254 },
255 description => "Delete subscription key.",
256 proxyto => 'node',
257 protected => 1,
258 parameters => {
259 additionalProperties => 0,
260 properties => {
261 node => get_standard_option('pve-node'),
262 },
263 },
264 returns => { type => 'null'},
265 code => sub {
266 my $subscription_file = '/etc/subscription';
267 return if ! -e $subscription_file;
268 unlink($subscription_file) or die "cannot delete subscription key: $!";
269 return undef;
270 }});
271
272 1;