Your message dated Sat, 29 Nov 2008 00:16:17 +0100
with message-id <[EMAIL PROTECTED]>
and subject line Re: Bug#413867: what about using libvirt
has caused the Debian Bug report #413867,
regarding config file driven wrapper script for kvm binary
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [EMAIL PROTECTED]
immediately.)


-- 
413867: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=413867
Debian Bug Tracking System
Contact [EMAIL PROTECTED] with problems
--- Begin Message ---
Package: kvm
Version: 12-1
Severity: wishlist


This is a configuration-file driven wrapper script for kvm, though it
may also be used with qemu (or any other qemu-based
virtualization/emulation binary, for that matter). It allows kvm/qemu
to be run with a fairly simple command line, getting all necessary
configuration options from a config file.

The primary rationale for this is to be able to use kvm as a Heartbeat
resource including savevm/loadvm, which allows to run a virtual
machine as a cluster service (and switching over the entire virtual
machine without rebooting, if shared storage or DRBD is available).

Attached is the launcher script itself, and an example configuration
file (doesn't showcase all options supported by the script). The
config file would supposedly go somewhere in /etc/kvm and is
referenced by the launcher using the -f option. Edit it to suit your
needs.

Help for the launcher is available with -h or --help. Supported
launcher commands can be listed with --help-commands.

The launcher keeps a pid file and a symlink to kvm's monitor pty in a
directory that by default is /var/run/kvm/<machinename>, where
<machinename> corresponds to the 'name' option set in the config
file. You need to create the /var/run/kvm directory beforehand, the
script won't do that for you. Or you set the 'directory' option to
something like /tmp in the config file.

Any feedback and suggestions are very very welcome. There are a couple
of FIXMEs in the code where I would be especially grateful for
valuable feedback.

The launcher script uses the subprocess module, hence requires Python
2.4 or above.

-- Package-specific info:


/proc/cpuinfo:

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 15
model name      : Intel(R) Core(TM)2 CPU          6300  @ 1.86GHz
stepping        : 6
cpu MHz         : 1862.060
cache size      : 2048 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 2
fpu             : yes
fpu_exception   : yes
cpuid level     : 10
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov 
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm syscall nx lm 
constant_tsc pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr lahf_lm
bogomips        : 3727.65
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 15
model name      : Intel(R) Core(TM)2 CPU          6300  @ 1.86GHz
stepping        : 6
cpu MHz         : 1862.060
cache size      : 2048 KB
physical id     : 0
siblings        : 2
core id         : 1
cpu cores       : 2
fpu             : yes
fpu_exception   : yes
cpuid level     : 10
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov 
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm syscall nx lm 
constant_tsc pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr lahf_lm
bogomips        : 3724.29
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:




-- System Information:
Debian Release: 4.0
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: amd64 (x86_64)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.20-netlinkhack
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)

Versions of packages kvm depends on:
ii  adduser  3.102                           Add and remove users and groups
ii  bridge-u 1.2-1                           Utilities for configuring the Linu
ii  iproute  20061002-3                      Professional tools to control the 
ii  libasoun 1.0.13-1                        ALSA library
ii  libc6    2.3.6.ds1-13                    GNU C Library: Shared libraries
ii  libsdl1. 1.2.11-8                        Simple DirectMedia Layer
ii  libuuid1 1.39+1.40-WIP-2006.11.14+dfsg-1 universally unique id library
ii  zlib1g   1:1.2.3-13                      compression library - runtime

Versions of packages kvm recommends:
pn  kvm-sour <none>                          (no description available)
ii  linux-im 2.6.18-7                        Linux 2.6.18 image on AMD64
ii  linux-im 2.6.18-7                        Linux 2.6.18 image on AMD64
ii  linux-im 2.6.18.dfsg.1-11                Linux 2.6.18 image on AMD64
ii  linux-im 2.6.20-10.00.Custom             Linux kernel binary image for vers
ii  linux-im 2.6.20-netlinkhack-10.00.Custom Linux kernel binary image for vers
ii  qemu     0.8.2-4                         fast processor emulator
pn  vde2     <none>                          (no description available)

-- no debconf information
#!/usr/bin/python
"""A scriptable launcher interface for qemu-based virtual machine
monitors such as kvm.

This can be used for launching a virtual machine ad-hoc on the
command-line, from within an init script, or from a resource script
used by a cluster manager such as Heartbeat.

The configuration syntax is quite simple, especially for people
familiar with qemu and/or kvm. An example configuration is provided
here:

# Global settings
[global]
name = jameson
memory = 256
smp = 1
boot = disk,cdrom,floppy
keyboard = de
vnc = 42

# Disks (may be either disk images or real block devices)
[disks]
hda = /dev/drbd2
#floppy = /dev/fd0
#cdrom = /tmp/test.iso
vmstate = /dev/drbd3

# USB configuration
[usb]
enable = true
device = tablet

# Network configuration for network device 0
[net_tap]
type = tap
vlan = 0
ifname = tap0
script = /etc/kvm/kvm-ifup

The only mandatory settings are 'name', which is an arbitrary name for
the VM, and at least one disk (hd[a-d], cdrom, or floppy) entry. All
other settings are optional and the corresponding qemu/kvm defaults
are used if unset.

This launcher will create a pid file for the launched process, along
with a symlink to its monitor pty, in a directory named
/var/run/(kvm|qemu)/<name>.

This module uses the subprocess API and hence requires Python 2.4 or
above.
"""

import sys
import logging, logging.handlers
import os, subprocess, signal
import time
import types

from optparse import OptionParser
from ConfigParser import RawConfigParser

### Exception class definitions ###

class VMRunningException(Exception):
    """Indicates that an action was taken on a running virtual machine
    that should only be executed when the machine is stopped. Examples
    include issuing a start or launch command on an already-running
    machine, or a clean command for a machine that is not stopped."""
    pass

class UnexpectedOutputException(Exception):
    """Indicates that the launcher binary produced unexcpected output."""
    pass

class InvalidPtyException(Exception):
    """Indicated that a pseudo-terminal is invalid or behaves in an
    unexpected manner."""
    pass

class UnsupportedCommandException(Exception):
    """Indicates that an unsupported command was issued (such as
    'superfrobnicate', perhaps)."""
    pass

class DiskConflictException(Exception):
    """Indicates that conflicting host disks (or disk images) were
    found for one and the same guest block device."""
    pass


### Abstract base class  ###

