Revision 1642a1d0 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 fdsend |
|
40 | 41 |
try: |
41 | 42 |
import affinity # pylint: disable=F0401 |
42 | 43 |
except ImportError: |
... | ... | |
1000 | 1001 |
needs_boot_flag = (v_major, v_min) < (0, 14) |
1001 | 1002 |
|
1002 | 1003 |
disk_type = hvp[constants.HV_DISK_TYPE] |
1003 |
if disk_type == constants.HT_DISK_PARAVIRTUAL: |
|
1004 |
if_val = ",if=virtio" |
|
1005 |
else: |
|
1006 |
if_val = ",if=%s" % disk_type |
|
1007 |
# Cache mode |
|
1008 |
disk_cache = hvp[constants.HV_DISK_CACHE] |
|
1009 |
if instance.disk_template in constants.DTS_EXT_MIRROR: |
|
1010 |
if disk_cache != "none": |
|
1011 |
# TODO: make this a hard error, instead of a silent overwrite |
|
1012 |
logging.warning("KVM: overriding disk_cache setting '%s' with 'none'" |
|
1013 |
" to prevent shared storage corruption on migration", |
|
1014 |
disk_cache) |
|
1015 |
cache_val = ",cache=none" |
|
1016 |
elif disk_cache != constants.HT_CACHE_DEFAULT: |
|
1017 |
cache_val = ",cache=%s" % disk_cache |
|
1018 |
else: |
|
1019 |
cache_val = "" |
|
1020 |
for cfdev, dev_path in block_devices: |
|
1021 |
if cfdev.mode != constants.DISK_RDWR: |
|
1022 |
raise errors.HypervisorError("Instance has read-only disks which" |
|
1023 |
" are not supported by KVM") |
|
1024 |
# TODO: handle FD_LOOP and FD_BLKTAP (?) |
|
1025 |
boot_val = "" |
|
1026 |
if boot_disk: |
|
1027 |
kvm_cmd.extend(["-boot", "c"]) |
|
1028 |
boot_disk = False |
|
1029 |
if needs_boot_flag and disk_type != constants.HT_DISK_IDE: |
|
1030 |
boot_val = ",boot=on" |
|
1031 |
|
|
1032 |
drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val, |
|
1033 |
cache_val) |
|
1034 |
kvm_cmd.extend(["-drive", drive_val]) |
|
1035 | 1004 |
|
1036 | 1005 |
#Now we can specify a different device type for CDROM devices. |
1037 | 1006 |
cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE] |
... | ... | |
1257 | 1226 |
kvm_nics = instance.nics |
1258 | 1227 |
hvparams = hvp |
1259 | 1228 |
|
1260 |
return (kvm_cmd, kvm_nics, hvparams) |
|
1229 |
return (kvm_cmd, kvm_nics, hvparams, block_devices)
|
|
1261 | 1230 |
|
1262 | 1231 |
def _WriteKVMRuntime(self, instance_name, data): |
1263 | 1232 |
"""Write an instance's KVM runtime |
... | ... | |
1283 | 1252 |
"""Save an instance's KVM runtime |
1284 | 1253 |
|
1285 | 1254 |
""" |
1286 |
kvm_cmd, kvm_nics, hvparams = kvm_runtime |
|
1255 |
kvm_cmd, kvm_nics, hvparams, block_devices = kvm_runtime
|
|
1287 | 1256 |
serialized_nics = [nic.ToDict() for nic in kvm_nics] |
1288 |
serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams)) |
|
1257 |
serialized_blockdevs = [(blk.ToDict(), link) for blk,link in block_devices] |
|
1258 |
serialized_form = serializer.Dump((kvm_cmd, serialized_nics, |
|
1259 |
hvparams, serialized_blockdevs)) |
|
1289 | 1260 |
self._WriteKVMRuntime(instance.name, serialized_form) |
1290 | 1261 |
|
1291 | 1262 |
def _LoadKVMRuntime(self, instance, serialized_runtime=None): |
... | ... | |
1295 | 1266 |
if not serialized_runtime: |
1296 | 1267 |
serialized_runtime = self._ReadKVMRuntime(instance.name) |
1297 | 1268 |
loaded_runtime = serializer.Load(serialized_runtime) |
1298 |
kvm_cmd, serialized_nics, hvparams = loaded_runtime |
|
1269 |
kvm_cmd, serialized_nics, hvparams, serialized_blockdevs = loaded_runtime
|
|
1299 | 1270 |
kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics] |
1300 |
return (kvm_cmd, kvm_nics, hvparams) |
|
1271 |
block_devices = [(objects.Disk.FromDict(sdisk), link) |
|
1272 |
for sdisk, link in serialized_blockdevs] |
|
1273 |
return (kvm_cmd, kvm_nics, hvparams, block_devices) |
|
1301 | 1274 |
|
1302 | 1275 |
def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None): |
1303 | 1276 |
"""Run the KVM cmd and check for errors |
... | ... | |
1340 | 1313 |
conf_hvp = instance.hvparams |
1341 | 1314 |
name = instance.name |
1342 | 1315 |
self._CheckDown(name) |
1316 |
boot_disk = conf_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK |
|
1343 | 1317 |
|
1344 | 1318 |
temp_files = [] |
1345 | 1319 |
|
1346 |
kvm_cmd, kvm_nics, up_hvp = kvm_runtime |
|
1320 |
kvm_cmd, kvm_nics, up_hvp, block_devices = kvm_runtime
|
|
1347 | 1321 |
up_hvp = objects.FillDict(conf_hvp, up_hvp) |
1348 | 1322 |
|
1349 | 1323 |
_, v_major, v_min, _ = self._GetKVMVersion() |
... | ... | |
1364 | 1338 |
utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap) |
1365 | 1339 |
kvm_cmd.extend(["-k", keymap_path]) |
1366 | 1340 |
|
1341 |
# whether this is an older KVM version that uses the boot=on flag |
|
1342 |
# on devices |
|
1343 |
needs_boot_flag = (v_major, v_min) < (0, 14) |
|
1344 |
|
|
1345 |
disk_type = up_hvp[constants.HV_DISK_TYPE] |
|
1346 |
if disk_type == constants.HT_DISK_PARAVIRTUAL: |
|
1347 |
if_val = ",if=virtio" |
|
1348 |
if (v_major, v_min) >= (0, 12): |
|
1349 |
disk_model = "virtio-blk-pci" |
|
1350 |
else: |
|
1351 |
disk_model = "virtio" |
|
1352 |
else: |
|
1353 |
if_val = ",if=%s" % disk_type |
|
1354 |
disk_model = disk_type |
|
1355 |
# Cache mode |
|
1356 |
disk_cache = up_hvp[constants.HV_DISK_CACHE] |
|
1357 |
if instance.disk_template in constants.DTS_EXT_MIRROR: |
|
1358 |
if disk_cache != "none": |
|
1359 |
# TODO: make this a hard error, instead of a silent overwrite |
|
1360 |
logging.warning("KVM: overriding disk_cache setting '%s' with 'none'" |
|
1361 |
" to prevent shared storage corruption on migration", |
|
1362 |
disk_cache) |
|
1363 |
cache_val = ",cache=none" |
|
1364 |
elif disk_cache != constants.HT_CACHE_DEFAULT: |
|
1365 |
cache_val = ",cache=%s" % disk_cache |
|
1366 |
else: |
|
1367 |
cache_val = "" |
|
1368 |
for cfdev, dev_path in block_devices: |
|
1369 |
if cfdev.mode != constants.DISK_RDWR: |
|
1370 |
raise errors.HypervisorError("Instance has read-only disks which" |
|
1371 |
" are not supported by KVM") |
|
1372 |
# TODO: handle FD_LOOP and FD_BLKTAP (?) |
|
1373 |
boot_val = "" |
|
1374 |
if boot_disk: |
|
1375 |
kvm_cmd.extend(["-boot", "c"]) |
|
1376 |
boot_disk = False |
|
1377 |
if needs_boot_flag and disk_type != constants.HT_DISK_IDE: |
|
1378 |
boot_val = ",boot=on" |
|
1379 |
drive_val = "file=%s,format=raw%s%s" % \ |
|
1380 |
(dev_path, boot_val, cache_val) |
|
1381 |
if cfdev.pci: |
|
1382 |
#TODO: name id after model |
|
1383 |
drive_val += (",bus=0,unit=%d,if=none,id=drive%d" % |
|
1384 |
(cfdev.pci, cfdev.idx)) |
|
1385 |
else: |
|
1386 |
drive_val += if_val |
|
1387 |
|
|
1388 |
kvm_cmd.extend(["-drive", drive_val]) |
|
1389 |
|
|
1390 |
if cfdev.pci: |
|
1391 |
dev_val = ("%s,bus=pci.0,addr=%s,drive=drive%d,id=virtio-blk-pci.%d" % |
|
1392 |
(disk_model, hex(cfdev.pci), cfdev.idx, cfdev.idx)) |
|
1393 |
kvm_cmd.extend(["-device", dev_val]) |
|
1394 |
|
|
1367 | 1395 |
# We have reasons to believe changing something like the nic driver/type |
1368 | 1396 |
# upon migration won't exactly fly with the instance kernel, so for nic |
1369 | 1397 |
# related parameters we'll use up_hvp |
... | ... | |
1398 | 1426 |
tapfds.append(tapfd) |
1399 | 1427 |
taps.append(tapname) |
1400 | 1428 |
if (v_major, v_min) >= (0, 12): |
1401 |
nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq) |
|
1402 |
tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra) |
|
1429 |
if nic.pci: |
|
1430 |
nic_idx = nic.idx |
|
1431 |
else: |
|
1432 |
nic_idx = nic_seq |
|
1433 |
nic_val = ("%s,mac=%s,netdev=netdev%d" % |
|
1434 |
(nic_model, nic.mac, nic_idx)) |
|
1435 |
if nic.pci: |
|
1436 |
nic_val += (",bus=pci.0,addr=%s,id=virtio-net-pci.%d" % |
|
1437 |
(hex(nic.pci), nic_idx)) |
|
1438 |
tap_val = "type=tap,id=netdev%d,fd=%d%s" % (nic_idx, tapfd, tap_extra) |
|
1403 | 1439 |
kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val]) |
1404 | 1440 |
else: |
1405 | 1441 |
nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq, |
... | ... | |
1550 | 1586 |
|
1551 | 1587 |
return result |
1552 | 1588 |
|
1589 |
def HotAddDisk(self, instance, disk, dev_path, seq): |
|
1590 |
"""Hotadd new disk to the VM |
|
1591 |
|
|
1592 |
""" |
|
1593 |
if not self._InstancePidAlive(instance.name)[2]: |
|
1594 |
logging.info("Cannot hotplug. Instance %s not alive" % instance.name) |
|
1595 |
return disk.ToDict() |
|
1596 |
|
|
1597 |
_, v_major, v_min, _ = self._GetKVMVersion() |
|
1598 |
if (v_major, v_min) >= (1, 0) and disk.pci: |
|
1599 |
idx = disk.idx |
|
1600 |
command = ("drive_add dummy file=%s,if=none,id=drive%d,format=raw" % |
|
1601 |
(dev_path, idx)) |
|
1602 |
|
|
1603 |
logging.info("%s" % command) |
|
1604 |
output = self._CallMonitorCommand(instance.name, command) |
|
1605 |
|
|
1606 |
command = ("device_add virtio-blk-pci,bus=pci.0,addr=%s," |
|
1607 |
"drive=drive%d,id=virtio-blk-pci.%d" |
|
1608 |
% (hex(disk.pci), idx, idx)) |
|
1609 |
logging.info("%s" % command) |
|
1610 |
output = self._CallMonitorCommand(instance.name, command) |
|
1611 |
for line in output.stdout.splitlines(): |
|
1612 |
logging.info("%s" % line) |
|
1613 |
|
|
1614 |
(kvm_cmd, kvm_nics, |
|
1615 |
hvparams, block_devices) = self._LoadKVMRuntime(instance) |
|
1616 |
block_devices.append((disk, dev_path)) |
|
1617 |
new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices) |
|
1618 |
self._SaveKVMRuntime(instance, new_kvm_runtime) |
|
1619 |
|
|
1620 |
return disk.ToDict() |
|
1621 |
|
|
1622 |
def HotDelDisk(self, instance, disk, seq): |
|
1623 |
"""Hotdel disk to the VM |
|
1624 |
|
|
1625 |
""" |
|
1626 |
if not self._InstancePidAlive(instance.name)[2]: |
|
1627 |
logging.info("Cannot hotplug. Instance %s not alive" % instance.name) |
|
1628 |
return disk.ToDict() |
|
1629 |
|
|
1630 |
_, v_major, v_min, _ = self._GetKVMVersion() |
|
1631 |
if (v_major, v_min) >= (1, 0) and disk.pci: |
|
1632 |
idx = disk.idx |
|
1633 |
|
|
1634 |
command = "device_del virtio-blk-pci.%d" % idx |
|
1635 |
logging.info("%s" % command) |
|
1636 |
output = self._CallMonitorCommand(instance.name, command) |
|
1637 |
for line in output.stdout.splitlines(): |
|
1638 |
logging.info("%s" % line) |
|
1639 |
|
|
1640 |
command = "drive_del drive%d" % idx |
|
1641 |
logging.info("%s" % command) |
|
1642 |
#output = self._CallMonitorCommand(instance.name, command) |
|
1643 |
#for line in output.stdout.splitlines(): |
|
1644 |
# logging.info("%s" % line) |
|
1645 |
|
|
1646 |
(kvm_cmd, kvm_nics, |
|
1647 |
hvparams, block_devices) = self._LoadKVMRuntime(instance) |
|
1648 |
rem = [(d, p) for d, p in block_devices |
|
1649 |
if d.idx is not None and d.idx == idx] |
|
1650 |
try: |
|
1651 |
block_devices.remove(rem[0]) |
|
1652 |
except (ValueError, IndexError): |
|
1653 |
logging.info("Disk with %d idx disappeared from runtime file", idx) |
|
1654 |
new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices) |
|
1655 |
self._SaveKVMRuntime(instance, new_kvm_runtime) |
|
1656 |
|
|
1657 |
return disk.ToDict() |
|
1658 |
|
|
1659 |
def HotAddNic(self, instance, nic, seq): |
|
1660 |
"""Hotadd new nic to the VM |
|
1661 |
|
|
1662 |
""" |
|
1663 |
if not self._InstancePidAlive(instance.name)[2]: |
|
1664 |
logging.info("Cannot hotplug. Instance %s not alive" % instance.name) |
|
1665 |
return nic.ToDict() |
|
1666 |
|
|
1667 |
_, v_major, v_min, _ = self._GetKVMVersion() |
|
1668 |
if (v_major, v_min) >= (1, 0) and nic.pci: |
|
1669 |
mac = nic.mac |
|
1670 |
idx = nic.idx |
|
1671 |
|
|
1672 |
(tap, fd) = _OpenTap() |
|
1673 |
logging.info("%s %d", tap, fd) |
|
1674 |
|
|
1675 |
self._PassTapFd(instance, fd, nic) |
|
1676 |
|
|
1677 |
command = ("netdev_add tap,id=netdev%d,fd=netdev%d" |
|
1678 |
% (idx, idx)) |
|
1679 |
logging.info("%s" % command) |
|
1680 |
output = self._CallMonitorCommand(instance.name, command) |
|
1681 |
for line in output.stdout.splitlines(): |
|
1682 |
logging.info("%s" % line) |
|
1683 |
|
|
1684 |
command = ("device_add virtio-net-pci,bus=pci.0,addr=%s,mac=%s," |
|
1685 |
"netdev=netdev%d,id=virtio-net-pci.%d" |
|
1686 |
% (hex(nic.pci), mac, idx, idx)) |
|
1687 |
logging.info("%s" % command) |
|
1688 |
output = self._CallMonitorCommand(instance.name, command) |
|
1689 |
for line in output.stdout.splitlines(): |
|
1690 |
logging.info("%s" % line) |
|
1691 |
|
|
1692 |
self._ConfigureNIC(instance, seq, nic, tap) |
|
1693 |
|
|
1694 |
(kvm_cmd, kvm_nics, |
|
1695 |
hvparams, block_devices) = self._LoadKVMRuntime(instance) |
|
1696 |
kvm_nics.append(nic) |
|
1697 |
new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices) |
|
1698 |
self._SaveKVMRuntime(instance, new_kvm_runtime) |
|
1699 |
|
|
1700 |
return nic.ToDict() |
|
1701 |
|
|
1702 |
def HotDelNic(self, instance, nic, seq): |
|
1703 |
"""Hotadd new nic to the VM |
|
1704 |
|
|
1705 |
""" |
|
1706 |
if not self._InstancePidAlive(instance.name)[2]: |
|
1707 |
logging.info("Cannot hotplug. Instance %s not alive" % instance.name) |
|
1708 |
return nic.ToDict() |
|
1709 |
|
|
1710 |
_, v_major, v_min, _ = self._GetKVMVersion() |
|
1711 |
if (v_major, v_min) >= (1, 0) and nic.pci: |
|
1712 |
mac = nic.mac |
|
1713 |
idx = nic.idx |
|
1714 |
|
|
1715 |
command = "device_del virtio-net-pci.%d" % idx |
|
1716 |
logging.info("%s" % command) |
|
1717 |
output = self._CallMonitorCommand(instance.name, command) |
|
1718 |
for line in output.stdout.splitlines(): |
|
1719 |
logging.info("%s" % line) |
|
1720 |
|
|
1721 |
command = "netdev_del netdev%d" % idx |
|
1722 |
logging.info("%s" % command) |
|
1723 |
output = self._CallMonitorCommand(instance.name, command) |
|
1724 |
for line in output.stdout.splitlines(): |
|
1725 |
logging.info("%s" % line) |
|
1726 |
|
|
1727 |
(kvm_cmd, kvm_nics, |
|
1728 |
hvparams, block_devices) = self._LoadKVMRuntime(instance) |
|
1729 |
rem = [n for n in kvm_nics if n.idx is not None and n.idx == nic.idx] |
|
1730 |
try: |
|
1731 |
kvm_nics.remove(rem[0]) |
|
1732 |
except (ValueError, IndexError): |
|
1733 |
logging.info("NIC with %d idx disappeared from runtime file", nic.idx) |
|
1734 |
new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices) |
|
1735 |
self._SaveKVMRuntime(instance, new_kvm_runtime) |
|
1736 |
|
|
1737 |
return nic.ToDict() |
|
1738 |
|
|
1739 |
def _PassTapFd(self, instance, fd, nic): |
|
1740 |
monsock = utils.ShellQuote(self._InstanceMonitor(instance.name)) |
|
1741 |
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
|
1742 |
s.connect(monsock) |
|
1743 |
idx = nic.idx |
|
1744 |
command = "getfd netdev%d\n" % idx |
|
1745 |
fds = [fd] |
|
1746 |
logging.info("%s", fds) |
|
1747 |
fdsend.sendfds(s, command, fds = fds) |
|
1748 |
s.close() |
|
1749 |
|
|
1553 | 1750 |
@classmethod |
1554 | 1751 |
def _ParseKVMVersion(cls, text): |
1555 | 1752 |
"""Parse the KVM version from the --help output. |
Also available in: Unified diff