--- 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 ---