trilok nuwal | 2 May 08:35
Picon

virt-install problem.

Hi All,

I am installing the RHEL PV through virt-install,

#virt-install  --name=dom1 --ram=500 --file=/home/disk --file-size=10 --mac=00:50:56:2d:31:6d --bridge=xenbr2 --nographics --noautoconsole --paravirt --location=nfs:server3.domain.com:/vol/vol1/tnuwal/rl5 --debug -x "ks=http://khost/ks.cfg ksdevice=eth2"


Here  eth2 is my public IP and xenbr2 is bridge over this.

When i start the install then i got following message on console of this.


Starting install...
libvir: Xen Daemon error : GET operation failed:
Domain installation still in progress.  You can reconnect
to the console to complete the installation process.

Now when i tried to connect the console ...Then it started the instllation But this stuck that it could not download the kickstart file http://khost/ks.cfg.
ks=http://khost.domain.com/ks.cfg.

Why is it happening as i can download this file on dom0 using wget. Because khost and my machine are well pingable.

       +-------------+ Error downloading kickstart file +-------------+
        |                                                              |
        | Unable to download the kickstart file.  Please modify the    |
        | kickstart parameter below or press Cancel to proceed as an   |
        | interactive installation.                                                               |
        |                                                                                                      |
        | http:///khost.domain.com/ks.cfg__________________            __ |
        |                                                                                                      |
        |            +----+                      +--------+                                          |
        |            | OK |                      | Cancel |                                            |
        |            +----+                      +--------+                                           |
        |                                                                                                       |
        |                                                                                                     |
        +--------------------------------------------------------------+


# Contents of the file are
lang en_US.UTF-8
langsupport --default=en_US.UTF-8 en_US.UTF-8
keyboard us
skipx
network --device eth2 --bootproto static --ip 139.185.48.217 --netmask 255.255.252.0 --gateway 139.185.48.1 --nameserver 130.35.249.41 --hostname os217.domain.com
rootpw root
firewall --disabled
selinux --disabled
authconfig --enableshadow --enablemd5
timezone America/Los_Angeles
text
reboot


.....Could u please tell me where i am doing wrong.

Thanks,
Trilok Nuwal




_______________________________________________
et-mgmt-tools mailing list
et-mgmt-tools@...
https://www.redhat.com/mailman/listinfo/et-mgmt-tools
Mark McLoughlin | 2 May 14:15
Picon
Favicon

[patch 0/8] [virtinst] Add installer concept and livecd installer example

Hey,
	This set of patches explores the notion of allowing virtinst
to have different installer types apart from the existing support
for distribution installers.

	This notion can be used to e.g. support livecd or pre-built
system images.

Cheers,
Mark.
Mark McLoughlin | 2 May 14:15
Picon
Favicon

[patch 1/8] Add Installer and re-factor existing code into DistroInstaller

This patch re-factors things so as to introduce the concept of
an "installer type". The idea is that we can create a VM using
not only a typical distribution installer, but also e.g. a livecd
installer or just a pre-built system image.

To that end an Installer class is added which is orthogonal
to the existing Guest class - i.e. the choice of installer is
independant of the choice of fully virt vs. paravirt.

In more detail the patch does the following:

  + Adds the Installer base class - sub-classes are expected to
    implement the prepare() and get_os_blob() methods

  + Moves the Guest type, scratchdir, boot, extraargs, location,
    and cdrom properties to the installer, but chains the original
    properties to the installer in order to maintain compatibility

  + Rather than having Guest sub-classes implement get_runtime_xml()
    and get_install_xml(), they now implement get_osblob() and chain
    up to Installer.get_osblob() passing the installer parameters
    like hvm or arch

  + Likewise, Guest sub-class implement a prepare_install() method
    which chains up to Installer.prepare() passing it parameters
    like need_bootdev and guest

  + All the existing prepare() and get_osblob() logic from FullyVirtGuest
    and ParaVirtGuest is consolidated in a single DistroInstaller class

  + In FullyVirtGuest we append the features XML to the osblob
    returned by the installer

  + If ParaVirtGuest or FullyVirtGuest is not passed an installer
    instance, they create a DistroInstaller instance as a fallback

Signed-off-by: Mark McLoughlin <markmc@...>

Index: virtinst--devel/virtinst/DistroManager.py
===================================================================
--- virtinst--devel.orig/virtinst/DistroManager.py
+++ virtinst--devel/virtinst/DistroManager.py
@@ -22,6 +22,7 @@ import subprocess
 import urlgrabber.grabber as grabber
 import urlgrabber.progress as progress
 import tempfile
+import Guest

 
 # This is a generic base class for fetching/extracting files from
