2011-05-13 14:00:56 +04:00
#!/usr/bin/perl
use strict ;
use warnings ;
use File::Find ;
die "syntax: $0 SRCDIR\n" unless int ( @ ARGV ) == 1 ;
my $ srcdir = shift @ ARGV ;
my $ symslibvirt = "$srcdir/libvirt_public.syms" ;
my $ symsqemu = "$srcdir/libvirt_qemu.syms" ;
Introduce an LXC specific public API & library
This patch introduces support for LXC specific public APIs. In
common with what was done for QEMU, this creates a libvirt_lxc.so
library and libvirt/libvirt-lxc.h header file.
The actual APIs are
int virDomainLxcOpenNamespace(virDomainPtr domain,
int **fdlist,
unsigned int flags);
int virDomainLxcEnterNamespace(virDomainPtr domain,
unsigned int nfdlist,
int *fdlist,
unsigned int *noldfdlist,
int **oldfdlist,
unsigned int flags);
which provide a way to use the setns() system call to move the
calling process into the container's namespace. It is not
practical to write in a generically applicable manner. The
nearest that we could get to such an API would be an API which
allows to pass a command + argv to be executed inside a
container. Even if we had such a generic API, this LXC specific
API is still useful, because it allows the caller to maintain
the current process context, in particular any I/O streams they
have open.
NB the virDomainLxcEnterNamespace() API is special in that it
runs client side, so does not involve the internal driver API.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2012-12-21 17:15:19 +04:00
my $ symslxc = "$srcdir/libvirt_lxc.syms" ;
2011-05-13 14:00:56 +04:00
my $ drivertable = "$srcdir/driver.h" ;
my % groupheaders = (
"virDriver" = > "Hypervisor APIs" ,
"virNetworkDriver" = > "Virtual Network APIs" ,
"virInterfaceDriver" = > "Host Interface APIs" ,
"virDeviceMonitor" = > "Host Device APIs" ,
"virStorageDriver" = > "Storage Pool APIs" ,
"virSecretDriver" = > "Secret APIs" ,
"virNWFilterDriver" = > "Network Filter APIs" ,
) ;
my @ srcs ;
find ( {
wanted = > sub {
2012-07-27 16:51:28 +04:00
if ( m !$srcdir/.*/\w+_(driver|tmpl|monitor|hal|udev)\.c$! ) {
push @ srcs , $ _ if $ _ !~ /vbox_driver\.c/ ;
}
2011-05-13 14:00:56 +04:00
} , no_chdir = > 1 } , $ srcdir ) ;
my $ line ;
# Get the list of all public APIs and their corresponding version
my % apis ;
open FILE , "<$symslibvirt"
or die "cannot read $symslibvirt: $!" ;
my $ vers ;
my $ prevvers ;
while ( defined ( $ line = <FILE> ) ) {
chomp $ line ;
next if $ line =~ /^\s*#/ ;
next if $ line =~ /^\s*$/ ;
next if $ line =~ /^\s*(global|local):/ ;
if ( $ line =~ /^\s*LIBVIRT_(\d+\.\d+\.\d+)\s*{\s*$/ ) {
2012-07-27 16:51:28 +04:00
if ( defined $ vers ) {
die "malformed syms file" ;
}
$ vers = $ 1 ;
2011-05-13 14:00:56 +04:00
} elsif ( $ line =~ /\s*}\s*;\s*$/ ) {
2012-07-27 16:51:28 +04:00
if ( defined $ prevvers ) {
die "malformed syms file" ;
}
$ prevvers = $ vers ;
$ vers = undef ;
2011-05-13 14:00:56 +04:00
} elsif ( $ line =~ /\s*}\s*LIBVIRT_(\d+\.\d+\.\d+)\s*;\s*$/ ) {
2012-07-27 16:51:28 +04:00
if ( $ 1 ne $ prevvers ) {
die "malformed syms file $1 != $vers" ;
}
$ prevvers = $ vers ;
$ vers = undef ;
2011-05-13 14:00:56 +04:00
} elsif ( $ line =~ /\s*(\w+)\s*;\s*$/ ) {
2012-07-27 16:51:28 +04:00
$ apis { $ 1 } = $ vers ;
2011-05-13 14:00:56 +04:00
} else {
2012-07-27 16:51:28 +04:00
die "unexpected data $line\n" ;
2011-05-13 14:00:56 +04:00
}
}
close FILE ;
# And the same for the QEMU specific APIs
open FILE , "<$symsqemu"
or die "cannot read $symsqemu: $!" ;
$ prevvers = undef ;
$ vers = undef ;
while ( defined ( $ line = <FILE> ) ) {
chomp $ line ;
next if $ line =~ /^\s*#/ ;
next if $ line =~ /^\s*$/ ;
next if $ line =~ /^\s*(global|local):/ ;
if ( $ line =~ /^\s*LIBVIRT_QEMU_(\d+\.\d+\.\d+)\s*{\s*$/ ) {
2012-07-27 16:51:28 +04:00
if ( defined $ vers ) {
die "malformed syms file" ;
}
$ vers = $ 1 ;
2011-05-13 14:00:56 +04:00
} elsif ( $ line =~ /\s*}\s*;\s*$/ ) {
2012-07-27 16:51:28 +04:00
if ( defined $ prevvers ) {
die "malformed syms file" ;
}
$ prevvers = $ vers ;
$ vers = undef ;
2011-05-13 14:00:56 +04:00
} elsif ( $ line =~ /\s*}\s*LIBVIRT_QEMU_(\d+\.\d+\.\d+)\s*;\s*$/ ) {
2012-07-27 16:51:28 +04:00
if ( $ 1 ne $ prevvers ) {
die "malformed syms file $1 != $vers" ;
}
$ prevvers = $ vers ;
$ vers = undef ;
2011-05-13 14:00:56 +04:00
} elsif ( $ line =~ /\s*(\w+)\s*;\s*$/ ) {
2012-07-27 16:51:28 +04:00
$ apis { $ 1 } = $ vers ;
2011-05-13 14:00:56 +04:00
} else {
2012-07-27 16:51:28 +04:00
die "unexpected data $line\n" ;
Introduce an LXC specific public API & library
This patch introduces support for LXC specific public APIs. In
common with what was done for QEMU, this creates a libvirt_lxc.so
library and libvirt/libvirt-lxc.h header file.
The actual APIs are
int virDomainLxcOpenNamespace(virDomainPtr domain,
int **fdlist,
unsigned int flags);
int virDomainLxcEnterNamespace(virDomainPtr domain,
unsigned int nfdlist,
int *fdlist,
unsigned int *noldfdlist,
int **oldfdlist,
unsigned int flags);
which provide a way to use the setns() system call to move the
calling process into the container's namespace. It is not
practical to write in a generically applicable manner. The
nearest that we could get to such an API would be an API which
allows to pass a command + argv to be executed inside a
container. Even if we had such a generic API, this LXC specific
API is still useful, because it allows the caller to maintain
the current process context, in particular any I/O streams they
have open.
NB the virDomainLxcEnterNamespace() API is special in that it
runs client side, so does not involve the internal driver API.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2012-12-21 17:15:19 +04:00
}
}
close FILE ;
# And the same for the LXC specific APIs
open FILE , "<$symslxc"
or die "cannot read $symslxc: $!" ;
$ prevvers = undef ;
$ vers = undef ;
while ( defined ( $ line = <FILE> ) ) {
chomp $ line ;
next if $ line =~ /^\s*#/ ;
next if $ line =~ /^\s*$/ ;
next if $ line =~ /^\s*(global|local):/ ;
if ( $ line =~ /^\s*LIBVIRT_LXC_(\d+\.\d+\.\d+)\s*{\s*$/ ) {
if ( defined $ vers ) {
die "malformed syms file" ;
}
$ vers = $ 1 ;
} elsif ( $ line =~ /\s*}\s*;\s*$/ ) {
if ( defined $ prevvers ) {
die "malformed syms file" ;
}
$ prevvers = $ vers ;
$ vers = undef ;
} elsif ( $ line =~ /\s*}\s*LIBVIRT_LXC_(\d+\.\d+\.\d+)\s*;\s*$/ ) {
if ( $ 1 ne $ prevvers ) {
die "malformed syms file $1 != $vers" ;
}
$ prevvers = $ vers ;
$ vers = undef ;
} elsif ( $ line =~ /\s*(\w+)\s*;\s*$/ ) {
$ apis { $ 1 } = $ vers ;
} else {
die "unexpected data $line\n" ;
2011-05-13 14:00:56 +04:00
}
}
close FILE ;
# Some special things which aren't public APIs,
# but we want to report
$ apis { virConnectDrvSupportsFeature } = "0.3.2" ;
$ apis { virDomainMigratePrepare } = "0.3.2" ;
$ apis { virDomainMigratePerform } = "0.3.2" ;
$ apis { virDomainMigrateFinish } = "0.3.2" ;
$ apis { virDomainMigratePrepare2 } = "0.5.0" ;
$ apis { virDomainMigrateFinish2 } = "0.5.0" ;
$ apis { virDomainMigratePrepareTunnel } = "0.7.2" ;
$ apis { virDomainMigrateBegin3 } = "0.9.2" ;
$ apis { virDomainMigratePrepare3 } = "0.9.2" ;
$ apis { virDomainMigratePrepareTunnel3 } = "0.9.2" ;
$ apis { virDomainMigratePerform3 } = "0.9.2" ;
$ apis { virDomainMigrateFinish3 } = "0.9.2" ;
Introduce yet another migration version in API.
Migration just seems to go from bad to worse. We already had to
introduce a second migration protocol when adding the QEMU driver,
since the one from Xen was insufficiently flexible to cope with
passing the data the QEMU driver required.
It turns out that this protocol still has some flaws that we
need to address. The current sequence is
* Src: DumpXML
- Generate XML to pass to dst
* Dst: Prepare
- Get ready to accept incoming VM
- Generate optional cookie to pass to src
* Src: Perform
- Start migration and wait for send completion
- Kill off VM if successful, resume if failed
* Dst: Finish
- Wait for recv completion and check status
- Kill off VM if unsuccessful
The problems with this are:
- Since the first step is a generic 'DumpXML' call, we can't
add in other migration specific data. eg, we can't include
any VM lease data from lock manager plugins
- Since the first step is a generic 'DumpXML' call, we can't
emit any 'migration begin' event on the source, or have
any hook that runs right at the start of the process
- Since there is no final step on the source, if the Finish
method fails to receive all migration data & has to kill
the VM, then there's no way to resume the original VM
on the source
This patch attempts to introduce a version 3 that uses the
improved 5 step sequence
* Src: Begin
- Generate XML to pass to dst
- Generate optional cookie to pass to dst
* Dst: Prepare
- Get ready to accept incoming VM
- Generate optional cookie to pass to src
* Src: Perform
- Start migration and wait for send completion
- Generate optional cookie to pass to dst
* Dst: Finish
- Wait for recv completion and check status
- Kill off VM if failed, resume if success
- Generate optional cookie to pass to src
* Src: Confirm
- Kill off VM if success, resume if failed
The API is designed to allow both input and output cookies
in all methods where applicable. This lets us pass around
arbitrary extra driver specific data between src & dst during
migration. Combined with the extra 'Begin' method this lets
us pass lease information from source to dst at the start of
migration
Moving the killing of the source VM out of Perform and
into Confirm, means we can now recover if the dst host
can't successfully Finish receiving migration data.
2010-11-02 15:43:44 +03:00
$ apis { virDomainMigrateConfirm3 } = "0.9.2" ;
2011-05-13 14:00:56 +04:00
# Now we want to get the mapping between public APIs
# and driver struct fields. This lets us later match
# update the driver impls with the public APis.
open FILE , "<$drivertable"
or die "cannot read $drivertable: $!" ;
# Group name -> hash of APIs { fields -> api name }
my % groups ;
my $ ingrp ;
while ( defined ( $ line = <FILE> ) ) {
2011-06-05 01:24:54 +04:00
if ( $ line =~ /struct _(vir\w*(?:Driver|Monitor))/ ) {
2012-07-27 16:51:28 +04:00
my $ grp = $ 1 ;
if ( $ grp ne "virStateDriver" &&
$ grp ne "virStreamDriver" ) {
$ ingrp = $ grp ;
$ groups { $ ingrp } = { apis = > { } , drivers = > { } } ;
}
2011-05-13 14:00:56 +04:00
} elsif ( $ ingrp ) {
2012-07-27 16:51:28 +04:00
if ( $ line =~ /^\s*vir(?:Drv|DevMon)(\w+)\s+(\w+);\s*$/ ) {
my $ field = $ 2 ;
my $ name = $ 1 ;
my $ api ;
if ( exists $ apis { "vir$name" } ) {
$ api = "vir$name" ;
} elsif ( exists $ apis { "virConnect$name" } ) {
$ api = "virConnect$name" ;
} elsif ( exists $ apis { "virNode$name" } ) {
$ api = "virNode$name" ;
} else {
die "driver $name does not have a public API" ;
}
$ groups { $ ingrp } - > { apis } - > { $ field } = $ api ;
} elsif ( $ line =~ /};/ ) {
$ ingrp = undef ;
}
2011-05-13 14:00:56 +04:00
}
}
close FILE ;
# Finally, we read all the primary driver files and extract
# the driver API tables from each one.
foreach my $ src ( @ srcs ) {
open FILE , "<$src" or
2012-07-27 16:51:28 +04:00
die "cannot read $src: $!" ;
2011-05-13 14:00:56 +04:00
$ ingrp = undef ;
my $ impl ;
while ( defined ( $ line = <FILE> ) ) {
2012-07-27 16:51:28 +04:00
if ( ! $ ingrp ) {
foreach my $ grp ( keys % groups ) {
if ( $ line =~ /^\s*(?:static\s+)?$grp\s+(\w+)\s*=\s*{/ ||
$ line =~ /^\s*(?:static\s+)?$grp\s+NAME\(\w+\)\s*=\s*{/ ) {
$ ingrp = $ grp ;
$ impl = $ src ;
if ( $ impl =~ m , . * / node_device_ ( \ w + ) \ . c , ) {
$ impl = $ 1 ;
} else {
$ impl =~ s , . * / ( \ w + ? ) _ ( ( \ w + ) _ ) ? ( \ w + ) \ . c , $ 1 , ;
}
if ( $ groups { $ ingrp } - > { drivers } - > { $ impl } ) {
die "Group $ingrp already contains $impl" ;
}
$ groups { $ ingrp } - > { drivers } - > { $ impl } = { } ;
}
}
} else {
if ( $ line =~ m !\s*\.(\w+)\s*=\s*(\w+)\s*,?\s*(?:/\*\s*(\d+\.\d+\.\d+)\s*\*/\s*)?$! ) {
my $ api = $ 1 ;
my $ meth = $ 2 ;
my $ vers = $ 3 ;
next if $ api eq "no" || $ api eq "name" ;
die "Method $meth in $src is missing version" unless defined $ vers ;
die "Driver method for $api is NULL in $src" if $ meth eq "NULL" ;
if ( ! exists ( $ groups { $ ingrp } - > { apis } - > { $ api } ) ) {
die "Found unexpected method $api in $ingrp\n" ;
}
$ groups { $ ingrp } - > { drivers } - > { $ impl } - > { $ api } = $ vers ;
if ( $ api eq "domainMigratePrepare" ||
$ api eq "domainMigratePrepare2" ||
$ api eq "domainMigratePrepare3" ) {
$ groups { $ ingrp } - > { drivers } - > { $ impl } - > { "domainMigrate" } = $ vers
unless $ groups { $ ingrp } - > { drivers } - > { $ impl } - > { "domainMigrate" } ;
}
} elsif ( $ line =~ /}/ ) {
$ ingrp = undef ;
}
}
2011-05-13 14:00:56 +04:00
}
close FILE ;
}
# The '.open' driver method is used for 3 public APIs, so we
# have a bit of manual fixup todo with the per-driver versioning
# and support matrix
$ groups { virDriver } - > { apis } - > { "openAuth" } = "virConnectOpenAuth" ;
$ groups { virDriver } - > { apis } - > { "openReadOnly" } = "virConnectOpenReadOnly" ;
$ groups { virDriver } - > { apis } - > { "domainMigrate" } = "virDomainMigrate" ;
my $ openAuthVers = ( 0 * 1000 * 1000 ) + ( 4 * 1000 ) + 0 ;
foreach my $ drv ( keys % { $ groups { "virDriver" } - > { drivers } } ) {
my $ openVersStr = $ groups { "virDriver" } - > { drivers } - > { $ drv } - > { "open" } ;
my $ openVers ;
if ( $ openVersStr =~ /(\d+)\.(\d+)\.(\d+)/ ) {
2012-07-27 16:51:28 +04:00
$ openVers = ( $ 1 * 1000 * 1000 ) + ( $ 2 * 1000 ) + $ 3 ;
2011-05-13 14:00:56 +04:00
}
# virConnectOpenReadOnly always matches virConnectOpen version
$ groups { "virDriver" } - > { drivers } - > { $ drv } - > { "openReadOnly" } =
2012-07-27 16:51:28 +04:00
$ groups { "virDriver" } - > { drivers } - > { $ drv } - > { "open" } ;
2011-05-13 14:00:56 +04:00
# virConnectOpenAuth is always 0.4.0 if the driver existed
# before this time, otherwise it matches the version of
# the driver's virConnectOpen entry
if ( $ openVersStr eq "Y" ||
2012-07-27 16:51:28 +04:00
$ openVers >= $ openAuthVers ) {
$ groups { "virDriver" } - > { drivers } - > { $ drv } - > { "openAuth" } = $ openVersStr ;
2011-05-13 14:00:56 +04:00
} else {
2012-07-27 16:51:28 +04:00
$ groups { "virDriver" } - > { drivers } - > { $ drv } - > { "openAuth" } = "0.4.0" ;
2011-05-13 14:00:56 +04:00
}
}
# Another special case for the virDomainCreateLinux which was replaced
# with virDomainCreateXML
$ groups { virDriver } - > { apis } - > { "domainCreateLinux" } = "virDomainCreateLinux" ;
my $ createAPIVers = ( 0 * 1000 * 1000 ) + ( 0 * 1000 ) + 3 ;
foreach my $ drv ( keys % { $ groups { "virDriver" } - > { drivers } } ) {
my $ createVersStr = $ groups { "virDriver" } - > { drivers } - > { $ drv } - > { "domainCreateXML" } ;
next unless defined $ createVersStr ;
my $ createVers ;
if ( $ createVersStr =~ /(\d+)\.(\d+)\.(\d+)/ ) {
2012-07-27 16:51:28 +04:00
$ createVers = ( $ 1 * 1000 * 1000 ) + ( $ 2 * 1000 ) + $ 3 ;
2011-05-13 14:00:56 +04:00
}
# virCreateLinux is always 0.0.3 if the driver existed
# before this time, otherwise it matches the version of
# the driver's virCreateXML entry
if ( $ createVersStr eq "Y" ||
2012-07-27 16:51:28 +04:00
$ createVers >= $ createAPIVers ) {
$ groups { "virDriver" } - > { drivers } - > { $ drv } - > { "domainCreateLinux" } = $ createVersStr ;
2011-05-13 14:00:56 +04:00
} else {
2012-07-27 16:51:28 +04:00
$ groups { "virDriver" } - > { drivers } - > { $ drv } - > { "domainCreateLinux" } = "0.0.3" ;
2011-05-13 14:00:56 +04:00
}
}
# Finally we generate the HTML file with the tables
print << EOF ;
<html>
<head>
<title> libvirt API support matrix </title>
</head>
<body>
<h1> libvirt API support matrix </h1>
< ul id = "toc" > </ul>
<p>
This page documents which < a href = "html/" > libvirt calls </a> work on
which libvirt drivers / hypervisors , and which version the API appeared
in .
</p>
EOF
foreach my $ grp ( sort { $ a cmp $ b } keys % groups ) {
print "<h2><a name=\"$grp\">" , $ groupheaders { $ grp } , "</a></h2>\n" ;
print << EOF ;
< table class = "top_table" >
<thead>
<tr>
<th> API </th>
<th> Version </th>
EOF
foreach my $ drv ( sort { $ a cmp $ b } keys % { $ groups { $ grp } - > { drivers } } ) {
2012-07-27 16:51:28 +04:00
print " <th>$drv</th>\n" ;
2011-05-13 14:00:56 +04:00
}
print << EOF ;
</tr>
</thead>
<tbody>
EOF
my $ row = 0 ;
foreach my $ field ( sort {
2012-07-27 16:51:28 +04:00
$ groups { $ grp } - > { apis } - > { $ a }
cmp
$ groups { $ grp } - > { apis } - > { $ b }
} keys % { $ groups { $ grp } - > { apis } } ) {
my $ api = $ groups { $ grp } - > { apis } - > { $ field } ;
my $ vers = $ apis { $ api } ;
print << EOF ;
2011-05-13 14:00:56 +04:00
<tr>
<td> < a href = \ " html /libvirt-libvirt.html#$api\">$api</ a > </td>
<td> $ vers </td>
EOF
foreach my $ drv ( sort { $ a cmp $ b } keys % { $ groups { $ grp } - > { drivers } } ) {
2012-07-27 16:51:28 +04:00
if ( exists $ groups { $ grp } - > { drivers } - > { $ drv } - > { $ field } ) {
print "<td>" , $ groups { $ grp } - > { drivers } - > { $ drv } - > { $ field } , "</td>\n" ;
} else {
print "<td></td>\n" ;
}
2011-05-13 14:00:56 +04:00
}
2012-07-27 16:51:28 +04:00
print << EOF ;
2011-05-13 14:00:56 +04:00
</tr>
EOF
$ row + + ;
if ( ( $ row % 15 ) == 0 ) {
print << EOF ;
<tr>
<th> API </th>
<th> Version </th>
EOF
foreach my $ drv ( sort { $ a cmp $ b } keys % { $ groups { $ grp } - > { drivers } } ) {
2012-07-27 16:51:28 +04:00
print " <th>$drv</th>\n" ;
2011-05-13 14:00:56 +04:00
}
print << EOF ;
</tr>
EOF
2012-07-27 16:51:28 +04:00
}
2011-05-13 14:00:56 +04:00
}
print << EOF ;
</tbody>
</table>
EOF
}
print << EOF ;
</body>
</html>
EOF