Commit eb58f9b1 authored by Guido Trotter's avatar Guido Trotter

Add KVM hypervisor code contains the code for ganeti to work under kvm.
This patch also modifies to ship that file, and
lib/hypervisor/ to import it, and add kvm to the
hypervisors map.

Reviewed-by: imsnah
parent 550e49b9
......@@ -89,6 +89,7 @@ hypervisor_PYTHON = \
lib/hypervisor/ \
lib/hypervisor/ \
lib/hypervisor/ \
lib/hypervisor/ \
rapi_PYTHON = \
......@@ -29,12 +29,14 @@ from ganeti import errors
from ganeti.hypervisor import hv_fake
from ganeti.hypervisor import hv_xen
from ganeti.hypervisor import hv_kvm
constants.HT_XEN_PVM30: hv_xen.XenPvmHypervisor,
constants.HT_XEN_HVM31: hv_xen.XenHvmHypervisor,
constants.HT_FAKE: hv_fake.FakeHypervisor,
constants.HT_KVM: hv_kvm.KVMHypervisor,
# Copyright (C) 2008 Google Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# General Public License for more details.
# 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., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""KVM hypervisor
import os
import os.path
import re
import tempfile
from cStringIO import StringIO
from ganeti import utils
from ganeti import constants
from ganeti import errors
from ganeti.hypervisor import hv_base
class KVMHypervisor(hv_base.BaseHypervisor):
"""Fake hypervisor interface.
This can be used for testing the ganeti code without having to have
a real virtualisation software installed.
_ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
_PIDS_DIR = _ROOT_DIR + "/pid"
_CTRL_DIR = _ROOT_DIR + "/ctrl"
def __init__(self):
# Let's make sure the directories we need exist, even if the RUN_DIR lives
# in a tmpfs filesystem or has been otherwise wiped out.
for dir in self._DIRS:
if not os.path.exists(dir):
def _WriteNetScript(self, instance, seq, nic):
"""Write a script to connect a net interface to the proper bridge.
This can be used by any qemu-type hypervisor.
@param instance: instance we're acting on
@type instance: instance object
@param seq: nic sequence number
@type seq: int
@param nic: nic we're acting on
@type nic: nic object
@return: netscript file name
@rtype: string
script = StringIO()
script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
script.write("export INSTANCE=%s\n" %
script.write("export MAC=%s\n" % nic.mac)
script.write("export IP=%s\n" % nic.ip)
script.write("export BRIDGE=%s\n" % nic.bridge)
script.write("export INTERFACE=$1\n")
# TODO: make this configurable at ./configure time
script.write("if [ -x /etc/ganeti/kvm-vif-bridge ]; then\n")
script.write(" # Execute the user-specific vif file\n")
script.write(" /etc/ganeti/kvm-vif-bridge\n")
script.write(" # Connect the interface to the bridge\n")
script.write(" /sbin/ifconfig $INTERFACE up\n")
script.write(" /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
# As much as we'd like to put this in our _ROOT_DIR, that will happen to be
# mounted noexec sometimes, so we'll have to find another place.
(tmpfd, tmpfile_name) = tempfile.mkstemp()
tmpfile = os.fdopen(tmpfd, 'w')
os.chmod(tmpfile_name, 0755)
return tmpfile_name
def ListInstances(self):
"""Get the list of running instances.
We can do this by listing our live instances directory and checking whether
the associated kvm process is still alive.
result = []
for name in os.listdir(self._PIDS_DIR):
file = "%s/%s" % (self._PIDS_DIR, name)
if utils.IsProcessAlive(utils.ReadPidFile(file)):
return result
def GetInstanceInfo(self, instance_name):
"""Get instance properties.
instance_name: the instance name
(name, id, memory, vcpus, stat, times)
pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
pid = utils.ReadPidFile(pidfile)
if not utils.IsProcessAlive(pid):
return None
cmdline_file = "/proc/%s/cmdline" % pid
fh = open(cmdline_file, 'r')
cmdline =
except IOError, err:
raise errors.HypervisorError("Failed to list instance %s: %s" %
(instance_name, err))
memory = 0
vcpus = 0
stat = "---b-"
times = "0"
arg_list = cmdline.split('\x00')
while arg_list:
arg = arg_list.pop(0)
if arg == '-m':
memory = arg_list.pop(0)
elif arg == '-smp':
vcpus = arg_list.pop(0)
return (instance_name, pid, memory, vcpus, stat, times)
def GetAllInstancesInfo(self):
"""Get properties of all instances.
[(name, id, memory, vcpus, stat, times),...]
data = []
for name in os.listdir(self._PIDS_DIR):
file = "%s/%s" % (self._PIDS_DIR, name)
if utils.IsProcessAlive(utils.ReadPidFile(file)):
return data
def StartInstance(self, instance, block_devices, extra_args):
"""Start an instance.
temp_files = []
pidfile = self._PIDS_DIR + "/%s" %
if utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
raise errors.HypervisorError("Failed to start instance %s: %s" %
(, "already running"))
kvm = constants.KVM_PATH
kvm_cmd = [kvm]
kvm_cmd.extend(['-m', instance.memory])
kvm_cmd.extend(['-smp', instance.vcpus])
kvm_cmd.extend(['-pidfile', pidfile])
# used just by the vnc server, if enabled
if not instance.hvm_acpi:
if not instance.nics:
kvm_cmd.extend(['-net', 'none'])
nic_seq = 0
for nic in instance.nics:
script = self._WriteNetScript(instance, nic_seq, nic)
# FIXME: handle other models
nic_val = "nic,macaddr=%s,model=virtio" % nic.mac
kvm_cmd.extend(['-net', nic_val])
# if os.path.blah(script):
kvm_cmd.extend(['-net', 'tap,script=%s' % script])
nic_seq += 1
boot_drive = True
for cfdev, rldev in block_devices:
# TODO: handle FD_LOOP and FD_BLKTAP (?)
# TODO: handle if= correctly
#drive_val = 'file=%s,format=raw,if=virtio' % rldev.dev_path
if boot_drive:
boot_val = ',boot=on'
boot_val = ''
drive_val = 'file=%s,format=raw,if=virtio' % rldev.dev_path
if boot_drive:
drive_val = '%s,boot=on' % drive_val
boot_drive = False
#drive_val = 'file=%s,if=virtio' % rldev.dev_path
kvm_cmd.extend(['-drive', drive_val])
#flagname = cfdev.iv_name.replace('s', 'h', 1)
#kvm_cmd.extend(['-%s' % flagname, drive_val])
# kernel handling
if instance.kernel_path in (None, constants.VALUE_DEFAULT):
kpath = constants.XEN_KERNEL # FIXME: other name??
if not os.path.exists(instance.kernel_path):
raise errors.HypervisorError("The kernel %s for instance %s is"
" missing" % (instance.kernel_path,
kpath = instance.kernel_path
kvm_cmd.extend(['-kernel', kpath])
# initrd handling
if instance.initrd_path in (None, constants.VALUE_DEFAULT):
if os.path.exists(constants.XEN_INITRD):
initrd_path = constants.XEN_INITRD
initrd_path = None
elif instance.initrd_path == constants.VALUE_NONE:
initrd_path = None
if not os.path.exists(instance.initrd_path):
raise errors.HypervisorError("The initrd %s for instance %s is"
" missing" % (instance.initrd_path,
initrd_path = instance.initrd_path
if initrd_path:
kvm_cmd.extend(['-initrd', initrd_path])
kvm_cmd.extend(['-append', 'console=ttyS0,38400 root=/dev/vda'])
# FIXME: handle vnc, if needed
# How do we decide whether to have it or not?? :(
base_control = '%s/%s' % (self._CTRL_DIR,
monitor_dev = 'unix:%s.monitor,server,nowait' % base_control
kvm_cmd.extend(['-monitor', monitor_dev])
serial_dev = 'unix:%s.serial,server,nowait' % base_control
kvm_cmd.extend(['-serial', serial_dev])
result = utils.RunCmd(kvm_cmd)
if result.failed:
raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
(, result.fail_reason,
if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
raise errors.HypervisorError("Failed to start instance %s: %s" %
for file in temp_files:
def StopInstance(self, instance, force=False):
"""Stop an instance.
pid_file = self._PIDS_DIR + "/%s" %
pid = utils.ReadPidFile(pid_file)
if pid > 0 and utils.IsProcessAlive(pid):
if force or not instance.hvm_acpi:
# This only works if the instance os has acpi support
monitor_socket = '%s/%s.monitor' % (self._CTRL_DIR,
socat = 'socat -u STDIN UNIX-CONNECT:%s' % monitor_socket
command = "echo 'system_powerdown' | %s" % socat
result = utils.RunCmd(command)
if result.failed:
raise errors.HypervisorError("Failed to stop instance %s: %s" %
(, result.fail_reason))
if not utils.IsProcessAlive(pid):
def RebootInstance(self, instance):
"""Reboot an instance.
# For some reason if we do a 'send-key ctrl-alt-delete' to the control
# socket the instance will stop, but now power up again. So we'll resort
# to shutdown and restart.
def GetNodeInfo(self):
"""Return information about the node.
The return value is a dict, which has to have the following items:
(all values in MiB)
- memory_total: the total memory size on the node
- memory_free: the available memory on the node for instances
- memory_dom0: the memory used by the node itself, if available
# global ram usage from the xm info command
# memory : 3583
# free_memory : 747
# note: in xen 3, memory has changed to total_memory
fh = file("/proc/meminfo")
data = fh.readlines()
except IOError, err:
raise errors.HypervisorError("Failed to list node info: %s" % err)
result = {}
sum_free = 0
for line in data:
splitfields = line.split(":", 1)
if len(splitfields) > 1:
key = splitfields[0].strip()
val = splitfields[1].strip()
if key == 'MemTotal':
result['memory_total'] = int(val.split()[0])/1024
elif key in ('MemFree', 'Buffers', 'Cached'):
sum_free += int(val.split()[0])/1024
elif key == 'Active':
result['memory_dom0'] = int(val.split()[0])/1024
result['memory_free'] = sum_free
cpu_total = 0
fh = open("/proc/cpuinfo")
cpu_total = len(re.findall("(?m)^processor\s*:\s*[0-9]+\s*$",
except EnvironmentError, err:
raise errors.HypervisorError("Failed to list node info: %s" % err)
result['cpu_total'] = cpu_total
return result
def GetShellCommandForConsole(instance):
"""Return a command for connecting to the console of an instance.
# TODO: we can either try the serial socket or suggest vnc
return "echo Console not available for the kvm hypervisor yet"
def Verify(self):
"""Verify the hypervisor.
Check that the binary exists.
if not os.path.exists(constants.KVM_PATH):
return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment