]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/APT.pm
api: apt versions: track optional pve-esxi-import-tools package
[pve-manager.git] / PVE / API2 / APT.pm
CommitLineData
21299915
DM
1package PVE::API2::APT;
2
3use strict;
4use warnings;
8794761f
DM
5
6use POSIX;
cd0bc36b 7use File::stat ();
a88002cf 8use IO::File;
d330e26f 9use File::Basename;
21299915 10
f5ed75de
DM
11use LWP::UserAgent;
12
9c767422
FE
13use Proxmox::RS::APT::Repositories;
14
8794761f 15use PVE::pvecfg;
21299915 16use PVE::Tools qw(extract_param);
f5ed75de 17use PVE::Cluster;
3ac3653e 18use PVE::DataCenterConfig;
21299915
DM
19use PVE::SafeSyslog;
20use PVE::INotify;
396c9e4a 21use PVE::Exception;
05855908 22use PVE::Notify;
21299915
DM
23use PVE::RESTHandler;
24use PVE::RPCEnvironment;
446b9669 25use PVE::API2Tools;
21299915 26
cd0bc36b 27use JSON;
21299915
DM
28use PVE::JSONSchema qw(get_standard_option);
29
30use AptPkg::Cache;
21299915 31use AptPkg::PkgRecords;
ee94b16a 32use AptPkg::System;
21299915 33
21299915 34my $get_apt_cache = sub {
89e4fc8c 35
c06e9cc8 36 my $apt_cache = AptPkg::Cache->new() || die "unable to initialize AptPkg::Cache\n";
21299915
DM
37
38 return $apt_cache;
39};
40
41use base qw(PVE::RESTHandler);
42
43__PACKAGE__->register_method({
89e4fc8c
TL
44 name => 'index',
45 path => '',
21299915
DM
46 method => 'GET',
47 description => "Directory index for apt (Advanced Package Tool).",
48 permissions => {
49 user => 'all',
50 },
51 parameters => {
89e4fc8c 52 additionalProperties => 0,
21299915
DM
53 properties => {
54 node => get_standard_option('pve-node'),
55 },
56 },
57 returns => {
58 type => "array",
59 items => {
60 type => "object",
61 properties => {
62 id => { type => 'string' },
63 },
64 },
65 links => [ { rel => 'child', href => "{id}" } ],
66 },
67 code => sub {
68 my ($param) = @_;
69
89e4fc8c 70 my $res = [
21299915 71 { id => 'changelog' },
9005e0df 72 { id => 'repositories' },
8794761f
DM
73 { id => 'update' },
74 { id => 'versions' },
21299915
DM
75 ];
76
77 return $res;
78 }});
79
a972b69d
DM
80my $get_pkgfile = sub {
81 my ($veriter) = @_;
21299915 82
a972b69d 83 foreach my $verfile (@{$veriter->{FileList}}) {
00d48356 84 my $pkgfile = $verfile->{File};
a972b69d
DM
85 next if !$pkgfile->{Origin};
86 return $pkgfile;
87 }
88
89 return undef;
90};
91
b688d438
DM
92my $assemble_pkginfo = sub {
93 my ($pkgname, $info, $current_ver, $candidate_ver) = @_;
00d48356 94
89e4fc8c 95 my $data = {
21299915
DM
96 Package => $info->{Name},
97 Title => $info->{ShortDesc},
a972b69d 98 Origin => 'unknown',
21299915
DM
99 };
100
a972b69d 101 if (my $pkgfile = &$get_pkgfile($candidate_ver)) {
2ba6d822 102 $data->{Origin} = $pkgfile->{Origin};
b688d438 103 }
00d48356 104
21299915
DM
105 if (my $desc = $info->{LongDesc}) {
106 $desc =~ s/^.*\n\s?//; # remove first line
107 $desc =~ s/\n / /g;
108 $data->{Description} = $desc;
109 }
89e4fc8c 110
21299915
DM
111 foreach my $k (qw(Section Arch Priority)) {
112 $data->{$k} = $candidate_ver->{$k};
113 }
114
115 $data->{Version} = $candidate_ver->{VerStr};
93305404 116 $data->{OldVersion} = $current_ver->{VerStr} if $current_ver;
21299915
DM
117
118 return $data;
119};
120
4806bc69
DM
121# we try to cache results
122my $pve_pkgstatus_fn = "/var/lib/pve-manager/pkgupdates";
745d942d 123my $read_cached_pkgstatus = sub {
c3264e92
TL
124 my $data = eval { decode_json(PVE::Tools::file_get_contents($pve_pkgstatus_fn, 5*1024*1024)) } // [];
125 warn "error reading cached package status in '$pve_pkgstatus_fn' - $@\n" if $@;
745d942d
DM
126 return $data;
127};
128
4806bc69 129my $update_pve_pkgstatus = sub {
446f9217
DM
130 syslog('info', "update new package list: $pve_pkgstatus_fn");
131
745d942d 132 my $oldpkglist = &$read_cached_pkgstatus();
c3264e92 133 my $notify_status = { map { $_->{Package} => $_->{NotifyStatus} } $oldpkglist->@* };
745d942d 134
4806bc69
DM
135 my $pkglist = [];
136
137 my $cache = &$get_apt_cache();
138 my $policy = $cache->policy;
139 my $pkgrecords = $cache->packages();
140
141 foreach my $pkgname (keys %$cache) {
142 my $p = $cache->{$pkgname};
2051816c 143 next if !$p->{SelectedState} || ($p->{SelectedState} ne 'Install');
26f876ce
DM
144 my $current_ver = $p->{CurrentVer} || next;
145 my $candidate_ver = $policy->candidate($p) || next;
913d8933
TL
146 next if $current_ver->{VerStr} eq $candidate_ver->{VerStr};
147
148 my $info = $pkgrecords->lookup($pkgname);
149 my $res = &$assemble_pkginfo($pkgname, $info, $current_ver, $candidate_ver);
150 push @$pkglist, $res;
151
152 # also check if we need any new package
153 # Note: this is just a quick hack (not recursive as it should be), because
154 # I found no way to get that info from AptPkg
155 my $deps = $candidate_ver->{DependsList} || next;
156
157 my ($found, $req);
158 for my $d (@$deps) {
159 if ($d->{DepType} eq 'Depends') {
160 $found = $d->{TargetPkg}->{SelectedState} eq 'Install' if !$found;
161 # need to check ProvidesList for virtual packages
162 if (!$found && (my $provides = $d->{TargetPkg}->{ProvidesList})) {
163 for my $provide ($provides->@*) {
164 $found = $provide->{OwnerPkg}->{SelectedState} eq 'Install';
165 last if $found;
166 }
167 }
168 $req = $d->{TargetPkg} if !$req;
169
170 if (!($d->{CompType} & AptPkg::Dep::Or)) {
171 if (!$found && $req) { # New required Package
172 my $tpname = $req->{Name};
173 my $tpinfo = $pkgrecords->lookup($tpname);
174 my $tpcv = $policy->candidate($req);
175 if ($tpinfo && $tpcv) {
176 my $res = &$assemble_pkginfo($tpname, $tpinfo, undef, $tpcv);
177 push @$pkglist, $res;
93305404
DM
178 }
179 }
913d8933
TL
180 undef $found;
181 undef $req;
93305404
DM
182 }
183 }
4806bc69
DM
184 }
185 }
745d942d
DM
186
187 # keep notification status (avoid sending mails abou new packages more than once)
188 foreach my $pi (@$pkglist) {
189 if (my $ns = $notify_status->{$pi->{Package}}) {
190 $pi->{NotifyStatus} = $ns if $ns eq $pi->{Version};
191 }
192 }
193
4806bc69
DM
194 PVE::Tools::file_set_contents($pve_pkgstatus_fn, encode_json($pkglist));
195
196 return $pkglist;
197};
198
21299915 199__PACKAGE__->register_method({
89e4fc8c
TL
200 name => 'list_updates',
201 path => 'update',
21299915
DM
202 method => 'GET',
203 description => "List available updates.",
204 permissions => {
205 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
206 },
207 protected => 1,
208 proxyto => 'node',
209 parameters => {
89e4fc8c 210 additionalProperties => 0,
21299915
DM
211 properties => {
212 node => get_standard_option('pve-node'),
213 },
214 },
215 returns => {
216 type => "array",
217 items => {
218 type => "object",
219 properties => {},
220 },
221 },
222 code => sub {
223 my ($param) = @_;
224
cd0bc36b
DM
225 if (my $st1 = File::stat::stat($pve_pkgstatus_fn)) {
226 my $st2 = File::stat::stat("/var/cache/apt/pkgcache.bin");
227 my $st3 = File::stat::stat("/var/lib/dpkg/status");
89e4fc8c 228
446f9217 229 if ($st2 && $st3 && $st2->mtime <= $st1->mtime && $st3->mtime <= $st1->mtime) {
745d942d 230 if (my $data = &$read_cached_pkgstatus()) {
cd0bc36b
DM
231 return $data;
232 }
233 }
234 }
235
4806bc69
DM
236 my $pkglist = &$update_pve_pkgstatus();
237
238 return $pkglist;
239 }});
240
05855908
LW
241my $updates_available_subject_template = "New software packages available ({{hostname}})";
242my $updates_available_body_template = <<EOT;
243The following updates are available:
244{{table updates}}
245EOT
246
4806bc69 247__PACKAGE__->register_method({
89e4fc8c
TL
248 name => 'update_database',
249 path => 'update',
4806bc69 250 method => 'POST',
ecddd2e2 251 description => "This is used to resynchronize the package index files from their sources (apt-get update).",
4806bc69
DM
252 permissions => {
253 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
254 },
255 protected => 1,
256 proxyto => 'node',
257 parameters => {
89e4fc8c 258 additionalProperties => 0,
4806bc69
DM
259 properties => {
260 node => get_standard_option('pve-node'),
a88002cf
DM
261 notify => {
262 type => 'boolean',
5fa9db35 263 description => "Send notification about new packages.",
a88002cf
DM
264 optional => 1,
265 default => 0,
266 },
2544e8d5
DM
267 quiet => {
268 type => 'boolean',
269 description => "Only produces output suitable for logging, omitting progress indicators.",
270 optional => 1,
271 default => 0,
272 },
4806bc69
DM
273 },
274 },
275 returns => {
276 type => 'string',
277 },
278 code => sub {
279 my ($param) = @_;
280
281 my $rpcenv = PVE::RPCEnvironment::get();
05855908 282 my $dcconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
21299915 283
4806bc69 284 my $authuser = $rpcenv->get_user();
21299915 285
4806bc69
DM
286 my $realcmd = sub {
287 my $upid = shift;
21299915 288
19ed44c0 289 # setup proxy for apt
19ed44c0
DM
290
291 my $aptconf = "// no proxy configured\n";
292 if ($dcconf->{http_proxy}) {
293 $aptconf = "Acquire::http::Proxy \"$dcconf->{http_proxy}\";\n";
294 }
295 my $aptcfn = "/etc/apt/apt.conf.d/76pveproxy";
296 PVE::Tools::file_set_contents($aptcfn, $aptconf);
297
ecddd2e2 298 my $cmd = ['apt-get', 'update'];
21299915 299
ecddd2e2 300 print "starting apt-get update\n" if !$param->{quiet};
89e4fc8c 301
f460dc12
DM
302 if ($param->{quiet}) {
303 PVE::Tools::run_command($cmd, outfunc => sub {}, errfunc => sub {});
304 } else {
305 PVE::Tools::run_command($cmd);
306 }
4806bc69 307
a88002cf
DM
308 my $pkglist = &$update_pve_pkgstatus();
309
310 if ($param->{notify} && scalar(@$pkglist)) {
05855908
LW
311 my $updates_table = {
312 schema => {
313 columns => [
314 {
315 label => "Package",
316 id => "package",
317 },
318 {
319 label => "Old Version",
320 id => "old-version",
321 },
322 {
323 label => "New Version",
324 id => "new-version",
325 }
326 ]
327 },
328 data => []
329 };
330
331 my $hostname = `hostname -f` || PVE::INotify::nodename();
332 chomp $hostname;
333
334 my $count = 0;
335 foreach my $p (sort {$a->{Package} cmp $b->{Package} } @$pkglist) {
336 next if $p->{NotifyStatus} && $p->{NotifyStatus} eq $p->{Version};
337 $count++;
338
339 push @{$updates_table->{data}}, {
340 "package" => $p->{Package},
341 "old-version" => $p->{OldVersion},
342 "new-version" => $p->{Version}
343 };
344 }
a88002cf 345
05855908 346 return if !$count;
a88002cf 347
5fa9db35 348 my $template_data = {
05855908
LW
349 updates => $updates_table,
350 hostname => $hostname,
351 };
a88002cf 352
5fa9db35
LW
353 # Additional metadata fields that can be used in notification
354 # matchers.
355 my $metadata_fields = {
356 type => 'package-updates',
357 hostname => $hostname,
358 };
359
05855908 360 PVE::Notify::info(
05855908
LW
361 $updates_available_subject_template,
362 $updates_available_body_template,
5fa9db35
LW
363 $template_data,
364 $metadata_fields,
05855908 365 );
745d942d 366
05855908
LW
367 foreach my $pi (@$pkglist) {
368 $pi->{NotifyStatus} = $pi->{Version};
a88002cf 369 }
05855908 370 PVE::Tools::file_set_contents($pve_pkgstatus_fn, encode_json($pkglist));
a88002cf 371 }
4806bc69
DM
372
373 return;
374 };
375
c2d3fbe0 376 return $rpcenv->fork_worker('aptupdate', undef, $authuser, $realcmd);
cd0bc36b 377
21299915
DM
378 }});
379
05855908
LW
380
381
b688d438 382__PACKAGE__->register_method({
89e4fc8c
TL
383 name => 'changelog',
384 path => 'changelog',
b688d438
DM
385 method => 'GET',
386 description => "Get package changelogs.",
387 permissions => {
388 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
389 },
a25fa6e9 390 proxyto => 'node',
b688d438 391 parameters => {
89e4fc8c 392 additionalProperties => 0,
b688d438
DM
393 properties => {
394 node => get_standard_option('pve-node'),
395 name => {
396 description => "Package name.",
397 type => 'string',
398 },
399 version => {
400 description => "Package version.",
401 type => 'string',
402 optional => 1,
89e4fc8c 403 },
b688d438
DM
404 },
405 },
406 returns => {
407 type => "string",
408 },
409 code => sub {
410 my ($param) = @_;
411
412 my $pkgname = $param->{name};
413
e337b294
FG
414 my $cmd = ['apt-get', 'changelog', '-qq'];
415 if (my $version = $param->{version}) {
416 push @$cmd, "$pkgname=$version";
b688d438 417 } else {
e337b294 418 push @$cmd, "$pkgname";
b688d438
DM
419 }
420
e337b294 421 my $output = "";
f5ed75de 422
e337b294
FG
423 my $rc = PVE::Tools::run_command(
424 $cmd,
425 timeout => 10,
426 logfunc => sub {
427 my $line = shift;
428 $output .= "$line\n";
429 },
430 noerr => 1,
431 );
2ba6d822 432
e337b294 433 $output .= "RC: $rc" if $rc != 0;
b688d438 434
e337b294 435 return $output;
b688d438
DM
436 }});
437
9005e0df
FE
438__PACKAGE__->register_method({
439 name => 'repositories',
440 path => 'repositories',
441 method => 'GET',
442 proxyto => 'node',
443 description => "Get APT repository information.",
444 permissions => {
445 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
446 },
447 parameters => {
448 additionalProperties => 0,
449 properties => {
450 node => get_standard_option('pve-node'),
451 },
452 },
453 returns => {
454 type => "object",
455 description => "Result from parsing the APT repository files in /etc/apt/.",
456 properties => {
457 files => {
458 type => "array",
459 description => "List of parsed repository files.",
460 items => {
461 type => "object",
462 properties => {
463 path => {
464 type => "string",
465 description => "Path to the problematic file.",
466 },
467 'file-type' => {
468 type => "string",
469 enum => [ 'list', 'sources' ],
470 description => "Format of the file.",
471 },
472 repositories => {
473 type => "array",
474 description => "The parsed repositories.",
475 items => {
476 type => "object",
477 properties => {
478 Types => {
479 type => "array",
480 description => "List of package types.",
481 items => {
482 type => "string",
483 enum => [ 'deb', 'deb-src' ],
484 },
485 },
486 URIs => {
487 description => "List of repository URIs.",
488 type => "array",
489 items => {
490 type => "string",
491 },
492 },
493 Suites => {
494 type => "array",
495 description => "List of package distribuitions",
496 items => {
497 type => "string",
498 },
499 },
500 Components => {
501 type => "array",
502 description => "List of repository components",
503 optional => 1, # not present if suite is absolute
504 items => {
505 type => "string",
506 },
507 },
508 Options => {
509 type => "array",
510 description => "Additional options",
511 optional => 1,
512 items => {
513 type => "object",
514 properties => {
515 Key => {
516 type => "string",
517 },
518 Values => {
519 type => "array",
520 items => {
521 type => "string",
522 },
523 },
524 },
525 },
526 },
527 Comment => {
528 type => "string",
529 description => "Associated comment",
530 optional => 1,
531 },
532 FileType => {
533 type => "string",
534 enum => [ 'list', 'sources' ],
535 description => "Format of the defining file.",
536 },
537 Enabled => {
538 type => "boolean",
539 description => "Whether the repository is enabled or not",
540 },
541 },
542 },
543 },
544 digest => {
545 type => "array",
546 description => "Digest of the file as bytes.",
547 items => {
548 type => "integer",
549 },
550 },
551 },
552 },
553 },
554 errors => {
555 type => "array",
556 description => "List of problematic repository files.",
557 items => {
558 type => "object",
559 properties => {
560 path => {
561 type => "string",
562 description => "Path to the problematic file.",
563 },
564 error => {
565 type => "string",
566 description => "The error message",
567 },
568 },
569 },
570 },
571 digest => {
572 type => "string",
573 description => "Common digest of all files.",
574 },
575 infos => {
576 type => "array",
577 description => "Additional information/warnings for APT repositories.",
578 items => {
579 type => "object",
580 properties => {
581 path => {
582 type => "string",
583 description => "Path to the associated file.",
584 },
585 index => {
586 type => "string",
587 description => "Index of the associated repository within the file.",
588 },
589 property => {
590 type => "string",
591 description => "Property from which the info originates.",
592 optional => 1,
593 },
594 kind => {
595 type => "string",
596 description => "Kind of the information (e.g. warning).",
597 },
598 message => {
599 type => "string",
600 description => "Information message.",
601 }
602 },
603 },
604 },
605 'standard-repos' => {
606 type => "array",
607 description => "List of standard repositories and their configuration status",
608 items => {
609 type => "object",
610 properties => {
611 handle => {
612 type => "string",
613 description => "Handle to identify the repository.",
614 },
615 name => {
616 type => "string",
617 description => "Full name of the repository.",
618 },
619 status => {
620 type => "boolean",
621 optional => 1,
622 description => "Indicating enabled/disabled status, if the " .
623 "repository is configured.",
624 },
625 },
626 },
627 },
628 },
629 },
630 code => sub {
631 my ($param) = @_;
632
9c767422 633 return Proxmox::RS::APT::Repositories::repositories("pve");
9005e0df
FE
634 }});
635
7e33f74b
FE
636__PACKAGE__->register_method({
637 name => 'add_repository',
638 path => 'repositories',
639 method => 'PUT',
640 description => "Add a standard repository to the configuration",
641 permissions => {
642 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
643 },
644 protected => 1,
645 proxyto => 'node',
646 parameters => {
647 additionalProperties => 0,
648 properties => {
649 node => get_standard_option('pve-node'),
650 handle => {
651 type => 'string',
652 description => "Handle that identifies a repository.",
653 },
654 digest => {
655 type => "string",
656 description => "Digest to detect modifications.",
657 maxLength => 80,
658 optional => 1,
659 },
660 },
661 },
662 returns => {
663 type => 'null',
664 },
665 code => sub {
666 my ($param) = @_;
667
9c767422 668 Proxmox::RS::APT::Repositories::add_repository($param->{handle}, "pve", $param->{digest});
7e33f74b
FE
669 }});
670
671__PACKAGE__->register_method({
672 name => 'change_repository',
673 path => 'repositories',
674 method => 'POST',
675 description => "Change the properties of a repository. Currently only allows enabling/disabling.",
676 permissions => {
677 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
678 },
679 protected => 1,
680 proxyto => 'node',
681 parameters => {
682 additionalProperties => 0,
683 properties => {
684 node => get_standard_option('pve-node'),
685 path => {
686 type => 'string',
687 description => "Path to the containing file.",
688 },
689 index => {
690 type => 'integer',
691 description => "Index within the file (starting from 0).",
692 },
693 enabled => {
694 type => 'boolean',
695 description => "Whether the repository should be enabled or not.",
696 optional => 1,
697 },
698 digest => {
699 type => "string",
700 description => "Digest to detect modifications.",
701 maxLength => 80,
702 optional => 1,
703 },
704 },
705 },
706 returns => {
707 type => 'null',
708 },
709 code => sub {
710 my ($param) = @_;
711
d3cd3c9c
FE
712 my $options = {};
713
714 my $enabled = $param->{enabled};
715 $options->{enabled} = int($enabled) if defined($enabled);
7e33f74b 716
9c767422 717 Proxmox::RS::APT::Repositories::change_repository(
7e33f74b 718 $param->{path},
d3cd3c9c 719 int($param->{index}),
7e33f74b
FE
720 $options,
721 $param->{digest}
722 );
723 }});
724
8794761f 725__PACKAGE__->register_method({
89e4fc8c
TL
726 name => 'versions',
727 path => 'versions',
8794761f 728 method => 'GET',
502a69ef 729 proxyto => 'node',
8794761f
DM
730 description => "Get package information for important Proxmox packages.",
731 permissions => {
732 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
733 },
734 parameters => {
89e4fc8c 735 additionalProperties => 0,
8794761f
DM
736 properties => {
737 node => get_standard_option('pve-node'),
738 },
739 },
740 returns => {
741 type => "array",
742 items => {
743 type => "object",
744 properties => {},
745 },
746 },
747 code => sub {
748 my ($param) = @_;
749
8794761f
DM
750 my $cache = &$get_apt_cache();
751 my $policy = $cache->policy;
752 my $pkgrecords = $cache->packages();
753
c861591b 754 # order most important things first
97f6e029 755 my @list = qw(proxmox-ve pve-manager);
8794761f 756
ee94b16a
FG
757 my $aptver = $AptPkg::System::_system->versioning();
758 my $byver = sub { $aptver->compare($cache->{$b}->{CurrentVer}->{VerStr}, $cache->{$a}->{CurrentVer}->{VerStr}) };
3fc687f5 759 push @list, sort $byver grep { /^(?:pve|proxmox)-kernel-/ && $cache->{$_}->{CurrentState} eq 'Installed' } keys %$cache;
c861591b
TL
760
761 my @opt_pack = qw(
8aeed8d1 762 amd64-microcode
c861591b 763 ceph
fcb80221 764 criu
b81057a9
TL
765 dnsmasq
766 frr-pythontools
c861591b 767 gfs2-utils
2e0f7840
TL
768 ifupdown
769 ifupdown2
8aeed8d1 770 intel-microcode
fcb80221
TL
771 ksm-control-daemon
772 ksmtuned
c861591b 773 libpve-apiclient-perl
4047ea24 774 libpve-network-perl
c861591b 775 openvswitch-switch
8794bb15 776 proxmox-backup-file-restore
49879475 777 proxmox-kernel-helper
d088f89b 778 proxmox-offline-mirror-helper
34d63c2b 779 pve-esxi-import-tools
a7a65b02 780 pve-zsync
c861591b
TL
781 zfsutils-linux
782 );
783
784 my @pkgs = qw(
fcb80221 785 ceph-fuse
c861591b 786 corosync
c861591b 787 glusterfs-client
ff472dae 788 libjs-extjs
fcb80221 789 libknet1
ff472dae
TL
790 libproxmox-acme-perl
791 libproxmox-backup-qemu0
ea659be6 792 libproxmox-rs-perl
c861591b 793 libpve-access-control
e308895f
HD
794 libpve-cluster-api-perl
795 libpve-cluster-perl
c861591b
TL
796 libpve-common-perl
797 libpve-guest-common-perl
798 libpve-http-server-perl
e308895f 799 livpve-notify-perl
ea659be6 800 libpve-rs-perl
c861591b
TL
801 libpve-storage-perl
802 libqb0
4cfe5534 803 libspice-server1
c861591b
TL
804 lvm2
805 lxc-pve
806 lxcfs
807 novnc-pve
6f5c3b98 808 proxmox-backup-client
118f9076 809 proxmox-mail-forward
fcb80221 810 proxmox-mini-journalreader
a7a65b02 811 proxmox-widget-toolkit
c861591b
TL
812 pve-cluster
813 pve-container
814 pve-docs
5d4bb1f7 815 pve-edk2-firmware
c861591b
TL
816 pve-firewall
817 pve-firmware
818 pve-ha-manager
a7a65b02 819 pve-i18n
c861591b 820 pve-qemu-kvm
a7a65b02 821 pve-xtermjs
c861591b
TL
822 qemu-server
823 smartmontools
a7a65b02 824 spiceterm
ff472dae 825 swtpm
c861591b
TL
826 vncterm
827 );
828
829 # add the rest ordered by name, easier to find for humans
830 push @list, (sort @pkgs, @opt_pack);
89e4fc8c 831
8794761f
DM
832 my (undef, undef, $kernel_release) = POSIX::uname();
833 my $pvever = PVE::pvecfg::version_text();
834
c861591b 835 my $pkglist = [];
8794761f
DM
836 foreach my $pkgname (@list) {
837 my $p = $cache->{$pkgname};
838 my $info = $pkgrecords->lookup($pkgname);
c0d696f9 839 my $candidate_ver = defined($p) ? $policy->candidate($p) : undef;
8794761f
DM
840 my $res;
841 if (my $current_ver = $p->{CurrentVer}) {
89e4fc8c 842 $res = $assemble_pkginfo->($pkgname, $info, $current_ver, $candidate_ver || $current_ver);
8794761f 843 } elsif ($candidate_ver) {
89e4fc8c 844 $res = $assemble_pkginfo->($pkgname, $info, $candidate_ver, $candidate_ver);
8794761f
DM
845 delete $res->{OldVersion};
846 } else {
847 next;
848 }
849 $res->{CurrentState} = $p->{CurrentState};
850
851 # hack: add some useful information (used by 'pveversion -v')
852 if ($pkgname eq 'pve-manager') {
853 $res->{ManagerVersion} = $pvever;
97f6e029 854 } elsif ($pkgname eq 'proxmox-ve') {
8794761f
DM
855 $res->{RunningKernel} = $kernel_release;
856 }
241c6f94
WL
857 if (grep( /^$pkgname$/, @opt_pack)) {
858 next if $res->{CurrentState} eq 'NotInstalled';
859 }
8794761f
DM
860
861 push @$pkglist, $res;
862 }
863
864 return $pkglist;
865 }});
866
21299915 8671;