]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Subscription.pm
api2: network: improve code readability
[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 Proxmox::RS::Subscription;
12
13 use PVE::Tools;
14 use PVE::ProcFSTools;
15 use PVE::Exception qw(raise_param_exc);
16 use PVE::INotify;
17 use PVE::Cluster qw (cfs_read_file cfs_write_file);
18 use PVE::DataCenterConfig;
19 use PVE::AccessControl;
20 use PVE::Storage;
21 use PVE::JSONSchema qw(get_standard_option);
22
23 use PVE::SafeSyslog;
24
25 use PVE::API2Tools;
26 use PVE::RESTHandler;
27
28 use base qw(PVE::RESTHandler);
29
30 my $subscription_pattern = 'pve([1248])([cbsp])-[0-9a-f]{10}';
31 my $filename = "/etc/subscription";
32
33 sub get_sockets {
34 my $info = PVE::ProcFSTools::read_cpuinfo();
35 return $info->{sockets};
36 }
37
38 sub parse_key {
39 my ($key, $noerr) = @_;
40
41 if ($key =~ m/^${subscription_pattern}$/) {
42 return wantarray ? ($1, $2) : $1; # number of sockets, level
43 }
44 return undef if $noerr;
45
46 die "Wrong subscription key format\n";
47 }
48
49 sub check_key {
50 my ($key, $req_sockets) = @_;
51
52 my ($sockets, $level) = parse_key($key);
53 if ($sockets < $req_sockets) {
54 die "wrong number of sockets ($sockets < $req_sockets)\n";
55 }
56 return ($sockets, $level);
57 }
58
59 sub read_etc_subscription {
60 my $req_sockets = get_sockets();
61 my $server_id = PVE::API2Tools::get_hwaddress();
62
63 my $info = Proxmox::RS::Subscription::read_subscription($filename);
64
65 return $info if !$info || $info->{status} ne 'active';
66
67 my ($sockets, $level);
68 eval { ($sockets, $level) = check_key($info->{key}, $req_sockets); };
69 if (my $err = $@) {
70 chomp $err;
71 $info->{status} = 'invalid';
72 $info->{message} = $err;
73 } else {
74 $info->{level} = $level;
75 }
76
77 return $info;
78 }
79
80 my sub cache_is_valid {
81 my ($info) = @_;
82
83 return if !$info || $info->{status} ne 'active';
84
85 my $checked_info = Proxmox::RS::Subscription::check_age($info, 1);
86 return $checked_info->{status} eq 'active'
87 }
88
89 sub write_etc_subscription {
90 my ($info) = @_;
91
92 my $server_id = PVE::API2Tools::get_hwaddress();
93 mkdir "/etc/apt/auth.conf.d";
94 Proxmox::RS::Subscription::write_subscription(
95 $filename, "/etc/apt/auth.conf.d/pve.conf", "enterprise.proxmox.com/debian/pve", $info);
96
97 # FIXME: improve this, especially the selection of valid ceph-releases
98 # NOTE: currently we should add future ceph releases as early as possible, to ensure that
99 my $ceph_auth = '';
100 for my $ceph_release ('quincy', 'reef') {
101 $ceph_auth .= "machine enterprise.proxmox.com/debian/ceph-${ceph_release}"
102 ." login $info->{key} password $info->{serverid}\n"
103 }
104 PVE::Tools::file_set_contents("/etc/apt/auth.conf.d/ceph.conf", $ceph_auth);
105 }
106
107 __PACKAGE__->register_method ({
108 name => 'get',
109 path => '',
110 method => 'GET',
111 description => "Read subscription info.",
112 proxyto => 'node',
113 permissions => { user => 'all' },
114 parameters => {
115 additionalProperties => 0,
116 properties => {
117 node => get_standard_option('pve-node'),
118 },
119 },
120 returns => { type => 'object'},
121 code => sub {
122 my ($param) = @_;
123
124 my $node = $param->{node};
125
126 my $rpcenv = PVE::RPCEnvironment::get();
127 my $authuser = $rpcenv->get_user();
128 my $has_permission = $rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1);
129
130 my $server_id = PVE::API2Tools::get_hwaddress();
131 my $url = "https://www.proxmox.com/proxmox-ve/pricing";
132
133 my $info = read_etc_subscription();
134 if (!$info) {
135 my $no_subscription_info = {
136 status => "notfound",
137 message => "There is no subscription key",
138 url => $url,
139 };
140 $no_subscription_info->{serverid} = $server_id if $has_permission;
141 return $no_subscription_info;
142 }
143
144 if (!$has_permission) {
145 return {
146 status => $info->{status},
147 message => $info->{message},
148 url => $url,
149 }
150 }
151
152 $info->{serverid} = $server_id;
153 $info->{sockets} = get_sockets();
154 $info->{url} = $url;
155
156 return $info
157 }});
158
159 __PACKAGE__->register_method ({
160 name => 'update',
161 path => '',
162 method => 'POST',
163 permissions => {
164 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
165 },
166 description => "Update subscription info.",
167 proxyto => 'node',
168 protected => 1,
169 parameters => {
170 additionalProperties => 0,
171 properties => {
172 node => get_standard_option('pve-node'),
173 force => {
174 description => "Always connect to server, even if local cache is still valid.",
175 type => 'boolean',
176 optional => 1,
177 default => 0
178 }
179 },
180 },
181 returns => { type => 'null'},
182 code => sub {
183 my ($param) = @_;
184
185 my $info = read_etc_subscription();
186 return undef if !$info;
187
188 my $server_id = PVE::API2Tools::get_hwaddress();
189 my $key = $info->{key};
190
191 die "Updating offline key not possible - please remove and re-add subscription key to switch to online key.\n"
192 if $info->{signature};
193
194 return undef if !$param->{force} && cache_is_valid($info); # key has been recently checked
195
196 my $req_sockets = get_sockets();
197 check_key($key, $req_sockets);
198
199 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
200 my $proxy = $dccfg->{http_proxy};
201
202 $info = Proxmox::RS::Subscription::check_subscription($key, $server_id, "", "Proxmox VE", $proxy);
203
204 write_etc_subscription($info);
205
206 return undef;
207 }});
208
209 __PACKAGE__->register_method ({
210 name => 'set',
211 path => '',
212 method => 'PUT',
213 permissions => {
214 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
215 },
216 description => "Set subscription key.",
217 proxyto => 'node',
218 protected => 1,
219 parameters => {
220 additionalProperties => 0,
221 properties => {
222 node => get_standard_option('pve-node'),
223 key => {
224 description => "Proxmox VE subscription key",
225 type => 'string',
226 pattern => $subscription_pattern,
227 maxLength => 32,
228 },
229 },
230 },
231 returns => { type => 'null'},
232 code => sub {
233 my ($param) = @_;
234
235 my $key = PVE::Tools::trim($param->{key});
236
237 my $new_info = {
238 status => 'New',
239 key => $key,
240 checktime => time(),
241 };
242
243 my $req_sockets = get_sockets();
244 my $server_id = PVE::API2Tools::get_hwaddress();
245
246 check_key($key, $req_sockets);
247
248 write_etc_subscription($new_info);
249
250 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
251 my $proxy = $dccfg->{http_proxy};
252
253 my $checked_info = Proxmox::RS::Subscription::check_subscription(
254 $key, $server_id, "", "Proxmox VE", $proxy);
255
256 write_etc_subscription($checked_info);
257
258 return undef;
259 }});
260
261 __PACKAGE__->register_method ({
262 name => 'delete',
263 path => '',
264 method => 'DELETE',
265 permissions => {
266 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
267 },
268 description => "Delete subscription key of this node.",
269 proxyto => 'node',
270 protected => 1,
271 parameters => {
272 additionalProperties => 0,
273 properties => {
274 node => get_standard_option('pve-node'),
275 },
276 },
277 returns => { type => 'null'},
278 code => sub {
279 my $subscription_file = '/etc/subscription';
280 return if ! -e $subscription_file;
281 unlink($subscription_file) or die "cannot delete subscription key: $!";
282 return undef;
283 }});
284
285 1;