Revision 6bf6870b lib/hypervisor/hv_kvm.py
b/lib/hypervisor/hv_kvm.py | ||
---|---|---|
37 | 37 |
import socket |
38 | 38 |
import stat |
39 | 39 |
import StringIO |
40 |
import copy |
|
41 |
from bitarray import bitarray |
|
40 | 42 |
try: |
41 | 43 |
import affinity # pylint: disable=F0401 |
42 | 44 |
except ImportError: |
43 | 45 |
affinity = None |
46 |
try: |
|
47 |
import fdsend # pylint: disable=F0401 |
|
48 |
except ImportError: |
|
49 |
fdsend = None |
|
44 | 50 |
|
45 | 51 |
from ganeti import utils |
46 | 52 |
from ganeti import constants |
... | ... | |
79 | 85 |
constants.HV_KVM_SPICE_USE_TLS, |
80 | 86 |
]) |
81 | 87 |
|
88 |
FREE = bitarray("0") |
|
89 |
|
|
90 |
def _GenerateDeviceKVMId(dev_type, dev): |
|
91 |
|
|
92 |
if not dev or not dev.pci: |
|
93 |
return None |
|
94 |
|
|
95 |
return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci) |
|
96 |
|
|
97 |
|
|
98 |
def _UpdatePCISlots(dev, pci_reservations): |
|
99 |
"""Update pci configuration for a stopped instance |
|
100 |
|
|
101 |
If dev has a pci slot the reserve it, else find first available. |
|
102 |
|
|
103 |
""" |
|
104 |
if dev.pci: |
|
105 |
free = dev.pci |
|
106 |
else: |
|
107 |
[free] = pci_reservations.search(FREE, 1) # pylint: disable=E1103 |
|
108 |
if not free: |
|
109 |
raise errors.HypervisorError("All PCI slots occupied") |
|
110 |
dev.pci = int(free) |
|
111 |
|
|
112 |
pci_reservations[free] = True |
|
113 |
|
|
114 |
|
|
115 |
def _RemoveFromRuntimeEntry(devices, device, fn): |
|
116 |
try: |
|
117 |
[rem] = [x for x in fn(devices) if x.uuid == device.uuid] |
|
118 |
devices.remove(rem) |
|
119 |
except (ValueError, IndexError): |
|
120 |
logging.info("No device with uuid %s in runtime file", device.uuid) |
|
121 |
|
|
82 | 122 |
|
83 | 123 |
def _GetTunFeatures(fd, _ioctl=fcntl.ioctl): |
84 | 124 |
"""Retrieves supported TUN features from file descriptor. |
... | ... | |
569 | 609 |
_BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S) |
570 | 610 |
_UUID_RE = re.compile(r"^-uuid\s", re.M) |
571 | 611 |
|
612 |
_INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*') |
|
613 |
_INFO_PCI_CMD = "info pci" |
|
614 |
_INFO_VERSION_RE = \ |
|
615 |
re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M) |
|
616 |
_INFO_VERSION_CMD = "info version" |
|
617 |
|
|
618 |
_DEFAULT_PCI_RESERVATIONS = "11110000000000000000000000000000" |
|
619 |
|
|
572 | 620 |
ANCILLARY_FILES = [ |
573 | 621 |
_KVM_NETWORK_SCRIPT, |
574 | 622 |
] |
... | ... | |
1021 | 1069 |
data.append(info) |
1022 | 1070 |
return data |
1023 | 1071 |
|
1072 |
def _GetExistingDeviceKVMId(self, instance, dev_type, dev): |
|
1073 |
(_, kvm_nics, __, block_devices) = self._LoadKVMRuntime(instance) |
|
1074 |
if dev_type == constants.HOTPLUG_NIC: |
|
1075 |
found = [n for n in kvm_nics |
|
1076 |
if n.uuid == dev.uuid] |
|
1077 |
elif dev_type == constants.HOTPLUG_DISK: |
|
1078 |
found = [d for d, _ in block_devices |
|
1079 |
if d.uuid == dev.uuid] |
|
1080 |
dev_info = None |
|
1081 |
if found: |
|
1082 |
dev_info = found[0] |
|
1083 |
return _GenerateDeviceKVMId(dev_type, dev_info) |
|
1084 |
|
|
1085 |
def _GenerateKVMBlockDevicesOptions(self, instance, kvm_cmd, block_devices, |
|
1086 |
pci_reservations, kvmhelp): |
|
1087 |
|
|
1088 |
hvp = instance.hvparams |
|
1089 |
boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK |
|
1090 |
|
|
1091 |
# whether this is an older KVM version that uses the boot=on flag |
|
1092 |
# on devices |
|
1093 |
needs_boot_flag = self._BOOT_RE.search(kvmhelp) |
|
1094 |
|
|
1095 |
disk_type = hvp[constants.HV_DISK_TYPE] |
|
1096 |
if disk_type == constants.HT_DISK_PARAVIRTUAL: |
|
1097 |
#TODO: parse kvm -device ? output |
|
1098 |
disk_model = "virtio-blk-pci" |
|
1099 |
if_val = ",if=virtio" |
|
1100 |
else: |
|
1101 |
if_val = ",if=%s" % disk_type |
|
1102 |
# Cache mode |
|
1103 |
disk_cache = hvp[constants.HV_DISK_CACHE] |
|
1104 |
if instance.disk_template in constants.DTS_EXT_MIRROR: |
|
1105 |
if disk_cache != "none": |
|
1106 |
# TODO: make this a hard error, instead of a silent overwrite |
|
1107 |
logging.warning("KVM: overriding disk_cache setting '%s' with 'none'" |
|
1108 |
" to prevent shared storage corruption on migration", |
|
1109 |
disk_cache) |
|
1110 |
cache_val = ",cache=none" |
|
1111 |
elif disk_cache != constants.HT_CACHE_DEFAULT: |
|
1112 |
cache_val = ",cache=%s" % disk_cache |
|
1113 |
else: |
|
1114 |
cache_val = "" |
|
1115 |
for cfdev, dev_path in block_devices: |
|
1116 |
if cfdev.mode != constants.DISK_RDWR: |
|
1117 |
raise errors.HypervisorError("Instance has read-only disks which" |
|
1118 |
" are not supported by KVM") |
|
1119 |
# TODO: handle FD_LOOP and FD_BLKTAP (?) |
|
1120 |
boot_val = "" |
|
1121 |
if boot_disk: |
|
1122 |
kvm_cmd.extend(["-boot", "c"]) |
|
1123 |
boot_disk = False |
|
1124 |
if needs_boot_flag and disk_type != constants.HT_DISK_IDE: |
|
1125 |
boot_val = ",boot=on" |
|
1126 |
drive_val = "file=%s,format=raw%s%s" % \ |
|
1127 |
(dev_path, boot_val, cache_val) |
|
1128 |
_UpdatePCISlots(cfdev, pci_reservations) |
|
1129 |
kvm_devid = _GenerateDeviceKVMId("DISK", cfdev) |
|
1130 |
if kvm_devid: |
|
1131 |
#TODO: name id after model |
|
1132 |
drive_val += (",if=none,id=%s" % kvm_devid) |
|
1133 |
drive_val += (",bus=0,unit=%d" % cfdev.pci) |
|
1134 |
else: |
|
1135 |
drive_val += if_val |
|
1136 |
|
|
1137 |
kvm_cmd.extend(["-drive", drive_val]) |
|
1138 |
|
|
1139 |
if kvm_devid: |
|
1140 |
dev_val = ("%s,drive=%s,id=%s" % |
|
1141 |
(disk_model, kvm_devid, kvm_devid)) |
|
1142 |
dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci) |
|
1143 |
kvm_cmd.extend(["-device", dev_val]) |
|
1144 |
|
|
1145 |
return kvm_cmd |
|
1146 |
|
|
1024 | 1147 |
def _GenerateKVMRuntime(self, instance, block_devices, startup_paused, |
1025 | 1148 |
kvmhelp): |
1026 | 1149 |
"""Generate KVM information to start an instance. |
... | ... | |
1089 | 1212 |
|
1090 | 1213 |
kernel_path = hvp[constants.HV_KERNEL_PATH] |
1091 | 1214 |
if kernel_path: |
1092 |
boot_disk = boot_cdrom = boot_floppy = boot_network = False
|
|
1215 |
boot_cdrom = boot_floppy = boot_network = False |
|
1093 | 1216 |
else: |
1094 |
boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK |
|
1095 | 1217 |
boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM |
1096 | 1218 |
boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY |
1097 | 1219 |
boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK |
... | ... | |
1107 | 1229 |
needs_boot_flag = self._BOOT_RE.search(kvmhelp) |
1108 | 1230 |
|
1109 | 1231 |
disk_type = hvp[constants.HV_DISK_TYPE] |
1110 |
if disk_type == constants.HT_DISK_PARAVIRTUAL: |
|
1111 |
if_val = ",if=virtio" |
|
1112 |
else: |
|
1113 |
if_val = ",if=%s" % disk_type |
|
1114 |
# Cache mode |
|
1115 |
disk_cache = hvp[constants.HV_DISK_CACHE] |
|
1116 |
if instance.disk_template in constants.DTS_EXT_MIRROR: |
|
1117 |
if disk_cache != "none": |
|
1118 |
# TODO: make this a hard error, instead of a silent overwrite |
|
1119 |
logging.warning("KVM: overriding disk_cache setting '%s' with 'none'" |
|
1120 |
" to prevent shared storage corruption on migration", |
|
1121 |
disk_cache) |
|
1122 |
cache_val = ",cache=none" |
|
1123 |
elif disk_cache != constants.HT_CACHE_DEFAULT: |
|
1124 |
cache_val = ",cache=%s" % disk_cache |
|
1125 |
else: |
|
1126 |
cache_val = "" |
|
1127 |
for cfdev, dev_path in block_devices: |
|
1128 |
if cfdev.mode != constants.DISK_RDWR: |
|
1129 |
raise errors.HypervisorError("Instance has read-only disks which" |
|
1130 |
" are not supported by KVM") |
|
1131 |
# TODO: handle FD_LOOP and FD_BLKTAP (?) |
|
1132 |
boot_val = "" |
|
1133 |
if boot_disk: |
|
1134 |
kvm_cmd.extend(["-boot", "c"]) |
|
1135 |
boot_disk = False |
|
1136 |
if needs_boot_flag and disk_type != constants.HT_DISK_IDE: |
|
1137 |
boot_val = ",boot=on" |
|
1138 |
|
|
1139 |
drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val, |
|
1140 |
cache_val) |
|
1141 |
kvm_cmd.extend(["-drive", drive_val]) |
|
1142 | 1232 |
|
1143 | 1233 |
#Now we can specify a different device type for CDROM devices. |
1144 | 1234 |
cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE] |
... | ... | |
1407 | 1497 |
kvm_nics = instance.nics |
1408 | 1498 |
hvparams = hvp |
1409 | 1499 |
|
1410 |
return (kvm_cmd, kvm_nics, hvparams) |
|
1500 |
return (kvm_cmd, kvm_nics, hvparams, block_devices)
|
|
1411 | 1501 |
|
1412 | 1502 |
def _WriteKVMRuntime(self, instance_name, data): |
1413 | 1503 |
"""Write an instance's KVM runtime |
... | ... | |
1433 | 1523 |
"""Save an instance's KVM runtime |
1434 | 1524 |
|
1435 | 1525 |
""" |
1436 |
kvm_cmd, kvm_nics, hvparams = kvm_runtime |
|
1526 |
kvm_cmd, kvm_nics, hvparams, block_devices = kvm_runtime |
|
1527 |
|
|
1437 | 1528 |
serialized_nics = [nic.ToDict() for nic in kvm_nics] |
1438 |
serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams)) |
|
1529 |
serialized_blockdevs = [(blk.ToDict(), link) for blk, link in block_devices] |
|
1530 |
serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams, |
|
1531 |
serialized_blockdevs)) |
|
1532 |
|
|
1439 | 1533 |
self._WriteKVMRuntime(instance.name, serialized_form) |
1440 | 1534 |
|
1441 | 1535 |
def _LoadKVMRuntime(self, instance, serialized_runtime=None): |
... | ... | |
1444 | 1538 |
""" |
1445 | 1539 |
if not serialized_runtime: |
1446 | 1540 |
serialized_runtime = self._ReadKVMRuntime(instance.name) |
1541 |
|
|
1447 | 1542 |
loaded_runtime = serializer.Load(serialized_runtime) |
1448 |
kvm_cmd, serialized_nics, hvparams = loaded_runtime |
|
1543 |
if len(loaded_runtime)==3: |
|
1544 |
serialized_blockdevs = [] |
|
1545 |
kvm_cmd, serialized_nics, hvparams = loaded_runtime |
|
1546 |
else: |
|
1547 |
kvm_cmd, serialized_nics, hvparams, serialized_blockdevs = loaded_runtime |
|
1548 |
|
|
1449 | 1549 |
kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics] |
1450 |
return (kvm_cmd, kvm_nics, hvparams) |
|
1550 |
block_devices = [(objects.Disk.FromDict(sdisk), link) |
|
1551 |
for sdisk, link in serialized_blockdevs] |
|
1552 |
|
|
1553 |
return (kvm_cmd, kvm_nics, hvparams, block_devices) |
|
1451 | 1554 |
|
1452 | 1555 |
def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None): |
1453 | 1556 |
"""Run the KVM cmd and check for errors |
... | ... | |
1472 | 1575 |
if not self._InstancePidAlive(name)[2]: |
1473 | 1576 |
raise errors.HypervisorError("Failed to start instance %s" % name) |
1474 | 1577 |
|
1578 |
# pylint: disable=R0914 |
|
1475 | 1579 |
def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None): |
1476 | 1580 |
"""Execute a KVM cmd, after completing it with some last minute data. |
1477 | 1581 |
|
... | ... | |
1495 | 1599 |
|
1496 | 1600 |
temp_files = [] |
1497 | 1601 |
|
1498 |
kvm_cmd, kvm_nics, up_hvp = kvm_runtime |
|
1602 |
kvm_cmd, kvm_nics, up_hvp, block_devices = kvm_runtime
|
|
1499 | 1603 |
# the first element of kvm_cmd is always the path to the kvm binary |
1500 | 1604 |
kvm_path = kvm_cmd[0] |
1605 |
|
|
1606 |
kvm_cmd_runtime = copy.deepcopy(kvm_cmd) |
|
1607 |
|
|
1501 | 1608 |
up_hvp = objects.FillDict(conf_hvp, up_hvp) |
1502 | 1609 |
|
1503 | 1610 |
# We know it's safe to run as a different user upon migration, so we'll use |
... | ... | |
1516 | 1623 |
utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap) |
1517 | 1624 |
kvm_cmd.extend(["-k", keymap_path]) |
1518 | 1625 |
|
1626 |
pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS) |
|
1627 |
|
|
1628 |
kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd, |
|
1629 |
block_devices, |
|
1630 |
pci_reservations, |
|
1631 |
kvmhelp) |
|
1632 |
|
|
1519 | 1633 |
# We have reasons to believe changing something like the nic driver/type |
1520 | 1634 |
# upon migration won't exactly fly with the instance kernel, so for nic |
1521 | 1635 |
# related parameters we'll use up_hvp |
... | ... | |
1556 | 1670 |
tapfds.append(tapfd) |
1557 | 1671 |
taps.append(tapname) |
1558 | 1672 |
if kvm_supports_netdev: |
1559 |
nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq) |
|
1560 |
tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra) |
|
1673 |
nic_val = "%s,mac=%s" % (nic_model, nic.mac) |
|
1674 |
_UpdatePCISlots(nic, pci_reservations) |
|
1675 |
kvm_devid = _GenerateDeviceKVMId("NIC", nic) |
|
1676 |
netdev = kvm_devid or "netdev%d" % nic_seq |
|
1677 |
nic_val += (",netdev=%s" % netdev) |
|
1678 |
if kvm_devid: |
|
1679 |
nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci))) |
|
1680 |
tap_val = ("type=tap,id=%s,fd=%d%s" % |
|
1681 |
(netdev, tapfd, tap_extra)) |
|
1561 | 1682 |
kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val]) |
1562 | 1683 |
else: |
1563 | 1684 |
nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq, |
... | ... | |
1680 | 1801 |
# explicitly requested resume the vm status. |
1681 | 1802 |
self._CallMonitorCommand(instance.name, self._CONT_CMD) |
1682 | 1803 |
|
1804 |
kvm_runtime_with_pci_info = (kvm_cmd_runtime, kvm_nics, |
|
1805 |
up_hvp, block_devices) |
|
1806 |
return kvm_runtime_with_pci_info |
|
1807 |
|
|
1683 | 1808 |
def StartInstance(self, instance, block_devices, startup_paused): |
1684 | 1809 |
"""Start an instance. |
1685 | 1810 |
|
... | ... | |
1690 | 1815 |
kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, |
1691 | 1816 |
startup_paused, kvmhelp) |
1692 | 1817 |
self._SaveKVMRuntime(instance, kvm_runtime) |
1693 |
self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp) |
|
1818 |
kvm_runtime_with_pci_info = self._ExecuteKVMRuntime(instance, kvm_runtime, |
|
1819 |
kvmhelp) |
|
1820 |
self._SaveKVMRuntime(instance, kvm_runtime_with_pci_info) |
|
1694 | 1821 |
|
1695 | 1822 |
def _CallMonitorCommand(self, instance_name, command): |
1696 | 1823 |
"""Invoke a command on the instance monitor. |
... | ... | |
1716 | 1843 |
|
1717 | 1844 |
return result |
1718 | 1845 |
|
1846 |
def _AnnotateFreePCISlot(self, instance, dev): |
|
1847 |
"""Get the first available pci slot of a runnung instance. |
|
1848 |
|
|
1849 |
""" |
|
1850 |
slots = bitarray(32) |
|
1851 |
slots.setall(False) # pylint: disable=E1101 |
|
1852 |
output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD) |
|
1853 |
for line in output.stdout.splitlines(): |
|
1854 |
match = self._INFO_PCI_RE.search(line) |
|
1855 |
if match: |
|
1856 |
slot = int(match.group(1)) |
|
1857 |
slots[slot] = True |
|
1858 |
|
|
1859 |
[free] = slots.search(FREE, 1) # pylint: disable=E1101 |
|
1860 |
if not free: |
|
1861 |
raise errors.HypervisorError("All PCI slots occupied") |
|
1862 |
|
|
1863 |
dev.pci = int(free) |
|
1864 |
|
|
1865 |
def _TryHotplug(self, instance, dev_type): |
|
1866 |
"""Get QEMU version from the instance's monitor. |
|
1867 |
|
|
1868 |
Hotplug is supported for running instances and for versions >= 1.0. |
|
1869 |
""" |
|
1870 |
if dev_type == constants.HOTPLUG_DISK: |
|
1871 |
hvp = instance.hvparams |
|
1872 |
security_model = hvp[constants.HV_SECURITY_MODEL] |
|
1873 |
use_chroot = hvp[constants.HV_KVM_USE_CHROOT] |
|
1874 |
if use_chroot or security_model != constants.HT_SM_NONE: |
|
1875 |
return False |
|
1876 |
output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD) |
|
1877 |
#TODO: search for netdev_add, drive_add, device_add..... |
|
1878 |
match = self._INFO_VERSION_RE.search(output.stdout) |
|
1879 |
if not match: |
|
1880 |
return False |
|
1881 |
v_major, v_min, _, _ = match.groups() |
|
1882 |
return (v_major, v_min) >= (1, 0) |
|
1883 |
|
|
1884 |
def _CallMonitorHotplugCommand(self, name, cmd): |
|
1885 |
output = self._CallMonitorCommand(name, cmd) |
|
1886 |
#TODO: parse output and check if succeeded |
|
1887 |
for line in output.stdout.splitlines(): |
|
1888 |
logging.info("%s", line) |
|
1889 |
|
|
1890 |
def HotplugDevice(self, instance, action, dev_type, device, extra, seq): |
|
1891 |
""" Generic method to hotplug device |
|
1892 |
|
|
1893 |
Depending on action and dev_type invoke the coresponding method that |
|
1894 |
does the actual hotplug (via KVM monitor commands). |
|
1895 |
Before and after, do all hotplug related steps (e.g. check if hotplug |
|
1896 |
is possible, annotate pci slot to device and generate/get existing |
|
1897 |
KVM device ids, read/write runtime file) |
|
1898 |
|
|
1899 |
""" |
|
1900 |
if self._TryHotplug(instance, dev_type): |
|
1901 |
(kvm_cmd, kvm_nics, hvparams, \ |
|
1902 |
block_devices) = self._LoadKVMRuntime(instance) |
|
1903 |
if action == constants.HOTPLUG_ADD: |
|
1904 |
self._AnnotateFreePCISlot(instance, device) |
|
1905 |
kvm_devid = _GenerateDeviceKVMId(dev_type, device) |
|
1906 |
if dev_type == constants.HOTPLUG_DISK: |
|
1907 |
self._HotAddDisk(instance, |
|
1908 |
device, extra, seq, kvm_devid, block_devices) |
|
1909 |
elif dev_type == constants.HOTPLUG_NIC and fdsend: |
|
1910 |
self._HotAddNic(instance, device, extra, seq, kvm_devid, kvm_nics) |
|
1911 |
elif action == constants.HOTPLUG_REMOVE: |
|
1912 |
kvm_devid = self._GetExistingDeviceKVMId(instance, dev_type, device) |
|
1913 |
if dev_type == constants.HOTPLUG_DISK: |
|
1914 |
self._HotDelDisk(instance, |
|
1915 |
device, extra, seq, kvm_devid, block_devices) |
|
1916 |
elif dev_type == constants.HOTPLUG_NIC and fdsend: |
|
1917 |
self._HotDelNic(instance, device, extra, seq, kvm_devid, kvm_nics) |
|
1918 |
self._SaveKVMRuntime(instance, |
|
1919 |
(kvm_cmd, kvm_nics, hvparams, block_devices)) |
|
1920 |
time.sleep(2) |
|
1921 |
|
|
1922 |
def _HotAddDisk(self, instance, disk, dev_path, _, kvm_devid, block_devices): |
|
1923 |
"""Hotplug/add new disk to and instance |
|
1924 |
|
|
1925 |
""" |
|
1926 |
command = ("drive_add dummy file=%s,if=none,id=%s,format=raw" % |
|
1927 |
(dev_path, kvm_devid)) |
|
1928 |
self._CallMonitorHotplugCommand(instance.name, command) |
|
1929 |
command = ("device_add virtio-blk-pci,bus=pci.0,addr=%s," |
|
1930 |
"drive=%s,id=%s" |
|
1931 |
% (hex(disk.pci), kvm_devid, kvm_devid)) |
|
1932 |
self._CallMonitorHotplugCommand(instance.name, command) |
|
1933 |
block_devices.append((disk, dev_path)) |
|
1934 |
|
|
1935 |
def _HotAddNic(self, instance, nic, _, seq, kvm_devid, kvm_nics): |
|
1936 |
"""Hotplug/add nic to an instance |
|
1937 |
|
|
1938 |
""" |
|
1939 |
(tap, fd) = _OpenTap() |
|
1940 |
self._PassTapFd(instance, fd, nic) |
|
1941 |
command = ("netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)) |
|
1942 |
self._CallMonitorHotplugCommand(instance.name, command) |
|
1943 |
command = ("device_add virtio-net-pci,bus=pci.0,addr=%s,mac=%s," |
|
1944 |
"netdev=%s,id=%s" |
|
1945 |
% (hex(nic.pci), nic.mac, kvm_devid, kvm_devid)) |
|
1946 |
self._CallMonitorHotplugCommand(instance.name, command) |
|
1947 |
self._ConfigureNIC(instance, seq, nic, tap) |
|
1948 |
utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap) |
|
1949 |
kvm_nics.append(nic) |
|
1950 |
|
|
1951 |
def _HotDelDisk(self, instance, disk, _, __, kvm_devid, block_devices): |
|
1952 |
"""Hotplug/remove disk from an instance |
|
1953 |
|
|
1954 |
""" |
|
1955 |
command = "device_del %s" % kvm_devid |
|
1956 |
self._CallMonitorHotplugCommand(instance.name, command) |
|
1957 |
#command = "drive_del %s" % uuid |
|
1958 |
#self._CallMonitorHotplugCommand(instance.name, command) |
|
1959 |
_RemoveFromRuntimeEntry(block_devices, disk, lambda x: [d for d, l in x]) |
|
1960 |
|
|
1961 |
def _HotDelNic(self, instance, nic, _, __, kvm_devid, kvm_nics): |
|
1962 |
"""Hotplug/remove existing nic from an instance |
|
1963 |
|
|
1964 |
""" |
|
1965 |
command = "device_del %s" % kvm_devid |
|
1966 |
self._CallMonitorHotplugCommand(instance.name, command) |
|
1967 |
command = "netdev_del %s" % kvm_devid |
|
1968 |
self._CallMonitorHotplugCommand(instance.name, command) |
|
1969 |
_RemoveFromRuntimeEntry(kvm_nics, nic, lambda x: x) |
|
1970 |
|
|
1971 |
def _PassTapFd(self, instance, fd, nic): |
|
1972 |
"""Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS |
|
1973 |
|
|
1974 |
""" |
|
1975 |
monsock = utils.ShellQuote(self._InstanceMonitor(instance.name)) |
|
1976 |
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
|
1977 |
s.connect(monsock) |
|
1978 |
kvm_devid = _GenerateDeviceKVMId("NIC", nic) |
|
1979 |
command = "getfd %s\n" % kvm_devid |
|
1980 |
fds = [fd] |
|
1981 |
logging.info("%s", fds) |
|
1982 |
fdsend.sendfds(s, command, fds = fds) |
|
1983 |
s.close() |
|
1984 |
|
|
1719 | 1985 |
@classmethod |
1720 | 1986 |
def _ParseKVMVersion(cls, text): |
1721 | 1987 |
"""Parse the KVM version from the --help output. |
... | ... | |
1831 | 2097 |
self._SaveKVMRuntime(instance, kvm_runtime) |
1832 | 2098 |
kvmpath = instance.hvparams[constants.HV_KVM_PATH] |
1833 | 2099 |
kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP) |
1834 |
self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp) |
|
2100 |
kvm_runtime_with_pci_info = \ |
|
2101 |
self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp) |
|
2102 |
self._SaveKVMRuntime(instance, kvm_runtime_with_pci_info) |
|
1835 | 2103 |
|
1836 | 2104 |
def MigrationInfo(self, instance): |
1837 | 2105 |
"""Get instance information to perform a migration. |
Also available in: Unified diff