[libvirt] Re: kvm-81: save / restore does not work - missing incoming stdio feature

Hi Daniel, I'm also VERY interesting in save/restore my Domains to disk - is there a roadmap back to this feature - did you talk with the qemu developers ? Or should I try to contact them ? regards Danny
From: Matthias Pfafferodt <matthias.pfafferodt <at> mapfa.de> Subject: Re: [libvirt] kvm: save / restore
Hello Daniel,
thnaks for your anser. Do you know the plans regarding save/restore in kvm and libvirt? I would like to save my vm's at shutdown and restore at boot time. At the moment this is not working and shutdown is used.
Am Thursday 18 December 2008 13:42:36 schrieb Daniel P. Berrange:
On Wed, Dec 17, 2008 at 01:46:43PM +0100, Matthias Pfafferodt wrote:
Hallo,
I use kvm-81 and libvirt 0.5.1. I can save a kvm donain but if I want to restore it I get the following error in the log file:
unknown migration protocol: stdio
I tried it using only kvm and got the same error.
How can I save / restore a VM to / from a file?
The new KVM release dropped support for the '-incoming stdio' syntax that libvirt was using so save/restore no longer works :-(
Daniel

Hi Daniel, Charles Duffy (thanks Charles !) told me how to fix libvirt for using the new "-incoming" API - yust replace "stdio" with "exec:cat" .. Works like a charm, tested with FC9, libvirt.0.5.1-2 and KVM-83 Here's the patch for libvirt: diff -uNr libvirt-0.5.1/src/qemu_driver.c libvirt-0.5.1-migrate-exec-patch/src/qemu_driver.c --- libvirt-0.5.1/src/qemu_driver.c 2009-01-18 11:07:57.000000000 +0100 +++ libvirt-0.5.1-migrate-exec-patch/src/qemu_driver.c 2009-01-18 10:40:42.000000000 +0100 @@ -2278,7 +2278,7 @@ /* Set the migration source and start it up. */ vm->stdin_fd = fd; - ret = qemudStartVMDaemon(conn, driver, vm, "stdio"); + ret = qemudStartVMDaemon(conn, driver, vm, "exec:cat"); close(fd); vm->stdin_fd = -1; if (ret < 0) { regards Danny
From: Matthias Pfafferodt <matthias.pfafferodt <at> mapfa.de> Subject: Re: [libvirt] kvm: save / restore
Hello Daniel,
thnaks for your anser. Do you know the plans regarding save/restore in kvm and libvirt? I would like to save my vm's at shutdown and restore at boot time. At the moment this is not working and shutdown is used.
Am Thursday 18 December 2008 13:42:36 schrieb Daniel P. Berrange:
On Wed, Dec 17, 2008 at 01:46:43PM +0100, Matthias Pfafferodt wrote:
Hallo,
I use kvm-81 and libvirt 0.5.1. I can save a kvm donain but if I want to restore it I get the following error in the log file:
unknown migration protocol: stdio
I tried it using only kvm and got the same error.
How can I save / restore a VM to / from a file?
The new KVM release dropped support for the '-incoming stdio' syntax that libvirt was using so save/restore no longer works :-(
Daniel

On Mon, Jan 19, 2009 at 01:53:48PM +0100, Daniel Schwager wrote:
Hi Daniel,
Charles Duffy (thanks Charles !) told me how to fix libvirt for using the new "-incoming" API - yust replace "stdio" with "exec:cat" ..
Works like a charm, tested with FC9, libvirt.0.5.1-2 and KVM-83
Unfortunately things are not that simple. This will work for new KVM, but break for older KVM which only supports 'stdio:', 'file://' and 'tcp://' for incoming migration. There needs to be some kind of version / support check here to decide which syntax to use. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Daniel P. Berrange wrote:
On Mon, Jan 19, 2009 at 01:53:48PM +0100, Daniel Schwager wrote:
Hi Daniel,
Charles Duffy (thanks Charles !) told me how to fix libvirt for using the new "-incoming" API - yust replace "stdio" with "exec:cat" ..
Works like a charm, tested with FC9, libvirt.0.5.1-2 and KVM-83
Unfortunately things are not that simple. This will work for new KVM, but break for older KVM which only supports 'stdio:', 'file://' and 'tcp://' for incoming migration. There needs to be some kind of version / support check here to decide which syntax to use.
Daniel
So are there guidelines posted somewhere for the backwards compatibility that must be maintained ? If you try to support every older version of each hypervisor / product you will quickly have a test matrix that will make it darn near impossible to actually release a new version of libvirt w/o a tremendously long test cycle. Doesn't it make more sense to just put a stake in the ground and say version X.Y of libvirt will only work with version A.B of (insert your favorite hypervisor here) Perhaps you keep multiple branches of libvirt "mostly" aligned with a generation of hypervisor and only back port the critical changes from the head. -mark

On Mon, Jan 19, 2009 at 07:20:07PM -0500, Mark Wagner wrote:
Daniel P. Berrange wrote:
On Mon, Jan 19, 2009 at 01:53:48PM +0100, Daniel Schwager wrote:
Hi Daniel,
Charles Duffy (thanks Charles !) told me how to fix libvirt for using the new "-incoming" API - yust replace "stdio" with "exec:cat" ..
Works like a charm, tested with FC9, libvirt.0.5.1-2 and KVM-83
Unfortunately things are not that simple. This will work for new KVM, but break for older KVM which only supports 'stdio:', 'file://' and 'tcp://' for incoming migration. There needs to be some kind of version / support check here to decide which syntax to use.
Daniel
So are there guidelines posted somewhere for the backwards compatibility that must be maintained ?
The code is written to work with all QEMU >= 0.8.2, which includes basically all KVM (bugs aside). In this case we've long supported the existing KVM migrate syntax and cannot break our existing users of it.
If you try to support every older version of each hypervisor / product you will quickly have a test matrix that will make it darn near impossible to actually release a new version of libvirt w/o a tremendously long test cycle.
This is why we have an extensive test suite to cover generation of command line arguments & explicitly test these kind of scenarios where one version changes the command line syntax. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Mon, Jan 19, 2009 at 01:01:15PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 19, 2009 at 01:53:48PM +0100, Daniel Schwager wrote:
Hi Daniel,
Charles Duffy (thanks Charles !) told me how to fix libvirt for using the new "-incoming" API - yust replace "stdio" with "exec:cat" ..
Works like a charm, tested with FC9, libvirt.0.5.1-2 and KVM-83
Unfortunately things are not that simple. This will work for new KVM, but break for older KVM which only supports 'stdio:', 'file://' and 'tcp://' for incoming migration. There needs to be some kind of version / support check here to decide which syntax to use.
The version check is quite tricky due to soo many combinations. So I've created some test scripts to check we're doing it right. This patch should make restore work again, by detecting if 'exec' is supported and then doing an automatic conversion from 'stdio' to 'exec:cat' in this case. It also prints clear error messages if it finds a QEMU which doesn't have the neccessary support - likewise detecting old KVM where the TCP migration wouldn't work correctly, just hanging on startup. b/tests/qemuxml2argvdata/qemuxml2argv-migrate.args | 1 b/tests/qemuxml2argvdata/qemuxml2argv-migrate.xml | 22 +++++++++ b/tests/qemuxml2argvdata/qemuxml2argv-restore-v1.args | 1 b/tests/qemuxml2argvdata/qemuxml2argv-restore-v1.xml | 22 +++++++++ b/tests/qemuxml2argvdata/qemuxml2argv-restore-v2.args | 1 b/tests/qemuxml2argvdata/qemuxml2argv-restore-v2.xml | 22 +++++++++ src/qemu_conf.c | 44 ++++++++++++++++++ src/qemu_conf.h | 20 ++++---- tests/qemuxml2argvtest.c | 23 +++++++-- 9 files changed, 144 insertions(+), 12 deletions(-) Daniel diff -r 1643740ed71c src/qemu_conf.c --- a/src/qemu_conf.c Wed Jan 21 16:28:43 2009 +0000 +++ b/src/qemu_conf.c Thu Jan 22 12:06:47 2009 +0000 @@ -355,6 +355,7 @@ int qemudExtractVersionInfo(const char * unsigned int major, minor, micro; unsigned int version; unsigned int flags = 0; + unsigned int kvmver = 0; if (retflags) *retflags = 0; @@ -378,6 +379,22 @@ int qemudExtractVersionInfo(const char * version = (major * 1000 * 1000) + (minor * 1000) + micro; + /* Optional KVM version */ + if (sscanf(help, "QEMU PC emulator version %*u.%*u.%*u (kvm-%u)", + &kvmver) == 1) { + if (kvmver >= 79) { + /* + * In kvm-79, KVM pulled in new migration code from + * QEMU which really messes things up for us + */ + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; + if (kvmver >= 80) + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; + } else { + flags |= QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO; + } + } + if (strstr(help, "-no-kqemu")) flags |= QEMUD_CMD_FLAG_KQEMU; if (strstr(help, "-no-reboot")) @@ -674,6 +691,33 @@ int qemudBuildCommandLine(virConnectPtr virUUIDFormat(vm->def->uuid, uuid); + /* Migration is very annoying due to wildly varying syntax & capabilities + * over time of KVM / QEMU codebases + */ + if (migrateFrom) { + if (STRPREFIX(migrateFrom, "tcp")) { + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, + "%s", _("TCP migration is not supported with this QEMU binary")); + return -1; + } + } else if (STREQ(migrateFrom, "stdio")) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { + migrateFrom = "exec:cat"; + } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, + "%s", _("STDIO migration is not supported with this QEMU binary")); + return -1; + } + } else if (STRPREFIX(migrateFrom, "exec")) { + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, + "%s", _("STDIO migration is not supported with this QEMU binary")); + return -1; + } + } + } + /* Need to explicitly disable KQEMU if * 1. Arch matches host arch * 2. Guest is 'qemu' diff -r 1643740ed71c src/qemu_conf.h --- a/src/qemu_conf.h Wed Jan 21 16:28:43 2009 +0000 +++ b/src/qemu_conf.h Thu Jan 22 12:06:47 2009 +0000 @@ -40,14 +40,18 @@ /* Internal flags to keep track of qemu command line capabilities */ enum qemud_cmd_flags { - QEMUD_CMD_FLAG_KQEMU = (1 << 0), - QEMUD_CMD_FLAG_VNC_COLON = (1 << 1), - QEMUD_CMD_FLAG_NO_REBOOT = (1 << 2), - QEMUD_CMD_FLAG_DRIVE = (1 << 3), - QEMUD_CMD_FLAG_DRIVE_BOOT = (1 << 4), - QEMUD_CMD_FLAG_NAME = (1 << 5), - QEMUD_CMD_FLAG_UUID = (1 << 6), - QEMUD_CMD_FLAG_DOMID = (1 << 7), /* Xenner only */ + QEMUD_CMD_FLAG_KQEMU = (1 << 0), /* Whether KQEMU is compiled in */ + QEMUD_CMD_FLAG_VNC_COLON = (1 << 1), /* Does the VNC take just port, or address + display */ + QEMUD_CMD_FLAG_NO_REBOOT = (1 << 2), /* Is the -no-reboot flag available */ + QEMUD_CMD_FLAG_DRIVE = (1 << 3), /* Is the new -drive arg available */ + QEMUD_CMD_FLAG_DRIVE_BOOT = (1 << 4), /* Does -drive support boot=on */ + QEMUD_CMD_FLAG_NAME = (1 << 5), /* Is the -name flag available */ + QEMUD_CMD_FLAG_UUID = (1 << 6), /* Is the -uuid flag available */ + QEMUD_CMD_FLAG_DOMID = (1 << 7), /* Xenner only, special -domid flag available */ + QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO = (1 << 8), /* Original migration code from KVM. Also had tcp, but we can't use that + * since it had a design bug blocking the entire monitor console */ + QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP = (1 << 9), /* New migration syntax after merge to QEMU with TCP transport */ + QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC = (1 << 10), /* New migration syntax after merge to QEMU with EXEC transport */ }; /* Main driver state */ diff -r 1643740ed71c tests/qemuxml2argvdata/qemuxml2argv-migrate.args --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/qemuxml2argvdata/qemuxml2argv-migrate.args Thu Jan 22 12:06:47 2009 +0000 @@ -0,0 +1,1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu-kvm -S -M pc -m 214 -smp 1 -nographic -monitor pty -pidfile /nowhere/QEMUGuest1.pid -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb -incoming tcp:10.0.0.1:5000 diff -r 1643740ed71c tests/qemuxml2argvdata/qemuxml2argv-migrate.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/qemuxml2argvdata/qemuxml2argv-migrate.xml Thu Jan 22 12:06:47 2009 +0000 @@ -0,0 +1,22 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + </disk> + </devices> +</domain> diff -r 1643740ed71c tests/qemuxml2argvdata/qemuxml2argv-restore-v1.args --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/qemuxml2argvdata/qemuxml2argv-restore-v1.args Thu Jan 22 12:06:47 2009 +0000 @@ -0,0 +1,1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu-kvm -S -M pc -m 214 -smp 1 -nographic -monitor pty -pidfile /nowhere/QEMUGuest1.pid -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb -incoming stdio diff -r 1643740ed71c tests/qemuxml2argvdata/qemuxml2argv-restore-v1.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/qemuxml2argvdata/qemuxml2argv-restore-v1.xml Thu Jan 22 12:06:47 2009 +0000 @@ -0,0 +1,22 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + </disk> + </devices> +</domain> diff -r 1643740ed71c tests/qemuxml2argvdata/qemuxml2argv-restore-v2.args --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/qemuxml2argvdata/qemuxml2argv-restore-v2.args Thu Jan 22 12:06:47 2009 +0000 @@ -0,0 +1,1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu-kvm -S -M pc -m 214 -smp 1 -nographic -monitor pty -pidfile /nowhere/QEMUGuest1.pid -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb -incoming exec:cat diff -r 1643740ed71c tests/qemuxml2argvdata/qemuxml2argv-restore-v2.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/qemuxml2argvdata/qemuxml2argv-restore-v2.xml Thu Jan 22 12:06:47 2009 +0000 @@ -0,0 +1,22 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + </disk> + </devices> +</domain> diff -r 1643740ed71c tests/qemuxml2argvtest.c --- a/tests/qemuxml2argvtest.c Wed Jan 21 16:28:43 2009 +0000 +++ b/tests/qemuxml2argvtest.c Thu Jan 22 12:06:47 2009 +0000 @@ -24,7 +24,8 @@ static struct qemud_driver driver; static int testCompareXMLToArgvFiles(const char *xml, const char *cmd, - int extraFlags) { + int extraFlags, + const char *migrateFrom) { char argvData[MAX_FILE]; char *expectargv = &(argvData[0]); char *actualargv = NULL; @@ -56,7 +57,7 @@ static int testCompareXMLToArgvFiles(con if (qemudBuildCommandLine(NULL, &driver, &vm, flags, &argv, &qenv, - NULL, NULL, NULL) < 0) + NULL, NULL, migrateFrom) < 0) goto fail; len = 1; /* for trailing newline */ @@ -122,6 +123,7 @@ static int testCompareXMLToArgvFiles(con struct testInfo { const char *name; int extraFlags; + const char *migrateFrom; }; static int testCompareXMLToArgvHelper(const void *data) { @@ -132,7 +134,7 @@ static int testCompareXMLToArgvHelper(co abs_srcdir, info->name); snprintf(args, PATH_MAX, "%s/qemuxml2argvdata/qemuxml2argv-%s.args", abs_srcdir, info->name); - return testCompareXMLToArgvFiles(xml, args, info->extraFlags); + return testCompareXMLToArgvFiles(xml, args, info->extraFlags, info->migrateFrom); } @@ -161,7 +163,15 @@ mymain(int argc, char **argv) #define DO_TEST(name, extraFlags) \ do { \ - struct testInfo info = { name, extraFlags }; \ + struct testInfo info = { name, extraFlags, NULL }; \ + if (virtTestRun("QEMU XML-2-ARGV " name, \ + 1, testCompareXMLToArgvHelper, &info) < 0) \ + ret = -1; \ + } while (0) + +#define DO_TEST_MIGRATE(name, extraFlags, migrateFrom) \ + do { \ + struct testInfo info = { name, extraFlags, migrateFrom }; \ if (virtTestRun("QEMU XML-2-ARGV " name, \ 1, testCompareXMLToArgvHelper, &info) < 0) \ ret = -1; \ @@ -228,6 +238,11 @@ mymain(int argc, char **argv) DO_TEST("hostdev-pci-address", 0); + DO_TEST_MIGRATE("restore-v1", QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO, "stdio"); + DO_TEST_MIGRATE("restore-v2", QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC, "stdio"); + DO_TEST_MIGRATE("restore-v2", QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC, "exec:cat"); + DO_TEST_MIGRATE("migrate", QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP, "tcp:10.0.0.1:5000"); + virCapabilitiesFree(driver.caps); return(ret==0 ? EXIT_SUCCESS : EXIT_FAILURE); -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Jan 22, 2009 at 12:10:51PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 19, 2009 at 01:01:15PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 19, 2009 at 01:53:48PM +0100, Daniel Schwager wrote:
Hi Daniel,
Charles Duffy (thanks Charles !) told me how to fix libvirt for using the new "-incoming" API - yust replace "stdio" with "exec:cat" ..
Works like a charm, tested with FC9, libvirt.0.5.1-2 and KVM-83
Unfortunately things are not that simple. This will work for new KVM, but break for older KVM which only supports 'stdio:', 'file://' and 'tcp://' for incoming migration. There needs to be some kind of version / support check here to decide which syntax to use.
The version check is quite tricky due to soo many combinations. So I've created some test scripts to check we're doing it right. This patch should make restore work again, by detecting if 'exec' is supported and then doing an automatic conversion from 'stdio' to 'exec:cat' in this case. It also prints clear error messages if it finds a QEMU which doesn't have the neccessary support - likewise detecting old KVM where the TCP migration wouldn't work correctly, just hanging on startup.
b/tests/qemuxml2argvdata/qemuxml2argv-migrate.args | 1 b/tests/qemuxml2argvdata/qemuxml2argv-migrate.xml | 22 +++++++++ b/tests/qemuxml2argvdata/qemuxml2argv-restore-v1.args | 1 b/tests/qemuxml2argvdata/qemuxml2argv-restore-v1.xml | 22 +++++++++ b/tests/qemuxml2argvdata/qemuxml2argv-restore-v2.args | 1 b/tests/qemuxml2argvdata/qemuxml2argv-restore-v2.xml | 22 +++++++++ src/qemu_conf.c | 44 ++++++++++++++++++ src/qemu_conf.h | 20 ++++---- tests/qemuxml2argvtest.c | 23 +++++++-- 9 files changed, 144 insertions(+), 12 deletions(-)
Looks OK at a brief scan, and can't be worse than failing like we do now, so +1. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones Read my OCaml programming blog: http://camltastic.blogspot.com/ Fedora now supports 68 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora
participants (4)
-
Daniel P. Berrange
-
Daniel Schwager
-
Mark Wagner
-
Richard W.M. Jones