Revision 2435f63b

b/lib/cmdlib.py
8724 8724
    results.append("%s%s" % (new_id, val))
8725 8725
  return results
8726 8726

  
8727
def _GetPCIInfo(lu, dev_type):
8728

  
8729
  if (hasattr(lu, 'op') and lu.op.hotplug):
8730
    # case of InstanceCreate()
8731
    if hasattr(lu, 'hotplug_info'):
8732
      if lu.hotplug_info is not None:
8733
        idx = getattr(lu.hotplug_info, dev_type)
8734
        setattr(lu.hotplug_info, dev_type, idx+1)
8735
        if dev_type == 'disks' and idx == 0:
8736
          lu.LogInfo("Disk 0 cannot be hotpluggable.")
8737
          return None, None
8738
        pci = lu.hotplug_info.pci_pool.pop()
8739
        lu.LogInfo("Choosing pci slot %d" % pci)
8740
        return idx, pci
8741
    # case of InstanceSetParams()
8742
    elif lu.instance.hotplug_info is not None:
8743
      idx, pci = lu.cfg.GetPCIInfo(lu.instance, dev_type)
8744
      lu.LogInfo("Choosing pci slot %d" % pci)
8745
      return idx, pci
8746

  
8747
  lu.LogWarning("Hotplug not supported for this instance.")
8748
  return None, None
8727

  
8728
def _GetHotplugIndex(lu, dev_type):
8729

  
8730
  # case of InstanceCreate()
8731
  # keep backwards compat and naming
8732
  if hasattr(lu, 'hotplug_info'):
8733
    if lu.hotplug_info is not None:
8734
      idx = getattr(lu.hotplug_info, dev_type)
8735
      setattr(lu.hotplug_info, dev_type, idx + 1)
8736
      return idx
8737
  # case of InstanceSetParams()
8738
  # give every device a idx so that it can be hotplugable
8739
  elif lu.instance.hotplug_info is not None:
8740
    idx = lu.cfg.GetHotplugIndex(lu.instance, dev_type)
8741
    return idx
8742

  
8743
  return None
8744

  
8745

  
8746
def _DeviceHotplugable(dev):
8747

  
8748
  return dev.idx is not None
8749

  
8750

  
8751
def _HotplugEnabled(instance):
8752

  
8753
  return instance.hotplug_info is not None
8749 8754

  
8750 8755

  
8751 8756
def _GenerateDRBD8Branch(lu, primary, secondary, size, vgnames, names,
......
8764 8769
                          logical_id=(vgnames[1], names[1]),
8765 8770
                          params={})
8766 8771

  
8767
  disk_idx, pci = _GetPCIInfo(lu, 'disks')
