Package ganeti :: Package hypervisor :: Module hv_kvm
[hide private]
[frames] | no frames]

Source Code for Module ganeti.hypervisor.hv_kvm

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2008, 2009, 2010 Google Inc. 
   5  # 
   6  # This program is free software; you can redistribute it and/or modify 
   7  # it under the terms of the GNU General Public License as published by 
   8  # the Free Software Foundation; either version 2 of the License, or 
   9  # (at your option) any later version. 
  10  # 
  11  # This program is distributed in the hope that it will be useful, but 
  12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  14  # General Public License for more details. 
  15  # 
  16  # You should have received a copy of the GNU General Public License 
  17  # along with this program; if not, write to the Free Software 
  18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
  19  # 02110-1301, USA. 
  20   
  21   
  22  """KVM hypervisor 
  23   
  24  """ 
  25   
  26  import errno 
  27  import os 
  28  import os.path 
  29  import re 
  30  import tempfile 
  31  import time 
  32  import logging 
  33  import pwd 
  34  from cStringIO import StringIO 
  35   
  36  from ganeti import utils 
  37  from ganeti import constants 
  38  from ganeti import errors 
  39  from ganeti import serializer 
  40  from ganeti import objects 
  41  from ganeti import uidpool 
  42  from ganeti import ssconf 
  43  from ganeti.hypervisor import hv_base 
  44  from ganeti import netutils 
  45   
  46   
  47  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge" 