@@ -585,3 +586,115 @@ def acquireBootDisk(baseuri, progresscb,
     finally:
         fetcher.cleanupLocation()

+class DistroInstaller(Guest.Installer):
+    def __init__(self, type = "xen", location = None, boot = None, extraargs = None):
+        Guest.Installer.__init__(self, type, location, boot, extraargs)
+
+    def get_location(self):
+        return self._location
+    def set_location(self, val):
+        if not (val.startswith("http://") or val.startswith("ftp://") or
+                val.startswith("nfs:") or val.startswith("/")):
+            raise ValueError("Install location must be an NFS, HTTP or FTP " +
+                             "network install source, or local file/device")
+        if os.geteuid() != 0 and val.startswith("nfs:"):
+            raise ValueError("NFS installations are only supported as root")
+        self._location = val
+    location = property(get_location, set_location)
+
+    def _prepare_cdrom(self, guest, distro, meter):
+        if self.location.startswith("/"):
+            # Huzzah, a local file/device
+            cdrom = self.location
+        else:
+            # Xen needs a boot.iso if its a http://, ftp://, or nfs:/ url
+            cdrom = DistroManager.acquireBootDisk(self.location,
+                                                  meter,
+                                                  scratchdir = self.scratchdir,
+                                                  distro = distro)
+            self._tmpfiles.append(cdrom)
+
+        guest.disks.append(Guest.VirtualDisk(cdrom,
+                                             device=Guest.VirtualDisk.DEVICE_CDROM,
+                                             readOnly=True,
+                                             transient=True))
+
+    def _prepare_kernel_and_initrd(self, guest, distro, meter):
+        if self.boot is not None:
+            # Got a local kernel/initrd already
+            self.install["kernel"] = self.boot["kernel"]
+            self.install["initrd"] = self.boot["initrd"]
+            if not self.extraargs is None:
+                self.install["extraargs"] = self.extraargs
+        else:
+            # Need to fetch the kernel & initrd from a remote site, or
+            # out of a loopback mounted disk image/device
+            (kernelfn, initrdfn, args) = acquireKernel(self.location,
+                                                       meter,
+                                                       scratchdir = self.scratchdir,
+                                                       type = self.type,
+                                                       distro = distro)
+            self.install["kernel"] = kernelfn
+            self.install["initrd"] = initrdfn
+            if not self.extraargs is None:
+                self.install["extraargs"] = self.extraargs + " " + args
+            else:
+                self.install["extraargs"] = args
+
+            self._tmpfiles.append(kernelfn)
+            self._tmpfiles.append(initrdfn)
+
+        # If they're installing off a local file/device, we map it
+        # through to a virtual harddisk
+        if self.location is not None and self.location.startswith("/"):
+            guest.disks.append(Guest.VirtualDisk(self.location,
+                                                 readOnly=True,
+                                                 transient=True))
+
+    def prepare(self, guest, need_bootdev, meter, distro = None):
+        self.cleanup()
+
+        self.install = {
+            "kernel" : "",
+            "initrd" : "",
+            "extraargs" : "",
+        }
+
+        if need_bootdev:
+            self._prepare_cdrom(guest, distro, meter)
+        else:
+            self._prepare_kernel_and_initrd(guest, distro, meter)
+
+    def _get_osblob(self, install, hvm, arch = None, loader = None):
+        osblob = ""
+        if install or hvm:
+            osblob = "<os>\n"
+
+            if hvm:
+                type = "hvm"
+            else:
+                type = "linux"
+
+            if arch:
+                osblob += "      <type arch='%s'>%s</type>\n" % (arch, type)
+            else:
+                osblob += "      <type>%s</type>\n" % type
+
+            if self.install["kernel"]:
+                osblob += "      <kernel>%s</kernel>\n"   % self.install["kernel"]
+                osblob += "      <initrd>%s</initrd>\n"   % self.install["initrd"]
+                osblob += "      <cmdline>%s</cmdline>\n" % self.install["extraargs"]
+            else:
+                if loader:
+                    osblob += "      <loader>%s</loader>\n" % loader
+
+                if install:
+                    osblob += "      <boot dev='cdrom'/>\n"
+                else:
+                    osblob += "      <boot dev='hd'/>\n"
+
+            osblob += "    </os>"
+        else:
+            osblob += "    <bootloader>/usr/bin/pygrub</bootloader>"
+
+        return osblob
Index: virtinst--devel/virtinst/FullVirtGuest.py
===================================================================
--- virtinst--devel.orig/virtinst/FullVirtGuest.py
+++ virtinst--devel/virtinst/FullVirtGuest.py
@@ -89,8 +89,10 @@ class FullVirtGuest(Guest.XenGuest):
     get_os_variant_label = staticmethod(get_os_variant_label)

 
-    def __init__(self, type=None, arch=None, connection=None, hypervisorURI=None, emulator=None):
-        Guest.Guest.__init__(self, type=type, connection=connection, hypervisorURI=hypervisorURI)
+    def __init__(self, type=None, arch=None, connection=None, hypervisorURI=None, emulator=None, installer=None):
+        if not installer:
+            installer = DistroManager.DistroInstaller(type = type)
+        Guest.Guest.__init__(self, type, connection, hypervisorURI, installer)
         self.disknode = "hd"
         self.features = { "acpi": None, "pae": util.is_pae_capable(), "apic": None }
         self.arch = arch
@@ -148,58 +150,18 @@ class FullVirtGuest(Guest.XenGuest):
     os_distro = property(get_os_distro)

     def _get_features_xml(self):
-        ret = ""
+        ret = "<features>\n"
         for (k, v) in self.features.items():
             if v:
-                ret += "<%s/>" %(k,)
-        return ret
-
-    def _get_loader_xml(self):
-        if self.loader is None:
-            return ""
-
-        return """    <loader>%(loader)s</loader>""" % { "loader": self.loader }
-
-    def _get_os_xml(self, bootdev, install=True):
-        if self.arch is None:
-            arch = ""
-        else:
-            arch = " arch='" + self.arch + "'"
-
-        if self.kernel is None or install == False:
-            return """<os>
-    <type%(arch)s>hvm</type>
-%(loader)s
-    <boot dev='%(bootdev)s'/>
-  </os>
-  <features>
-    %(features)s
-  </features>""" % \
-    { "bootdev": bootdev, \
-      "arch": arch, \
-      "loader": self._get_loader_xml(), \
-      "features": self._get_features_xml() }
-        else:
-            return """<os>
-    <type%(arch)s>hvm</type>
-    <kernel>%(kernel)s</kernel>
-    <initrd>%(initrd)s</initrd>
-    <cmdline>%(extra)s</cmdline>
-  </os>
-  <features>
-    %(features)s
-  </features>""" % \
-    { "kernel": self.kernel, \
-      "initrd": self.initrd, \
-      "extra": self.extraargs, \
-      "arch": arch, \
-      "features": self._get_features_xml() }
+                ret += "      <%s/>\n" %(k,)
+        return ret + "    </features>"

-    def _get_install_xml(self):
-        return self._get_os_xml("cdrom", True)
+    def _get_osblob(self, install):
+        osblob = self.installer._get_osblob(install, True, self.arch, self.loader)
+        if osblob is None:
+            return None

-    def _get_runtime_xml(self):
-        return self._get_os_xml("hd", False)
+        return "%s\n    %s" % (osblob, self._get_features_xml())

     def _get_device_xml(self, install = True):
         if self.emulator is None:
@@ -217,42 +179,18 @@ class FullVirtGuest(Guest.XenGuest):
         self.set_os_type_parameters(self.os_type, self.os_variant)
         Guest.Guest.validate_parms(self)

-    def _prepare_install_location(self, meter):
-        cdrom = None
-        tmpfiles = []
-        self.kernel = None
-        self.initrd = None
-        if self.location.startswith("/"):
-            # Huzzah, a local file/device
-            cdrom = self.location
-        else:
-            # Hmm, qemu ought to be able to boot off a kernel/initrd but
-            # for some reason it often fails, hence disabled here..
-            if self.type == "qemuXXX":
-                # QEMU can go straight off a kernel/initrd
-                if self.boot is not None:
-                    # Got a local kernel/initrd already
-                    self.kernel = self.boot["kernel"]
-                    self.initrd = self.boot["initrd"]
-                else:
-                    (kernelfn,initrdfn,args) = DistroManager.acquireKernel(self.location, meter,
scratchdir=self.scratchdir, distro=self.os_distro)
-                    self.kernel = kernelfn
-                    self.initrd = initrdfn
-                    if self.extraargs is not None:
-                        self.extraargs = self.extraargs + " " + args
-                    else:
-                        self.extraargs = args
-                    tmpfiles.append(kernelfn)
-                    tmpfiles.append(initrdfn)
-            else:
-                # Xen needs a boot.iso if its a http://, ftp://, or nfs:/ url
-                cdrom = DistroManager.acquireBootDisk(self.location, meter, scratchdir=self.scratchdir, distro=self.os_distro)
-                tmpfiles.append(cdrom)
-
-        if cdrom is not None:
-            self.disks.append(Guest.VirtualDisk(cdrom, device=Guest.VirtualDisk.DEVICE_CDROM,
readOnly=True, transient=True))
+    def _prepare_install(self, meter):
+        need_bootdev = True

-        return tmpfiles
+        # Hmm, qemu ought to be able to boot off a kernel/initrd but
+        # for some reason it often fails, hence disabled here..
+        if self.type == "qemuXXX":
+            need_bootdev = False
+
+        self._installer.prepare(guest = self,
+                                need_bootdev = need_bootdev,
+                                meter = meter,
+                                distro = self.os_distro)

     def get_continue_inst(self):
         if self.os_type is not None:
Index: virtinst--devel/virtinst/Guest.py
===================================================================
--- virtinst--devel.orig/virtinst/Guest.py
+++ virtinst--devel/virtinst/Guest.py
@@ -338,14 +338,80 @@ class SDLVirtualGraphics(XenGraphics):
 class XenSDLGraphics(SDLVirtualGraphics):
     pass

-class Guest(object):
-    def __init__(self, type=None, connection=None, hypervisorURI=None):
+class Installer(object):
+    def __init__(self, type = "xen", location = None, boot = None, extraargs = None):
+        self._location = None
+        self._extraargs = None
+        self._boot = None
+
         if type is None:
             type = "xen"
-        self._type = type
+        self.type = type
+
+        if not location is None:
+            self.location = location
+        if not boot is None:
+            self.boot = boot
+        if not extraargs is None:
+            self.extraargs = extraargs
+
+        self._tmpfiles = []
+
+    def cleanup(self):
+        for f in self._tmpfiles:
+            logging.debug("Removing " + f)
+            os.unlink(f)
+        self._tmpfiles = []
+
+    def get_type(self):
+        return self._type
+    def set_type(self, val):
+        self._type = val
+    type = property(get_type, set_type)
+
+    def get_scratchdir(self):
+        if self.type == "xen":
+            return "/var/lib/xen"
+        return "/var/tmp"
+    scratchdir = property(get_scratchdir)
+
+    def get_location(self):
+        return self._location
+    def set_location(self, val):
+        self._location = val
+    location = property(get_location, set_location)
+
+    # kernel + initrd pair to use for installing as opposed to using a location
+    def get_boot(self):
+        return self._boot
+    def set_boot(self, val):
+        if type(val) == tuple:
+            if len(val) != 2:
+                raise ValueError, "Must pass both a kernel and initrd"
+            (k, i) = val
+            self._boot = {"kernel": k, "initrd": i}
+        elif type(val) == dict:
+            if not val.has_key("kernel") or not val.has_key("initrd"):
+                raise ValueError, "Must pass both a kernel and initrd"
+            self._boot = val
+        elif type(val) == list:
+            if len(val) != 2:
+                raise ValueError, "Must pass both a kernel and initrd"
+            self._boot = {"kernel": val[0], "initrd": val[1]}
+    boot = property(get_boot, set_boot)
+
+    # extra arguments to pass to the guest installer
+    def get_extraargs(self):
+        return self._extraargs
+    def set_extraargs(self, val):
+        self._extraargs = val
+    extraargs = property(get_extraargs, set_extraargs)
+
+class Guest(object):
+    def __init__(self, type=None, connection=None, hypervisorURI=None, installer=None):
+        self._installer = installer
         self.disks = []
         self.nics = []
-        self._location = None
         self._name = None
         self._uuid = None
         self._memory = None
@@ -362,24 +428,19 @@ class Guest(object):
             raise RuntimeError, "Unable to connect to hypervisor, aborting installation!"

         self.disknode = None # this needs to be set in the subclass
-        self._boot = None
-        self._extraargs = ""
+
+    def get_installer(self):
+        return self._installer
+    installer = property(get_installer)
+

     def get_type(self):
-        return self._type
+        return self._installer.type
     def set_type(self, val):
-        self._type = type
+        self._installer.type = type
     type = property(get_type, set_type)

 
-    def get_scratchdir(self):
-        if self.type == "xen":
-            return "/var/lib/xen"
-        return "/var/tmp"
-    scratchdir = property(get_scratchdir)
-
-
-
     # Domain name of the guest
     def get_name(self):
         return self._name
@@ -439,61 +500,6 @@ class Guest(object):
     vcpus = property(get_vcpus, set_vcpus)
-    # kernel + initrd pair to use for installing as opposed to using a location
-    def get_boot(self):
-        return self._boot
-    def set_boot(self, val):
-        if type(val) == tuple:
-            if len(val) != 2:
-                raise ValueError, "Must pass both a kernel and initrd"
-            (k, i) = val
-            self._boot = {"kernel": k, "initrd": i}
-        elif type(val) == dict:
-            if not val.has_key("kernel") or not val.has_key("initrd"):
-                raise ValueError, "Must pass both a kernel and initrd"
-            self._boot = val
-        elif type(val) == list:
-            if len(val) != 2:
-                raise ValueError, "Must pass both a kernel and initrd"
-            self._boot = {"kernel": val[0], "initrd": val[1]}
-    boot = property(get_boot, set_boot)
-
-    # extra arguments to pass to the guest installer
-    def get_extra_args(self):
-        return self._extraargs
-    def set_extra_args(self, val):
-        self._extraargs = val
-    extraargs = property(get_extra_args, set_extra_args)
-
-
-    # install location for the PV guest
-    # this is a string pointing to an NFS, HTTP or FTP install source 
-    def get_install_location(self):
-        return self._location
-    def set_install_location(self, val):
-        if not (val.startswith("http://") or val.startswith("ftp://") or
-                val.startswith("nfs:") or val.startswith("/")):
-            raise ValueError, "Install location must be an NFS, HTTP or FTP network install source, or local file/device"
-        if os.geteuid() != 0 and val.startswith("nfs:"):
-            raise ValueError, "NFS installations are only supported as root"
-        self._location = val
-    location = property(get_install_location, set_install_location)
-
-
-    # Legacy, deprecated
-    def get_cdrom(self):
-        if self._location is not None and self._location.startswith("/"):
-            return self._location
-        return None
-    def set_cdrom(self, val):
-        val = os.path.abspath(val)
-        if not os.path.exists(val):
-            raise ValueError, "CD device must exist!"
-        self.set_install_location(val)
-    cdrom = property(get_cdrom, set_cdrom)
-
-
-
     # graphics setup
     def get_graphics(self):
         return self._graphics
@@ -549,6 +555,38 @@ class Guest(object):
     graphics = property(get_graphics, set_graphics)

 
+    # Legacy, deprecated properties
+    def get_scratchdir(self):
+        return self._installer.scratchdir
+    scratchdir = property(get_scratchdir)
+
+    def get_boot(self):
+        return self._installer.boot
+    def set_boot(self, val):
+        self._installer.boot = val
+    boot = property(get_boot, set_boot)
+
+    def get_location(self):
+        return self._installer.location
+    def set_location(self, val):
+        self._installer.location = val
+    location = property(get_location, set_location)
+
+    def get_extraargs(self):
+        return self._installer.extraargs
+    def set_extraargs(self, val):
+        self._installer.extraargs = val
+    extraargs = property(get_extraargs, set_extraargs)
+
+    def get_cdrom(self):
+        if self._installer.location is not None and self._installer.location.startswith("/"):
+            return self._installer.location
+        return None
+    def set_cdrom(self, val):
+        self._installer.location = os.path.abspath(val)
+    cdrom = property(get_cdrom, set_cdrom)
+
+
     def _create_devices(self,progresscb):
         """Ensure that devices are setup"""
         for disk in self.disks:
@@ -580,15 +618,17 @@ class Guest(object):

     def get_config_xml(self, install = True, disk_boot = False):
         if install:
-            if disk_boot:
-                osblob = self._get_runtime_xml()
-            else:
-                osblob = self._get_install_xml()
             action = "destroy"
         else:
-            osblob = self._get_runtime_xml()
             action = "restart"

+        if disk_boot:
+            install = False
+
+        osblob = self._get_osblob(install)
+        if not osblob:
+            return None
+
         return """<domain type='%(type)s'>
   <name>%(name)s</name>
   <currentMemory>%(ramkb)s</currentMemory>
@@ -622,13 +662,11 @@ class Guest(object):
             # BaseMeter does nothing, but saves a lot of null checking
             meter = progress.BaseMeter()

-        tmpfiles = self._prepare_install_location(meter)
+        self._prepare_install(meter)
         try:
             return self._do_install(consolecb, meter)
         finally:
-            for file in tmpfiles:
-                logging.debug("Removing " + file)
-                os.unlink(file)
+            self._installer.cleanup()

     def _do_install(self, consolecb, meter):
         try:
Index: virtinst--devel/virtinst/ParaVirtGuest.py
===================================================================
--- virtinst--devel.orig/virtinst/ParaVirtGuest.py
+++ virtinst--devel/virtinst/ParaVirtGuest.py
@@ -18,24 +18,14 @@ import Guest
 import DistroManager

 class ParaVirtGuest(Guest.XenGuest):
-    def __init__(self, type=None, connection=None, hypervisorURI=None):
-        Guest.Guest.__init__(self, type=type, connection=connection, hypervisorURI=hypervisorURI)
+    def __init__(self, type=None, connection=None, hypervisorURI=None, installer=None):
+        if not installer:
+            installer = DistroManager.DistroInstaller(type = type)
+        Guest.Guest.__init__(self, type, connection, hypervisorURI, installer)
         self.disknode = "xvd"

-    def _get_install_xml(self):
-        return """<os>
-    <type>linux</type>
-    <kernel>%(kernel)s</kernel>
-    <initrd>%(initrd)s</initrd>
-    <cmdline>%(extra)s</cmdline>
-  </os>""" % \
-    { "kernel": self.kernel, \
-      "initrd": self.initrd, \
-      "extra": self.extraargs }
-
-
-    def _get_runtime_xml(self):
-        return """<bootloader>/usr/bin/pygrub</bootloader>"""
+    def _get_osblob(self, install):
+        return self.installer._get_osblob(install, hvm = False)

     def _connectSerialConsole(self):
         # *sigh*  would be nice to have a python version of xmconsole
@@ -53,31 +43,8 @@ class ParaVirtGuest(Guest.XenGuest):
             raise RuntimeError, "A location must be specified to install from"
         Guest.Guest.validate_parms(self)

-    def _prepare_install_location(self, meter):
-        tmpfiles = []
-        if self.boot is not None:
-            # Got a local kernel/initrd already
-            self.kernel = self.boot["kernel"]
-            self.initrd = self.boot["initrd"]
-        else:
-            # Need to fetch the kernel & initrd from a remote site, or
-            # out of a loopback mounted disk image/device
-            (kernelfn,initrdfn,args) = DistroManager.acquireKernel(self.location, meter,
scratchdir=self.scratchdir, type=self.type)
-            self.kernel = kernelfn
-            self.initrd = initrdfn
-            if self.extraargs is not None:
-                self.extraargs = self.extraargs + " " + args
-            else:
-                self.extraargs = args
-            tmpfiles.append(kernelfn)
-            tmpfiles.append(initrdfn)
-
-        # If they're installing off a local file/device, we map it
-        # through to a virtual harddisk
-        if self.location is not None and self.location.startswith("/"):
-            self.disks.append(Guest.VirtualDisk(self.location, readOnly=True, transient=True))
-
-        return tmpfiles
+    def _prepare_install(self, meter):
+        self._installer.prepare(guest = self, need_bootdev = False, meter = meter)

     def _get_disk_xml(self, install = True):
         """Get the disk config in the libvirt XML format"""
Index: virtinst--devel/virtinst/__init__.py
===================================================================
--- virtinst--devel.orig/virtinst/__init__.py
+++ virtinst--devel/virtinst/__init__.py
@@ -2,3 +2,4 @@ import util
 from Guest import Guest, VirtualDisk, VirtualNetworkInterface, XenGuest, XenDisk, XenNetworkInterface
 from FullVirtGuest import FullVirtGuest
 from ParaVirtGuest import ParaVirtGuest
+from DistroManager import DistroInstaller

--

-- 
Mark McLoughlin | 2 May 14:16
Picon
Favicon

[patch 2/8] Make virt-install use DistroInstaller

A simple patch to make virt-install instantiate the appropriate
guest type with a DistroInstaller instance. This should be the
same as passing installer=None to the guest constructors.

Signed-off-by: Mark McLoughlin <markmc@...>

Index: virtinst--devel/virt-install
===================================================================
--- virtinst--devel.orig/virt-install
+++ virtinst--devel/virt-install
@@ -516,10 +516,12 @@ def main():
             elif virtinst.util.is_kqemu_capable():
                 type = "kqemu"

+    installer = virtinst.DistroInstaller(type = type)
+
     if hvm:
-        guest = virtinst.FullVirtGuest(connection=conn, type=type, arch=options.arch)
+        guest = virtinst.FullVirtGuest(connection=conn, installer=installer, arch=options.arch)
     else:
-        guest = virtinst.ParaVirtGuest(connection=conn, type=type)
+        guest = virtinst.ParaVirtGuest(connection=conn, installer=installer)

     # now let's get some of the common questions out of the way
     get_name(options.name, guest)

--

-- 
Mark McLoughlin | 2 May 14:16
Picon
Favicon

[patch 3/8] Move post install check into DistroInstaller

The existing heuristic we use in virt-install to detect whether
an install has completed successfully is dependant on the type
of installer being used - i.e. if you create a VM with a livecd
or a raw ext3 image, there may be no disk with an MBR at the end.

This patch moves that logic into a new DistroInstaller method.

Signed-off-by: Mark McLoughlin <markmc@...>

Index: virtinst--devel/virt-install
===================================================================
--- virtinst--devel.orig/virt-install
+++ virtinst--devel/virt-install
@@ -17,7 +17,6 @@
 import os, sys, string
 from optparse import OptionParser, OptionValueError
 import subprocess
-import struct
 import logging
 import libxml2
 import urlgrabber.progress as progress
@@ -582,11 +581,7 @@ def main():
     # the domain is no longer running
     # FIXME: this is just a hacky heuristic, but I'll take what I can get
     try:
-        fd = os.open(guest.disks[0].path, os.O_RDONLY)
-        buf = os.read(fd, 512)
-        os.close(fd)
-        if len(buf) == 512 and \
-               struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,):
+        if guest.post_install_check():
             # things installed enough that we should be able to restart
             # the domain
             if continue_inst:
Index: virtinst--devel/virtinst/DistroManager.py
===================================================================
--- virtinst--devel.orig/virtinst/DistroManager.py
+++ virtinst--devel/virtinst/DistroManager.py
@@ -18,6 +18,7 @@ import os
 import gzip
 import re
 import stat
+import struct
 import subprocess
 import urlgrabber.grabber as grabber
 import urlgrabber.progress as progress
@@ -698,3 +699,10 @@ class DistroInstaller(Guest.Installer):
             osblob += "    <bootloader>/usr/bin/pygrub</bootloader>"

         return osblob
+
+    def post_install_check(self, guest):
+        # Check for the 0xaa55 signature at the end of the MBR
+        fd = os.open(guest.disks[0].path, os.O_RDONLY)
+        buf = os.read(fd, 512)
+        os.close(fd)
+        return len(buf) == 512 and struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,)
Index: virtinst--devel/virtinst/Guest.py
===================================================================
--- virtinst--devel.orig/virtinst/Guest.py
+++ virtinst--devel/virtinst/Guest.py
@@ -727,6 +727,9 @@ class Guest(object):
         # for inactive guest, or get the still running install..
         return self.conn.lookupByName(self.name)

+    def post_install_check(self):
+        return self.installer.post_install_check(self)
+
     def connect_console(self, consolecb):
         logging.debug("Restarted guest, looking to see if it is running")
         # sleep in .25 second increments until either a) we get running

--

-- 
Mark McLoughlin | 2 May 14:16
Picon
Favicon

[patch 4/8] Re-factor virt-installs post-install checking code

This patch attempts to clean up the two similar "start guest,
check to see if it succeeded" code paths into a single code
path.

Signed-off-by: Mark McLoughlin <markmc@...>

Index: virtinst--devel/virt-install
===================================================================
--- virtinst--devel.orig/virt-install
+++ virtinst--devel/virt-install
@@ -565,51 +565,47 @@ def main():
     # we've got everything -- try to start the install
     try:
         print "\n\nStarting install..."
-        dom = guest.start_install(conscb,progresscb)
-        if dom is None:
-            print "Guest installation failed"
-            sys.exit(0)
-        elif dom.info()[0] != libvirt.VIR_DOMAIN_SHUTOFF:
-            # domain seems to be running
-            print "Domain installation still in progress.  You can reconnect "
-            print "to the console to complete the installation process."
-            sys.exit(0)
-    except RuntimeError, e:
-        print >> sys.stderr, "ERROR: ", e
-        sys.exit(1)

-    # the domain is no longer running
-    # FIXME: this is just a hacky heuristic, but I'll take what I can get
-    try:
-        if guest.post_install_check():
-            # things installed enough that we should be able to restart
-            # the domain
-            if continue_inst:
-                # continue to installation.
+        started = False
+        while True:
+            if not started:
+                dom = guest.start_install(conscb,progresscb)
+            elif continue_inst:
                 dom = guest.continue_install(conscb,progresscb)
-                if dom is None:
-                    print "Guest installation failed"
-                    sys.exit(0)
-                elif dom.info()[0] != libvirt.VIR_DOMAIN_SHUTOFF:
-                    # domain seems to be running
-                    print "Domain installation still in progress.  You can reconnect "
-                    print "to the console to complete the installation process."
+                continue_inst = False
+            else:
+                break
+
+            if dom is None:
+                print "Guest installation failed"
+                sys.exit(0)
+            elif dom.info()[0] != libvirt.VIR_DOMAIN_SHUTOFF:
+                # domain seems to be running
+                print "Domain installation still in progress.  You can reconnect "
+                print "to the console to complete the installation process."
+                sys.exit(0)
+
+            if not started:
+                started = True
+                if not guest.post_install_check():
+                    print ("Domain installation does not appear to have been\n"
+                           "successful.  If it was, you can restart your domain\n"
+                           "by running 'virsh start %s'; otherwise, please\n"
+                           "restart your installation.") %(guest.name,)
                     sys.exit(0)
-            print "Guest installation complete... restarting guest."
-            dom.create()
-            guest.connect_console(conscb)
-        else:
-            print ("Domain installation does not appear to have been\n"
-                   "successful.  If it was, you can restart your domain\n"
-                   "by running 'virsh start %s'; otherwise, please\n"
-                   "restart your installation.") %(guest.name,)
+
+        print "Guest installation complete... restarting guest."
+        dom.create()
+        guest.connect_console(conscb)
+    except RuntimeError, e:
+        print >> sys.stderr, "ERROR: ", e
+        sys.exit(1)
     except Exception, e:
-        print "exception was:", e
         print ("Domain installation may not have been\n"
                "successful.  If it was, you can restart your domain\n"
                "by running 'virsh start %s'; otherwise, please\n"
                "restart your installation.") %(guest.name,)
-
+        raise

 if __name__ == "__main__":
     main()

--

-- 
Mark McLoughlin | 2 May 14:16
Picon
Favicon

[patch 5/8] Allow the installer to skip the first VM boot

Not all installer types need the VM to be executed twice - i.e.
the "install" phase may not need the VM to be run, and we only
start the VM post-install.

This patch implements that by allowing get_config_xml(install=True)
to return None, causing the first VM run to be skipped.

Signed-off-by: Mark McLoughlin <markmc@...>

Index: virtinst--devel/virtinst/Guest.py
===================================================================
--- virtinst--devel.orig/virtinst/Guest.py
+++ virtinst--devel/virtinst/Guest.py
@@ -675,38 +675,39 @@ class Guest(object):
         except libvirt.libvirtError:
             pass

+        child = None
         self._create_devices(meter)
         install_xml = self.get_config_xml()
-        logging.debug("Creating guest from '%s'" % ( install_xml ))
-        meter.start(size=None, text="Creating domain...")
-        self.domain = self.conn.createLinux(install_xml, 0)
-        if self.domain is None:
-            raise RuntimeError, "Unable to create domain for guest, aborting installation!"
-        meter.end(0)
-
-        logging.debug("Created guest, looking to see if it is running")
-        # sleep in .25 second increments until either a) we find
-        # our domain or b) it's been 5 seconds.  this is so that
-        # we can try to gracefully handle domain creation failures
-        num = 0
-        d = None
-        while num < (5 / .25): # 5 seconds, .25 second sleeps
-            try:
-                d = self.conn.lookupByName(self.name)
-                break
-            except libvirt.libvirtError, e:
-                logging.debug("No guest running yet " + str(e))
-                pass
-            num += 1
-            time.sleep(0.25)
+        if install_xml:
+            logging.debug("Creating guest from '%s'" % ( install_xml ))
+            meter.start(size=None, text="Creating domain...")
+            self.domain = self.conn.createLinux(install_xml, 0)
+            if self.domain is None:
+                raise RuntimeError, "Unable to create domain for guest, aborting installation!"
+            meter.end(0)
+
+            logging.debug("Created guest, looking to see if it is running")
+            # sleep in .25 second increments until either a) we find
+            # our domain or b) it's been 5 seconds.  this is so that
+            # we can try to gracefully handle domain creation failures
+            num = 0
+            d = None
+            while num < (5 / .25): # 5 seconds, .25 second sleeps
+                try:
+                    d = self.conn.lookupByName(self.name)
+                    break
+                except libvirt.libvirtError, e:
+                    logging.debug("No guest running yet " + str(e))
+                    pass
+                num += 1
+                time.sleep(0.25)

-        if d is None:
-            raise RuntimeError, "It appears that your installation has crashed.  You should be able to find more
information in the logs"
+            if d is None:
+                raise RuntimeError, "It appears that your installation has crashed.  You should be able to find more
information in the logs"

-        child = None
-        if consolecb:
-            logging.debug("Launching console callback")
-            child = consolecb(self.domain)
+            if consolecb:
+                logging.debug("Launching console callback")
+                child = consolecb(self.domain)

         boot_xml = self.get_config_xml(install = False)
         logging.debug("Saving XML boot config '%s'" % ( boot_xml ))
@@ -718,9 +719,8 @@ class Guest(object):
             except OSError, (errno, msg):
                 print __name__, "waitpid:", msg

-        # ensure there's time for the domain to finish destroying if the
-        # install has finished or the guest crashed
-        if consolecb:
+            # ensure there's time for the domain to finish destroying if the
+            # install has finished or the guest crashed
             time.sleep(1)

         # This should always work, because it'll lookup a config file

--

-- 
Mark McLoughlin | 2 May 14:16
Picon
Favicon

[patch 6/8] This patch adds two new options to virt-install:

  + --nodisks - if creating a VM to run a livecd or a prebuilt
    system image, it is not neccessary to create another disk
    for the VM

  + --installer - this option allows the user to choose the
    installer type

Signed-off-by: Mark McLoughlin <markmc@...>

Index: virtinst--devel/virt-install
===================================================================
--- virtinst--devel.orig/virt-install
+++ virtinst--devel/virt-install
@@ -149,7 +149,11 @@ def get_disk(disk, size, sparse, guest, 
         guest.disks.append(d)
         break

-def get_disks(disk, size, sparse, guest, hvm, conn):
+def get_disks(disk, size, sparse, nodisks, guest, hvm, conn):
+    if nodisks:
+        if disk or size:
+            raise ValueError, "Cannot use --file with --nodisks"
+        return
     # ensure we have equal length lists 
     if (type(disk) == type(size) == list):
         if len(disk) != len(size):
@@ -318,6 +322,8 @@ def parse_args():
     parser.add_option("", "--nonsparse", action="store_false",
                       default=True, dest="sparse",
                       help="Don't use sparse files for disks.  Note that this will be significantly slower for guest creation")
+    parser.add_option("", "--nodisks", action="store_true",
+                      help="Don't set up any disks for the guest.")

     # network options
     parser.add_option("-m", "--mac", type="string",
@@ -353,7 +359,10 @@ def parse_args():
                       action="callback", callback=check_before_store,
                       help="Connect to hypervisor with URI",
                       default=virtinst.util.default_connection())
-    
+    parser.add_option("", "--installer", type="string", dest="installer",
+                      action="callback", callback=check_before_store,
+                      help="Specify the installation method to use e.g. 'distro', ...")
+
     # fullvirt options
     parser.add_option("-v", "--hvm", action="store_true", dest="fullvirt",
                       help="This guest should be a fully virtualized guest")
@@ -515,7 +524,11 @@ def main():
             elif virtinst.util.is_kqemu_capable():
                 type = "kqemu"

-    installer = virtinst.DistroInstaller(type = type)
+    if not options.installer or options.installer == "distro":
+        installer = virtinst.DistroInstaller(type = type)
+    else:
+        print >> sys.stderr, "Unknown installer type '%s'" % options.installer
+        sys.exit(1)

     if hvm:
         guest = virtinst.FullVirtGuest(connection=conn, installer=installer, arch=options.arch)
@@ -529,7 +542,7 @@ def main():
     get_vcpus(options.vcpus, options.check_cpu, guest, conn)

     # set up disks
-    get_disks(options.diskfile, options.disksize, options.sparse,
+    get_disks(options.diskfile, options.disksize, options.sparse, options.nodisks,
               guest, hvm, conn)

     # set up network information

--

-- 
Mark McLoughlin | 2 May 14:16
Picon
Favicon

[patch 7/8] Add CapabilitiesParser module

This adds a simple module for parsing libvirt's getCapabilities()
XML.

It might seem more straightforward to just use XPath for this, but
in the code to support arbitrary system images, you iterate over
the various ways that the image can be booted and the various ways
in which a hypervisor can boot a guest and find the best match. For
that kind of use, the API below is much easier to use.

Note, this parser rejects things like architectures it doesn't
know about, which needs to be fixed in order to have forward
compatibility.

Signed-off-by: Mark McLoughlin <markmc@...>

Index: virtinst--devel/virtinst/CapabilitiesParser.py
===================================================================
--- /dev/null
+++ virtinst--devel/virtinst/CapabilitiesParser.py
@@ -0,0 +1,245 @@
+#!/usr/bin/python -tt
+
+# Some code for parsing libvirt's capabilities XML
+#
+# Copyright 2007  Red Hat, Inc.
+# Mark McLoughlin <markmc@...>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import libxml2
+
+class CapabilitiesParserException(Exception):
+    def __init__(self, msg):
+        Exception.__init__(self, msg)
+
+FEATURE_ACPI    = 0x01
+FEATURE_APIC    = 0x02
+FEATURE_PAE     = 0x04
+FEATURE_NONPAE  = 0x08
+FEATURE_VMX     = 0x10
+FEATURE_SVM     = 0x20
+FEATURE_IA64_BE = 0x40
+
+features_map = {
+    "acpi"    : FEATURE_ACPI,
+    "apic"    : FEATURE_APIC,
+    "pae"     : FEATURE_PAE,
+    "nonpae"  : FEATURE_NONPAE,
+    "vmx"     : FEATURE_VMX,
+    "svm"     : FEATURE_SVM,
+    "ia64_be" : FEATURE_IA64_BE
+}
+
+NUM_FEATURES = len(features_map)
+
+def _parse_features(self, node):
+    features = 0
+
+    child = node.children
+    while child:
+        if child.name in features_map:
+            features |= features_map[child.name]
+
+        child = child.next
+
+    return features
+
+class Host(object):
+    ARCHES = [ "i686", "x86_64" ]
+
+    def __init__(self, node = None):
+        self._arch     = None
+        self._features = 0
+
+        if not node is None:
+            self.parseXML(node)
+
+    def get_arch(self):
+        return self._arch
+    def set_arch(self, arch):
+        if not arch in self.ARCHES:
+            raise CapabilitiesParserException("'%s' is not a supported architecture" % arch)
+        self._arch = arch
+    arch = property(get_arch, set_arch)
+
+    def get_features(self):
+        return self._features
+    def set_features(self, features):
+        if (features >> NUM_FEATURES) != 0:
+            raise CapabilitiesParserException("Invalid feature set '0x%x'" % features)
+        self._features = features
+    features = property(get_features, set_features)
+
+    def parseXML(self, node):
+        child = node.children
+        while child:
+            if child.name != "cpu":
+                child = child.next
+                continue
+
+            n = child.children
+            while n:
+                if n.name == "arch":
+                    self.arch = n.content
+                elif n.name == "features":
+                    self.features |= _parse_features(self, n)
+                n = n.next
+
+            child = child.next
+
+class Guest(object):
+    OS_TYPES = [ "xen", "hvm" ]
+    HYPERVISOR_TYPES = [ "xen", "qemu", "kqemu", "kvm" ]
+    ARCHES = [ "i686", "x86_64", "mips", "mipsel", "sparc", "ppc" ]
+
+    def __init__(self, node = None):
+        self._os_type         = None
+        self._hypervisor_type = None
+        self._arch            = None
+        self._features        = 0
+
+        if not node is None:
+            self.parseXML(node)
+
+    def get_os_type(self):
+        return self._os_type
+    def set_os_type(self, os_type):
+        if not os_type in self.OS_TYPES:
+            raise CapabilitiesParserException("'%s' is not a supported OS type" % os_type)
+        self._os_type = os_type
+    os_type = property(get_os_type, set_os_type)
+
+    def get_hypervisor_type(self):
+        return self._hypervisor_type
+    def set_hypervisor_type(self, hypervisor_type):
+        if not hypervisor_type in self.HYPERVISOR_TYPES:
+            raise CapabilitiesParserException("'%s' is not a supported hypervisor type" % hypervisor_type)
+        self._hypervisor_type = hypervisor_type
+    hypervisor_type = property(get_hypervisor_type, set_hypervisor_type)
+
+    def get_arch(self):
+        return self._arch
+    def set_arch(self, arch):
+        if not arch in self.ARCHES:
+            raise CapabilitiesParserException("'%s' is not a supported architecture" % arch)
+        self._arch = arch
+    arch = property(get_arch, set_arch)
+
+    def get_features(self):
+        return self._features
+    def set_features(self, features):
+        if (features >> NUM_FEATURES) != 0:
+            raise CapabilitiesParserException("Invalid feature set '0x%x'" % features)
+        self._features = features
+    features = property(get_features, set_features)
+
+    def parseXML(self, node):
+        child = node.children
+        while child:
+            if child.name == "os_type":
+                self.os_type = child.content
+            elif child.name == "features":
+                self.features |= _parse_features(self, child)
+            elif child.name == "arch":
+                self.arch = child.prop("name")
+                n = child.children
+                while n:
+                    # NB. for now, ignoring the rest of arch e.g. wordsize etc.
+                    if n.name == "domain":
+                        self.hypervisor_type = n.prop("type")
+                    n = n.next
+
+            child = child.next
+
+class Capabilities(object):
+    def __init__(self, node = None):
+        self._host = None
+        self.guests = []
+
+        if not node is None:
+            self.parseXML(node)
+
+    def get_host(self):
+        return self._host
+    def set_host(self, host):
+        if not self._host is None:
+            raise CapabilitiesParserException("Only a single <host> element is allowed")
+        self._host = host
+    host = property(get_host, set_host)
+
+    def parseXML(self, node):
+        child = node.children
+        while child:
+            if child.name == "host":
+                self.host = Host(child)
+            elif child.name == "guest":
+                self.guests.append(Guest(child))
+            child = child.next
+
+def parse(xml):
+    class ErrorHandler:
+        def __init__(self):
+            self.msg = ""
+        def handler(self, ctx, str):
+            self.msg += str
+    error = ErrorHandler()
+    libxml2.registerErrorHandler(error.handler, None)
+
+    try:
+        #
+        # FIXME: when we can rely on python-2.5 and its support for
+        #        try ... except ... finally, remove this inner try
+        #
+        try:
+            doc = libxml2.readMemory(xml, len(xml),
+                                     None, None,
+                                     libxml2.XML_PARSE_NOBLANKS)
+        except (libxml2.parserError, libxml2.treeError), e:
+            raise CapabilitiesParserException("%s\n%s" % (e, error.msg))
+    finally:
+        libxml2.registerErrorHandler(None, None)
+
+    try:
+        root = doc.getRootElement()
+        if root.name != "capabilities":
+            raise CapabilitiesParserException("Root element is not 'capabilties'")
+
+        capabilities = Capabilities(root)
+    finally:
+        doc.freeDoc()
+
+    return capabilities
+
+if __name__ == "__main__":
+    import libvirt
+
+    for uri in (None, "qemu:///system"):
+        cnx = libvirt.open(uri)
+
+        caps = parse(cnx.getCapabilities())
+
+        print "host arch: %s" % caps.host.arch
+        if caps.host.features:
+            print "host features:"
+            for feature in features_map:
+                if caps.host.features & features_map[feature]:
+                    print "    " + feature
+
+        for guest in caps.guests:
+            print
+            print "guest arch: %s" % guest.arch
+            print "guest os type: %s" % guest.os_type
+            print "guest hypervisor type: %s" % guest.hypervisor_type
+            if guest.features:
+                print "guest features:"
+                for feature in features_map:
+                    if guest.features & features_map[feature]:
+                        print "    " + feature
+
+        print

--

-- 
Mark McLoughlin | 2 May 14:17
Picon
Favicon

[patch 8/8] Add the livecd installer

This patch adds a LiveCDInstaller class which has the simple
job of setting up the cdrom disk and returning the appropriate
<os> blob to boot from it.

Signed-off-by: Mark McLoughlin <markmc@...>

Index: virtinst--devel/virt-install
===================================================================
--- virtinst--devel.orig/virt-install
+++ virtinst--devel/virt-install
@@ -526,6 +526,8 @@ def main():

     if not options.installer or options.installer == "distro":
         installer = virtinst.DistroInstaller(type = type)
+    elif options.installer == "livecd":
+        installer = virtinst.LiveCDInstaller(type = type)
     else:
         print >> sys.stderr, "Unknown installer type '%s'" % options.installer
         sys.exit(1)
Index: virtinst--devel/virtinst/LiveCDInstaller.py
===================================================================
--- /dev/null
+++ virtinst--devel/virtinst/LiveCDInstaller.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python -tt
+#
+# An installer class for LiveCD images
+#
+# Copyright 2007  Red Hat, Inc.
+# Mark McLoughlin <markmc@...>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import os
+
+import Guest
+import CapabilitiesParser
+
+class LiveCDInstallerException(Exception):
+    def __init__(self, msg):
+        Exception.__init__(self, msg)
+
+class LiveCDInstaller(Guest.Installer):
+    def __init__(self, type = "xen", location = None):
+        Guest.Installer.__init__(self, type, location)
+
+    def prepare(self, guest, need_bootdev, meter, distro = None):
+        self.cleanup()
+
+        if not os.path.exists(self.location):
+            raise LiveCDInstallerException("LiveCD image '%s' does not exist" % self.location)
+
+        capabilities = CapabilitiesParser.parse(guest.conn.getCapabilities())
+
+        found = False
+        for guest_caps in capabilities.guests:
+            if guest_caps.os_type == "hvm":
+                found = True
+                break
+
+        if not found:
+            raise LiveCDInstallerException("HVM virtualisation not supported; cannot boot LiveCD")
+
+        disk = Guest.VirtualDisk(self.location,
+                                 device = Guest.VirtualDisk.DEVICE_CDROM,
+                                 readOnly = True)
+        guest.disks.insert(0, disk)
+
+    def _get_osblob(self, install, hvm, arch = None, loader = None):
+        if install:
+            return None
+
+        osblob  = "<os>\n"
+        osblob += "      <type>hvm</type>\n"
+        if loader:
+            osblob += "      <loader>%s</loader>\n" % loader
+        osblob += "      <boot dev='cdrom'/>\n"
+        osblob += "    </os>"
+
+        return osblob
+
+    def post_install_check(self, guest):
+        return True
+
Index: virtinst--devel/virtinst/__init__.py
===================================================================
--- virtinst--devel.orig/virtinst/__init__.py
+++ virtinst--devel/virtinst/__init__.py
@@ -3,3 +3,4 @@ from Guest import Guest, VirtualDisk, Vi
 from FullVirtGuest import FullVirtGuest
 from ParaVirtGuest import ParaVirtGuest
 from DistroManager import DistroInstaller
+from LiveCDInstaller import LiveCDInstaller

--

-- 

Gmane