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