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
141 _CHROOT_DIR = _ROOT_DIR + "/chroot"
142
143
144
145
146
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
214
215 @classmethod
221
222 @classmethod
228
229 @classmethod
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
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
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
306 """Returns the instance monitor socket name
307
308 """
309 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
310
311 @classmethod
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
332 """Returns the instance KVM runtime filename
333
334 """
335 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
336
337 @classmethod
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
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
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
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
411
412 @staticmethod
456
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
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
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
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
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
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
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
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
653
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
692
693 kvm_nics = instance.nics
694 hvparams = hvp
695
696 return (kvm_cmd, kvm_nics, hvparams)
697
707
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
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
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
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
769
770
771
772
773
774
775
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
792
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
798
799
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
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
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
845
846
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
861
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
907
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
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):
960
969
990
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
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
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
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
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
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
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
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
1172
1173 @classmethod
1212
1213 @classmethod
1232
1233 @classmethod
1235 """KVM powercycle, just a wrapper over Linux powercycle.
1236
1237 """
1238 cls.LinuxPowercycle()
1239