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, 2011, 2012 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  import struct 
  35  import fcntl 
  36  import shutil 
  37   
  38  from ganeti import utils 
  39  from ganeti import constants 
  40  from ganeti import errors 
  41  from ganeti import serializer 
  42  from ganeti import objects 
  43  from ganeti import uidpool 
  44  from ganeti import ssconf 
  45  from ganeti.hypervisor import hv_base 
  46  from ganeti import netutils 
  47  from ganeti.utils import wrapper as utils_wrapper 
  48   
  49   
  50  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge" 
  51   
  52  # TUN/TAP driver constants, taken from <linux/if_tun.h> 
  53  # They are architecture-independent and already hardcoded in qemu-kvm source, 
  54  # so we can safely include them here. 
  55  TUNSETIFF = 0x400454ca 
  56  TUNGETIFF = 0x800454d2 
  57  TUNGETFEATURES = 0x800454cf 
  58  IFF_TAP = 0x0002 
  59  IFF_NO_PI = 0x1000 
  60  IFF_VNET_HDR = 0x4000 
61 62 63 -def _ProbeTapVnetHdr(fd):
64 """Check whether to enable the IFF_VNET_HDR flag. 65 66 To do this, _all_ of the following conditions must be met: 67 1. TUNGETFEATURES ioctl() *must* be implemented 68 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag 69 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in 70 drivers/net/tun.c there is no way to test this until after the tap device 71 has been created using TUNSETIFF, and there is no way to change the 72 IFF_VNET_HDR flag after creating the interface, catch-22! However both 73 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27, 74 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is. 75 76 @type fd: int 77 @param fd: the file descriptor of /dev/net/tun 78 79 """ 80 req = struct.pack("I", 0) 81 try: 82 res = fcntl.ioctl(fd, TUNGETFEATURES, req) 83 except EnvironmentError: 84 logging.warning("TUNGETFEATURES ioctl() not implemented") 85 return False 86 87 tunflags = struct.unpack("I", res)[0] 88 if tunflags & IFF_VNET_HDR: 89 return True 90 else: 91 logging.warning("Host does not support IFF_VNET_HDR, not enabling") 92 return False
93
94 95 -def _OpenTap(vnet_hdr=True):
96 """Open a new tap device and return its file descriptor. 97 98 This is intended to be used by a qemu-type hypervisor together with the -net 99 tap,fd=<fd> command line parameter. 100 101 @type vnet_hdr: boolean 102 @param vnet_hdr: Enable the VNET Header 103 @return: (ifname, tapfd) 104 @rtype: tuple 105 106 """ 107 try: 108 tapfd = os.open("/dev/net/tun", os.O_RDWR) 109 except EnvironmentError: 110 raise errors.HypervisorError("Failed to open /dev/net/tun") 111 112 flags = IFF_TAP | IFF_NO_PI 113 114 if vnet_hdr and _ProbeTapVnetHdr(tapfd): 115 flags |= IFF_VNET_HDR 116 117 # The struct ifreq ioctl request (see netdevice(7)) 118 ifr = struct.pack("16sh", "", flags) 119 120 try: 121 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr) 122 except EnvironmentError: 123 raise errors.HypervisorError("Failed to allocate a new TAP device") 124 125 # Get the interface name from the ioctl 126 ifname = struct.unpack("16sh", res)[0].strip("\x00") 127 return (ifname, tapfd)
128
129 130 -class KVMHypervisor(hv_base.BaseHypervisor):
131 """KVM hypervisor interface""" 132 CAN_MIGRATE = True 133 134 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor" 135 _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids 136 _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids 137 _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets 138 _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data 139 _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations 140 _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps 141 # KVM instances with chroot enabled are started in empty chroot directories. 142 _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories 143 # After an instance is stopped, its chroot directory is removed. 144 # If the chroot directory is not empty, it can't be removed. 145 # A non-empty chroot directory indicates a possible security incident. 146 # To support forensics, the non-empty chroot directory is quarantined in 147 # a separate directory, called 'chroot-quarantine'. 148 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine" 149 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR, 150 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR] 151 152 PARAMETERS = { 153 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK, 154 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK, 155 constants.HV_ROOT_PATH: hv_base.NO_CHECK, 156 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK, 157 constants.HV_ACPI: hv_base.NO_CHECK, 158 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK, 159 constants.HV_VNC_BIND_ADDRESS: 160 (False, lambda x: (netutils.IP4Address.IsValid(x) or 161 utils.IsNormAbsPath(x)), 162 "the VNC bind address must be either a valid IP address or an absolute" 163 " pathname", None, None), 164 constants.HV_VNC_TLS: hv_base.NO_CHECK, 165 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK, 166 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK, 167 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK, 168 constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later 169 constants.HV_KVM_SPICE_IP_VERSION: 170 (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or 171 x in constants.VALID_IP_VERSIONS), 172 "the SPICE IP version should be 4 or 6", 173 None, None), 174 constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 175 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 176 constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK, 177 constants.HV_BOOT_ORDER: 178 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES), 179 constants.HV_NIC_TYPE: 180 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES), 181 constants.HV_DISK_TYPE: 182 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES), 183 constants.HV_KVM_CDROM_DISK_TYPE: 184 hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES), 185 constants.HV_USB_MOUSE: 186 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES), 187 constants.HV_KEYMAP: hv_base.NO_CHECK, 188 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK, 189 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK, 190 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK, 191 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK, 192 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK, 193 constants.HV_DISK_CACHE: 194 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES), 195 constants.HV_SECURITY_MODEL: 196 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES), 197 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK, 198 constants.HV_KVM_FLAG: 199 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES), 200 constants.HV_VHOST_NET: hv_base.NO_CHECK, 201 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK, 202 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK, 203 constants.HV_REBOOT_BEHAVIOR: 204 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS) 205 } 206 207 _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)", 208 re.M | re.I) 209 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5 210 _MIGRATION_INFO_RETRY_DELAY = 2 211 212 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b") 213 214 ANCILLARY_FILES = [ 215 _KVM_NETWORK_SCRIPT, 216 ] 217
218 - def __init__(self):
219 hv_base.BaseHypervisor.__init__(self) 220 # Let's make sure the directories we need exist, even if the RUN_DIR lives 221 # in a tmpfs filesystem or has been otherwise wiped out. 222 dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS] 223 utils.EnsureDirs(dirs)
224 225 @classmethod
226 - def _InstancePidFile(cls, instance_name):
227 """Returns the instance pidfile. 228 229 """ 230 return utils.PathJoin(cls._PIDS_DIR, instance_name)
231 232 @classmethod
233 - def _InstanceUidFile(cls, instance_name):
234 """Returns the instance uidfile. 235 236 """ 237 return utils.PathJoin(cls._UIDS_DIR, instance_name)
238 239 @classmethod
240 - def _InstancePidInfo(cls, pid):
241 """Check pid file for instance information. 242 243 Check that a pid file is associated with an instance, and retrieve 244 information from its command line. 245 246 @type pid: string or int 247 @param pid: process id of the instance to check 248 @rtype: tuple 249 @return: (instance_name, memory, vcpus) 250 @raise errors.HypervisorError: when an instance cannot be found 251 252 """ 253 alive = utils.IsProcessAlive(pid) 254 if not alive: 255 raise errors.HypervisorError("Cannot get info for pid %s" % pid) 256 257 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline") 258 try: 259 cmdline = utils.ReadFile(cmdline_file) 260 except EnvironmentError, err: 261 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" % 262 (pid, err)) 263 264 instance = None 265 memory = 0 266 vcpus = 0 267 268 arg_list = cmdline.split("\x00") 269 while arg_list: 270 arg = arg_list.pop(0) 271 if arg == "-name": 272 instance = arg_list.pop(0) 273 elif arg == "-m": 274 memory = int(arg_list.pop(0)) 275 elif arg == "-smp": 276 vcpus = int(arg_list.pop(0)) 277 278 if instance is None: 279 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm" 280 " instance" % pid) 281 282 return (instance, memory, vcpus)
283
284 - def _InstancePidAlive(self, instance_name):
285 """Returns the instance pidfile, pid, and liveness. 286 287 @type instance_name: string 288 @param instance_name: instance name 289 @rtype: tuple 290 @return: (pid file name, pid, liveness) 291 292 """ 293 pidfile = self._InstancePidFile(instance_name) 294 pid = utils.ReadPidFile(pidfile) 295 296 alive = False 297 try: 298 cmd_instance = self._InstancePidInfo(pid)[0] 299 alive = (cmd_instance == instance_name) 300 except errors.HypervisorError: 301 pass 302 303 return (pidfile, pid, alive)
304
305 - def _CheckDown(self, instance_name):
306 """Raises an error unless the given instance is down. 307 308 """ 309 alive = self._InstancePidAlive(instance_name)[2] 310 if alive: 311 raise errors.HypervisorError("Failed to start instance %s: %s" % 312 (instance_name, "already running"))
313 314 @classmethod
315 - def _InstanceMonitor(cls, instance_name):
316 """Returns the instance monitor socket name 317 318 """ 319 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
320 321 @classmethod
322 - def _InstanceSerial(cls, instance_name):
323 """Returns the instance serial socket name 324 325 """ 326 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
327 328 @staticmethod
330 """Returns the correct parameters for socat 331 332 If we have a new-enough socat we can use raw mode with an escape character. 333 334 """ 335 if constants.SOCAT_USE_ESCAPE: 336 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE 337 else: 338 return "echo=0,icanon=0"
339 340 @classmethod
341 - def _InstanceKVMRuntime(cls, instance_name):
342 """Returns the instance KVM runtime filename 343 344 """ 345 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
346 347 @classmethod
348 - def _InstanceChrootDir(cls, instance_name):
349 """Returns the name of the KVM chroot dir of the instance 350 351 """ 352 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
353 354 @classmethod
355 - def _InstanceNICDir(cls, instance_name):
356 """Returns the name of the directory holding the tap device files for a 357 given instance. 358 359 """ 360 return utils.PathJoin(cls._NICS_DIR, instance_name)
361 362 @classmethod
363 - def _InstanceNICFile(cls, instance_name, seq):
364 """Returns the name of the file containing the tap device for a given NIC 365 366 """ 367 return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
368 369 @classmethod
370 - def _InstanceKeymapFile(cls, instance_name):
371 """Returns the name of the file containing the keymap for a given instance 372 373 """ 374 return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
375 376 @classmethod
377 - def _TryReadUidFile(cls, uid_file):
378 """Try to read a uid file 379 380 """ 381 if os.path.exists(uid_file): 382 try: 383 uid = int(utils.ReadOneLineFile(uid_file)) 384 return uid 385 except EnvironmentError: 386 logging.warning("Can't read uid file", exc_info=True) 387 except (TypeError, ValueError): 388 logging.warning("Can't parse uid file contents", exc_info=True) 389 return None
390 391 @classmethod
392 - def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
393 """Removes an instance's rutime sockets/files/dirs. 394 395 """ 396 utils.RemoveFile(pidfile) 397 utils.RemoveFile(cls._InstanceMonitor(instance_name)) 398 utils.RemoveFile(cls._InstanceSerial(instance_name)) 399 utils.RemoveFile(cls._InstanceKVMRuntime(instance_name)) 400 utils.RemoveFile(cls._InstanceKeymapFile(instance_name)) 401 uid_file = cls._InstanceUidFile(instance_name) 402 uid = cls._TryReadUidFile(uid_file) 403 utils.RemoveFile(uid_file) 404 if uid is not None: 405 uidpool.ReleaseUid(uid) 406 try: 407 shutil.rmtree(cls._InstanceNICDir(instance_name)) 408 except OSError, err: 409 if err.errno != errno.ENOENT: 410 raise 411 try: 412 chroot_dir = cls._InstanceChrootDir(instance_name) 413 utils.RemoveDir(chroot_dir) 414 except OSError, err: 415 if err.errno == errno.ENOTEMPTY: 416 # The chroot directory is expected to be empty, but it isn't. 417 new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR, 418 prefix="%s-%s-" % 419 (instance_name, 420 utils.TimestampForFilename())) 421 logging.warning("The chroot directory of instance %s can not be" 422 " removed as it is not empty. Moving it to the" 423 " quarantine instead. Please investigate the" 424 " contents (%s) and clean up manually", 425 instance_name, new_chroot_dir) 426 utils.RenameFile(chroot_dir, new_chroot_dir) 427 else: 428 raise
429 430 @staticmethod
431 - def _ConfigureNIC(instance, seq, nic, tap):
432 """Run the network configuration script for a specified NIC 433 434 @param instance: instance we're acting on 435 @type instance: instance object 436 @param seq: nic sequence number 437 @type seq: int 438 @param nic: nic we're acting on 439 @type nic: nic object 440 @param tap: the host's tap interface this NIC corresponds to 441 @type tap: str 442 443 """ 444 445 if instance.tags: 446 tags = " ".join(instance.tags) 447 else: 448 tags = "" 449 450 env = { 451 "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"], 452 "INSTANCE": instance.name, 453 "MAC": nic.mac, 454 "MODE": nic.nicparams[constants.NIC_MODE], 455 "INTERFACE": tap, 456 "INTERFACE_INDEX": str(seq), 457 "TAGS": tags, 458 } 459 460 if nic.ip: 461 env["IP"] = nic.ip 462 463 if nic.nicparams[constants.NIC_LINK]: 464 env["LINK"] = nic.nicparams[constants.NIC_LINK] 465 466 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 467 env["BRIDGE"] = nic.nicparams[constants.NIC_LINK] 468 469 result = utils.RunCmd([constants.KVM_IFUP, tap], env=env) 470 if result.failed: 471 raise errors.HypervisorError("Failed to configure interface %s: %s." 472 " Network configuration script output: %s" % 473 (tap, result.fail_reason, result.output))
474
475 - def ListInstances(self):
476 """Get the list of running instances. 477 478 We can do this by listing our live instances directory and 479 checking whether the associated kvm process is still alive. 480 481 """ 482 result = [] 483 for name in os.listdir(self._PIDS_DIR): 484 if self._InstancePidAlive(name)[2]: 485 result.append(name) 486 return result
487
488 - def GetInstanceInfo(self, instance_name):
489 """Get instance properties. 490 491 @type instance_name: string 492 @param instance_name: the instance name 493 @rtype: tuple of strings 494 @return: (name, id, memory, vcpus, stat, times) 495 496 """ 497 _, pid, alive = self._InstancePidAlive(instance_name) 498 if not alive: 499 return None 500 501 _, memory, vcpus = self._InstancePidInfo(pid) 502 stat = "---b-" 503 times = "0" 504 505 return (instance_name, pid, memory, vcpus, stat, times)
506
507 - def GetAllInstancesInfo(self):
508 """Get properties of all instances. 509 510 @return: list of tuples (name, id, memory, vcpus, stat, times) 511 512 """ 513 data = [] 514 for name in os.listdir(self._PIDS_DIR): 515 try: 516 info = self.GetInstanceInfo(name) 517 except errors.HypervisorError: 518 continue 519 if info: 520 data.append(info) 521 return data
522
523 - def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
524 """Generate KVM information to start an instance. 525 526 @attention: this function must not have any side-effects; for 527 example, it must not write to the filesystem, or read values 528 from the current system the are expected to differ between 529 nodes, since it is only run once at instance startup; 530 actions/kvm arguments that can vary between systems should be 531 done in L{_ExecuteKVMRuntime} 532 533 """ 534 _, v_major, v_min, _ = self._GetKVMVersion() 535 536 pidfile = self._InstancePidFile(instance.name) 537 kvm = constants.KVM_PATH 538 kvm_cmd = [kvm] 539 # used just by the vnc server, if enabled 540 kvm_cmd.extend(["-name", instance.name]) 541 kvm_cmd.extend(["-m", instance.beparams[constants.BE_MEMORY]]) 542 kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]]) 543 kvm_cmd.extend(["-pidfile", pidfile]) 544 kvm_cmd.extend(["-daemonize"]) 545 if not instance.hvparams[constants.HV_ACPI]: 546 kvm_cmd.extend(["-no-acpi"]) 547 if startup_paused: 548 kvm_cmd.extend(["-S"]) 549 if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \ 550 constants.INSTANCE_REBOOT_EXIT: 551 kvm_cmd.extend(["-no-reboot"]) 552 553 hvp = instance.hvparams 554 kernel_path = hvp[constants.HV_KERNEL_PATH] 555 if kernel_path: 556 boot_disk = boot_cdrom = boot_floppy = boot_network = False 557 else: 558 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK 559 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM 560 boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY 561 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK 562 563 self.ValidateParameters(hvp) 564 565 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED: 566 kvm_cmd.extend(["-enable-kvm"]) 567 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED: 568 kvm_cmd.extend(["-disable-kvm"]) 569 570 if boot_network: 571 kvm_cmd.extend(["-boot", "n"]) 572 573 # whether this is an older KVM version that uses the boot=on flag 574 # on devices 575 needs_boot_flag = (v_major, v_min) < (0, 14) 576 577 disk_type = hvp[constants.HV_DISK_TYPE] 578 if disk_type == constants.HT_DISK_PARAVIRTUAL: 579 if_val = ",if=virtio" 580 else: 581 if_val = ",if=%s" % disk_type 582 # Cache mode 583 disk_cache = hvp[constants.HV_DISK_CACHE] 584 if instance.disk_template in constants.DTS_EXT_MIRROR: 585 if disk_cache != "none": 586 # TODO: make this a hard error, instead of a silent overwrite 587 logging.warning("KVM: overriding disk_cache setting '%s' with 'none'" 588 " to prevent shared storage corruption on migration", 589 disk_cache) 590 cache_val = ",cache=none" 591 elif disk_cache != constants.HT_CACHE_DEFAULT: 592 cache_val = ",cache=%s" % disk_cache 593 else: 594 cache_val = "" 595 for cfdev, dev_path in block_devices: 596 if cfdev.mode != constants.DISK_RDWR: 597 raise errors.HypervisorError("Instance has read-only disks which" 598 " are not supported by KVM") 599 # TODO: handle FD_LOOP and FD_BLKTAP (?) 600 boot_val = "" 601 if boot_disk: 602 kvm_cmd.extend(["-boot", "c"]) 603 boot_disk = False 604 if needs_boot_flag and disk_type != constants.HT_DISK_IDE: 605 boot_val = ",boot=on" 606 607 drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val, 608 cache_val) 609 kvm_cmd.extend(["-drive", drive_val]) 610 611 #Now we can specify a different device type for CDROM devices. 612 cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE] 613 if not cdrom_disk_type: 614 cdrom_disk_type = disk_type 615 616 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH] 617 if iso_image: 618 options = ",format=raw,media=cdrom" 619 # set cdrom 'if' type 620 if boot_cdrom: 621 actual_cdrom_type = constants.HT_DISK_IDE 622 elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL: 623 actual_cdrom_type = "virtio" 624 else: 625 actual_cdrom_type = cdrom_disk_type 626 if_val = ",if=%s" % actual_cdrom_type 627 # set boot flag, if needed 628 boot_val = "" 629 if boot_cdrom: 630 kvm_cmd.extend(["-boot", "d"]) 631 if needs_boot_flag: 632 boot_val = ",boot=on" 633 # and finally build the entire '-drive' value 634 drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val) 635 kvm_cmd.extend(["-drive", drive_val]) 636 637 iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH] 638 if iso_image2: 639 options = ",format=raw,media=cdrom" 640 if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL: 641 if_val = ",if=virtio" 642 else: 643 if_val = ",if=%s" % cdrom_disk_type 644 drive_val = "file=%s%s%s" % (iso_image2, options, if_val) 645 kvm_cmd.extend(["-drive", drive_val]) 646 647 floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH] 648 if floppy_image: 649 options = ",format=raw,media=disk" 650 if boot_floppy: 651 kvm_cmd.extend(["-boot", "a"]) 652 options = "%s,boot=on" % options 653 if_val = ",if=floppy" 654 options = "%s%s" % (options, if_val) 655 drive_val = "file=%s%s" % (floppy_image, options) 656 kvm_cmd.extend(["-drive", drive_val]) 657 658 if kernel_path: 659 kvm_cmd.extend(["-kernel", kernel_path]) 660 initrd_path = hvp[constants.HV_INITRD_PATH] 661 if initrd_path: 662 kvm_cmd.extend(["-initrd", initrd_path]) 663 root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH], 664 hvp[constants.HV_KERNEL_ARGS]] 665 if hvp[constants.HV_SERIAL_CONSOLE]: 666 root_append.append("console=ttyS0,38400") 667 kvm_cmd.extend(["-append", " ".join(root_append)]) 668 669 mem_path = hvp[constants.HV_MEM_PATH] 670 if mem_path: 671 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"]) 672 673 monitor_dev = ("unix:%s,server,nowait" % 674 self._InstanceMonitor(instance.name)) 675 kvm_cmd.extend(["-monitor", monitor_dev]) 676 if hvp[constants.HV_SERIAL_CONSOLE]: 677 serial_dev = ("unix:%s,server,nowait" % 678 self._InstanceSerial(instance.name)) 679 kvm_cmd.extend(["-serial", serial_dev]) 680 else: 681 kvm_cmd.extend(["-serial", "none"]) 682 683 mouse_type = hvp[constants.HV_USB_MOUSE] 684 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS] 685 spice_bind = hvp[constants.HV_KVM_SPICE_BIND] 686 spice_ip_version = None 687 688 if mouse_type: 689 kvm_cmd.extend(["-usb"]) 690 kvm_cmd.extend(["-usbdevice", mouse_type]) 691 elif vnc_bind_address: 692 kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET]) 693 694 if vnc_bind_address: 695 if netutils.IP4Address.IsValid(vnc_bind_address): 696 if instance.network_port > constants.VNC_BASE_PORT: 697 display = instance.network_port - constants.VNC_BASE_PORT 698 if vnc_bind_address == constants.IP4_ADDRESS_ANY: 699 vnc_arg = ":%d" % (display) 700 else: 701 vnc_arg = "%s:%d" % (vnc_bind_address, display) 702 else: 703 logging.error("Network port is not a valid VNC display (%d < %d)." 704 " Not starting VNC", instance.network_port, 705 constants.VNC_BASE_PORT) 706 vnc_arg = "none" 707 708 # Only allow tls and other option when not binding to a file, for now. 709 # kvm/qemu gets confused otherwise about the filename to use. 710 vnc_append = "" 711 if hvp[constants.HV_VNC_TLS]: 712 vnc_append = "%s,tls" % vnc_append 713 if hvp[constants.HV_VNC_X509_VERIFY]: 714 vnc_append = "%s,x509verify=%s" % (vnc_append, 715 hvp[constants.HV_VNC_X509]) 716 elif hvp[constants.HV_VNC_X509]: 717 vnc_append = "%s,x509=%s" % (vnc_append, 718 hvp[constants.HV_VNC_X509]) 719 if hvp[constants.HV_VNC_PASSWORD_FILE]: 720 vnc_append = "%s,password" % vnc_append 721 722 vnc_arg = "%s%s" % (vnc_arg, vnc_append) 723 724 else: 725 vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name) 726 727 kvm_cmd.extend(["-vnc", vnc_arg]) 728 elif spice_bind: 729 # FIXME: this is wrong here; the iface ip address differs 730 # between systems, so it should be done in _ExecuteKVMRuntime 731 if netutils.IsValidInterface(spice_bind): 732 # The user specified a network interface, we have to figure out the IP 733 # address. 734 addresses = netutils.GetInterfaceIpAddresses(spice_bind) 735 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION] 736 737 # if the user specified an IP version and the interface does not 738 # have that kind of IP addresses, throw an exception 739 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED: 740 if not addresses[spice_ip_version]: 741 raise errors.HypervisorError("spice: unable to get an IPv%s address" 742 " for %s" % (spice_ip_version, 743 spice_bind)) 744 745 # the user did not specify an IP version, we have to figure it out 746 elif (addresses[constants.IP4_VERSION] and 747 addresses[constants.IP6_VERSION]): 748 # we have both ipv4 and ipv6, let's use the cluster default IP 749 # version 750 cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily() 751 spice_ip_version = netutils.IPAddress.GetVersionFromAddressFamily( 752 cluster_family) 753 elif addresses[constants.IP4_VERSION]: 754 spice_ip_version = constants.IP4_VERSION 755 elif addresses[constants.IP6_VERSION]: 756 spice_ip_version = constants.IP6_VERSION 757 else: 758 raise errors.HypervisorError("spice: unable to get an IP address" 759 " for %s" % (spice_bind)) 760 761 spice_address = addresses[spice_ip_version][0] 762 763 else: 764 # spice_bind is known to be a valid IP address, because 765 # ValidateParameters checked it. 766 spice_address = spice_bind 767 768 spice_arg = "addr=%s,port=%s,disable-ticketing" % (spice_address, 769 instance.network_port) 770 if spice_ip_version: 771 spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version) 772 773 logging.info("KVM: SPICE will listen on port %s", instance.network_port) 774 kvm_cmd.extend(["-spice", spice_arg]) 775 776 else: 777 kvm_cmd.extend(["-nographic"]) 778 779 if hvp[constants.HV_USE_LOCALTIME]: 780 kvm_cmd.extend(["-localtime"]) 781 782 if hvp[constants.HV_KVM_USE_CHROOT]: 783 kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)]) 784 785 # Save the current instance nics, but defer their expansion as parameters, 786 # as we'll need to generate executable temp files for them. 787 kvm_nics = instance.nics 788 hvparams = hvp 789 790 return (kvm_cmd, kvm_nics, hvparams)
791
792 - def _WriteKVMRuntime(self, instance_name, data):
793 """Write an instance's KVM runtime 794 795 """ 796 try: 797 utils.WriteFile(self._InstanceKVMRuntime(instance_name), 798 data=data) 799 except EnvironmentError, err: 800 raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
801
802 - def _ReadKVMRuntime(self, instance_name):
803 """Read an instance's KVM runtime 804 805 """ 806 try: 807 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name)) 808 except EnvironmentError, err: 809 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err) 810 return file_content
811
812 - def _SaveKVMRuntime(self, instance, kvm_runtime):
813 """Save an instance's KVM runtime 814 815 """ 816 kvm_cmd, kvm_nics, hvparams = kvm_runtime 817 serialized_nics = [nic.ToDict() for nic in kvm_nics] 818 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams)) 819 self._WriteKVMRuntime(instance.name, serialized_form)
820
821 - def _LoadKVMRuntime(self, instance, serialized_runtime=None):
822 """Load an instance's KVM runtime 823 824 """ 825 if not serialized_runtime: 826 serialized_runtime = self._ReadKVMRuntime(instance.name) 827 loaded_runtime = serializer.Load(serialized_runtime) 828 kvm_cmd, serialized_nics, hvparams = loaded_runtime 829 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics] 830 return (kvm_cmd, kvm_nics, hvparams)
831
832 - def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
833 """Run the KVM cmd and check for errors 834 835 @type name: string 836 @param name: instance name 837 @type kvm_cmd: list of strings 838 @param kvm_cmd: runcmd input for kvm 839 @type tap_fds: list of int 840 @param tap_fds: fds of tap devices opened by Ganeti 841 842 """ 843 try: 844 result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds) 845 finally: 846 for fd in tap_fds: 847 utils_wrapper.CloseFdNoError(fd) 848 849 if result.failed: 850 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" % 851 (name, result.fail_reason, result.output)) 852 if not self._InstancePidAlive(name)[2]: 853 raise errors.HypervisorError("Failed to start instance %s" % name)
854
855 - def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
856 """Execute a KVM cmd, after completing it with some last minute data. 857 858 @type incoming: tuple of strings 859 @param incoming: (target_host_ip, port) 860 861 """ 862 # Small _ExecuteKVMRuntime hv parameters programming howto: 863 # - conf_hvp contains the parameters as configured on ganeti. they might 864 # have changed since the instance started; only use them if the change 865 # won't affect the inside of the instance (which hasn't been rebooted). 866 # - up_hvp contains the parameters as they were when the instance was 867 # started, plus any new parameter which has been added between ganeti 868 # versions: it is paramount that those default to a value which won't 869 # affect the inside of the instance as well. 870 conf_hvp = instance.hvparams 871 name = instance.name 872 self._CheckDown(name) 873 874 temp_files = [] 875 876 kvm_cmd, kvm_nics, up_hvp = kvm_runtime 877 up_hvp = objects.FillDict(conf_hvp, up_hvp) 878 879 _, v_major, v_min, _ = self._GetKVMVersion() 880 881 # We know it's safe to run as a different user upon migration, so we'll use 882 # the latest conf, from conf_hvp. 883 security_model = conf_hvp[constants.HV_SECURITY_MODEL] 884 if security_model == constants.HT_SM_USER: 885 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]]) 886 887 keymap = conf_hvp[constants.HV_KEYMAP] 888 if keymap: 889 keymap_path = self._InstanceKeymapFile(name) 890 # If a keymap file is specified, KVM won't use its internal defaults. By 891 # first including the "en-us" layout, an error on loading the actual 892 # layout (e.g. because it can't be found) won't lead to a non-functional 893 # keyboard. A keyboard with incorrect keys is still better than none. 894 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap) 895 kvm_cmd.extend(["-k", keymap_path]) 896 897 # We have reasons to believe changing something like the nic driver/type 898 # upon migration won't exactly fly with the instance kernel, so for nic 899 # related parameters we'll use up_hvp 900 tapfds = [] 901 taps = [] 902 if not kvm_nics: 903 kvm_cmd.extend(["-net", "none"]) 904 else: 905 vnet_hdr = False 906 tap_extra = "" 907 nic_type = up_hvp[constants.HV_NIC_TYPE] 908 if nic_type == constants.HT_NIC_PARAVIRTUAL: 909 # From version 0.12.0, kvm uses a new sintax for network configuration. 910 if (v_major, v_min) >= (0, 12): 911 nic_model = "virtio-net-pci" 912 vnet_hdr = True 913 else: 914 nic_model = "virtio" 915 916 if up_hvp[constants.HV_VHOST_NET]: 917 # vhost_net is only available from version 0.13.0 or newer 918 if (v_major, v_min) >= (0, 13): 919 tap_extra = ",vhost=on" 920 else: 921 raise errors.HypervisorError("vhost_net is configured" 922 " but it is not available") 923 else: 924 nic_model = nic_type 925 926 for nic_seq, nic in enumerate(kvm_nics): 927 tapname, tapfd = _OpenTap(vnet_hdr) 928 tapfds.append(tapfd) 929 taps.append(tapname) 930 if (v_major, v_min) >= (0, 12): 931 nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq) 932 tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra) 933 kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val]) 934 else: 935 nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq, 936 nic.mac, nic_model) 937 tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd) 938 kvm_cmd.extend(["-net", tap_val, "-net", nic_val]) 939 940 if incoming: 941 target, port = incoming 942 kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)]) 943 944 # Changing the vnc password doesn't bother the guest that much. At most it 945 # will surprise people who connect to it. Whether positively or negatively 946 # it's debatable. 947 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE] 948 vnc_pwd = None 949 if vnc_pwd_file: 950 try: 951 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True) 952 except EnvironmentError, err: 953 raise errors.HypervisorError("Failed to open VNC password file %s: %s" 954 % (vnc_pwd_file, err)) 955 956 if conf_hvp[constants.HV_KVM_USE_CHROOT]: 957 utils.EnsureDirs([(self._InstanceChrootDir(name), 958 constants.SECURE_DIR_MODE)]) 959 960 # Configure the network now for starting instances and bridged interfaces, 961 # during FinalizeMigration for incoming instances' routed interfaces 962 for nic_seq, nic in enumerate(kvm_nics): 963 if (incoming and 964 nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED): 965 continue 966 self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq]) 967 968 if security_model == constants.HT_SM_POOL: 969 ss = ssconf.SimpleStore() 970 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n") 971 all_uids = set(uidpool.ExpandUidPool(uid_pool)) 972 uid = uidpool.RequestUnusedUid(all_uids) 973 try: 974 username = pwd.getpwuid(uid.GetUid()).pw_name 975 kvm_cmd.extend(["-runas", username]) 976 self._RunKVMCmd(name, kvm_cmd, tapfds) 977 except: 978 uidpool.ReleaseUid(uid) 979 raise 980 else: 981 uid.Unlock() 982 utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr()) 983 else: 984 self._RunKVMCmd(name, kvm_cmd, tapfds) 985 986 utils.EnsureDirs([(self._InstanceNICDir(instance.name), 987 constants.RUN_DIRS_MODE)]) 988 for nic_seq, tap in enumerate(taps): 989 utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq), 990 data=tap) 991 992 if vnc_pwd: 993 change_cmd = "change vnc password %s" % vnc_pwd 994 self._CallMonitorCommand(instance.name, change_cmd) 995 996 for filename in temp_files: 997 utils.RemoveFile(filename)
998
999 - def StartInstance(self, instance, block_devices, startup_paused):
1000 """Start an instance. 1001 1002 """ 1003 self._CheckDown(instance.name) 1004 kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, 1005 startup_paused) 1006 self._SaveKVMRuntime(instance, kvm_runtime) 1007 self._ExecuteKVMRuntime(instance, kvm_runtime)
1008
1009 - def _CallMonitorCommand(self, instance_name, command):
1010 """Invoke a command on the instance monitor. 1011 1012 """ 1013 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" % 1014 (utils.ShellQuote(command), 1015 constants.SOCAT_PATH, 1016 utils.ShellQuote(self._InstanceMonitor(instance_name)))) 1017 result = utils.RunCmd(socat) 1018 if result.failed: 1019 msg = ("Failed to send command '%s' to instance %s." 1020 " output: %s, error: %s, fail_reason: %s" % 1021 (command, instance_name, 1022 result.stdout, result.stderr, result.fail_reason)) 1023 raise errors.HypervisorError(msg) 1024 1025 return result
1026 1027 @classmethod
1028 - def _ParseKVMVersion(cls, text):
1029 """Parse the KVM version from the --help output. 1030 1031 @type text: string 1032 @param text: output of kvm --help 1033 @return: (version, v_maj, v_min, v_rev) 1034 @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved 1035 1036 """ 1037 match = cls._VERSION_RE.search(text.splitlines()[0]) 1038 if not match: 1039 raise errors.HypervisorError("Unable to get KVM version") 1040 1041 v_all = match.group(0) 1042 v_maj = int(match.group(1)) 1043 v_min = int(match.group(2)) 1044 if match.group(4): 1045 v_rev = int(match.group(4)) 1046 else: 1047 v_rev = 0 1048 return (v_all, v_maj, v_min, v_rev)
1049 1050 @classmethod
1051 - def _GetKVMVersion(cls):
1052 """Return the installed KVM version. 1053 1054 @return: (version, v_maj, v_min, v_rev) 1055 @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved 1056 1057 """ 1058 result = utils.RunCmd([constants.KVM_PATH, "--help"]) 1059 if result.failed: 1060 raise errors.HypervisorError("Unable to get KVM version") 1061 return cls._ParseKVMVersion(result.output)
1062
1063 - def StopInstance(self, instance, force=False, retry=False, name=None):
1064 """Stop an instance. 1065 1066 """ 1067 if name is not None and not force: 1068 raise errors.HypervisorError("Cannot shutdown cleanly by name only") 1069 if name is None: 1070 name = instance.name 1071 acpi = instance.hvparams[constants.HV_ACPI] 1072 else: 1073 acpi = False 1074 _, pid, alive = self._InstancePidAlive(name) 1075 if pid > 0 and alive: 1076 if force or not acpi: 1077 utils.KillProcess(pid) 1078 else: 1079 self._CallMonitorCommand(name, "system_powerdown")
1080
1081 - def CleanupInstance(self, instance_name):
1082 """Cleanup after a stopped instance 1083 1084 """ 1085 pidfile, pid, alive = self._InstancePidAlive(instance_name) 1086 if pid > 0 and alive: 1087 raise errors.HypervisorError("Cannot cleanup a live instance") 1088 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1089
1090 - def RebootInstance(self, instance):
1091 """Reboot an instance. 1092 1093 """ 1094 # For some reason if we do a 'send-key ctrl-alt-delete' to the control 1095 # socket the instance will stop, but now power up again. So we'll resort 1096 # to shutdown and restart. 1097 _, _, alive = self._InstancePidAlive(instance.name) 1098 if not alive: 1099 raise errors.HypervisorError("Failed to reboot instance %s:" 1100 " not running" % instance.name) 1101 # StopInstance will delete the saved KVM runtime so: 1102 # ...first load it... 1103 kvm_runtime = self._LoadKVMRuntime(instance) 1104 # ...now we can safely call StopInstance... 1105 if not self.StopInstance(instance): 1106 self.StopInstance(instance, force=True) 1107 # ...and finally we can save it again, and execute it... 1108 self._SaveKVMRuntime(instance, kvm_runtime) 1109 self._ExecuteKVMRuntime(instance, kvm_runtime)
1110
1111 - def MigrationInfo(self, instance):
1112 """Get instance information to perform a migration. 1113 1114 @type instance: L{objects.Instance} 1115 @param instance: instance to be migrated 1116 @rtype: string 1117 @return: content of the KVM runtime file 1118 1119 """ 1120 return self._ReadKVMRuntime(instance.name)
1121
1122 - def AcceptInstance(self, instance, info, target):
1123 """Prepare to accept an instance. 1124 1125 @type instance: L{objects.Instance} 1126 @param instance: instance to be accepted 1127 @type info: string 1128 @param info: content of the KVM runtime file on the source node 1129 @type target: string 1130 @param target: target host (usually ip), on this node 1131 1132 """ 1133 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info) 1134 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT]) 1135 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1136
1137 - def FinalizeMigration(self, instance, info, success):
1138 """Finalize an instance migration. 1139 1140 Stop the incoming mode KVM. 1141 1142 @type instance: L{objects.Instance} 1143 @param instance: instance whose migration is being finalized 1144 1145 """ 1146 if success: 1147 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info) 1148 kvm_nics = kvm_runtime[1] 1149 1150 for nic_seq, nic in enumerate(kvm_nics): 1151 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1152 # Bridged interfaces have already been configured 1153 continue 1154 try: 1155 tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq)) 1156 except EnvironmentError, err: 1157 logging.warning("Failed to find host interface for %s NIC #%d: %s", 1158 instance.name, nic_seq, str(err)) 1159 continue 1160 try: 1161 self._ConfigureNIC(instance, nic_seq, nic, tap) 1162 except errors.HypervisorError, err: 1163 logging.warning(str(err)) 1164 1165 self._WriteKVMRuntime(instance.name, info) 1166 else: 1167 self.StopInstance(instance, force=True)
1168
1169 - def MigrateInstance(self, instance, target, live):
1170 """Migrate an instance to a target node. 1171 1172 The migration will not be attempted if the instance is not 1173 currently running. 1174 1175 @type instance: L{objects.Instance} 1176 @param instance: the instance to be migrated 1177 @type target: string 1178 @param target: ip address of the target node 1179 @type live: boolean 1180 @param live: perform a live migration 1181 1182 """ 1183 instance_name = instance.name 1184 port = instance.hvparams[constants.HV_MIGRATION_PORT] 1185 pidfile, pid, alive = self._InstancePidAlive(instance_name) 1186 if not alive: 1187 raise errors.HypervisorError("Instance not running, cannot migrate") 1188 1189 if not live: 1190 self._CallMonitorCommand(instance_name, "stop") 1191 1192 migrate_command = ("migrate_set_speed %dm" % 1193 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH]) 1194 self._CallMonitorCommand(instance_name, migrate_command) 1195 1196 migrate_command = ("migrate_set_downtime %dms" % 1197 instance.hvparams[constants.HV_MIGRATION_DOWNTIME]) 1198 self._CallMonitorCommand(instance_name, migrate_command) 1199 1200 migrate_command = "migrate -d tcp:%s:%s" % (target, port) 1201 self._CallMonitorCommand(instance_name, migrate_command) 1202 1203 info_command = "info migrate" 1204 done = False 1205 broken_answers = 0 1206 while not done: 1207 result = self._CallMonitorCommand(instance_name, info_command) 1208 match = self._MIGRATION_STATUS_RE.search(result.stdout) 1209 if not match: 1210 broken_answers += 1 1211 if not result.stdout: 1212 logging.info("KVM: empty 'info migrate' result") 1213 else: 1214 logging.warning("KVM: unknown 'info migrate' result: %s", 1215 result.stdout) 1216 time.sleep(self._MIGRATION_INFO_RETRY_DELAY) 1217 else: 1218 status = match.group(1) 1219 if status == "completed": 1220 done = True 1221 elif status == "active": 1222 # reset the broken answers count 1223 broken_answers = 0 1224 time.sleep(self._MIGRATION_INFO_RETRY_DELAY) 1225 elif status == "failed" or status == "cancelled": 1226 if not live: 1227 self._CallMonitorCommand(instance_name, 'cont') 1228 raise errors.HypervisorError("Migration %s at the kvm level" % 1229 status) 1230 else: 1231 logging.warning("KVM: unknown migration status '%s'", status) 1232 broken_answers += 1 1233 time.sleep(self._MIGRATION_INFO_RETRY_DELAY) 1234 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS: 1235 raise errors.HypervisorError("Too many 'info migrate' broken answers") 1236 1237 utils.KillProcess(pid) 1238 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1239
1240 - def GetNodeInfo(self):
1241 """Return information about the node. 1242 1243 This is just a wrapper over the base GetLinuxNodeInfo method. 1244 1245 @return: a dict with the following keys (values in MiB): 1246 - memory_total: the total memory size on the node 1247 - memory_free: the available memory on the node for instances 1248 - memory_dom0: the memory used by the node itself, if available 1249 1250 """ 1251 return self.GetLinuxNodeInfo()
1252 1253 @classmethod
1254 - def GetInstanceConsole(cls, instance, hvparams, beparams):
1255 """Return a command for connecting to the console of an instance. 1256 1257 """ 1258 if hvparams[constants.HV_SERIAL_CONSOLE]: 1259 cmd = [constants.KVM_CONSOLE_WRAPPER, 1260 constants.SOCAT_PATH, utils.ShellQuote(instance.name), 1261 utils.ShellQuote(cls._InstanceMonitor(instance.name)), 1262 "STDIO,%s" % cls._SocatUnixConsoleParams(), 1263 "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)] 1264 return objects.InstanceConsole(instance=instance.name, 1265 kind=constants.CONS_SSH, 1266 host=instance.primary_node, 1267 user=constants.GANETI_RUNAS, 1268 command=cmd) 1269 1270 vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS] 1271 if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT: 1272 display = instance.network_port - constants.VNC_BASE_PORT 1273 return objects.InstanceConsole(instance=instance.name, 1274 kind=constants.CONS_VNC, 1275 host=vnc_bind_address, 1276 port=instance.network_port, 1277 display=display) 1278 1279 return objects.InstanceConsole(instance=instance.name, 1280 kind=constants.CONS_MESSAGE, 1281 message=("No serial shell for instance %s" % 1282 instance.name))
1283
1284 - def Verify(self):
1285 """Verify the hypervisor. 1286 1287 Check that the binary exists. 1288 1289 """ 1290 if not os.path.exists(constants.KVM_PATH): 1291 return "The kvm binary ('%s') does not exist." % constants.KVM_PATH 1292 if not os.path.exists(constants.SOCAT_PATH): 1293 return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1294 1295 @classmethod
1296 - def CheckParameterSyntax(cls, hvparams):
1297 """Check the given parameters for validity. 1298 1299 @type hvparams: dict 1300 @param hvparams: dictionary with parameter names/value 1301 @raise errors.HypervisorError: when a parameter is not valid 1302 1303 """ 1304 super(KVMHypervisor, cls).CheckParameterSyntax(hvparams) 1305 1306 kernel_path = hvparams[constants.HV_KERNEL_PATH] 1307 if kernel_path: 1308 if not hvparams[constants.HV_ROOT_PATH]: 1309 raise errors.HypervisorError("Need a root partition for the instance," 1310 " if a kernel is defined") 1311 1312 if (hvparams[constants.HV_VNC_X509_VERIFY] and 1313 not hvparams[constants.HV_VNC_X509]): 1314 raise errors.HypervisorError("%s must be defined, if %s is" % 1315 (constants.HV_VNC_X509, 1316 constants.HV_VNC_X509_VERIFY)) 1317 1318 boot_order = hvparams[constants.HV_BOOT_ORDER] 1319 if (boot_order == constants.HT_BO_CDROM and 1320 not hvparams[constants.HV_CDROM_IMAGE_PATH]): 1321 raise errors.HypervisorError("Cannot boot from cdrom without an" 1322 " ISO path") 1323 1324 security_model = hvparams[constants.HV_SECURITY_MODEL] 1325 if security_model == constants.HT_SM_USER: 1326 if not hvparams[constants.HV_SECURITY_DOMAIN]: 1327 raise errors.HypervisorError("A security domain (user to run kvm as)" 1328 " must be specified") 1329 elif (security_model == constants.HT_SM_NONE or 1330 security_model == constants.HT_SM_POOL): 1331 if hvparams[constants.HV_SECURITY_DOMAIN]: 1332 raise errors.HypervisorError("Cannot have a security domain when the" 1333 " security model is 'none' or 'pool'") 1334 1335 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 1336 if spice_bind: 1337 spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION] 1338 if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED: 1339 # if an IP version is specified, the spice_bind parameter must be an 1340 # IP of that family 1341 if (netutils.IP4Address.IsValid(spice_bind) and 1342 spice_ip_version != constants.IP4_VERSION): 1343 raise errors.HypervisorError("spice: got an IPv4 address (%s), but" 1344 " the specified IP version is %s" % 1345 (spice_bind, spice_ip_version)) 1346 1347 if (netutils.IP6Address.IsValid(spice_bind) and 1348 spice_ip_version != constants.IP6_VERSION): 1349 raise errors.HypervisorError("spice: got an IPv6 address (%s), but" 1350 " the specified IP version is %s" % 1351 (spice_bind, spice_ip_version))
1352 1353 @classmethod
1354 - def ValidateParameters(cls, hvparams):
1355 """Check the given parameters for validity. 1356 1357 @type hvparams: dict 1358 @param hvparams: dictionary with parameter names/value 1359 @raise errors.HypervisorError: when a parameter is not valid 1360 1361 """ 1362 super(KVMHypervisor, cls).ValidateParameters(hvparams) 1363 1364 security_model = hvparams[constants.HV_SECURITY_MODEL] 1365 if security_model == constants.HT_SM_USER: 1366 username = hvparams[constants.HV_SECURITY_DOMAIN] 1367 try: 1368 pwd.getpwnam(username) 1369 except KeyError: 1370 raise errors.HypervisorError("Unknown security domain user %s" 1371 % username) 1372 1373 spice_bind = hvparams[constants.HV_KVM_SPICE_BIND] 1374 if spice_bind: 1375 # only one of VNC and SPICE can be used currently. 1376 if hvparams[constants.HV_VNC_BIND_ADDRESS]: 1377 raise errors.HypervisorError("both SPICE and VNC are configured, but" 1378 " only one of them can be used at a" 1379 " given time.") 1380 1381 # KVM version should be >= 0.14.0 1382 _, v_major, v_min, _ = cls._GetKVMVersion() 1383 if (v_major, v_min) < (0, 14): 1384 raise errors.HypervisorError("spice is configured, but it is not" 1385 " available in versions of KVM < 0.14") 1386 1387 # if spice_bind is not an IP address, it must be a valid interface 1388 bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) 1389 or netutils.IP6Address.IsValid(spice_bind)) 1390 if not bound_to_addr and not netutils.IsValidInterface(spice_bind): 1391 raise errors.HypervisorError("spice: the %s parameter must be either" 1392 " a valid IP address or interface name" % 1393 constants.HV_KVM_SPICE_BIND)
1394 1395 @classmethod
1396 - def PowercycleNode(cls):
1397 """KVM powercycle, just a wrapper over Linux powercycle. 1398 1399 """ 1400 cls.LinuxPowercycle()
1401