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 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 from ganeti import netutils
45
46
47 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
51 """Write a script to connect a net interface to the proper bridge.
52
53 This can be used by any qemu-type hypervisor.
54
55 @type instance: L{objects.Instance}
56 @param instance: Instance object
57 @type nic: L{objects.NIC}
58 @param nic: NIC object
59 @type index: int
60 @param index: NIC index
61 @return: Script
62 @rtype: string
63
64 """
65 if instance.tags:
66 tags = " ".join(instance.tags)
67 else:
68 tags = ""
69
70 buf = StringIO()
71 sw = utils.ShellWriter(buf)
72 sw.Write("#!/bin/sh")
73 sw.Write("# this is autogenerated by Ganeti, please do not edit")
74 sw.Write("export PATH=$PATH:/sbin:/usr/sbin")
75 sw.Write("export INSTANCE=%s", utils.ShellQuote(instance.name))
76 sw.Write("export MAC=%s", utils.ShellQuote(nic.mac))
77 sw.Write("export MODE=%s",
78 utils.ShellQuote(nic.nicparams[constants.NIC_MODE]))
79 sw.Write("export INTERFACE=\"$1\"")
80 sw.Write("export TAGS=%s", utils.ShellQuote(tags))
81
82 if nic.ip:
83 sw.Write("export IP=%s", utils.ShellQuote(nic.ip))
84
85 if nic.nicparams[constants.NIC_LINK]:
86 sw.Write("export LINK=%s",
87 utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
88
89 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
90 sw.Write("export BRIDGE=%s",
91 utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
92
93
94 sw.Write("if [ -x %s ]; then", utils.ShellQuote(_KVM_NETWORK_SCRIPT))
95 sw.IncIndent()
96 try:
97 sw.Write("# Execute the user-specific vif file")
98 sw.Write(_KVM_NETWORK_SCRIPT)
99 finally:
100 sw.DecIndent()
101 sw.Write("else")
102 sw.IncIndent()
103 try:
104 sw.Write("ifconfig $INTERFACE 0.0.0.0 up")
105
106 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
107 sw.Write("# Connect the interface to the bridge")
108 sw.Write("brctl addif $BRIDGE $INTERFACE")
109
110 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
111 if not nic.ip:
112 raise errors.HypervisorError("nic/%d is routed, but has no IP"
113 " address" % index)
114
115 sw.Write("# Route traffic targeted at the IP to the interface")
116 if nic.nicparams[constants.NIC_LINK]:
117 sw.Write("while ip rule del dev $INTERFACE; do :; done")
118 sw.Write("ip rule add dev $INTERFACE table $LINK")
119 sw.Write("ip route replace $IP table $LINK proto static"
120 " dev $INTERFACE")
121 else:
122 sw.Write("ip route replace $IP proto static dev $INTERFACE")
123
124 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
125 sw.Write(" if [ -d %s ]; then", interface_v4_conf)
126 sw.IncIndent()
127 try:
128 sw.Write("echo 1 > %s/proxy_arp", interface_v4_conf)
129 sw.Write("echo 1 > %s/forwarding", interface_v4_conf)
130 finally:
131 sw.DecIndent()
132 sw.Write("fi")
133
134 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
135 sw.Write("if [ -d %s ]; then", interface_v6_conf)
136 sw.IncIndent()
137 try:
138 sw.Write("echo 1 > %s/proxy_ndp", interface_v6_conf)
139 sw.Write("echo 1 > %s/forwarding", interface_v6_conf)
140 finally:
141 sw.DecIndent()
142 sw.Write("fi")
143 finally:
144 sw.DecIndent()
145 sw.Write("fi")
146
147 return buf.getvalue()
148
151 """KVM hypervisor interface"""
152 CAN_MIGRATE = True
153
154 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
155 _PIDS_DIR = _ROOT_DIR + "/pid"
156 _UIDS_DIR = _ROOT_DIR + "/uid"
157 _CTRL_DIR = _ROOT_DIR + "/ctrl"
158 _CONF_DIR = _ROOT_DIR + "/conf"
159
160 _CHROOT_DIR = _ROOT_DIR + "/chroot"
161
162
163
164
165
166 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
167 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR,
168 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
169
170 PARAMETERS = {
171 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
172 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
173 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
174 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
175 constants.HV_ACPI: hv_base.NO_CHECK,
176 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
177 constants.HV_VNC_BIND_ADDRESS:
178 (False, lambda x: (netutils.IsValidIP4(x) or utils.IsNormAbsPath(x)),
179 "the VNC bind address must be either a valid IP address or an absolute"
180 " pathname", None, None),
181 constants.HV_VNC_TLS: hv_base.NO_CHECK,
182 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
183 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
184 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
185 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
186 constants.HV_BOOT_ORDER:
187 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
188 constants.HV_NIC_TYPE:
189 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
190 constants.HV_DISK_TYPE:
191 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
192 constants.HV_USB_MOUSE:
193 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
194 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
195 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
196 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
197 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
198 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
199 constants.HV_DISK_CACHE:
200 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
201 constants.HV_SECURITY_MODEL:
202 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
203 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
204 constants.HV_KVM_FLAG:
205 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
206 constants.HV_VHOST_NET: hv_base.NO_CHECK,
207 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
208 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
209 }
210
211 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
212 re.M | re.I)
213 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
214 _MIGRATION_INFO_RETRY_DELAY = 2
215
216 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
217
218 ANCILLARY_FILES = [
219 _KVM_NETWORK_SCRIPT,
220 ]
221
228
229 @classmethod
235
236 @classmethod
242
243 @classmethod
245 """Check pid file for instance information.
246
247 Check that a pid file is associated with an instance, and retrieve
248 information from its command line.
249
250 @type pid: string or int
251 @param pid: process id of the instance to check
252 @rtype: tuple
253 @return: (instance_name, memory, vcpus)
254 @raise errors.HypervisorError: when an instance cannot be found
255
256 """
257 alive = utils.IsProcessAlive(pid)
258 if not alive:
259 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
260
261 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
262 try:
263 cmdline = utils.ReadFile(cmdline_file)
264 except EnvironmentError, err:
265 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
266 (pid, err))
267
268 instance = None
269 memory = 0
270 vcpus = 0
271
272 arg_list = cmdline.split('\x00')
273 while arg_list:
274 arg = arg_list.pop(0)
275 if arg == "-name":
276 instance = arg_list.pop(0)
277 elif arg == "-m":
278 memory = int(arg_list.pop(0))
279 elif arg == "-smp":
280 vcpus = int(arg_list.pop(0))
281
282 if instance is None:
283 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
284 " instance" % pid)
285
286 return (instance, memory, vcpus)
287
289 """Returns the instance pidfile, pid, and liveness.
290
291 @type instance_name: string
292 @param instance_name: instance name
293 @rtype: tuple
294 @return: (pid file name, pid, liveness)
295
296 """
297 pidfile = self._InstancePidFile(instance_name)
298 pid = utils.ReadPidFile(pidfile)
299
300 alive = False
301 try:
302 cmd_instance = self._InstancePidInfo(pid)[0]
303 alive = (cmd_instance == instance_name)
304 except errors.HypervisorError:
305 pass
306
307 return (pidfile, pid, alive)
308
310 """Raises an error unless the given instance is down.
311
312 """
313 alive = self._InstancePidAlive(instance_name)[2]
314 if alive:
315 raise errors.HypervisorError("Failed to start instance %s: %s" %
316 (instance_name, "already running"))
317
318 @classmethod
320 """Returns the instance monitor socket name
321
322 """
323 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
324
325 @classmethod
327 """Returns the instance serial socket name
328
329 """
330 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
331
332 @staticmethod
334 """Returns the correct parameters for socat
335
336 If we have a new-enough socat we can use raw mode with an escape character.
337
338 """
339 if constants.SOCAT_USE_ESCAPE:
340 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
341 else:
342 return "echo=0,icanon=0"
343
344 @classmethod
346 """Returns the instance KVM runtime filename
347
348 """
349 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
350
351 @classmethod
353 """Returns the name of the KVM chroot dir of the instance
354
355 """
356 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
357
358 @classmethod
360 """Try to read a uid file
361
362 """
363 if os.path.exists(uid_file):
364 try:
365 uid = int(utils.ReadOneLineFile(uid_file))
366 return uid
367 except EnvironmentError:
368 logging.warning("Can't read uid file", exc_info=True)
369 except (TypeError, ValueError):
370 logging.warning("Can't parse uid file contents", exc_info=True)
371 return None
372
373 @classmethod
405
406 @staticmethod
408 """Write a script to connect a net interface to the proper bridge.
409
410 This can be used by any qemu-type hypervisor.
411
412 @param instance: instance we're acting on
413 @type instance: instance object
414 @param seq: nic sequence number
415 @type seq: int
416 @param nic: nic we're acting on
417 @type nic: nic object
418 @return: netscript file name
419 @rtype: string
420
421 """
422 script = _WriteNetScript(instance, nic, seq)
423
424
425
426 (tmpfd, tmpfile_name) = tempfile.mkstemp()
427 tmpfile = os.fdopen(tmpfd, 'w')
428 try:
429 tmpfile.write(script)
430 finally:
431 tmpfile.close()
432 os.chmod(tmpfile_name, 0755)
433 return tmpfile_name
434
436 """Get the list of running instances.
437
438 We can do this by listing our live instances directory and
439 checking whether the associated kvm process is still alive.
440
441 """
442 result = []
443 for name in os.listdir(self._PIDS_DIR):
444 if self._InstancePidAlive(name)[2]:
445 result.append(name)
446 return result
447
449 """Get instance properties.
450
451 @type instance_name: string
452 @param instance_name: the instance name
453 @rtype: tuple of strings
454 @return: (name, id, memory, vcpus, stat, times)
455
456 """
457 _, pid, alive = self._InstancePidAlive(instance_name)
458 if not alive:
459 return None
460
461 _, memory, vcpus = self._InstancePidInfo(pid)
462 stat = "---b-"
463 times = "0"
464
465 return (instance_name, pid, memory, vcpus, stat, times)
466
468 """Get properties of all instances.
469
470 @return: list of tuples (name, id, memory, vcpus, stat, times)
471
472 """
473 data = []
474 for name in os.listdir(self._PIDS_DIR):
475 try:
476 info = self.GetInstanceInfo(name)
477 except errors.HypervisorError:
478 continue
479 if info:
480 data.append(info)
481 return data
482
484 """Generate KVM information to start an instance.
485
486 """
487 pidfile = self._InstancePidFile(instance.name)
488 kvm = constants.KVM_PATH
489 kvm_cmd = [kvm]
490
491 kvm_cmd.extend(['-name', instance.name])
492 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
493 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
494 kvm_cmd.extend(['-pidfile', pidfile])
495 kvm_cmd.extend(['-daemonize'])
496 if not instance.hvparams[constants.HV_ACPI]:
497 kvm_cmd.extend(['-no-acpi'])
498
499 hvp = instance.hvparams
500 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
501 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
502 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
503
504 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
505 kvm_cmd.extend(["-enable-kvm"])
506 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
507 kvm_cmd.extend(["-disable-kvm"])
508
509 if boot_network:
510 kvm_cmd.extend(['-boot', 'n'])
511
512 disk_type = hvp[constants.HV_DISK_TYPE]
513 if disk_type == constants.HT_DISK_PARAVIRTUAL:
514 if_val = ',if=virtio'
515 else:
516 if_val = ',if=%s' % disk_type
517
518 disk_cache = hvp[constants.HV_DISK_CACHE]
519 if disk_cache != constants.HT_CACHE_DEFAULT:
520 cache_val = ",cache=%s" % disk_cache
521 else:
522 cache_val = ""
523 for cfdev, dev_path in block_devices:
524 if cfdev.mode != constants.DISK_RDWR:
525 raise errors.HypervisorError("Instance has read-only disks which"
526 " are not supported by KVM")
527
528 if boot_disk:
529 kvm_cmd.extend(['-boot', 'c'])
530 if disk_type != constants.HT_DISK_IDE:
531 boot_val = ',boot=on'
532 else:
533 boot_val = ''
534
535 boot_disk = False
536 else:
537 boot_val = ''
538
539 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
540 cache_val)
541 kvm_cmd.extend(['-drive', drive_val])
542
543 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
544 if iso_image:
545 options = ',format=raw,media=cdrom'
546 if boot_cdrom:
547 kvm_cmd.extend(['-boot', 'd'])
548 if disk_type != constants.HT_DISK_IDE:
549 options = '%s,boot=on' % options
550 else:
551 if disk_type == constants.HT_DISK_PARAVIRTUAL:
552 if_val = ',if=virtio'
553 else:
554 if_val = ',if=%s' % disk_type
555 options = '%s%s' % (options, if_val)
556 drive_val = 'file=%s%s' % (iso_image, options)
557 kvm_cmd.extend(['-drive', drive_val])
558
559 kernel_path = hvp[constants.HV_KERNEL_PATH]
560 if kernel_path:
561 kvm_cmd.extend(['-kernel', kernel_path])
562 initrd_path = hvp[constants.HV_INITRD_PATH]
563 if initrd_path:
564 kvm_cmd.extend(['-initrd', initrd_path])
565 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
566 hvp[constants.HV_KERNEL_ARGS]]
567 if hvp[constants.HV_SERIAL_CONSOLE]:
568 root_append.append('console=ttyS0,38400')
569 kvm_cmd.extend(['-append', ' '.join(root_append)])
570
571 mem_path = hvp[constants.HV_MEM_PATH]
572 if mem_path:
573 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
574
575 mouse_type = hvp[constants.HV_USB_MOUSE]
576 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
577
578 if mouse_type:
579 kvm_cmd.extend(['-usb'])
580 kvm_cmd.extend(['-usbdevice', mouse_type])
581 elif vnc_bind_address:
582 kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
583
584 if vnc_bind_address:
585 if netutils.IsValidIP4(vnc_bind_address):
586 if instance.network_port > constants.VNC_BASE_PORT:
587 display = instance.network_port - constants.VNC_BASE_PORT
588 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
589 vnc_arg = ':%d' % (display)
590 else:
591 vnc_arg = '%s:%d' % (vnc_bind_address, display)
592 else:
593 logging.error("Network port is not a valid VNC display (%d < %d)."
594 " Not starting VNC", instance.network_port,
595 constants.VNC_BASE_PORT)
596 vnc_arg = 'none'
597
598
599
600 vnc_append = ''
601 if hvp[constants.HV_VNC_TLS]:
602 vnc_append = '%s,tls' % vnc_append
603 if hvp[constants.HV_VNC_X509_VERIFY]:
604 vnc_append = '%s,x509verify=%s' % (vnc_append,
605 hvp[constants.HV_VNC_X509])
606 elif hvp[constants.HV_VNC_X509]:
607 vnc_append = '%s,x509=%s' % (vnc_append,
608 hvp[constants.HV_VNC_X509])
609 if hvp[constants.HV_VNC_PASSWORD_FILE]:
610 vnc_append = '%s,password' % vnc_append
611
612 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
613
614 else:
615 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
616
617 kvm_cmd.extend(['-vnc', vnc_arg])
618 else:
619 kvm_cmd.extend(['-nographic'])
620
621 monitor_dev = ("unix:%s,server,nowait" %
622 self._InstanceMonitor(instance.name))
623 kvm_cmd.extend(['-monitor', monitor_dev])
624 if hvp[constants.HV_SERIAL_CONSOLE]:
625 serial_dev = ('unix:%s,server,nowait' %
626 self._InstanceSerial(instance.name))
627 kvm_cmd.extend(['-serial', serial_dev])
628 else:
629 kvm_cmd.extend(['-serial', 'none'])
630
631 if hvp[constants.HV_USE_LOCALTIME]:
632 kvm_cmd.extend(['-localtime'])
633
634 if hvp[constants.HV_KVM_USE_CHROOT]:
635 kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
636
637
638
639 kvm_nics = instance.nics
640 hvparams = hvp
641
642 return (kvm_cmd, kvm_nics, hvparams)
643
653
655 """Read an instance's KVM runtime
656
657 """
658 try:
659 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
660 except EnvironmentError, err:
661 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
662 return file_content
663
665 """Save an instance's KVM runtime
666
667 """
668 kvm_cmd, kvm_nics, hvparams = kvm_runtime
669 serialized_nics = [nic.ToDict() for nic in kvm_nics]
670 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
671 self._WriteKVMRuntime(instance.name, serialized_form)
672
674 """Load an instance's KVM runtime
675
676 """
677 if not serialized_runtime:
678 serialized_runtime = self._ReadKVMRuntime(instance.name)
679 loaded_runtime = serializer.Load(serialized_runtime)
680 kvm_cmd, serialized_nics, hvparams = loaded_runtime
681 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
682 return (kvm_cmd, kvm_nics, hvparams)
683
685 """Run the KVM cmd and check for errors
686
687 @type name: string
688 @param name: instance name
689 @type kvm_cmd: list of strings
690 @param kvm_cmd: runcmd input for kvm
691
692 """
693 result = utils.RunCmd(kvm_cmd)
694 if result.failed:
695 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
696 (name, result.fail_reason, result.output))
697 if not self._InstancePidAlive(name)[2]:
698 raise errors.HypervisorError("Failed to start instance %s" % name)
699
701 """Execute a KVM cmd, after completing it with some last minute data
702
703 @type incoming: tuple of strings
704 @param incoming: (target_host_ip, port)
705
706 """
707
708
709
710
711
712
713
714
715 conf_hvp = instance.hvparams
716 name = instance.name
717 self._CheckDown(name)
718
719 temp_files = []
720
721 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
722 up_hvp = objects.FillDict(conf_hvp, up_hvp)
723
724
725
726 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
727 if security_model == constants.HT_SM_USER:
728 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
729
730
731
732
733 if not kvm_nics:
734 kvm_cmd.extend(["-net", "none"])
735 else:
736 tap_extra = ""
737 nic_type = up_hvp[constants.HV_NIC_TYPE]
738 if nic_type == constants.HT_NIC_PARAVIRTUAL:
739 nic_model = "model=virtio"
740 if up_hvp[constants.HV_VHOST_NET]:
741 tap_extra = ",vhost=on"
742 else:
743 nic_model = "model=%s" % nic_type
744
745 for nic_seq, nic in enumerate(kvm_nics):
746 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
747 script = self._WriteNetScriptFile(instance, nic_seq, nic)
748 tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
749 kvm_cmd.extend(["-net", nic_val])
750 kvm_cmd.extend(["-net", tap_val])
751 temp_files.append(script)
752
753 if incoming:
754 target, port = incoming
755 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
756
757
758
759
760 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
761 vnc_pwd = None
762 if vnc_pwd_file:
763 try:
764 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
765 except EnvironmentError, err:
766 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
767 % (vnc_pwd_file, err))
768
769 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
770 utils.EnsureDirs([(self._InstanceChrootDir(name),
771 constants.SECURE_DIR_MODE)])
772
773 if security_model == constants.HT_SM_POOL:
774 ss = ssconf.SimpleStore()
775 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
776 all_uids = set(uidpool.ExpandUidPool(uid_pool))
777 uid = uidpool.RequestUnusedUid(all_uids)
778 try:
779 username = pwd.getpwuid(uid.GetUid()).pw_name
780 kvm_cmd.extend(["-runas", username])
781 self._RunKVMCmd(name, kvm_cmd)
782 except:
783 uidpool.ReleaseUid(uid)
784 raise
785 else:
786 uid.Unlock()
787 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
788 else:
789 self._RunKVMCmd(name, kvm_cmd)
790
791 if vnc_pwd:
792 change_cmd = 'change vnc password %s' % vnc_pwd
793 self._CallMonitorCommand(instance.name, change_cmd)
794
795 for filename in temp_files:
796 utils.RemoveFile(filename)
797
806
808 """Invoke a command on the instance monitor.
809
810 """
811 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
812 (utils.ShellQuote(command),
813 constants.SOCAT_PATH,
814 utils.ShellQuote(self._InstanceMonitor(instance_name))))
815 result = utils.RunCmd(socat)
816 if result.failed:
817 msg = ("Failed to send command '%s' to instance %s."
818 " output: %s, error: %s, fail_reason: %s" %
819 (command, instance_name,
820 result.stdout, result.stderr, result.fail_reason))
821 raise errors.HypervisorError(msg)
822
823 return result
824
825 @classmethod
827 """Return the installed KVM version
828
829 @return: (version, v_maj, v_min, v_rev), or None
830
831 """
832 result = utils.RunCmd([constants.KVM_PATH, "--help"])
833 if result.failed:
834 return None
835 match = cls._VERSION_RE.search(result.output.splitlines()[0])
836 if not match:
837 return None
838 return (match.group(0), match.group(1), match.group(2), match.group(3))
839
840 - def StopInstance(self, instance, force=False, retry=False, name=None):
857
866
887
889 """Get instance information to perform a migration.
890
891 @type instance: L{objects.Instance}
892 @param instance: instance to be migrated
893 @rtype: string
894 @return: content of the KVM runtime file
895
896 """
897 return self._ReadKVMRuntime(instance.name)
898
900 """Prepare to accept an instance.
901
902 @type instance: L{objects.Instance}
903 @param instance: instance to be accepted
904 @type info: string
905 @param info: content of the KVM runtime file on the source node
906 @type target: string
907 @param target: target host (usually ip), on this node
908
909 """
910 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
911 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
912 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
913
915 """Finalize an instance migration.
916
917 Stop the incoming mode KVM.
918
919 @type instance: L{objects.Instance}
920 @param instance: instance whose migration is being finalized
921
922 """
923 if success:
924 self._WriteKVMRuntime(instance.name, info)
925 else:
926 self.StopInstance(instance, force=True)
927
929 """Migrate an instance to a target node.
930
931 The migration will not be attempted if the instance is not
932 currently running.
933
934 @type instance: L{objects.Instance}
935 @param instance: the instance to be migrated
936 @type target: string
937 @param target: ip address of the target node
938 @type live: boolean
939 @param live: perform a live migration
940
941 """
942 instance_name = instance.name
943 port = instance.hvparams[constants.HV_MIGRATION_PORT]
944 pidfile, pid, alive = self._InstancePidAlive(instance_name)
945 if not alive:
946 raise errors.HypervisorError("Instance not running, cannot migrate")
947
948 if not live:
949 self._CallMonitorCommand(instance_name, 'stop')
950
951 migrate_command = ('migrate_set_speed %dm' %
952 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
953 self._CallMonitorCommand(instance_name, migrate_command)
954
955 migrate_command = ('migrate_set_downtime %dms' %
956 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
957 self._CallMonitorCommand(instance_name, migrate_command)
958
959 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
960 self._CallMonitorCommand(instance_name, migrate_command)
961
962 info_command = 'info migrate'
963 done = False
964 broken_answers = 0
965 while not done:
966 result = self._CallMonitorCommand(instance_name, info_command)
967 match = self._MIGRATION_STATUS_RE.search(result.stdout)
968 if not match:
969 broken_answers += 1
970 if not result.stdout:
971 logging.info("KVM: empty 'info migrate' result")
972 else:
973 logging.warning("KVM: unknown 'info migrate' result: %s",
974 result.stdout)
975 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
976 else:
977 status = match.group(1)
978 if status == 'completed':
979 done = True
980 elif status == 'active':
981
982 broken_answers = 0
983 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
984 elif status == 'failed' or status == 'cancelled':
985 if not live:
986 self._CallMonitorCommand(instance_name, 'cont')
987 raise errors.HypervisorError("Migration %s at the kvm level" %
988 status)
989 else:
990 logging.warning("KVM: unknown migration status '%s'", status)
991 broken_answers += 1
992 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
993 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
994 raise errors.HypervisorError("Too many 'info migrate' broken answers")
995
996 utils.KillProcess(pid)
997 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
998
1000 """Return information about the node.
1001
1002 This is just a wrapper over the base GetLinuxNodeInfo method.
1003
1004 @return: a dict with the following keys (values in MiB):
1005 - memory_total: the total memory size on the node
1006 - memory_free: the available memory on the node for instances
1007 - memory_dom0: the memory used by the node itself, if available
1008
1009 """
1010 return self.GetLinuxNodeInfo()
1011
1012 @classmethod
1035
1046
1047
1048 @classmethod
1087
1088 @classmethod
1107
1108 @classmethod
1110 """KVM powercycle, just a wrapper over Linux powercycle.
1111
1112 """
1113 cls.LinuxPowercycle()
1114