]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/APT.pm
apt-get upgrade: pass --force-confdef and --force-confold to dpkg
[pve-manager.git] / PVE / API2 / APT.pm
CommitLineData
21299915
DM
1package PVE::API2::APT;
2
3use strict;
4use warnings;
cd0bc36b 5use File::stat ();
21299915 6
f5ed75de
DM
7use LWP::UserAgent;
8
21299915 9use PVE::Tools qw(extract_param);
f5ed75de 10use PVE::Cluster;
21299915
DM
11use PVE::SafeSyslog;
12use PVE::INotify;
396c9e4a 13use PVE::Exception;
21299915
DM
14use PVE::RESTHandler;
15use PVE::RPCEnvironment;
16
cd0bc36b 17use JSON;
21299915
DM
18use PVE::JSONSchema qw(get_standard_option);
19
20use AptPkg::Cache;
21use AptPkg::Version;
22use AptPkg::PkgRecords;
23
21299915
DM
24my $get_apt_cache = sub {
25
c06e9cc8 26 my $apt_cache = AptPkg::Cache->new() || die "unable to initialize AptPkg::Cache\n";
21299915
DM
27
28 return $apt_cache;
29};
30
31use base qw(PVE::RESTHandler);
32
33__PACKAGE__->register_method({
34 name => 'index',
35 path => '',
36 method => 'GET',
37 description => "Directory index for apt (Advanced Package Tool).",
38 permissions => {
39 user => 'all',
40 },
41 parameters => {
42 additionalProperties => 0,
43 properties => {
44 node => get_standard_option('pve-node'),
45 },
46 },
47 returns => {
48 type => "array",
49 items => {
50 type => "object",
51 properties => {
52 id => { type => 'string' },
53 },
54 },
55 links => [ { rel => 'child', href => "{id}" } ],
56 },
57 code => sub {
58 my ($param) = @_;
59
60 my $res = [
61 { id => 'update' },
62 { id => 'upgrade' },
63 { id => 'changelog' },
64 ];
65
66 return $res;
67 }});
68
b688d438
DM
69my $get_changelug_url = sub {
70 my ($pkgname, $info, $candidate_ver) = @_;
21299915 71
00d48356
DM
72 my $changelog_url;
73 foreach my $verfile (@{$candidate_ver->{FileList}}) {
74 my $pkgfile = $verfile->{File};
75 my $origin = $pkgfile->{Origin};
76 my $comp = $pkgfile->{Component};
77 if ($origin && $comp) {
78 my $pkgver = $candidate_ver->{VerStr};
371dcc92
DM
79 $pkgver =~ s/^\d+://; # strip epoch
80 my $srcpkg = $info->{SourcePkg} || $pkgname;
81 my $firstLetter = substr($srcpkg, 0, 1);
00d48356 82 if ($origin eq 'Debian') {
b688d438 83 $changelog_url = "http://packages.debian.org/changelogs/pool/$comp/" .
371dcc92 84 "$firstLetter/$srcpkg/${srcpkg}_$pkgver/changelog";
00d48356
DM
85 }
86 last;
87 }
88 }
b688d438
DM
89 return $changelog_url;
90};
91
92my $assemble_pkginfo = sub {
93 my ($pkgname, $info, $current_ver, $candidate_ver) = @_;
00d48356 94
21299915
DM
95 my $data = {
96 Package => $info->{Name},
97 Title => $info->{ShortDesc},
98 };
99
b688d438
DM
100 if (my $changelog_url = &$get_changelug_url($pkgname, $info, $candidate_ver)) {
101 $data->{ChangeLogUrl} = $changelog_url;
102 }
00d48356 103
21299915
DM
104 if (my $desc = $info->{LongDesc}) {
105 $desc =~ s/^.*\n\s?//; # remove first line
106 $desc =~ s/\n / /g;
107 $data->{Description} = $desc;
108 }
109
110 foreach my $k (qw(Section Arch Priority)) {
111 $data->{$k} = $candidate_ver->{$k};
112 }
113
114 $data->{Version} = $candidate_ver->{VerStr};
115 $data->{OldVersion} = $current_ver->{VerStr};
116
117 return $data;
118};
119
4806bc69
DM
120# we try to cache results
121my $pve_pkgstatus_fn = "/var/lib/pve-manager/pkgupdates";
122
123my $update_pve_pkgstatus = sub {
124
446f9217
DM
125 syslog('info', "update new package list: $pve_pkgstatus_fn");
126
4806bc69
DM
127 my $pkglist = [];
128
129 my $cache = &$get_apt_cache();
130 my $policy = $cache->policy;
131 my $pkgrecords = $cache->packages();
132
133 foreach my $pkgname (keys %$cache) {
134 my $p = $cache->{$pkgname};
135 next if $p->{SelectedState} ne 'Install';
136 my $current_ver = $p->{CurrentVer};
137 my $candidate_ver = $policy->candidate($p);
138
396c9e4a 139 if ($current_ver->{VerStr} ne $candidate_ver->{VerStr}) {
4806bc69
DM
140 my $info = $pkgrecords->lookup($pkgname);
141 my $res = &$assemble_pkginfo($pkgname, $info, $current_ver, $candidate_ver);
142 push @$pkglist, $res;
143 }
144 }
145
146 PVE::Tools::file_set_contents($pve_pkgstatus_fn, encode_json($pkglist));
147
148 return $pkglist;
149};
150
21299915
DM
151__PACKAGE__->register_method({
152 name => 'list_updates',
153 path => 'update',
154 method => 'GET',
155 description => "List available updates.",
156 permissions => {
157 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
158 },
159 protected => 1,
160 proxyto => 'node',
161 parameters => {
162 additionalProperties => 0,
163 properties => {
164 node => get_standard_option('pve-node'),
165 },
166 },
167 returns => {
168 type => "array",
169 items => {
170 type => "object",
171 properties => {},
172 },
173 },
174 code => sub {
175 my ($param) = @_;
176
cd0bc36b
DM
177 if (my $st1 = File::stat::stat($pve_pkgstatus_fn)) {
178 my $st2 = File::stat::stat("/var/cache/apt/pkgcache.bin");
179 my $st3 = File::stat::stat("/var/lib/dpkg/status");
180
446f9217 181 if ($st2 && $st3 && $st2->mtime <= $st1->mtime && $st3->mtime <= $st1->mtime) {
cd0bc36b
DM
182 my $data;
183 eval {
184 my $jsonstr = PVE::Tools::file_get_contents($pve_pkgstatus_fn, 5*1024*1024);
185 $data = decode_json($jsonstr);
186 };
187 if (my $err = $@) {
188 warn "error readin cached package status in $pve_pkgstatus_fn\n";
189 # continue and overwrite cache with new content
190 } else {
191 return $data;
192 }
193 }
194 }
195
4806bc69
DM
196 my $pkglist = &$update_pve_pkgstatus();
197
198 return $pkglist;
199 }});
200
201__PACKAGE__->register_method({
202 name => 'update_database',
203 path => 'update',
204 method => 'POST',
205 description => "This is used to resynchronize the package index files from their sources (apt-get update).",
206 permissions => {
207 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
208 },
209 protected => 1,
210 proxyto => 'node',
211 parameters => {
212 additionalProperties => 0,
213 properties => {
214 node => get_standard_option('pve-node'),
215 },
216 },
217 returns => {
218 type => 'string',
219 },
220 code => sub {
221 my ($param) = @_;
222
223 my $rpcenv = PVE::RPCEnvironment::get();
21299915 224
4806bc69 225 my $authuser = $rpcenv->get_user();
21299915 226
4806bc69
DM
227 my $realcmd = sub {
228 my $upid = shift;
21299915 229
4806bc69 230 my $cmd = ['apt-get', 'update'];
21299915 231
4806bc69
DM
232 print "starting apt-get update\n";
233
234 PVE::Tools::run_command($cmd);
235
236 &$update_pve_pkgstatus();
237
238 return;
239 };
240
c2d3fbe0
DM
241 return $rpcenv->fork_worker('aptupdate', undef, $authuser, $realcmd);
242
243 }});
244
245__PACKAGE__->register_method({
246 name => 'upgrade',
247 path => 'upgrade',
248 method => 'POST',
249 description => "Install the newest versions of all packages (apt-get dist-upgrade).",
250 permissions => {
251 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
252 },
253 protected => 1,
254 proxyto => 'node',
255 parameters => {
256 additionalProperties => 0,
257 properties => {
258 node => get_standard_option('pve-node'),
259 },
260 },
261 returns => {
262 type => 'string',
263 },
264 code => sub {
265 my ($param) = @_;
266
267 my $rpcenv = PVE::RPCEnvironment::get();
268
269 my $authuser = $rpcenv->get_user();
270
271 my $realcmd = sub {
272 my $upid = shift;
273
274 my $cmd = ['apt-get', 'dist-upgrade', '--assume-yes'];
275
9f6658a9
DM
276 push @$cmd, '-o', 'Dpkg::Options::=--force-confdef';
277
278 push @$cmd, '-o', 'Dpkg::Options::=--force-confold';
279
c2d3fbe0
DM
280 print "starting apt-get dist-upgrade\n";
281
282 $ENV{DEBIAN_FRONTEND} = 'noninteractive';
283
284 PVE::Tools::run_command($cmd);
285
286 &$update_pve_pkgstatus();
287
288 return;
289 };
290
291 return $rpcenv->fork_worker('aptupgrade', undef, $authuser, $realcmd);
cd0bc36b 292
21299915
DM
293 }});
294
b688d438
DM
295__PACKAGE__->register_method({
296 name => 'changelog',
297 path => 'changelog',
298 method => 'GET',
299 description => "Get package changelogs.",
300 permissions => {
301 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
302 },
303 parameters => {
304 additionalProperties => 0,
305 properties => {
306 node => get_standard_option('pve-node'),
307 name => {
308 description => "Package name.",
309 type => 'string',
310 },
311 version => {
312 description => "Package version.",
313 type => 'string',
314 optional => 1,
315 },
316 },
317 },
318 returns => {
319 type => "string",
320 },
321 code => sub {
322 my ($param) = @_;
323
324 my $pkgname = $param->{name};
325
326 my $cache = &$get_apt_cache();
327 my $policy = $cache->policy;
328 my $p = $cache->{$pkgname} || die "no such package '$pkgname'\n";
329 my $pkgrecords = $cache->packages();
330
331 my $ver;
332 if ($param->{version}) {
333 if (my $available = $p->{VersionList}) {
334 for my $v (@$available) {
335 if ($v->{VerStr} eq $param->{version}) {
336 $ver = $v;
337 last;
338 }
339 }
340 }
341 die "package '$pkgname' version '$param->{version}' is not avalable\n" if !$ver;
342 } else {
343 $ver = $policy->candidate($p) || die "no installation candidate for package '$pkgname'\n";
344 }
345
346 my $info = $pkgrecords->lookup($pkgname);
347
348 my $url = &$get_changelug_url($pkgname, $info, $ver) ||
349 die "changelog for '${pkgname}_$ver->{VerStr}' not available\n";
350
351 my $data = "";
352
f5ed75de
DM
353 my $dccfg = PVE::Cluster::cfs_read_file('datacenter.cfg');
354 my $proxy = $dccfg->{http_proxy};
355
356 my $ua = LWP::UserAgent->new;
357 $ua->agent("PVE/1.0");
358 $ua->timeout(10);
359 $ua->max_size(1024*1024);
360
361 if ($proxy) {
362 $ua->proxy(['http'], $proxy);
363 } else {
364 $ua->env_proxy;
365 }
366
367 my $response = $ua->get($url);
b688d438 368
f5ed75de
DM
369 if ($response->is_success) {
370 $data = $response->decoded_content;
371 } else {
396c9e4a 372 PVE::Exception::raise($response->message, code => $response->code);
f5ed75de 373 }
b688d438
DM
374
375 return $data;
376 }});
377
21299915 3781;