class VMConfig(object):
    """Abstract class representing any qemu-based virtual machine configuration."""

    # These are the commands exposed via the command-line interface
    COMMANDS = ['start', 'stop', 'status', 'launch', 'save', 'suspend', 'resume',  'kill', 'clean', 'status', 'hb_status']

    PATH_PREFIX = '' # Pretty much exclusively used for unit tests!

    BINARY_NAME = None
    STATE_DIR = None
    PIDFILE_NAME = None
    MONITOR_SYMLINK_NAME = 'monitor'
    SERIAL_SYMLINK_NAME = 'serial'

    def __init__(self, name=None, config=None):
        # First, check whether we have meaningful values for these
        # class variables (subclasses set these)
        for attr in ['BINARY_NAME', 'STATE_DIR', 'PIDFILE_NAME']:
            if not getattr(self, attr):
                raise NotImplementedError("Missing value for class attribute %s. Do you need to fix your subclass?" % attr)

        self.__disks = {}
        self.__nics = {}
        
        # get the root logger
        self.__logger = logging.getLogger()

        if config:
            # Load the configuration, this will also set the name,
            # which in turn will change the logger
            self.load_config(config)
        elif name:
            # If we didn't get a configuration, we at least need a name 
            self.name = name
        else:
            # Sorry, won't work without a name
            raise ValueError("The configuration instance must be named. Did you forget to supply a name in the configuration file?")


    ## Internally used instance methods ##

    def __set_name(self, name):
        """Sets the name representing this VM. This is normally set from the config file and can be any arbitrary name."""
        self.__name = name
        self.__logger = logging.getLogger(name)
        self.__logger.debug("Set VM name to '%s'." % name)
        self.directory = os.path.join(self.STATE_DIR, name)

    def __get_name(self):
        return self.__name
        
    def __set_disk(self, guestdisk, hostdisk):
        """Sets the specified host disk image or block device in the guest's disk configuration."""
        if hostdisk is None:
            del self.__disks[guestdisk]
        elif guestdisk in self.__disks and hostdisk:
            if self.__disks[guestdisk] == hostdisk:
                self.__logger.warn("Guest disk %s specified more than once (host disk %s)." %
                             (guestdisk, hostdisk))
            else:
                raise DiskConflictException("Guest disk %s specified more than once with conflicting host disks (%s, %s)." %
                                            (guestdisk, hostdisk, self.__disks[guestdisk]))
        else:                         
            self.__disks[guestdisk] = hostdisk
            self.__logger.debug("Added host disk (or image) %s as guest disk %s." % (hostdisk, guestdisk))

    def __get_disk(self, disk):
        try:
            return self.__disks[disk]
        except KeyError:
            return None

    def __set_fda(self, fda): self.__set_disk('fda', fda)

    def __get_fda(self): return self.__get_disk('fda')
    
    def __set_fdb(self, fdb): self.__set_disk('fdb', fdb)

    def __get_fdb(self): return self.__get_disk('fdb')

    def __set_hda(self, hda): self.__set_disk('hda', hda)

    def __get_hda(self): return self.__get_disk('hda')
    
    def __set_hdb(self, hdb): self.__set_disk('hdb', hdb)

    def __get_hdb(self): return self.__get_disk('fda')

    def __set_hdc(self, hdc): 
        if self.cdrom:
            raise DiskConflictException("Can't set cdrom (%s) and hdc (%s) on same VM." % (self.cdrom, hdc))
        else:
            self.__set_disk('hdc', hdc)

    def __get_hdc(self): return self.__get_disk('hdc')

    def __set_hdd(self, hdd): self.__set_disk('hdd', hdd)

    def __get_hdd(self): return self.__get_disk('hdd')
    
    def __set_cdrom(self, cdrom):
        if self.hdc:
            raise DiskConflictException("Can't set cdrom (%s) and hdc (%s) on same VM." % (cdrom, self.hdc))
        else:
            self.__set_disk('cdrom', cdrom)

    def __get_cdrom(self): return self.__get_disk('cdrom')

    def __set_directory(self, directory):
        self.__directory = os.path.join(self.PATH_PREFIX, directory)
        self.__logger.debug("Set state directory to %s." % self.__directory)

    def __get_directory(self):
        return self.__directory
    
    def __set_vmstate_disk(self, vmstate_disk):
        self.__vmstate_disk = vmstate_disk
        self.__logger.debug("Added virtual machine state disk (or image) %s." % self.__vmstate_disk)

    def __get_vmstate_disk(self):
        return self.__vmstate_disk

    def __get_monitor_symlink(self):
        return os.path.join(self.directory, self.MONITOR_SYMLINK_NAME)

    def __get_pidfile(self):
        return os.path.join(self.directory, self.PIDFILE_NAME)

    def __get_pid(self):
        return int(open(self.pidfile, 'r').readline().strip())

    def __set_memory(self, memory):
        self.__memory = int(memory)
        self.__logger.debug("Set guest memory size to %sM." % memory)


    def __get_memory(self):
        return self.__memory

    def __set_keyboard(self, kbd):
        self.__keyboard = kbd
        self.__logger.debug("Set guest keyboard layout to '%s'." % kbd)


    def __get_keyboard(self):
        return self.__keyboard


    def __set_snapshot(self, snapshot):
        if snapshot:
            self.__logger.info("Enabling snapshot mode. No writes to the disk images will be made.")
        else:
            self.__logger.debug("Not enabling snapshot mode.")
        self.__snapshot = snapshot


    def __get_snapshot(self): return self.__snapshot

    def __set_boot(self, boot):
        boot_dict = {'disk': 'c',
                           'cdrom': 'd',
                           'floppy': 'a' }
        if type(boot) == types.StringType:
            if len(boot) <= 3:
                # it's is a qemu-style boot order as in "cda"
                self.__boot = boot
            else:
                # it's a comma-separated string as in
                # "disk,cdrom,floppy" or just "disk"
                self.__boot = "".join([boot_dict[item.strip()] for item in boot.split(",")])
        else:
            # it's a list as in ['disk', 'cdrom', 'floppy']
            self.__boot = "".join([boot_dict[item] for item in boot])
        self.__logger.debug("Boot order set to %s." % self.__boot)

    def __get_boot(self): return self.__boot

    def add_nic(self, nic_index, nic_params):
        valid_nic_types = ['user', 'tap', 'socket']
        params = dict(nic_params)

        if params['type'] not in valid_nic_types:
            raise ValueError('Network interface type must be one of %s.' % ", ".join(valid_nic_types))
        
        self.__nics[nic_index] = params
        self.__logger.debug("Added network interface %s with parameters %s." % (nic_index, params)) 

    
    def __get_launch_args(self):
        """Creates the argument string used by the qemu/kvm binary."""
        launch_args = []

        # Set the arguments for the guest disks
        for disk in [("-" + k, v) for k,v in self.__disks.items()]:
            launch_args += (list(disk)) 

        # Set the guest's memory size, if set
        try:
            launch_args += (['-m', str(self.memory)])
        except AttributeError: pass

        # Set the guest's keyboard layout, if specified
        try:
            launch_args += (['-k', self.keyboard])
        except AttributeError: pass

        # Enable snapshot mode, if set
        try:
            if self.snapshot:
                launch_args.append('-snapshot')
        except AttributeError: pass

        # Use non-default boot order, if set
        try:
            launch_args += (['-boot', self.boot])
        except AttributeError: pass

        # Set the arguments for the guest NICs
        # FIXME: Need to find a more elegant way to do this
        if len(self.__nics) == 0:
            launch_args += ['-net', 'none']
        for nic in self.__nics.values():
            launch_args.extend(['-net', ",".join(['nic'] + ["%s=%s" % (k,v) for k,v in nic.items() if k in ['model', 'macaddr', 'vlan']])])
            if nic['type'] == 'user':
                launch_args.extend(['-net', ",".join(['user'] + ["%s=%s" % (k,v) for k,v in nic.items() if k in ['hostname', 'vlan']])])
            elif nic['type'] == 'tap':
                launch_args.extend(['-net', ",".join(['tap'] + ["%s=%s" % (k,v) for k,v in nic.items() if k in ['ifname', 'script', 'vlan']])])
            elif nic['type'] == 'socket':
                launch_args.extend(['-net', ",".join(['socket'] + ["%s=%s" % (k,v) for k,v in nic.items() if k in ['listen', 'connect', 'mcast', 'vlan']])])
        # Enable VNC server, if set
        try:
            launch_args.extend(['-vnc', str(self.vnc)])
        except AttributeError: pass
            
        # Add the argument for the monitor pty
        launch_args.extend(['-monitor', 'pty'])

        # Add the argument for the pidfile
        launch_args.extend(['-pidfile', os.path.join(self.directory, self.PIDFILE_NAME)])
        
        # return the argument list
        self.__logger.debug("%s launch arguments: %s" % (self.BINARY_NAME, str(launch_args)))
        return launch_args

    def __get_resume_args(self):
        """Creates the argument string used by the qemu/kvm binary
        (including the -loadvm option for loading a saved virtual
        machine state at startup)."""
        resume_args = self.launch_args
        try:
            resume_args += ['-loadvm', self.vmstate]
        except AttributeError: pass

        return resume_args

    def __get_monitor_tty(self):
        """Retrieves the pseudo-terminal used by the qemu/kvm monitor."""
        if os.path.exists(self.monitor_symlink):
            tty = open(self.monitor_symlink, 'w')
            if not tty.isatty():
                raise InvalidPtyException("%s is not a tty!" % tty.name)
            return tty
        else:
            raise InvalidPtyException("%s does not exist!" % self.monitor_symlink)

    def __set_vnc(self, vnc):
        self.__vnc = int(vnc)
        self.__logger.debug("Set VNC to display %i." % self.__vnc)

    def __get_vnc(self):
        return self.__vnc

    def __is_running(self):
        """Determines whether the VM is running."""
        self.__logger.debug("Checking status of VM '%s'." % self.name)
        pidfile = self.pidfile
        if os.path.exists(pidfile):
            # there is a pid file, find out if it's stale
            pid = int(open(pidfile, 'r').readline().strip())
            try:
                # old UNIX way of sending a process the signal 0
                os.kill(pid, 0)
                self.__logger.debug("Process %i is still running." % pid)
                running = True
            except OSError:
                # this fires if the process wasn't running
                os.remove(pidfile)
                self.__logger.info("Removed stale pidfile %s." % pidfile)
                running = False
        else:
            # No pidfile found, assume VM is not running
            self.__logger.debug("'%s' appears not to be running, pidfile %s not found." % (self.name, pidfile))
            running = False
        return running

    def load_config(self, config_file):
        """Parses the configuration file, and configures the instance accordingly."""
        config = RawConfigParser()
        self.__logger.debug("Reading configuration options from %s" % config_file)

        if hasattr(config_file, 'read') and callable(config_file.read):
            # This looks like a file-like object
            config.readfp(config_file)
        else:
            # OK, it's a string instead
            config.read(config_file)

        # Set instance attributes from configuration items in the [global] section
        for key, value in config.items('global'):
            setattr(self, key, value)

        # Configure guest disks from configuration items in the [disks] section
        for guestdisk, hostdisk in config.items('disks'):
            setattr(self, guestdisk, hostdisk)

        # Configure guest network interfaces from configuration items in the [net*] sections
        for name, section in  [(section.lstrip('net_'), section)  for section in config.sections() if section.startswith('net')]:
            self.add_nic(name, config.items(section))

    def __reallaunch(self, args):
        """Launches a new virtual machine, ignoring any saved machine state."""

        # Don't launch a VM that's already running
        if self.running:
            raise VMRunningException("Attempted to launch VM '%s' that is already running with pid %i." % (self.name, self.pid))

        # Check whether the state directory exists, and create it otherwise
        if not os.path.isdir(self.directory):
            os.mkdir(self.directory)
            self.__logger.info("Created directory %s." % self.directory)

        launch_args = self.launch_args
        # The launch_args contain the -pidfile argument, hence Popen must
        # be invoked after the creation of the directory
        self.__logger.info("Launching %s with these args: '%s'" % (self.BINARY_NAME, " ".join(args)))

        # Create a buffer for holding kvm's "char device redirected" output
        pipe = subprocess.Popen([self.BINARY_NAME] + args, stderr=subprocess.PIPE)

        line = pipe.stderr.readline().strip()
        self.__logger.debug("%s said: '%s'" % (self.BINARY_NAME, line))

        # FIXME this is ugly ugly ugly. But qemu/kvm seems not to
        # indicate in any other way just what pty it is using for
        # monitor I/O... improvements welcome.
        if not line.startswith("char device redirected to "):
            # uh-oh
            raise UnexpectedOutputException("Unexpected output from %s: '%s'" % (self.BINARY_NAME, line))
        chardev = line.lstrip("char device redirected to ")

        # Create a symlink to the monitor device
        symlink = self.monitor_symlink
        if os.path.exists(symlink):
            if os.path.islink(symlink):
                os.remove(symlink)
                self.__logger.info("Removed stale symlink %s." % symlink)
            else:
                raise InvalidPtyException("%s exists, but is not a symlink. Cowardly refusing to continue." % symlink)
        os.symlink(chardev, symlink)
        self.__logger.debug("Created symlink %s to %s." % (symlink, chardev))        


    ## Methods invoked from the command-line interface ##

    def launch(self):
        self.__reallaunch(self.launch_args)
        self.__logger.info("Launched VM %s, running as pid %i." % (self.name, self.pid))

    def resume(self):
        """Resumes a stopped virtual machine, by reloading a saved
        virtual machine state on startup."""
        self.__reallaunch(self.resume_args)
        self.__logger.info("Resumed VM %s, machine state restored from %s, running as pid %i." % (self.name, self.vmstate, self.pid))

    def start(self):
        """Starts a virtual machine, restoring the VM's state if so
        configured."""
        if self.vmstate:
            self.resume()
        else:
            self.launch()

    def save(self):
        """Saves the virtual machine's state."""
        monitor = self.__get_monitor_tty()
        monitor.write("savevm %s\n" % self.vmstate)
        monitor.flush()
        self.__logger.info("Saved VM state to %s." % self.vmstate)

    def suspend(self):
        """Saves the virtual machine's state, then stops the virtual machine.
        The difference between this method and stop() is that this
        method will raise an exception if it is invoked on an instance
        where no VM state disk (or image) is configured.
        """
        self.save()
        self.kill()
        
    def stop(self):
        """Saves the virtual machine's state, then stops the virtual machine.
        The difference between this method and is that this method will
        only attempt to save the machine's state if the instance is
        configured for that, and simply stop the virtual machine otherwise.
        """
        if self.vmstate:
            self.save()
        self.kill()

    def kill(self):
        """Stops the virtual machine, not saving the machine's
        state. This method will attempt to stop the machine by issuing
        a"quit" command on the monitor
        interface. When that fails, it sends a SIGTERM signal to the
        process. If that to results in an error, an exception is
        raised."""
        try:
            monitor = self.__get_monitor_tty()
            # Stop the VM
            monitor.write("quit\n")
            monitor.flush()
            # FIXME yet another ugly hack. Is there a better way to
            # wait until a VM is really stopped?
            while self.running:
                time.sleep(1)
            self.__logger.info("Shut down VM %s." % self.name)
            monitor.close()
        except:
            # Something went wrong, kill the old-fashioned way
            self.__logger.warn("Failed to shut down VM %s via the monitor interface." % self.name)
            os.kill(self.pid, signal.SIGTERM)
            self.__logger.info("Sent termination signal (SIGTERM) to process %i." % self.pid)
        # Now we should remove the symlink
        self.clean()
        
    def clean(self):
        if self.running:
            raise VMRunningException("Can't remove files used by a running virtual machine.")
        else:
            os.remove(self.monitor_symlink)
            self.__logger.debug("Removed monitor symlink %s." % self.monitor_symlink)
            
    def status(self):
        """Returns the virtual machine's status in a human-readable manner."""
        if self.running:
            print ("VM '%s' is running with pid %i." % (self.name, self.pid))
        else:
            print ("VM '%s' is stopped." % (self.name))

    def heartbeat_status(self):
        """Returns the virtual machine's status in a manner suitable
        for Heartbeat resource scripts."""
        if self.running:
            print "running"
        else:
            print "stopped"

    hb_status = heartbeat_status

    ## Property definitions ##
    boot = property(__get_boot, __set_boot)
    cdrom = property(__get_cdrom, __set_cdrom)
    directory = property(__get_directory, __set_directory)
    fda = property(__get_fda, __set_fda)
    fdb = property(__get_fdb, __set_fdb)
    floppy = fda
    hda = property(__get_hda, __set_hda)
    hdb = property(__get_hdb, __set_hdb)
    hdc = property(__get_hdc, __set_hdc)
    hdd = property(__get_hdd, __set_hdd)
    keyboard = property(__get_keyboard, __set_keyboard)
    launch_args = property(__get_launch_args)
    memory = property(__get_memory, __set_memory)
    monitor_symlink = property(__get_monitor_symlink)
    name = property(__get_name, __set_name)
    pid = property(__get_pid)
    pidfile = property(__get_pidfile)
    resume_args = property(__get_resume_args)
    running = property(__is_running)
    snapshot = property(__get_snapshot, __set_snapshot)
    vmstate = property(__get_vmstate_disk, __set_vmstate_disk)
    vnc = property(__get_vnc, __set_vnc)


