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