1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
53
54
55 TUNSETIFF = 0x400454ca
56 TUNGETIFF = 0x800454d2
57 TUNGETFEATURES = 0x800454cf
58 IFF_TAP = 0x0002
59 IFF_NO_PI = 0x1000
60 IFF_VNET_HDR = 0x4000
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
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
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
126 ifname = struct.unpack("16sh", res)[0].strip("\x00")
127 return (ifname, tapfd)
128
131 """KVM hypervisor interface"""
132 CAN_MIGRATE = True
133
134 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
135 _PIDS_DIR = _ROOT_DIR + "/pid"
136 _UIDS_DIR = _ROOT_DIR + "/uid"
137 _CTRL_DIR = _ROOT_DIR + "/ctrl"
138 _CONF_DIR = _ROOT_DIR + "/conf"
139 _NICS_DIR = _ROOT_DIR + "/nic"
140 _KEYMAP_DIR = _ROOT_DIR + "/keymap"
141
142 _CHROOT_DIR = _ROOT_DIR + "/chroot"
143
144
145
146
147
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,
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
224
225 @classmethod
231
232 @classmethod
238
239 @classmethod
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
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
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
316 """Returns the instance monitor socket name
317
318 """
319 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
320
321 @classmethod
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
342 """Returns the instance KVM runtime filename
343
344 """
345 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
346
347 @classmethod
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
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
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
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
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
429
430 @staticmethod
474
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
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
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
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
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
574
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
583 disk_cache = hvp[constants.HV_DISK_CACHE]
584 if instance.disk_template in constants.DTS_EXT_MIRROR:
585 if disk_cache != "none":
586
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
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
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
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
628 boot_val = ""
629 if boot_cdrom:
630 kvm_cmd.extend(["-boot", "d"])
631 if needs_boot_flag:
632 boot_val = ",boot=on"
633
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
709
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
730
731 if netutils.IsValidInterface(spice_bind):
732
733
734 addresses = netutils.GetInterfaceIpAddresses(spice_bind)
735 spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
736
737
738
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
746 elif (addresses[constants.IP4_VERSION] and
747 addresses[constants.IP6_VERSION]):
748
749
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
765
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
786
787 kvm_nics = instance.nics
788 hvparams = hvp
789
790 return (kvm_cmd, kvm_nics, hvparams)
791
801
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
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
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
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
863
864
865
866
867
868
869
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
882
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
891
892
893
894 utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
895 kvm_cmd.extend(["-k", keymap_path])
896
897
898
899
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
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
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
945
946
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
961
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):
1008
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
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
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):
1080
1089
1110
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
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
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
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
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
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
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
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
1294
1295 @classmethod
1352
1353 @classmethod
1394
1395 @classmethod
1397 """KVM powercycle, just a wrapper over Linux powercycle.
1398
1399 """
1400 cls.LinuxPowercycle()
1401