backup: Wire up qemu full pull backup commands over QMP
[libvirt/ericb.git] / docs / hvsupport.pl
blob4f4d86fbf12c5c3aecdb50cc17fae64fea253ba1
1 #!/usr/bin/env perl
3 use strict;
4 use warnings;
6 use File::Find;
8 die "syntax: $0 SRCDIR\n" unless int(@ARGV) == 1;
10 my $srcdir = shift @ARGV;
12 my $symslibvirt = "$srcdir/libvirt_public.syms";
13 my $symsqemu = "$srcdir/libvirt_qemu.syms";
14 my $symslxc = "$srcdir/libvirt_lxc.syms";
15 my @drivertable = (
16 "$srcdir/driver-hypervisor.h",
17 "$srcdir/driver-interface.h",
18 "$srcdir/driver-network.h",
19 "$srcdir/driver-nodedev.h",
20 "$srcdir/driver-nwfilter.h",
21 "$srcdir/driver-secret.h",
22 "$srcdir/driver-state.h",
23 "$srcdir/driver-storage.h",
24 "$srcdir/driver-stream.h",
27 my %groupheaders = (
28 "virHypervisorDriver" => "Hypervisor APIs",
29 "virNetworkDriver" => "Virtual Network APIs",
30 "virInterfaceDriver" => "Host Interface APIs",
31 "virNodeDeviceDriver" => "Host Device APIs",
32 "virStorageDriver" => "Storage Pool APIs",
33 "virSecretDriver" => "Secret APIs",
34 "virNWFilterDriver" => "Network Filter APIs",
38 my @srcs;
39 find({
40 wanted => sub {
41 if (m!$srcdir/.*/\w+_(driver|common|tmpl|monitor|hal|udev)\.c$!) {
42 push @srcs, $_ if $_ !~ /vbox_driver\.c/;
44 }, no_chdir => 1}, $srcdir);
46 # Map API functions to the header and documentation files they're in
47 # so that we can generate proper hyperlinks to their documentation.
49 # The function names are grep'd from the XML output of apibuild.py.
50 sub getAPIFilenames {
51 my $filename = shift;
53 my %files;
54 my $line;
56 open FILE, "<", $filename or die "cannot read $filename: $!";
58 while (defined($line = <FILE>)) {
59 if ($line =~ /function name='([^']+)' file='([^']+)'/) {
60 $files{$1} = $2;
64 close FILE;
66 if (keys %files == 0) {
67 die "No functions found in $filename. Has the apibuild.py output changed?";
69 return \%files;
72 sub parseSymsFile {
73 my $apisref = shift;
74 my $prefix = shift;
75 my $filename = shift;
76 my $xmlfilename = shift;
78 my $line;
79 my $vers;
80 my $prevvers;
82 my $filenames = getAPIFilenames($xmlfilename);
84 open FILE, "<$filename"
85 or die "cannot read $filename: $!";
87 while (defined($line = <FILE>)) {
88 chomp $line;
89 next if $line =~ /^\s*#/;
90 next if $line =~ /^\s*$/;
91 next if $line =~ /^\s*(global|local):/;
92 if ($line =~ /^\s*${prefix}_(\d+\.\d+\.\d+)\s*{\s*$/) {
93 if (defined $vers) {
94 die "malformed syms file";
96 $vers = $1;
97 } elsif ($line =~ /\s*}\s*;\s*$/) {
98 if (defined $prevvers) {
99 die "malformed syms file";
101 $prevvers = $vers;
102 $vers = undef;
103 } elsif ($line =~ /\s*}\s*${prefix}_(\d+\.\d+\.\d+)\s*;\s*$/) {
104 if ($1 ne $prevvers) {
105 die "malformed syms file $1 != $vers";
107 $prevvers = $vers;
108 $vers = undef;
109 } elsif ($line =~ /\s*(\w+)\s*;\s*$/) {
110 $$apisref{$1} = {};
111 $$apisref{$1}->{vers} = $vers;
112 $$apisref{$1}->{file} = $$filenames{$1};
113 } else {
114 die "unexpected data $line\n";
118 close FILE;
121 my %apis;
122 # Get the list of all public APIs and their corresponding version
123 parseSymsFile(\%apis, "LIBVIRT", $symslibvirt, "$srcdir/../docs/libvirt-api.xml");
125 # And the same for the QEMU specific APIs
126 parseSymsFile(\%apis, "LIBVIRT_QEMU", $symsqemu, "$srcdir/../docs/libvirt-qemu-api.xml");
128 # And the same for the LXC specific APIs
129 parseSymsFile(\%apis, "LIBVIRT_LXC", $symslxc, "$srcdir/../docs/libvirt-lxc-api.xml");
132 # Some special things which aren't public APIs,
133 # but we want to report
134 $apis{virConnectSupportsFeature}->{vers} = "0.3.2";
135 $apis{virDomainMigratePrepare}->{vers} = "0.3.2";
136 $apis{virDomainMigratePerform}->{vers} = "0.3.2";
137 $apis{virDomainMigrateFinish}->{vers} = "0.3.2";
138 $apis{virDomainMigratePrepare2}->{vers} = "0.5.0";
139 $apis{virDomainMigrateFinish2}->{vers} = "0.5.0";
140 $apis{virDomainMigratePrepareTunnel}->{vers} = "0.7.2";
142 $apis{virDomainMigrateBegin3}->{vers} = "0.9.2";
143 $apis{virDomainMigratePrepare3}->{vers} = "0.9.2";
144 $apis{virDomainMigratePrepareTunnel3}->{vers} = "0.9.2";
145 $apis{virDomainMigratePerform3}->{vers} = "0.9.2";
146 $apis{virDomainMigrateFinish3}->{vers} = "0.9.2";
147 $apis{virDomainMigrateConfirm3}->{vers} = "0.9.2";
149 $apis{virDomainMigrateBegin3Params}->{vers} = "1.1.0";
150 $apis{virDomainMigratePrepare3Params}->{vers} = "1.1.0";
151 $apis{virDomainMigratePrepareTunnel3Params}->{vers} = "1.1.0";
152 $apis{virDomainMigratePerform3Params}->{vers} = "1.1.0";
153 $apis{virDomainMigrateFinish3Params}->{vers} = "1.1.0";
154 $apis{virDomainMigrateConfirm3Params}->{vers} = "1.1.0";
158 # Now we want to get the mapping between public APIs
159 # and driver struct fields. This lets us later match
160 # update the driver impls with the public APis.
162 my $line;
164 # Group name -> hash of APIs { fields -> api name }
165 my %groups;
166 my $ingrp;
167 foreach my $drivertable (@drivertable) {
168 open FILE, "<$drivertable"
169 or die "cannot read $drivertable: $!";
171 while (defined($line = <FILE>)) {
172 if ($line =~ /struct _(vir\w*Driver)/) {
173 my $grp = $1;
174 if ($grp ne "virStateDriver" &&
175 $grp ne "virStreamDriver") {
176 $ingrp = $grp;
177 $groups{$ingrp} = { apis => {}, drivers => {} };
179 } elsif ($ingrp) {
180 if ($line =~ /^\s*vir(?:Drv)(\w+)\s+(\w+);\s*$/) {
181 my $field = $2;
182 my $name = $1;
184 my $api;
185 if (exists $apis{"vir$name"}) {
186 $api = "vir$name";
187 } elsif ($name =~ /\w+(Open|Close|URIProbe)/) {
188 next;
189 } else {
190 die "driver $name does not have a public API";
192 $groups{$ingrp}->{apis}->{$field} = $api;
193 } elsif ($line =~ /};/) {
194 $ingrp = undef;
199 close FILE;
203 # Finally, we read all the primary driver files and extract
204 # the driver API tables from each one.
206 foreach my $src (@srcs) {
207 open FILE, "<$src" or
208 die "cannot read $src: $!";
210 my $groups_regex = join("|", keys %groups);
211 $ingrp = undef;
212 my $impl;
213 while (defined($line = <FILE>)) {
214 if (!$ingrp) {
215 # skip non-matching lines early to save time
216 next if not $line =~ /$groups_regex/;
218 if ($line =~ /^\s*(?:static\s+)?($groups_regex)\s+(\w+)\s*=\s*{/ ||
219 $line =~ /^\s*(?:static\s+)?($groups_regex)\s+NAME\(\w+\)\s*=\s*{/) {
220 $ingrp = $1;
221 $impl = $src;
223 if ($impl =~ m,.*/node_device_(\w+)\.c,) {
224 $impl = $1;
225 } else {
226 $impl =~ s,.*/(\w+?)_((\w+)_)?(\w+)\.c,$1,;
229 if ($groups{$ingrp}->{drivers}->{$impl}) {
230 die "Group $ingrp already contains $impl";
233 $groups{$ingrp}->{drivers}->{$impl} = {};
236 } else {
237 if ($line =~ m!\s*\.(\w+)\s*=\s*(\w+)\s*,?\s*(?:/\*\s*(\d+\.\d+\.\d+)\s*(?:-\s*(\d+\.\d+\.\d+))?\s*\*/\s*)?$!) {
238 my $api = $1;
239 my $meth = $2;
240 my $vers = $3;
241 my $deleted = $4;
243 next if $api eq "no" || $api eq "name";
245 if ($meth eq "NULL" && !defined $deleted) {
246 die "Method impl for $api is NULL, but no deleted version is provided";
248 if ($meth ne "NULL" && defined $deleted) {
249 die "Method impl for $api is non-NULL, but deleted version is provided";
252 die "Method $meth in $src is missing version" unless defined $vers || $api eq "connectURIProbe";
254 if (!exists($groups{$ingrp}->{apis}->{$api})) {
255 next if $api =~ /\w(Open|Close|URIProbe)/;
257 die "Found unexpected method $api in $ingrp\n";
260 $groups{$ingrp}->{drivers}->{$impl}->{$api} = {};
261 $groups{$ingrp}->{drivers}->{$impl}->{$api}->{vers} = $vers;
262 $groups{$ingrp}->{drivers}->{$impl}->{$api}->{deleted} = $deleted;
263 if ($api eq "domainMigratePrepare" ||
264 $api eq "domainMigratePrepare2" ||
265 $api eq "domainMigratePrepare3") {
266 if (!$groups{$ingrp}->{drivers}->{$impl}->{"domainMigrate"}) {
267 $groups{$ingrp}->{drivers}->{$impl}->{"domainMigrate"} = {};
268 $groups{$ingrp}->{drivers}->{$impl}->{"domainMigrate"}->{vers} = $vers;
272 } elsif ($line =~ /}/) {
273 $ingrp = undef;
278 close FILE;
282 # The '.open' driver method is used for 3 public APIs, so we
283 # have a bit of manual fixup todo with the per-driver versioning
284 # and support matrix
286 $groups{virHypervisorDriver}->{apis}->{"openAuth"} = "virConnectOpenAuth";
287 $groups{virHypervisorDriver}->{apis}->{"openReadOnly"} = "virConnectOpenReadOnly";
288 $groups{virHypervisorDriver}->{apis}->{"domainMigrate"} = "virDomainMigrate";
290 my $openAuthVers = (0 * 1000 * 1000) + (4 * 1000) + 0;
292 foreach my $drv (keys %{$groups{"virHypervisorDriver"}->{drivers}}) {
293 my $openVersStr = $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"connectOpen"}->{vers};
294 my $openVers;
295 if ($openVersStr =~ /(\d+)\.(\d+)\.(\d+)/) {
296 $openVers = ($1 * 1000 * 1000) + ($2 * 1000) + $3;
299 # virConnectOpenReadOnly always matches virConnectOpen version
300 $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"connectOpenReadOnly"} =
301 $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"connectOpen"};
303 $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"connectOpenAuth"} = {};
305 # virConnectOpenAuth is always 0.4.0 if the driver existed
306 # before this time, otherwise it matches the version of
307 # the driver's virConnectOpen entry
308 if ($openVersStr eq "Y" ||
309 $openVers >= $openAuthVers) {
310 $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"connectOpenAuth"}->{vers} = $openVersStr;
311 } else {
312 $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"connectOpenAuth"}->{vers} = "0.4.0";
317 # Another special case for the virDomainCreateLinux which was replaced
318 # with virDomainCreateXML
319 $groups{virHypervisorDriver}->{apis}->{"domainCreateLinux"} = "virDomainCreateLinux";
321 my $createAPIVers = (0 * 1000 * 1000) + (0 * 1000) + 3;
323 foreach my $drv (keys %{$groups{"virHypervisorDriver"}->{drivers}}) {
324 my $createVersStr = $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"domainCreateXML"}->{vers};
325 next unless defined $createVersStr;
326 my $createVers;
327 if ($createVersStr =~ /(\d+)\.(\d+)\.(\d+)/) {
328 $createVers = ($1 * 1000 * 1000) + ($2 * 1000) + $3;
331 $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"domainCreateLinux"} = {};
333 # virCreateLinux is always 0.0.3 if the driver existed
334 # before this time, otherwise it matches the version of
335 # the driver's virCreateXML entry
336 if ($createVersStr eq "Y" ||
337 $createVers >= $createAPIVers) {
338 $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"domainCreateLinux"}->{vers} = $createVersStr;
339 } else {
340 $groups{"virHypervisorDriver"}->{drivers}->{$drv}->{"domainCreateLinux"}->{vers} = "0.0.3";
345 # Finally we generate the HTML file with the tables
347 print <<EOF;
348 <?xml version="1.0" encoding="UTF-8"?>
349 <!DOCTYPE html>
350 <html xmlns="http://www.w3.org/1999/xhtml">
351 <body class="hvsupport">
352 <h1>libvirt API support matrix</h1>
354 <ul id="toc"></ul>
357 This page documents which <a href="html/">libvirt calls</a> work on
358 which libvirt drivers / hypervisors, and which version the API appeared
359 in. If a hypervisor driver later dropped support for the API, the version
360 when it was removed is also mentioned (highlighted in
361 <span class="removedhv">dark red</span>).
362 </p>
366 foreach my $grp (sort { $a cmp $b } keys %groups) {
367 print "<h2><a name=\"$grp\">", $groupheaders{$grp}, "</a></h2>\n";
368 print <<EOF;
369 <table class="top_table">
370 <thead>
371 <tr>
372 <th>API</th>
373 <th>Version</th>
376 foreach my $drv (sort { $a cmp $b } keys %{$groups{$grp}->{drivers}}) {
377 print " <th>$drv</th>\n";
380 print <<EOF;
381 </tr>
382 </thead>
383 <tbody>
386 my $row = 0;
387 foreach my $field (sort {
388 $groups{$grp}->{apis}->{$a}
390 $groups{$grp}->{apis}->{$b}
391 } keys %{$groups{$grp}->{apis}}) {
392 my $api = $groups{$grp}->{apis}->{$field};
393 my $vers = $apis{$api}->{vers};
394 my $htmlgrp = $apis{$api}->{file};
395 print <<EOF;
396 <tr>
397 <td>
400 if (defined $htmlgrp) {
401 print <<EOF;
402 <a href=\"html/libvirt-$htmlgrp.html#$api\">$api</a>
405 } else {
406 print $api;
408 print <<EOF;
409 </td>
410 <td>$vers</td>
413 foreach my $drv (sort {$a cmp $b } keys %{$groups{$grp}->{drivers}}) {
414 print "<td>";
415 if (exists $groups{$grp}->{drivers}->{$drv}->{$field}) {
416 if ($groups{$grp}->{drivers}->{$drv}->{$field}->{vers}) {
417 print $groups{$grp}->{drivers}->{$drv}->{$field}->{vers};
419 if ($groups{$grp}->{drivers}->{$drv}->{$field}->{deleted}) {
420 print " - <span class=\"removedhv\">", $groups{$grp}->{drivers}->{$drv}->{$field}->{deleted}, "</span>";
423 print "</td>\n";
426 print <<EOF;
427 </tr>
430 $row++;
431 if (($row % 15) == 0) {
432 print <<EOF;
433 <tr>
434 <th>API</th>
435 <th>Version</th>
438 foreach my $drv (sort { $a cmp $b } keys %{$groups{$grp}->{drivers}}) {
439 print " <th>$drv</th>\n";
442 print <<EOF;
443 </tr>
449 print <<EOF;
450 </tbody>
451 </table>
455 print <<EOF;
456 </body>
457 </html>