48 49 50 -def _WriteNetScript(instance, nic, index):
51 """Write a script to connect a net interface to the proper bridge. 52 53 This can be used by any qemu-type hypervisor. 54 55 @type instance: L{objects.Instance} 56 @param instance: Instance object 57 @type nic: L{objects.NIC} 58 @param nic: NIC object 59 @type index: int 60 @param index: NIC index 61 @return: Script 62 @rtype: string 63 64 """ 65 if instance.tags: 66 tags = " ".join(instance.tags) 67 else: 68 tags = "" 69 70 buf = StringIO() 71 sw = utils.ShellWriter(buf) 72 sw.Write("#!/bin/sh") 73 sw.Write("# this is autogenerated by Ganeti, please do not edit") 74 sw.Write("export PATH=$PATH:/sbin:/usr/sbin") 75 sw.Write("export INSTANCE=%s", utils.ShellQuote(instance.name)) 76 sw.Write("export MAC=%s", utils.ShellQuote(nic.mac)) 77 sw.Write("export MODE=%s", 78 utils.ShellQuote(nic.nicparams[constants.NIC_MODE])) 79 sw.Write("export INTERFACE=\"$1\"") 80 sw.Write("export TAGS=%s", utils.ShellQuote(tags)) 81 82 if nic.ip: 83 sw.Write("export IP=%s", utils.ShellQuote(nic.ip)) 84 85 if nic.nicparams[constants.NIC_LINK]: 86 sw.Write("export LINK=%s", 87 utils.ShellQuote(nic.nicparams[constants.NIC_LINK])) 88 89 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 90 sw.Write("export BRIDGE=%s", 91 utils.ShellQuote(nic.nicparams[constants.NIC_LINK])) 92 93 # TODO: make this configurable at ./configure time 94 sw.Write("if [ -x %s ]; then", utils.ShellQuote(_KVM_NETWORK_SCRIPT)) 95 sw.IncIndent() 96 try: 97 sw.Write("# Execute the user-specific vif file") 98 sw.Write(_KVM_NETWORK_SCRIPT) 99 finally: 100 sw.DecIndent() 101 sw.Write("else") 102 sw.IncIndent() 103 try: 104 sw.Write("ifconfig $INTERFACE 0.0.0.0 up") 105 106 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 107 sw.Write("# Connect the interface to the bridge") 108 sw.Write("brctl addif $BRIDGE $INTERFACE") 109 110 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED: 111 if not nic.ip: 112 raise errors.HypervisorError("nic/%d is routed, but has no IP" 113 " address" % index) 114 115 sw.Write("# Route traffic targeted at the IP to the interface") 116 if nic.nicparams[constants.NIC_LINK]: 117 sw.Write("while ip rule del dev $INTERFACE; do :; done") 118 sw.Write("ip rule add dev $INTERFACE table $LINK") 119 sw.Write("ip route replace $IP table $LINK proto static" 120 " dev $INTERFACE") 121 else: 122 sw.Write("ip route replace $IP proto static dev $INTERFACE") 123 124 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE" 125 sw.Write(" if [ -d %s ]; then", interface_v4_conf) 126 sw.IncIndent() 127 try: 128 sw.Write("echo 1 > %s/proxy_arp", interface_v4_conf) 129 sw.Write("echo 1 > %s/forwarding", interface_v4_conf) 130 finally: 131 sw.DecIndent() 132 sw.Write("fi") 133 134 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE" 135 sw.Write("if [ -d %s ]; then", interface_v6_conf) 136 sw.IncIndent() 137 try: 138 sw.Write("echo 1 > %s/proxy_ndp", interface_v6_conf) 139 sw.Write("echo 1 > %s/forwarding", interface_v6_conf) 140 finally: 141 sw.DecIndent() 142 sw.Write("fi") 143 finally: 144 sw.DecIndent() 145 sw.Write("fi") 146 147 return buf.getvalue()
148
149 150 -class KVMHypervisor(hv_base.BaseHypervisor):
151 """KVM hypervisor interface""" 152 CAN_MIGRATE = True 153 154 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor" 155 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids 156 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids 157 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets 158 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data 159 # KVM instances with chroot enabled are started in empty chroot directories. 160 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories 161 # After an instance is stopped, its chroot directory is removed. 162 # If the chroot directory is not empty, it can't be removed. 163 # A non-empty chroot directory indicates a possible security incident. 164 # To support forensics, the non-empty chroot directory is quarantined in 165 # a separate directory, called 'chroot-quarantine'. 166 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine" 167 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, 168 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR] 169 170 PARAMETERS = { 171 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK, 172 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK, 173 constants.HV_ROOT_PATH: hv_base.NO_CHECK, 174 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK, 175 constants.HV_ACPI: hv_base.NO_CHECK, 176 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK, 177 constants.HV_VNC_BIND_ADDRESS: 178 (False, lambda x: (netutils.IsValidIP4(x) or utils.IsNormAbsPath(x)), 179 "the VNC bind address must be either a valid IP address or an absolute" 180 " pathname", None, None), 181 constants.HV_VNC_TLS: hv_base.NO_CHECK, 182 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK, 183 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK, 184 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK, 185 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 186 constants.HV_BOOT_ORDER: 187 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES), 188 constants.HV_NIC_TYPE: 189 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES), 190 constants.HV_DISK_TYPE: 191 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES), 192 constants.HV_USB_MOUSE: 193 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES), 194 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK, 195 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK, 196 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK, 197 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 198 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK, 199 constants.HV_DISK_CACHE: 200 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES), 201 constants.HV_SECURITY_MODEL: 202 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES), 203 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK, 204 constants.HV_KVM_FLAG: 205 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES), 206 constants.HV_VHOST_NET: hv_base.NO_CHECK, 207 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK, 208 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK, 209 } 210 211 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)', 212 re.M | re.I) 213 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5 214 _MIGRATION_INFO_RETRY_DELAY = 2 215 216 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b") 217 218 ANCILLARY_FILES = [ 219 _KVM_NETWORK_SCRIPT, 220 ] 221
222 - def __init__(self):
223 hv_base.BaseHypervisor.__init__(self) 224 # Let's make sure the directories we need exist, even if the RUN_DIR lives 225 # in a tmpfs filesystem or has been otherwise wiped out. 226 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS] 227 utils.EnsureDirs(dirs)
228 229 @classmethod
230 - def _InstancePidFile(cls, instance_name):
231 """Returns the instance pidfile. 232 233 """ 234 return utils.PathJoin(cls._PIDS_DIR, instance_name)
235 236 @classmethod
237 - def _InstanceUidFile(cls, instance_name):
238 """Returns the instance uidfile. 239 240 """ 241 return utils.PathJoin(cls._UIDS_DIR, instance_name)
242 243 @classmethod
244 - def _InstancePidInfo(cls, pid):
245 """Check pid file for instance information. 246 247 Check that a pid file is associated with an instance, and retrieve 248 information from its command line. 249 250 @type pid: string or int 251 @param pid: process id of the instance to check 252 @rtype: tuple 253 @return: (instance_name, memory, vcpus) 254 @raise errors.HypervisorError: when an instance cannot be found 255 256 """ 257 alive = utils.IsProcessAlive(pid) 258 if not alive: 259 raise errors.HypervisorError("Cannot get info for pid %s" % pid) 260 261 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline") 262 try: 263 cmdline = utils.ReadFile(cmdline_file) 264 except EnvironmentError, err: 265 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" % 266 (pid, err)) 267 268 instance = None 269 memory = 0 270 vcpus = 0 271 272 arg_list = cmdline.split('\x00') 273 while arg_list: 274 arg = arg_list.pop(0) 275 if arg == "-name": 276 instance = arg_list.pop(0) 277 elif arg == "-m": 278 memory = int(arg_list.pop(0)) 279 elif arg == "-smp": 280 vcpus = int(arg_list.pop(0)) 281 282 if instance is None: 283 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm" 284 " instance" % pid) 285 286 return (instance, memory, vcpus)
287
288 - def _InstancePidAlive(self, instance_name):
289 """Returns the instance pidfile, pid, and liveness. 290 291 @type instance_name: string 292 @param instance_name: instance name 293 @rtype: tuple 294 @return: (pid file name, pid, liveness) 295 296 """ 297 pidfile = self._InstancePidFile(instance_name) 298 pid = utils.ReadPidFile(pidfile) 299 300 alive = False 301 try: 302 cmd_instance = self._InstancePidInfo(pid)[0] 303 alive = (cmd_instance == instance_name) 304 except errors.HypervisorError: 305 pass 306 307 return (pidfile, pid, alive)
308
309 - def _CheckDown(self, instance_name):
310 """Raises an error unless the given instance is down. 311 312 """ 313 alive = self._InstancePidAlive(instance_name)[2] 314 if alive: 315 raise errors.HypervisorError("Failed to start instance %s: %s" % 316 (instance_name, "already running"))
317 318 @classmethod
319 - def _InstanceMonitor(cls, instance_name):
320 """Returns the instance monitor socket name 321 322 """ 323 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
324 325 @classmethod
326 - def _InstanceSerial(cls, instance_name):
327 """Returns the instance serial socket name 328 329 """ 330 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
331 332 @staticmethod
334 """Returns the correct parameters for socat 335 336 If we have a new-enough socat we can use raw mode with an escape character. 337 338 """ 339 if constants.SOCAT_USE_ESCAPE: 340 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE 341 else: 342 return "echo=0,icanon=0"
343 344 @classmethod
345 - def _InstanceKVMRuntime(cls, instance_name):
346 """Returns the instance KVM runtime filename 347 348 """ 349 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
350 351 @classmethod
352 - def _InstanceChrootDir(cls, instance_name):
353 """Returns the name of the KVM chroot dir of the instance 354 355 """ 356 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
357 358 @classmethod
359 - def _TryReadUidFile(cls, uid_file):
360 """Try to read a uid file 361 362 """ 363 if os.path.exists(uid_file): 364 try: 365 uid = int(utils.ReadOneLineFile(uid_file)) 366 return uid 367 except EnvironmentError: 368 logging.warning("Can't read uid file", exc_info=True) 369 except (TypeError, ValueError): 370 logging.warning("Can't parse uid file contents", exc_info=True) 371 return None
372 373 @classmethod
374 - def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
375 """Removes an instance's rutime sockets/files/dirs. 376 377 """ 378 utils.RemoveFile(pidfile) 379 utils.RemoveFile(cls._InstanceMonitor(instance_name)) 380 utils.RemoveFile(cls._InstanceSerial(instance_name)) 381 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name)) 382 uid_file = cls._InstanceUidFile(instance_name) 383 uid = cls._TryReadUidFile(uid_file) 384 utils.RemoveFile(uid_file) 385 if uid is not None: 386 uidpool.ReleaseUid(uid) 387 try: 388 chroot_dir = cls._InstanceChrootDir(instance_name) 389 utils.RemoveDir(chroot_dir) 390 except OSError, err: 391 if err.errno == errno.ENOTEMPTY: 392 # The chroot directory is expected to be empty, but it isn't. 393 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR, 394 prefix="%s-%s-" % 395 (instance_name, 396 utils.TimestampForFilename())) 397 logging.warning("The chroot directory of instance %s can not be" 398 " removed as it is not empty. Moving it to the" 399 " quarantine instead. Please investigate the" 400 " contents (%s) and clean up manually", 401 instance_name, new_chroot_dir) 402 utils.RenameFile(chroot_dir, new_chroot_dir) 403 else: 404 raise
405 406 @staticmethod
407 - def _WriteNetScriptFile(instance, seq, nic):
408 """Write a script to connect a net interface to the proper bridge. 409 410 This can be used by any qemu-type hypervisor. 411 412 @param instance: instance we're acting on 413 @type instance: instance object 414 @param seq: nic sequence number 415 @type seq: int 416 @param nic: nic we're acting on 417 @type nic: nic object 418 @return: netscript file name 419 @rtype: string 420 421 """ 422 script = _WriteNetScript(instance, nic, seq) 423 424 # As much as we'd like to put this in our _ROOT_DIR, that will happen to be 425 # mounted noexec sometimes, so we'll have to find another place. 426 (tmpfd, tmpfile_name) = tempfile.mkstemp() 427 tmpfile = os.fdopen(tmpfd, 'w') 428 try: 429 tmpfile.write(script) 430 finally: 431 tmpfile.close() 432 os.chmod(tmpfile_name, 0755) 433 return tmpfile_name
434
435 - def ListInstances(self):
436 """Get the list of running instances. 437 438 We can do this by listing our live instances directory and 439 checking whether the associated kvm process is still alive. 440 441 """ 442 result = [] 443 for name in os.listdir(self._PIDS_DIR): 444 if self._InstancePidAlive(name)[2]: 445 result.append(name) 446 return result
447
448 - def GetInstanceInfo(self, instance_name):
449 """Get instance properties. 450 451 @type instance_name: string 452 @param instance_name: the instance name 453 @rtype: tuple of strings 454 @return: (name, id, memory, vcpus, stat, times) 455 456 """ 457 _, pid, alive = self._InstancePidAlive(instance_name) 458 if not alive: 459 return None 460 461 _, memory, vcpus = self._InstancePidInfo(pid) 462 stat = "---b-" 463 times = "0" 464 465 return (instance_name, pid, memory, vcpus, stat, times)
466
467 - def GetAllInstancesInfo(self):
468 """Get properties of all instances. 469 470 @return: list of tuples (name, id, memory, vcpus, stat, times) 471 472 """ 473 data = [] 474 for name in os.listdir(self._PIDS_DIR): 475 try: 476 info = self.GetInstanceInfo(name) 477 except errors.HypervisorError: 478 continue 479 if info: 480 data.append(info) 481 return data
482
483 - def _GenerateKVMRuntime(self, instance, block_devices):
484 """Generate KVM information to start an instance. 485 486 """ 487 pidfile = self._InstancePidFile(instance.name) 488 kvm = constants.KVM_PATH 489 kvm_cmd = [kvm] 490 # used just by the vnc server, if enabled 491 kvm_cmd.extend(['-name', instance.name]) 492 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]]) 493 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]]) 494 kvm_cmd.extend(['-pidfile', pidfile]) 495 kvm_cmd.extend(['-daemonize']) 496 if not instance.hvparams[constants.HV_ACPI]: 497 kvm_cmd.extend(['-no-acpi']) 498 499 hvp = instance.hvparams 500 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK 501 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM 502 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK 503 504 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED: 505 kvm_cmd.extend(["-enable-kvm"]) 506 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED: 507 kvm_cmd.extend(["-disable-kvm"]) 508 509 if boot_network: 510 kvm_cmd.extend(['-boot', 'n']) 511 512 disk_type = hvp[constants.HV_DISK_TYPE] 513 if disk_type == constants.HT_DISK_PARAVIRTUAL: 514 if_val = ',if=virtio' 515 else: 516 if_val = ',if=%s' % disk_type 517 # Cache mode 518 disk_cache = hvp[constants.HV_DISK_CACHE] 519 if disk_cache != constants.HT_CACHE_DEFAULT: 520 cache_val = ",cache=%s" % disk_cache 521 else: 522 cache_val = "" 523 for cfdev, dev_path in block_devices: 524 if cfdev.mode != constants.DISK_RDWR: 525 raise errors.HypervisorError("Instance has read-only disks which" 526 " are not supported by KVM") 527 # TODO: handle FD_LOOP and FD_BLKTAP (?) 528 if boot_disk: 529 kvm_cmd.extend(['-boot', 'c']) 530 if disk_type != constants.HT_DISK_IDE: 531 boot_val = ',boot=on' 532 else: 533 boot_val = '' 534 # We only boot from the first disk 535 boot_disk = False 536 else: 537 boot_val = '' 538 539 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val, 540 cache_val) 541 kvm_cmd.extend(['-drive', drive_val]) 542 543 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH] 544 if iso_image: 545 options = ',format=raw,media=cdrom' 546 if boot_cdrom: 547 kvm_cmd.extend(['-boot', 'd']) 548 if disk_type != constants.HT_DISK_IDE: 549 options = '%s,boot=on' % options 550 else: 551 if disk_type == constants.HT_DISK_PARAVIRTUAL: 552 if_val = ',if=virtio' 553 else: 554 if_val = ',if=%s' % disk_type 555 options = '%s%s' % (options, if_val) 556 drive_val = 'file=%s%s' % (iso_image, options) 557 kvm_cmd.extend(['-drive', drive_val]) 558 559 kernel_path = hvp[constants.HV_KERNEL_PATH] 560 if kernel_path: 561 kvm_cmd.extend(['-kernel', kernel_path]) 562 initrd_path = hvp[constants.HV_INITRD_PATH] 563 if initrd_path: 564 kvm_cmd.extend(['-initrd', initrd_path]) 565 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH], 566 hvp[constants.HV_KERNEL_ARGS]] 567 if hvp[constants.HV_SERIAL_CONSOLE]: 568 root_append.append('console=ttyS0,38400') 569 kvm_cmd.extend(['-append', ' '.join(root_append)]) 570 571 mem_path = hvp[constants.HV_MEM_PATH] 572 if mem_path: 573 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"]) 574 575 mouse_type = hvp[constants.HV_USB_MOUSE] 576 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS] 577 578 if mouse_type: 579 kvm_cmd.extend(['-usb']) 580 kvm_cmd.extend(['-usbdevice', mouse_type]) 581 elif vnc_bind_address: 582 kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET]) 583 584 if vnc_bind_address: 585 if netutils.IsValidIP4(vnc_bind_address): 586 if instance.network_port > constants.VNC_BASE_PORT: 587 display = instance.network_port - constants.VNC_BASE_PORT 588 if vnc_bind_address == constants.IP4_ADDRESS_ANY: 589 vnc_arg = ':%d' % (display) 590 else: 591 vnc_arg = '%s:%d' % (vnc_bind_address, display) 592 else: 593 logging.error("Network port is not a valid VNC display (%d < %d)." 594 " Not starting VNC", instance.network_port, 595 constants.VNC_BASE_PORT) 596 vnc_arg = 'none' 597 598 # Only allow tls and other option when not binding to a file, for now. 599 # kvm/qemu gets confused otherwise about the filename to use. 600 vnc_append = '' 601 if hvp[constants.HV_VNC_TLS]: 602 vnc_append = '%s,tls' % vnc_append 603 if hvp[constants.HV_VNC_X509_VERIFY]: 604 vnc_append = '%s,x509verify=%s' % (vnc_append, 605 hvp[constants.HV_VNC_X509]) 606 elif hvp[constants.HV_VNC_X509]: 607 vnc_append = '%s,x509=%s' % (vnc_append, 608 hvp[constants.HV_VNC_X509]) 609 if hvp[constants.HV_VNC_PASSWORD_FILE]: 610 vnc_append = '%s,password' % vnc_append 611 612 vnc_arg = '%s%s' % (vnc_arg, vnc_append) 613 614 else: 615 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name) 616 617 kvm_cmd.extend(['-vnc', vnc_arg]) 618 else: 619 kvm_cmd.extend(['-nographic']) 620 621 monitor_dev = ("unix:%s,server,nowait" % 622 self._InstanceMonitor(instance.name)) 623 kvm_cmd.extend(['-monitor', monitor_dev]) 624 if hvp[constants.HV_SERIAL_CONSOLE]: 625 serial_dev = ('unix:%s,server,nowait' % 626 self._InstanceSerial(instance.name)) 627 kvm_cmd.extend(['-serial', serial_dev]) 628 else: 629 kvm_cmd.extend(['-serial', 'none']) 630 631 if hvp[constants.HV_USE_LOCALTIME]: 632 kvm_cmd.extend(['-localtime']) 633 634 if hvp[constants.HV_KVM_USE_CHROOT]: 635 kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)]) 636 637 # Save the current instance nics, but defer their expansion as parameters, 638 # as we'll need to generate executable temp files for them. 639 kvm_nics = instance.nics 640 hvparams = hvp 641 642 return (kvm_cmd, kvm_nics, hvparams)
643
644 - def _WriteKVMRuntime(self, instance_name, data):
645 """Write an instance's KVM runtime 646 647 """ 648 try: 649 utils.WriteFile(self._InstanceKVMRuntime(instance_name), 650 data=data) 651 except EnvironmentError, err: 652 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
653
654 - def _ReadKVMRuntime(self, instance_name):
655 """Read an instance's KVM runtime 656 657 """ 658 try: 659 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name)) 660 except EnvironmentError, err: 661 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err) 662 return file_content
663
664 - def _SaveKVMRuntime(self, instance, kvm_runtime):
665 """Save an instance's KVM runtime 666 667 """ 668 kvm_cmd, kvm_nics, hvparams = kvm_runtime 669 serialized_nics = [nic.ToDict() for nic in kvm_nics] 670 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams)) 671 self._WriteKVMRuntime(instance.name, serialized_form)
672
673 - def _LoadKVMRuntime(self, instance, serialized_runtime=None):
674 """Load an instance's KVM runtime 675 676 """ 677 if not serialized_runtime: 678 serialized_runtime = self._ReadKVMRuntime(instance.name) 679 loaded_runtime = serializer.Load(serialized_runtime) 680 kvm_cmd, serialized_nics, hvparams = loaded_runtime 681 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics] 682 return (kvm_cmd, kvm_nics, hvparams)
683
684 - def _RunKVMCmd(self, name, kvm_cmd):
685 """Run the KVM cmd and check for errors 686 687 @type name: string 688 @param name: instance name 689 @type kvm_cmd: list of strings 690 @param kvm_cmd: runcmd input for kvm 691 692 """ 693 result = utils.RunCmd(kvm_cmd) 694 if result.failed: 695 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" % 696 (name, result.fail_reason, result.output)) 697 if not self._InstancePidAlive(name)[2]: 698 raise errors.HypervisorError("Failed to start instance %s" % name)
699
700 - def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
701 """Execute a KVM cmd, after completing it with some last minute data 702 703 @type incoming: tuple of strings 704 @param incoming: (target_host_ip, port) 705 706 """ 707 # Small _ExecuteKVMRuntime hv parameters programming howto: 708 # - conf_hvp contains the parameters as configured on ganeti. they might 709 # have changed since the instance started; only use them if the change 710 # won't affect the inside of the instance (which hasn't been rebooted). 711 # - up_hvp contains the parameters as they were when the instance was 712 # started, plus any new parameter which has been added between ganeti 713 # versions: it is paramount that those default to a value which won't 714 # affect the inside of the instance as well. 715 conf_hvp = instance.hvparams 716 name = instance.name 717 self._CheckDown(name) 718 719 temp_files = [] 720 721 kvm_cmd, kvm_nics, up_hvp = kvm_runtime 722 up_hvp = objects.FillDict(conf_hvp, up_hvp) 723 724 # We know it's safe to run as a different user upon migration, so we'll use 725 # the latest conf, from conf_hvp. 726 security_model = conf_hvp[constants.HV_SECURITY_MODEL] 727 if security_model == constants.HT_SM_USER: 728 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]]) 729 730 # We have reasons to believe changing something like the nic driver/type 731 # upon migration won't exactly fly with the instance kernel, so for nic 732 # related parameters we'll use up_hvp 733 if not kvm_nics: 734 kvm_cmd.extend(["-net", "none"]) 735 else: 736 tap_extra = "" 737 nic_type = up_hvp[constants.HV_NIC_TYPE] 738 if nic_type == constants.HT_NIC_PARAVIRTUAL: 739 nic_model = "model=virtio" 740 if up_hvp[constants.HV_VHOST_NET]: 741 tap_extra = ",vhost=on" 742 else: 743 nic_model = "model=%s" % nic_type 744 745 for nic_seq, nic in enumerate(kvm_nics): 746 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model) 747 script = self._WriteNetScriptFile(instance, nic_seq, nic) 748 tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra) 749 kvm_cmd.extend(["-net", nic_val]) 750 kvm_cmd.extend(["-net", tap_val]) 751 temp_files.append(script) 752 753 if incoming: 754 target, port = incoming 755 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)]) 756 757 # Changing the vnc password doesn't bother the guest that much. At most it 758 # will surprise people who connect to it. Whether positively or negatively 759 # it's debatable. 760 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE] 761 vnc_pwd = None 762 if vnc_pwd_file: 763 try: 764 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True) 765 except EnvironmentError, err: 766 raise errors.HypervisorError("Failed to open VNC password file %s: %s" 767 % (vnc_pwd_file, err)) 768 769 if conf_hvp[constants.HV_KVM_USE_CHROOT]: 770 utils.EnsureDirs([(self._InstanceChrootDir(name), 771 constants.SECURE_DIR_MODE)]) 772 773 if security_model == constants.HT_SM_POOL: 774 ss = ssconf.SimpleStore() 775 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n") 776 all_uids = set(uidpool.ExpandUidPool(uid_pool)) 777 uid = uidpool.RequestUnusedUid(all_uids) 778 try: 779 username = pwd.getpwuid(uid.GetUid()).pw_name 780 kvm_cmd.extend(["-runas", username]) 781 self._RunKVMCmd(name, kvm_cmd) 782 except: 783 uidpool.ReleaseUid(uid) 784 raise 785 else: 786 uid.Unlock() 787 utils.WriteFile(self._InstanceUidFile(name), data=str(uid)) 788 else: 789 self._RunKVMCmd(name, kvm_cmd) 790 791 if vnc_pwd: 792 change_cmd = 'change vnc password %s' % vnc_pwd 793 self._CallMonitorCommand(instance.name, change_cmd) 794 795 for filename in temp_files: 796 utils.RemoveFile(filename)
797
798 - def StartInstance(self, instance, block_devices):
799 """Start an instance. 800 801 """ 802 self._CheckDown(instance.name) 803 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices) 804 self._SaveKVMRuntime(instance, kvm_runtime) 805 self._ExecuteKVMRuntime(instance, kvm_runtime)
806
807 - def _CallMonitorCommand(self, instance_name, command):
808 """Invoke a command on the instance monitor. 809 810 """ 811 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" % 812 (utils.ShellQuote(command), 813 constants.SOCAT_PATH, 814 utils.ShellQuote(self._InstanceMonitor(instance_name)))) 815 result = utils.RunCmd(socat) 816 if result.failed: 817 msg = ("Failed to send command '%s' to instance %s." 818 " output: %s, error: %s, fail_reason: %s" % 819 (command, instance_name, 820 result.stdout, result.stderr, result.fail_reason)) 821 raise errors.HypervisorError(msg) 822 823 return result
824 825 @classmethod
826 - def _GetKVMVersion(cls):
827 """Return the installed KVM version 828 829 @return: (version, v_maj, v_min, v_rev), or None 830 831 """ 832 result = utils.RunCmd([constants.KVM_PATH, "--help"]) 833 if result.failed: 834 return None 835 match = cls._VERSION_RE.search(result.output.splitlines()[0]) 836 if not match: 837 return None 838 return (match.group(0), match.group(1), match.group(2), match.group(3))
839
840 - def StopInstance(self, instance, force=False, retry=False, name=None):
841 """Stop an instance. 842 843 """ 844 if name is not None and not force: 845 raise errors.HypervisorError("Cannot shutdown cleanly by name only") 846 if name is None: 847 name = instance.name 848 acpi = instance.hvparams[constants.HV_ACPI] 849 else: 850 acpi = False 851 _, pid, alive = self._InstancePidAlive(name) 852 if pid > 0 and alive: 853 if force or not acpi: 854 utils.KillProcess(pid) 855 else: 856 self._CallMonitorCommand(name, 'system_powerdown')
857
858 - def CleanupInstance(self, instance_name):
859 """Cleanup after a stopped instance 860 861 """ 862 pidfile, pid, alive = self._InstancePidAlive(instance_name) 863 if pid > 0 and alive: 864 raise errors.HypervisorError("Cannot cleanup a live instance") 865 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
866
867 - def RebootInstance(self, instance):
868 """Reboot an instance. 869 870 """ 871 # For some reason if we do a 'send-key ctrl-alt-delete' to the control 872 # socket the instance will stop, but now power up again. So we'll resort 873 # to shutdown and restart. 874 _, _, alive = self._InstancePidAlive(instance.name) 875 if not alive: 876 raise errors.HypervisorError("Failed to reboot instance %s:" 877 " not running" % instance.name) 878 # StopInstance will delete the saved KVM runtime so: 879 # ...first load it... 880 kvm_runtime = self._LoadKVMRuntime(instance) 881 # ...now we can safely call StopInstance... 882 if not self.StopInstance(instance): 883 self.StopInstance(instance, force=True) 884 # ...and finally we can save it again, and execute it... 885 self._SaveKVMRuntime(instance, kvm_runtime) 886 self._ExecuteKVMRuntime(instance, kvm_runtime)
887
888 - def MigrationInfo(self, instance):
889 """Get instance information to perform a migration. 890 891 @type instance: L{objects.Instance} 892 @param instance: instance to be migrated 893 @rtype: string 894 @return: content of the KVM runtime file 895 896 """ 897 return self._ReadKVMRuntime(instance.name)
898
899 - def AcceptInstance(self, instance, info, target):
900 """Prepare to accept an instance. 901 902 @type instance: L{objects.Instance} 903 @param instance: instance to be accepted 904 @type info: string 905 @param info: content of the KVM runtime file on the source node 906 @type target: string 907 @param target: target host (usually ip), on this node 908 909 """ 910 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info) 911 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT]) 912 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
913
914 - def FinalizeMigration(self, instance, info, success):
915 """Finalize an instance migration. 916 917 Stop the incoming mode KVM. 918 919 @type instance: L{objects.Instance} 920 @param instance: instance whose migration is being finalized 921 922 """ 923 if success: 924 self._WriteKVMRuntime(instance.name, info) 925 else: 926 self.StopInstance(instance, force=True)
927
928 - def MigrateInstance(self, instance, target, live):
929 """Migrate an instance to a target node. 930 931 The migration will not be attempted if the instance is not 932 currently running. 933 934 @type instance: L{objects.Instance} 935 @param instance: the instance to be migrated 936 @type target: string 937 @param target: ip address of the target node 938 @type live: boolean 939 @param live: perform a live migration 940 941 """ 942 instance_name = instance.name 943 port = instance.hvparams[constants.HV_MIGRATION_PORT] 944 pidfile, pid, alive = self._InstancePidAlive(instance_name) 945 if not alive: 946 raise errors.HypervisorError("Instance not running, cannot migrate") 947 948 if not live: 949 self._CallMonitorCommand(instance_name, 'stop') 950 951 migrate_command = ('migrate_set_speed %dm' % 952 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH]) 953 self._CallMonitorCommand(instance_name, migrate_command) 954 955 migrate_command = ('migrate_set_downtime %dms' % 956 instance.hvparams[constants.HV_MIGRATION_DOWNTIME]) 957 self._CallMonitorCommand(instance_name, migrate_command) 958 959 migrate_command = 'migrate -d tcp:%s:%s' % (target, port) 960 self._CallMonitorCommand(instance_name, migrate_command) 961 962 info_command = 'info migrate' 963 done = False 964 broken_answers = 0 965 while not done: 966 result = self._CallMonitorCommand(instance_name, info_command) 967 match = self._MIGRATION_STATUS_RE.search(result.stdout) 968 if not match: 969 broken_answers += 1 970 if not result.stdout: 971 logging.info("KVM: empty 'info migrate' result") 972 else: 973 logging.warning("KVM: unknown 'info migrate' result: %s", 974 result.stdout) 975 time.sleep(self._MIGRATION_INFO_RETRY_DELAY) 976 else: 977 status = match.group(1) 978 if status == 'completed': 979 done = True 980 elif status == 'active': 981 # reset the broken answers count 982 broken_answers = 0 983 time.sleep(self._MIGRATION_INFO_RETRY_DELAY) 984 elif status == 'failed' or status == 'cancelled': 985 if not live: 986 self._CallMonitorCommand(instance_name, 'cont') 987 raise errors.HypervisorError("Migration %s at the kvm level" % 988 status) 989 else: 990 logging.warning("KVM: unknown migration status '%s'", status) 991 broken_answers += 1 992 time.sleep(self._MIGRATION_INFO_RETRY_DELAY) 993 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS: 994 raise errors.HypervisorError("Too many 'info migrate' broken answers") 995 996 utils.KillProcess(pid) 997 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
998
999 - def GetNodeInfo(self):
1000 """Return information about the node. 1001 1002 This is just a wrapper over the base GetLinuxNodeInfo method. 1003 1004 @return: a dict with the following keys (values in MiB): 1005 - memory_total: the total memory size on the node 1006 - memory_free: the available memory on the node for instances 1007 - memory_dom0: the memory used by the node itself, if available 1008 1009 """ 1010 return self.GetLinuxNodeInfo()
1011 1012 @classmethod
1013 - def GetShellCommandForConsole(cls, instance, hvparams, beparams):
1014 """Return a command for connecting to the console of an instance. 1015 1016 """ 1017 if hvparams[constants.HV_SERIAL_CONSOLE]: 1018 shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" % 1019 (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(), 1020 utils.ShellQuote(cls._InstanceSerial(instance.name)))) 1021 else: 1022 shell_command = "echo 'No serial shell for instance %s'" % instance.name 1023 1024 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS] 1025 if vnc_bind_address: 1026 if instance.network_port > constants.VNC_BASE_PORT: 1027 display = instance.network_port - constants.VNC_BASE_PORT 1028 vnc_command = ("echo 'Instance has VNC listening on %s:%d" 1029 " (display: %d)'" % (vnc_bind_address, 1030 instance.network_port, 1031 display)) 1032 shell_command = "%s; %s" % (vnc_command, shell_command) 1033 1034 return shell_command
1035
1036 - def Verify(self):
1037 """Verify the hypervisor. 1038 1039 Check that the binary exists. 1040 1041 """ 1042 if not os.path.exists(constants.KVM_PATH): 1043 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH 1044 if not os.path.exists(constants.SOCAT_PATH): 1045 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1046 1047 1048 @classmethod
1049 - def CheckParameterSyntax(cls, hvparams):
1050 """Check the given parameters for validity. 1051 1052 @type hvparams: dict 1053 @param hvparams: dictionary with parameter names/value 1054 @raise errors.HypervisorError: when a parameter is not valid 1055 1056 """ 1057 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams) 1058 1059 kernel_path = hvparams[constants.HV_KERNEL_PATH] 1060 if kernel_path: 1061 if not hvparams[constants.HV_ROOT_PATH]: 1062 raise errors.HypervisorError("Need a root partition for the instance," 1063 " if a kernel is defined") 1064 1065 if (hvparams[constants.HV_VNC_X509_VERIFY] and 1066 not hvparams[constants.HV_VNC_X509]): 1067 raise errors.HypervisorError("%s must be defined, if %s is" % 1068 (constants.HV_VNC_X509, 1069 constants.HV_VNC_X509_VERIFY)) 1070 1071 boot_order = hvparams[constants.HV_BOOT_ORDER] 1072 if (boot_order == constants.HT_BO_CDROM and 1073 not hvparams[constants.HV_CDROM_IMAGE_PATH]): 1074 raise errors.HypervisorError("Cannot boot from cdrom without an" 1075 " ISO path") 1076 1077 security_model = hvparams[constants.HV_SECURITY_MODEL] 1078 if security_model == constants.HT_SM_USER: 1079 if not hvparams[constants.HV_SECURITY_DOMAIN]: 1080 raise errors.HypervisorError("A security domain (user to run kvm as)" 1081 " must be specified") 1082 elif (security_model == constants.HT_SM_NONE or 1083 security_model == constants.HT_SM_POOL): 1084 if hvparams[constants.HV_SECURITY_DOMAIN]: 1085 raise errors.HypervisorError("Cannot have a security domain when the" 1086 " security model is 'none' or 'pool'")
1087 1088 @classmethod
1089 - def ValidateParameters(cls, hvparams):
1090 """Check the given parameters for validity. 1091 1092 @type hvparams: dict 1093 @param hvparams: dictionary with parameter names/value 1094 @raise errors.HypervisorError: when a parameter is not valid 1095 1096 """ 1097 super(KVMHypervisor, cls).ValidateParameters(hvparams) 1098 1099 security_model = hvparams[constants.HV_SECURITY_MODEL] 1100 if security_model == constants.HT_SM_USER: 1101 username = hvparams[constants.HV_SECURITY_DOMAIN] 1102 try: 1103 pwd.getpwnam(username) 1104 except KeyError: 1105 raise errors.HypervisorError("Unknown security domain user %s" 1106 % username)
1107 1108 @classmethod
1109 - def PowercycleNode(cls):
1110 """KVM powercycle, just a wrapper over Linux powercycle. 1111 1112 """ 1113 cls.LinuxPowercycle()
1114