### Implementation for KVM ###
    
class KVMConfig(VMConfig):
    """An object representing a KVM virtual machine configuration."""

    STATE_DIR = "/var/run/kvm"
    PIDFILE_NAME = "kvm.pid"
    BINARY_NAME = 'kvm'


### Implementation for Qemu ###

class QemuConfig(VMConfig):
    """An object representing a Qemu virtual machine configuration."""

    STATE_DIR = "/var/run/qemu"
    PIDFILE_NAME = "qemu.pid"
    BINARY_NAME = 'qemu'


### Module-level convenience functions ###

def parse_options(argv=sys.argv):
    parser = OptionParser()

    parser.add_option("-f",
                      "--file",
                      dest="config_file",
                      metavar="FILE",
                      help="Read configuration from FILE",
                      action="store")
    parser.add_option("-q",
                      "--quiet",
                      dest="loglevel",
                      action="store_const",
                      help="Suppress most output",
                      const=logging.ERROR)
    parser.add_option("-v",
                      "--verbose",
                      dest="loglevel",
                      action="store_const",
                      help="Enable verbose output",
                      const=logging.INFO)
    parser.add_option("--debug",
                      dest="loglevel",
                      action="store_const",
                      help="Enable debug output",
                      const=logging.DEBUG)
    parser.add_option("--syslog",
                      dest="syslog",
                      action="store_true",
                      help="Log to syslog instead of console")
    parser.add_option("--help-commands",
                      dest="help_commands",
                      action="store_true",
                      help="List supported commands")

    parser.set_default("loglevel",
                       logging.WARN)
    parser.set_default("syslog",
                       False)
    parser.set_default("help_commands",
                       False)

    parser.set_usage("[options] -f FILE COMMAND")

    (opts, args) = parser.parse_args(argv)
    return (opts, args)


def setup_logging(loglevel=logging.WARN, use_syslog=False):
    # Set up logging (to stderr by default,
    # or to syslog if specified)
    root_logger = logging.getLogger('')
    stderr = logging.StreamHandler(sys.stderr)
    if use_syslog:
        # Set up a syslog handler
        syslog = logging.handlers.SysLogHandler("/dev/log")
        syslog.setFormatter(logging.Formatter('%(name)s: %(levelname)s %(message)s'))
        root_logger.addHandler(syslog)
        # Only log errors and above to both syslog and stderr
        stderr.setFormatter(logging.Formatter('%(message)s'))
        stderr.setLevel(logging.ERROR)
    else:
        # Log to stderr as specified 
        stderr.setFormatter(logging.Formatter('%(name)s %(levelname)s %(message)s'))
        stderr.setLevel(loglevel)
    root_logger.addHandler(stderr)
    root_logger.setLevel(loglevel)


def help_commands():
    print "Supported commands (most users will only ever need 'start', 'stop', and 'status'): \n"
    for cmd in VMConfig.COMMANDS:
        attr = getattr(VMConfig, cmd)
        if attr.__doc__:
            doc = attr.__doc__.split(".")[0]
            print "%s\t%s" % (cmd, doc)

### Application entry point ###

def main(argv=sys.argv):
    opts, args = parse_options(argv)
    setup_logging(opts.loglevel, opts.syslog)

    if opts.help_commands:
        help_commands()
    else:
        if os.path.basename(argv[0]) in ['kvmlaunch', 'kvmlaunch.py']:
            vmconfig = KVMConfig(config=opts.config_file)
        else:
            vmconfig = QemuConfig(config=opts.config_file)
        if args[1] in vmconfig.COMMANDS: # command sanity check
            getattr(vmconfig, args[1])()
        else:
            raise UnsupportedCommandException("Unsupported command: '%s'." % args[1])

if __name__ == "__main__":
    try:
        main()
    except Exception, e:
        logging.critical(str(e))

# Global settings
[global]
name = example
memory = 256
boot = disk,cdrom,floppy
keyboard = de
vnc = 42

# Disks (may be either disk images or real block devices)
[disks]
hda = /tmp/linux-0.2.img
#floppy = /dev/fd0
#cdrom = /tmp/somecdimage.iso
vmstate = /tmp/

# Network configuration for network device 0
[net_tap]
type = tap
vlan = 0
ifname = tap0
script = /etc/kvm/kvm-ifup

#[net_user]
#type = user
#vlan = 1

--- End Message ---
--- Begin Message ---
On Fri, Nov 28, 2008 at 09:20:26PM +0100, Florian Haas wrote:
> Oh c'mon, _now_ someone's looking at this? :-) Ditch the patch and
> forget about it. libvirt is the way to go.
First time I had a look at the qemu package, sorry for the late reply.
I'll close the bug then, thanks for the feedback.
Cheers,
 -- Guido


--- End Message ---

Reply via email to