]>
Commit | Line | Data |
---|---|---|
21299915 DM |
1 | package PVE::API2::APT; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
cd0bc36b | 5 | use File::stat (); |
21299915 DM |
6 | |
7 | use PVE::Tools qw(extract_param); | |
8 | use PVE::SafeSyslog; | |
9 | use PVE::INotify; | |
10 | use PVE::Exception qw(raise_param_exc); | |
11 | use PVE::RESTHandler; | |
12 | use PVE::RPCEnvironment; | |
13 | ||
cd0bc36b | 14 | use JSON; |
21299915 DM |
15 | use PVE::JSONSchema qw(get_standard_option); |
16 | ||
17 | use AptPkg::Cache; | |
18 | use AptPkg::Version; | |
19 | use AptPkg::PkgRecords; | |
20 | ||
21 | my $apt_cache; | |
22 | ||
23 | my $get_apt_cache = sub { | |
24 | ||
25 | return $apt_cache if $apt_cache; | |
26 | ||
27 | $apt_cache = AptPkg::Cache->new() || die "unable to initialize AptPkg::Cache\n"; | |
28 | ||
29 | return $apt_cache; | |
30 | }; | |
31 | ||
32 | use base qw(PVE::RESTHandler); | |
33 | ||
34 | __PACKAGE__->register_method({ | |
35 | name => 'index', | |
36 | path => '', | |
37 | method => 'GET', | |
38 | description => "Directory index for apt (Advanced Package Tool).", | |
39 | permissions => { | |
40 | user => 'all', | |
41 | }, | |
42 | parameters => { | |
43 | additionalProperties => 0, | |
44 | properties => { | |
45 | node => get_standard_option('pve-node'), | |
46 | }, | |
47 | }, | |
48 | returns => { | |
49 | type => "array", | |
50 | items => { | |
51 | type => "object", | |
52 | properties => { | |
53 | id => { type => 'string' }, | |
54 | }, | |
55 | }, | |
56 | links => [ { rel => 'child', href => "{id}" } ], | |
57 | }, | |
58 | code => sub { | |
59 | my ($param) = @_; | |
60 | ||
61 | my $res = [ | |
62 | { id => 'update' }, | |
63 | { id => 'upgrade' }, | |
64 | { id => 'changelog' }, | |
65 | ]; | |
66 | ||
67 | return $res; | |
68 | }}); | |
69 | ||
70 | my $assemble_pkginfo = sub { | |
71 | my ($pkgname, $info, $current_ver, $candidate_ver) = @_; | |
72 | ||
00d48356 DM |
73 | my $changelog_url; |
74 | foreach my $verfile (@{$candidate_ver->{FileList}}) { | |
75 | my $pkgfile = $verfile->{File}; | |
76 | my $origin = $pkgfile->{Origin}; | |
77 | my $comp = $pkgfile->{Component}; | |
78 | if ($origin && $comp) { | |
79 | my $pkgver = $candidate_ver->{VerStr}; | |
371dcc92 DM |
80 | $pkgver =~ s/^\d+://; # strip epoch |
81 | my $srcpkg = $info->{SourcePkg} || $pkgname; | |
82 | my $firstLetter = substr($srcpkg, 0, 1); | |
00d48356 DM |
83 | if ($origin eq 'Debian') { |
84 | $changelog_url = "http://packages.debian.org/changelogs/pool/main/" . | |
371dcc92 | 85 | "$firstLetter/$srcpkg/${srcpkg}_$pkgver/changelog"; |
00d48356 DM |
86 | } |
87 | last; | |
88 | } | |
89 | } | |
90 | ||
21299915 DM |
91 | my $data = { |
92 | Package => $info->{Name}, | |
93 | Title => $info->{ShortDesc}, | |
94 | }; | |
95 | ||
00d48356 DM |
96 | $data->{ChangeLogUrl} = $changelog_url if $changelog_url; |
97 | ||
21299915 DM |
98 | if (my $desc = $info->{LongDesc}) { |
99 | $desc =~ s/^.*\n\s?//; # remove first line | |
100 | $desc =~ s/\n / /g; | |
101 | $data->{Description} = $desc; | |
102 | } | |
103 | ||
104 | foreach my $k (qw(Section Arch Priority)) { | |
105 | $data->{$k} = $candidate_ver->{$k}; | |
106 | } | |
107 | ||
108 | $data->{Version} = $candidate_ver->{VerStr}; | |
109 | $data->{OldVersion} = $current_ver->{VerStr}; | |
110 | ||
111 | return $data; | |
112 | }; | |
113 | ||
4806bc69 DM |
114 | # we try to cache results |
115 | my $pve_pkgstatus_fn = "/var/lib/pve-manager/pkgupdates"; | |
116 | ||
117 | my $update_pve_pkgstatus = sub { | |
118 | ||
119 | my $pkglist = []; | |
120 | ||
121 | my $cache = &$get_apt_cache(); | |
122 | my $policy = $cache->policy; | |
123 | my $pkgrecords = $cache->packages(); | |
124 | ||
125 | foreach my $pkgname (keys %$cache) { | |
126 | my $p = $cache->{$pkgname}; | |
127 | next if $p->{SelectedState} ne 'Install'; | |
128 | my $current_ver = $p->{CurrentVer}; | |
129 | my $candidate_ver = $policy->candidate($p); | |
130 | ||
131 | if ($current_ver->{VerStr} ne $candidate_ver->{VerStr}) { | |
132 | my $info = $pkgrecords->lookup($pkgname); | |
133 | my $res = &$assemble_pkginfo($pkgname, $info, $current_ver, $candidate_ver); | |
134 | push @$pkglist, $res; | |
135 | } | |
136 | } | |
137 | ||
138 | PVE::Tools::file_set_contents($pve_pkgstatus_fn, encode_json($pkglist)); | |
139 | ||
140 | return $pkglist; | |
141 | }; | |
142 | ||
21299915 DM |
143 | __PACKAGE__->register_method({ |
144 | name => 'list_updates', | |
145 | path => 'update', | |
146 | method => 'GET', | |
147 | description => "List available updates.", | |
148 | permissions => { | |
149 | check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]], | |
150 | }, | |
151 | protected => 1, | |
152 | proxyto => 'node', | |
153 | parameters => { | |
154 | additionalProperties => 0, | |
155 | properties => { | |
156 | node => get_standard_option('pve-node'), | |
157 | }, | |
158 | }, | |
159 | returns => { | |
160 | type => "array", | |
161 | items => { | |
162 | type => "object", | |
163 | properties => {}, | |
164 | }, | |
165 | }, | |
166 | code => sub { | |
167 | my ($param) = @_; | |
168 | ||
cd0bc36b DM |
169 | if (my $st1 = File::stat::stat($pve_pkgstatus_fn)) { |
170 | my $st2 = File::stat::stat("/var/cache/apt/pkgcache.bin"); | |
171 | my $st3 = File::stat::stat("/var/lib/dpkg/status"); | |
172 | ||
c2d3fbe0 | 173 | if ($st2->mtime <= $st1->mtime && $st3->mtime <= $st1->mtime) { |
cd0bc36b DM |
174 | my $data; |
175 | eval { | |
176 | my $jsonstr = PVE::Tools::file_get_contents($pve_pkgstatus_fn, 5*1024*1024); | |
177 | $data = decode_json($jsonstr); | |
178 | }; | |
179 | if (my $err = $@) { | |
180 | warn "error readin cached package status in $pve_pkgstatus_fn\n"; | |
181 | # continue and overwrite cache with new content | |
182 | } else { | |
183 | return $data; | |
184 | } | |
185 | } | |
186 | } | |
187 | ||
4806bc69 DM |
188 | my $pkglist = &$update_pve_pkgstatus(); |
189 | ||
190 | return $pkglist; | |
191 | }}); | |
192 | ||
193 | __PACKAGE__->register_method({ | |
194 | name => 'update_database', | |
195 | path => 'update', | |
196 | method => 'POST', | |
197 | description => "This is used to resynchronize the package index files from their sources (apt-get update).", | |
198 | permissions => { | |
199 | check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]], | |
200 | }, | |
201 | protected => 1, | |
202 | proxyto => 'node', | |
203 | parameters => { | |
204 | additionalProperties => 0, | |
205 | properties => { | |
206 | node => get_standard_option('pve-node'), | |
207 | }, | |
208 | }, | |
209 | returns => { | |
210 | type => 'string', | |
211 | }, | |
212 | code => sub { | |
213 | my ($param) = @_; | |
214 | ||
215 | my $rpcenv = PVE::RPCEnvironment::get(); | |
21299915 | 216 | |
4806bc69 | 217 | my $authuser = $rpcenv->get_user(); |
21299915 | 218 | |
4806bc69 DM |
219 | my $realcmd = sub { |
220 | my $upid = shift; | |
21299915 | 221 | |
4806bc69 | 222 | my $cmd = ['apt-get', 'update']; |
21299915 | 223 | |
4806bc69 DM |
224 | print "starting apt-get update\n"; |
225 | ||
226 | PVE::Tools::run_command($cmd); | |
227 | ||
228 | &$update_pve_pkgstatus(); | |
229 | ||
230 | return; | |
231 | }; | |
232 | ||
c2d3fbe0 DM |
233 | return $rpcenv->fork_worker('aptupdate', undef, $authuser, $realcmd); |
234 | ||
235 | }}); | |
236 | ||
237 | __PACKAGE__->register_method({ | |
238 | name => 'upgrade', | |
239 | path => 'upgrade', | |
240 | method => 'POST', | |
241 | description => "Install the newest versions of all packages (apt-get dist-upgrade).", | |
242 | permissions => { | |
243 | check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]], | |
244 | }, | |
245 | protected => 1, | |
246 | proxyto => 'node', | |
247 | parameters => { | |
248 | additionalProperties => 0, | |
249 | properties => { | |
250 | node => get_standard_option('pve-node'), | |
251 | }, | |
252 | }, | |
253 | returns => { | |
254 | type => 'string', | |
255 | }, | |
256 | code => sub { | |
257 | my ($param) = @_; | |
258 | ||
259 | my $rpcenv = PVE::RPCEnvironment::get(); | |
260 | ||
261 | my $authuser = $rpcenv->get_user(); | |
262 | ||
263 | my $realcmd = sub { | |
264 | my $upid = shift; | |
265 | ||
266 | my $cmd = ['apt-get', 'dist-upgrade', '--assume-yes']; | |
267 | ||
268 | print "starting apt-get dist-upgrade\n"; | |
269 | ||
270 | $ENV{DEBIAN_FRONTEND} = 'noninteractive'; | |
271 | ||
272 | PVE::Tools::run_command($cmd); | |
273 | ||
274 | &$update_pve_pkgstatus(); | |
275 | ||
276 | return; | |
277 | }; | |
278 | ||
279 | return $rpcenv->fork_worker('aptupgrade', undef, $authuser, $realcmd); | |
cd0bc36b | 280 | |
21299915 DM |
281 | }}); |
282 | ||
283 | 1; |