8768
  drbd_dev = objects.Disk(idx=disk_idx, pci=pci,
8769
                          dev_type=constants.LD_DRBD8, size=size,
8772
  drbd_dev = objects.Disk(dev_type=constants.LD_DRBD8, size=size,
8770 8773
                          logical_id=(primary, secondary, port,
8771 8774
                                      p_minor, s_minor,
8772 8775
                                      shared_secret),
8773 8776
                          children=[dev_data, dev_meta],
8774 8777
                          iv_name=iv_name, params={})
8778
  if lu.op.hotplug:
8779
    drbd_dev.idx = _GetHotplugIndex(lu, 'disks')
8780

  
8775 8781
  return drbd_dev
8776 8782

  
8777 8783

  
......
8876 8882
      feedback_fn("* disk %s, size %s" %
8877 8883
                  (disk_index, utils.FormatUnit(size, "h")))
8878 8884

  
8879
      disk_idx, pci = _GetPCIInfo(lu, 'disks')
8885
      disk_obj = objects.Disk(dev_type=dev_type, size=size,
8886
                              logical_id=logical_id_fn(idx, disk_index, disk),
8887
                              iv_name="disk/%d" % disk_index,
8888
                              mode=disk[constants.IDISK_MODE],
8889
                              params={})
8890
      if lu.op.hotplug:
8891
        disk_obj.idx = _GetHotplugIndex(lu, 'disks')
8880 8892

  
8881
      disks.append(objects.Disk(dev_type=dev_type, size=size,
8882
                                logical_id=logical_id_fn(idx, disk_index, disk),
8883
                                iv_name="disk/%d" % disk_index,
8884
                                mode=disk[constants.IDISK_MODE],
8885
                                params={}, idx=disk_idx, pci=pci))
8893
      disks.append(disk_obj)
8886 8894

  
8887 8895
  return disks
8888 8896

  
......
9774 9782
    self.hotplug_info = None
9775 9783
    if self.op.hotplug:
9776 9784
      self.LogInfo("Enabling hotplug.")
9777
      self.hotplug_info = objects.HotplugInfo(disks=0, nics=0,
9778
                                              pci_pool=list(range(16,32)))
9785
      self.hotplug_info = objects.HotplugInfo(disks=0, nics=0)
9786

  
9779 9787
    # NIC buildup
9780 9788
    self.nics = []
9781 9789
    for idx, nic in enumerate(self.op.nics):
......
9835 9843

  
9836 9844
      check_params = cluster.SimpleFillNIC(nicparams)
9837 9845
      objects.NIC.CheckParameterSyntax(check_params)
9838
      nic_idx, pci = _GetPCIInfo(self, 'nics')
9839
      self.nics.append(objects.NIC(idx=nic_idx, pci=pci,
9840
                                   mac=mac, ip=nic_ip,
9841
                                   nicparams=check_params))
9846
      nic_obj = objects.NIC(mac=mac, ip=nic_ip, nicparams=check_params)
9847
      if self.op.hotplug:
9848
        nic_obj.idx = _GetHotplugIndex(self, 'nics')
9849

  
9850
      self.nics.append(nic_obj)
9842 9851

  
9843 9852
    # disk checks/pre-build
9844 9853
    default_vg = self.cfg.GetVGName()
......
12833 12842
        self.LogWarning("Failed to create volume %s (%s) on node '%s': %s",
12834 12843
                        disk.iv_name, disk, node, err)
12835 12844

  
12836
    if self.op.hotplug and disk.pci and _InstanceRunning(self, self.instance):
12845
    if self.op.hotplug and _HotplugEnabled(self.instance):
12837 12846
      self.LogInfo("Trying to hotplug device.")
12838 12847
      _, device_info = _AssembleInstanceDisks(self, self.instance,
12839 12848
                                                    [disk], check=False)
12840 12849
      _, _, dev_path = device_info[0]
12841 12850
      #TODO: handle result
12842
      self.rpc.call_hot_add_disk(self.instance.primary_node,
12843
                                 self.instance, disk, dev_path, idx)
12851
      result = self.rpc.call_hot_add_disk(self.instance.primary_node,
12852
                                          self.instance, disk, dev_path, idx)
12853
      result.Raise("Could not hotplug device.")
12854
      disk.pci = result.payload
12855

  
12844 12856
    return (disk, [
12845 12857
      ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
12846 12858
      ])
......
12862 12874
    """
12863 12875
    #TODO: log warning in case hotplug is not possible
12864 12876
    #      handle errors
12865
    if root.pci and not self.op.hotplug:
12866
      raise errors.OpPrereqError("Cannot remove a disk that has"
12867
                                 " been hotplugged"
12868
                                 " without removing it with hotplug",
12869
                                 errors.ECODE_INVAL)
12870
    if self.op.hotplug and root.pci:
12871
      if _InstanceRunning(self, self.instance):
12872
        self.LogInfo("Trying to hotplug device.")
12873
        self.rpc.call_hot_del_disk(self.instance.primary_node,
12874
                                   self.instance, root, idx)
12875
        _ShutdownInstanceDisks(self, self.instance, [root])
12876
      self.cfg.UpdatePCIInfo(self.instance, root.pci)
12877
    if self.op.hotplug and _DeviceHotplugable(root):
12878
      self.LogInfo("Trying to hotplug device.")
12879
      result = self.rpc.call_hot_del_disk(self.instance.primary_node,
12880
                                          self.instance, root, idx)
12881
      result.Raise("Could not hotplug device.")
12882
      self.LogInfo("Hotplug done.")
12883
      _ShutdownInstanceDisks(self, self.instance, [root])
12877 12884

  
12878 12885
    (anno_disk,) = _AnnotateDiskParams(self.instance, [root], self.cfg)
12879 12886
    for node, disk in anno_disk.ComputeNodeTree(self.instance.primary_node):
......
12901 12908
    #TODO: log warning in case hotplug is not possible
12902 12909
    #      handle errors
12903 12910
    #      return changes
12904
    if self.op.hotplug:
12905
      nic.idx, nic.pci = _GetPCIInfo(self, 'nics')
12906
      if nic.pci is not None and _InstanceRunning(self, self.instance):
12907
        self.rpc.call_hot_add_nic(self.instance.primary_node,
12908
                                  self.instance, nic, idx)
12911
    if self.op.hotplug and _HotplugEnabled(self.instance):
12912
      nic.idx = _GetHotplugIndex(self, 'nics')
12913
      result = self.rpc.call_hot_add_nic(self.instance.primary_node,
12914
                                         self.instance, nic, idx)
12915
      result.Raise("Could not hotplug device")
12916
      nic.pci = result.payload
12917
      self.Log("Hotplug done.")
12918

  
12909 12919
    desc =  [
12910 12920
      ("nic.%d" % idx,
12911 12921
       "add:mac=%s,ip=%s,mode=%s,link=%s" %
......
12933 12943

  
12934 12944
    #TODO: log warning in case hotplug is not possible
12935 12945
    #      handle errors
12936
    if self.op.hotplug and nic.pci and _InstanceRunning(self, self.instance):
12946
    if self.op.hotplug and _DeviceHotplugable(nic):
12937 12947
      self.LogInfo("Trying to hotplug device.")
12938
      self.rpc.call_hot_del_nic(self.instance.primary_node,
12939
                                self.instance, nic, idx)
12940
      self.rpc.call_hot_add_nic(self.instance.primary_node,
12941
                                self.instance, nic, idx)
12948
      result = self.rpc.call_hot_del_nic(self.instance.primary_node,
12949
                                         self.instance, nic, idx)
12950
      result.Raise("Could not hotplug device.")
12951

  
12952
      result = self.rpc.call_hot_add_nic(self.instance.primary_node,
12953
                                         self.instance, nic, idx)
12954
      result.Raise("Could not hotplug device.")
12955
      self.Log("Hotplug done.")
12956
      nic.pci = result.payload
12957

  
12942 12958
    return changes
12943 12959

  
12944 12960
  def _RemoveNic(self, idx, nic, _):
12945
    if nic.pci and not self.op.hotplug:
12946
      raise errors.OpPrereqError("Cannot remove a nic that has been hotplugged"
12947
                                 " without removing it with hotplug",
12948
                                 errors.ECODE_INVAL)
12949 12961
    #TODO: log warning in case hotplug is not possible
12950 12962
    #      handle errors
12951
    if self.op.hotplug and nic.pci:
12952
      if _InstanceRunning(self, self.instance):
12953
        self.LogInfo("Trying to hotplug device.")
12954
        self.rpc.call_hot_del_nic(self.instance.primary_node,
12955
                                  self.instance, nic, idx)
12956
      self.cfg.UpdatePCIInfo(self.instance, nic.pci)
12963
    if self.op.hotplug and _DeviceHotplugable(nic):
12964
      self.LogInfo("Trying to hotplug device.")
12965
      result = self.rpc.call_hot_del_nic(self.instance.primary_node,
12966
                                         self.instance, nic, idx)
12967
      result.Raise("Could not hotplug device.")
12968
      self.Log("Hotplug done.")
12957 12969

  
12958 12970

  
12959 12971
  def Exec(self, feedback_fn):
b/lib/config.py
290 290
      self._temporary_macs.Reserve(ec_id, mac)
291 291

  
292 292
  @locking.ssynchronized(_config_lock)
293
  def GetPCIInfo(self, instance, dev_type):
293
  def GetHotplugIndex(self, instance, dev_type):
294 294

  
295
    if not instance.hotplug_info:
296
      return None, None
297 295
    idx = getattr(instance.hotplug_info, dev_type)
298
    setattr(instance.hotplug_info, dev_type, idx+1)
299
    pci = instance.hotplug_info.pci_pool.pop()
296
    setattr(instance.hotplug_info, dev_type, idx + 1)
300 297
    self._WriteConfig()
301 298

  
302
    return idx, pci
303

  
304
  @locking.ssynchronized(_config_lock)
305
  def UpdatePCIInfo(self, instance, pci_slot):
306

  
307
    if instance.hotplug_info:
308
      logging.info("Releasing PCI slot %d for instance %s",
309
                    pci_slot, instance.name)
310
      instance.hotplug_info.pci_pool.append(pci_slot)
311
      self._WriteConfig()
299
    return idx
312 300

  
313 301
  @locking.ssynchronized(_config_lock, shared=1)
314 302
  def ReserveLV(self, lv_name, ec_id):
b/lib/hypervisor/hv_kvm.py
38 38
import stat
39 39
import StringIO
40 40
import fdsend
41
from bitarray import bitarray
41 42
try:
42 43
  import affinity   # pylint: disable=F0401
43 44
except ImportError:
......
68 69
IFF_NO_PI = 0x1000
69 70
IFF_VNET_HDR = 0x4000
70 71

  
72
FREE = bitarray("0")
71 73

  
72 74
def _ProbeTapVnetHdr(fd):
73 75
  """Check whether to enable the IFF_VNET_HDR flag.
......
512 514
  _CPU_INFO_CMD = "info cpus"
513 515
  _CONT_CMD = "cont"
514 516

  
517
  _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
518
  _INFO_PCI_CMD = "info pci"
519

  
520

  
515 521
  ANCILLARY_FILES = [
516 522
    _KVM_NETWORK_SCRIPT,
517 523
    ]
......
990 996
          boot_val = ",boot=on"
991 997
      drive_val = "file=%s,format=raw%s%s" % \
992 998
                  (dev_path, boot_val, cache_val)
993
      if cfdev.pci:
999
      if cfdev.idx is not None:
994 1000
        #TODO: name id after model
995
        drive_val += (",bus=0,unit=%d,if=none,id=drive%d" %
996
                      (cfdev.pci, cfdev.idx))
1001
        drive_val += (",if=none,id=drive%d" % cfdev.idx)
1002
        if cfdev.pci is not None:
1003
          drive_val += (",bus=0,unit=%d" % cfdev.pci)
997 1004
      else:
998 1005
        drive_val += if_val
999 1006

  
1000 1007
      kvm_cmd.extend(["-drive", drive_val])
1001 1008

  
1002
      if cfdev.pci:
1003
        dev_val = ("%s,bus=pci.0,addr=%s,drive=drive%d,id=virtio-blk-pci.%d" %
1004
                   (disk_model, hex(cfdev.pci), cfdev.idx, cfdev.idx))
1009
      if cfdev.idx is not None:
1010
        dev_val = ("%s,drive=drive%d,id=virtio-blk-pci.%d" %
1011
                    (disk_model, cfdev.idx, cfdev.idx))
1012
        if cfdev.pci is not None:
1013
          dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1005 1014
        kvm_cmd.extend(["-device", dev_val])
1006 1015

  
1007 1016
    return kvm_cmd
......
1465 1474
        tapfds.append(tapfd)
1466 1475
        taps.append(tapname)
1467 1476
        if (v_major, v_min) >= (0, 12):
1468
          if nic.pci:
1469
            nic_idx = nic.idx
1477
          nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1478
          if nic.idx:
1479
            nic_val += (",netdev=netdev%d,id=virtio-net-pci.%d" %
1480
                        (nic.idx, nic.idx))
1481
            if nic.pci is not None:
1482
              nic_val += (",bus=pci.0,addr=%s" % hex(nic.pci))
1470 1483
          else:
1471
            nic_idx = nic_seq
1472
          nic_val = ("%s,mac=%s,netdev=netdev%d" %
1473
                     (nic_model, nic.mac, nic_idx))
1474
          if nic.pci:
1475
            nic_val += (",bus=pci.0,addr=%s,id=virtio-net-pci.%d" %
1476
                        (hex(nic.pci), nic_idx))
1477
          tap_val = "type=tap,id=netdev%d,fd=%d%s" % (nic_idx, tapfd, tap_extra)
1484
            nic_val += (",netdev=netdev%d,id=virtio-net-pci.%d" %
1485
                        (nic_seq, nic_seq))
1486
          tap_val = ("type=tap,id=netdev%d,fd=%d%s" %
1487
                     (nic.idx or nic_seq, tapfd, tap_extra))
1478 1488
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1479 1489
        else:
1480 1490
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
......
1625 1635

  
1626 1636
    return result
1627 1637

  
1638
  def _FindFreePCISlot(self, instance_name):
1639
    slots = bitarray(32)
1640
    slots.setall(False)
1641
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
1642
    for line in output.stdout.splitlines():
1643
      match = self._INFO_PCI_RE.search(line)
1644
      if match:
1645
        slot = int(match.group(1))
1646
        slots[slot] = True
1647

  
1648
    free = slots.search(FREE, 1)
1649
    if not free:
1650
      raise errors.HypervisorError("All PCI slots occupied")
1651

  
1652
    return int(free[0])
1653

  
1654
  def _HotplugEnabled(self, instance_name):
1655
    if not self._InstancePidAlive(instance_name)[2]:
1656
      logging.info("Cannot hotplug. Instance %s not alive", instance_name)
1657
      return False
1658

  
1659
    _, v_major, v_min, _ = self._GetKVMVersion()
1660
    return (v_major, v_min) >= (1, 0)
1661

  
1628 1662
  def HotAddDisk(self, instance, disk, dev_path, _):
1629 1663
    """Hotadd new disk to the VM
1630 1664

  
1631 1665
    """
1632
    if not self._InstancePidAlive(instance.name)[2]:
1633
      logging.info("Cannot hotplug. Instance %s not alive", instance.name)
1634
      return disk.ToDict()
1635

  
1636
    _, v_major, v_min, _ = self._GetKVMVersion()
1637
    if (v_major, v_min) >= (1, 0) and disk.pci:
1666
    if self._HotplugEnabled(instance.name):
1667
      disk.pci = self._FindFreePCISlot(instance.name)
1638 1668
      idx = disk.idx
1639 1669
      command = ("drive_add dummy file=%s,if=none,id=drive%d,format=raw" %
1640 1670
                 (dev_path, idx))
......
1656 1686
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1657 1687
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1658 1688

  
1659
    return disk.ToDict()
1689
    return disk.pci
1660 1690

  
1661 1691
  def HotDelDisk(self, instance, disk, _):
1662 1692
    """Hotdel disk to the VM
1663 1693

  
1664 1694
    """
1665
    if not self._InstancePidAlive(instance.name)[2]:
1666
      logging.info("Cannot hotplug. Instance %s not alive", instance.name)
1667
      return disk.ToDict()
1668

  
1669
    _, v_major, v_min, _ = self._GetKVMVersion()
1670
    if (v_major, v_min) >= (1, 0) and disk.pci:
1695
    if self._HotplugEnabled(instance.name):
1671 1696
      idx = disk.idx
1672 1697

  
1673 1698
      command = "device_del virtio-blk-pci.%d" % idx
......
1693 1718
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1694 1719
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1695 1720

  
1696
    return disk.ToDict()
1697

  
1698 1721
  def HotAddNic(self, instance, nic, seq):
1699 1722
    """Hotadd new nic to the VM
1700 1723

  
1701 1724
    """
1702
    if not self._InstancePidAlive(instance.name)[2]:
1703
      logging.info("Cannot hotplug. Instance %s not alive", instance.name)
1704
      return nic.ToDict()
1705

  
1706
    _, v_major, v_min, _ = self._GetKVMVersion()
1707
    if (v_major, v_min) >= (1, 0) and nic.pci:
1725
    if self._HotplugEnabled(instance.name):
1726
      nic.pci = self._FindFreePCISlot(instance.name)
1708 1727
      mac = nic.mac
1709 1728
      idx = nic.idx
1710 1729

  
......
1736 1755
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1737 1756
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1738 1757

  
1739
    return nic.ToDict()
1758
    return nic.pci
1740 1759

  
1741 1760
  def HotDelNic(self, instance, nic, _):
1742 1761
    """Hotadd new nic to the VM
1743 1762

  
1744 1763
    """
1745
    if not self._InstancePidAlive(instance.name)[2]:
1746
      logging.info("Cannot hotplug. Instance %s not alive", instance.name)
1747
      return nic.ToDict()
1748

  
1749
    _, v_major, v_min, _ = self._GetKVMVersion()
1750
    if (v_major, v_min) >= (1, 0) and nic.pci:
1764
    if self._HotplugEnabled(instance.name):
1751 1765
      idx = nic.idx
1752 1766

  
1753 1767
      command = "device_del virtio-net-pci.%d" % idx
......
1772 1786
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1773 1787
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1774 1788

  
1775
    return nic.ToDict()
1776 1789

  
1777 1790
  def _PassTapFd(self, instance, fd, nic):
1778 1791
    monsock = utils.ShellQuote(self._InstanceMonitor(instance.name))
b/lib/objects.py
504 504
        self.cluster.drbd_usermode_helper = constants.DEFAULT_DRBD_HELPER
505 505

  
506 506
class HotplugInfo(ConfigObject):
507
  __slots__ = ["nics", "disks", "pci_pool"]
507
  __slots__ = ["nics", "disks"]
508 508

  
509 509

  
510 510
class NIC(ConfigObject):
b/tools/cfgupgrade
95 95
  return False
96 96

  
97 97

  
98
def UpgradeInstances(config_data):
99
  for instance in config_data["instances"].values():
100
    hotplug_info = instance.get("hotplug_info", None)
101
    if hotplug_info:
102
      try:
103
        del hotplug_info["pci_pool"]
104
      except:
105
        pass
106

  
107

  
98 108
def main():
99 109
  """Main program.
100 110

  
......
237 247
    if not options.dry_run:
238 248
      utils.RemoveFile(options.WATCHER_STATEFILE)
239 249

  
250
  UpgradeInstances(config_data)
251

  
240 252
  try:
241 253
    logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
242 254
    utils.WriteFile(file_name=options.CONFIG_DATA_PATH,

Also available in: Unified diff