Commit a813462d authored by Dimitris Aragiorgis's avatar Dimitris Aragiorgis Committed by Hrvoje Ribicic

utils: Introduce GetFreeSlot() function

Since this is a generic function that implements bitarray logic move
it from kvm to utils so that it can be easily used across all modules.

Make it raise errors.GenericError if it cannot find a free slot in
the given bitarray.

We add this function in a separate utils module (bitarrays) that can be
extended in the future. Currently only the network module and the
KVM hypervisor make use of bitarrays. This patch is a step forward
unifying these pieces of code and make both use the same bitarray
utility functions.

Add unittest for bitarrays utils module.
Signed-off-by: default avatarDimitris Aragiorgis <dimara@grnet.gr>
Reviewed-by: default avatarHrvoje Ribicic <riba@google.com>
parent f11d9396
...@@ -596,7 +596,8 @@ utils_PYTHON = \ ...@@ -596,7 +596,8 @@ utils_PYTHON = \
lib/utils/text.py \ lib/utils/text.py \
lib/utils/version.py \ lib/utils/version.py \
lib/utils/wrapper.py \ lib/utils/wrapper.py \
lib/utils/x509.py lib/utils/x509.py \
lib/utils/bitarrays.py
docinput = \ docinput = \
doc/admin.rst \ doc/admin.rst \
...@@ -1801,6 +1802,7 @@ python_tests = \ ...@@ -1801,6 +1802,7 @@ python_tests = \
test/py/ganeti.utils.version_unittest.py \ test/py/ganeti.utils.version_unittest.py \
test/py/ganeti.utils.wrapper_unittest.py \ test/py/ganeti.utils.wrapper_unittest.py \
test/py/ganeti.utils.x509_unittest.py \ test/py/ganeti.utils.x509_unittest.py \
test/py/ganeti.utils.bitarrays_unittest.py \
test/py/ganeti.utils_unittest.py \ test/py/ganeti.utils_unittest.py \
test/py/ganeti.vcluster_unittest.py \ test/py/ganeti.vcluster_unittest.py \
test/py/ganeti.workerpool_unittest.py \ test/py/ganeti.workerpool_unittest.py \
......
...@@ -83,10 +83,6 @@ _SPICE_ADDITIONAL_PARAMS = frozenset([ ...@@ -83,10 +83,6 @@ _SPICE_ADDITIONAL_PARAMS = frozenset([
constants.HV_KVM_SPICE_USE_TLS, constants.HV_KVM_SPICE_USE_TLS,
]) ])
# Constant bitarray that reflects to a free pci slot
# Use it with bitarray.search()
_AVAILABLE_PCI_SLOT = bitarray("0")
# below constants show the format of runtime file # below constants show the format of runtime file
# the nics are in second possition, while the disks in 4th (last) # the nics are in second possition, while the disks in 4th (last)
# moreover disk entries are stored as a list of in tuples # moreover disk entries are stored as a list of in tuples
...@@ -186,38 +182,6 @@ def _GenerateDeviceKVMId(dev_type, dev): ...@@ -186,38 +182,6 @@ def _GenerateDeviceKVMId(dev_type, dev):
return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci) return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
def _GetFreeSlot(slots, slot=None, reserve=False):
"""Helper method to get first available slot in a bitarray
@type slots: bitarray
@param slots: the bitarray to operate on
@type slot: integer
@param slot: if given we check whether the slot is free
@type reserve: boolean
@param reserve: whether to reserve the first available slot or not
@return: the idx of the (first) available slot
@raise errors.HotplugError: If all slots in a bitarray are occupied
or the given slot is not free.
"""
if slot is not None:
assert slot < len(slots)
if slots[slot]:
raise errors.HypervisorError("Slots %d occupied" % slot)
else:
avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
if not avail:
raise errors.HypervisorError("All slots occupied")
slot = int(avail[0])
if reserve:
slots[slot] = True
return slot
def _GetExistingDeviceInfo(dev_type, device, runtime): def _GetExistingDeviceInfo(dev_type, device, runtime):
"""Helper function to get an existing device inside the runtime file """Helper function to get an existing device inside the runtime file
...@@ -1123,15 +1087,15 @@ class KVMHypervisor(hv_base.BaseHypervisor): ...@@ -1123,15 +1087,15 @@ class KVMHypervisor(hv_base.BaseHypervisor):
# while the Audio controller *must* be in slot 3. # while the Audio controller *must* be in slot 3.
# That's why we bridge this option early in command line # That's why we bridge this option early in command line
if soundhw in self._SOUNDHW_WITH_PCI_SLOT: if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
_ = _GetFreeSlot(pci_reservations, reserve=True) _ = utils.GetFreeSlot(pci_reservations, reserve=True)
kvm_cmd.extend(["-soundhw", soundhw]) kvm_cmd.extend(["-soundhw", soundhw])
if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI: if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
# The SCSI controller requires another PCI slot. # The SCSI controller requires another PCI slot.
_ = _GetFreeSlot(pci_reservations, reserve=True) _ = utils.GetFreeSlot(pci_reservations, reserve=True)
# Add id to ballon and place to the first available slot (3 or 4) # Add id to ballon and place to the first available slot (3 or 4)
addr = _GetFreeSlot(pci_reservations, reserve=True) addr = utils.GetFreeSlot(pci_reservations, reserve=True)
pci_info = ",bus=pci.0,addr=%s" % hex(addr) pci_info = ",bus=pci.0,addr=%s" % hex(addr)
kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info]) kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
kvm_cmd.extend(["-daemonize"]) kvm_cmd.extend(["-daemonize"])
...@@ -1369,7 +1333,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): ...@@ -1369,7 +1333,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
else: else:
# Enable the spice agent communication channel between the host and the # Enable the spice agent communication channel between the host and the
# agent. # agent.
addr = _GetFreeSlot(pci_reservations, reserve=True) addr = utils.GetFreeSlot(pci_reservations, reserve=True)
pci_info = ",bus=pci.0,addr=%s" % hex(addr) pci_info = ",bus=pci.0,addr=%s" % hex(addr)
kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info]) kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
kvm_cmd.extend([ kvm_cmd.extend([
...@@ -1420,12 +1384,12 @@ class KVMHypervisor(hv_base.BaseHypervisor): ...@@ -1420,12 +1384,12 @@ class KVMHypervisor(hv_base.BaseHypervisor):
kvm_disks = [] kvm_disks = []
for disk, link_name, uri in block_devices: for disk, link_name, uri in block_devices:
disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True) disk.pci = utils.GetFreeSlot(pci_reservations, disk.pci, True)
kvm_disks.append((disk, link_name, uri)) kvm_disks.append((disk, link_name, uri))
kvm_nics = [] kvm_nics = []
for nic in instance.nics: for nic in instance.nics:
nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True) nic.pci = utils.GetFreeSlot(pci_reservations, nic.pci, True)
kvm_nics.append(nic) kvm_nics.append(nic)
hvparams = hvp hvparams = hvp
...@@ -1857,7 +1821,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): ...@@ -1857,7 +1821,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
slot = int(match.group(1)) slot = int(match.group(1))
slots[slot] = True slots[slot] = True
dev.pci = _GetFreeSlot(slots) dev.pci = utils.GetFreeSlot(slots)
def VerifyHotplugSupport(self, instance, action, dev_type): def VerifyHotplugSupport(self, instance, action, dev_type):
"""Verifies that hotplug is supported. """Verifies that hotplug is supported.
......
...@@ -69,6 +69,7 @@ from ganeti.utils.text import * ...@@ -69,6 +69,7 @@ from ganeti.utils.text import *
from ganeti.utils.wrapper import * from ganeti.utils.wrapper import *
from ganeti.utils.version import * from ganeti.utils.version import *
from ganeti.utils.x509 import * from ganeti.utils.x509 import *
from ganeti.utils.bitarrays import *
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$") _VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
......
#
#
# Copyright (C) 2014 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Utility functions for managing bitarrays.
"""
from bitarray import bitarray
from ganeti import errors
# Constant bitarray that reflects to a free slot
# Use it with bitarray.search()
_AVAILABLE_SLOT = bitarray("0")
def GetFreeSlot(slots, slot=None, reserve=False):
"""Helper method to get first available slot in a bitarray
@type slots: bitarray
@param slots: the bitarray to operate on
@type slot: integer
@param slot: if given we check whether the slot is free
@type reserve: boolean
@param reserve: whether to reserve the first available slot or not
@return: the idx of the (first) available slot
@raise errors.OpPrereqError: If all slots in a bitarray are occupied
or the given slot is not free.
"""
if slot is not None:
assert slot < len(slots)
if slots[slot]:
raise errors.GenericError("Slot %d occupied" % slot)
else:
avail = slots.search(_AVAILABLE_SLOT, 1)
if not avail:
raise errors.GenericError("All slots occupied")
slot = int(avail[0])
if reserve:
slots[slot] = True
return slot
#!/usr/bin/python
#
# Copyright (C) 2014 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Script for unittesting the bitarray utility functions"""
import unittest
import testutils
from bitarray import bitarray
from ganeti import errors
from ganeti.utils import bitarrays
_FREE = bitarray("11100010")
_FULL = bitarray("11111111")
class GetFreeSlotTest(unittest.TestCase):
"""Test function that finds a free slot in a bitarray"""
def testFreeSlot(self):
self.assertEquals(bitarrays.GetFreeSlot(_FREE), 3)
def testReservedSlot(self):
self.assertRaises(errors.GenericError,
bitarrays.GetFreeSlot,
_FREE, slot=1)
def testNoFreeSlot(self):
self.assertRaises(errors.GenericError,
bitarrays.GetFreeSlot,
_FULL)
def testGetAndReserveSlot(self):
self.assertEquals(bitarrays.GetFreeSlot(_FREE, slot=5, reserve=True), 5)
self.assertEquals(_FREE, bitarray("11100110"))
if __name__ == "__main__":
testutils.GanetiTestProgram()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment