diff --git a/lib/utils.py b/lib/utils.py index 4e8cfe7e70126fc6d3b7495eeff701215336dbc8..ade6b9b5420eb6b279fa29e73ea8aaf4ddd4bd55 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -1039,6 +1039,27 @@ def WriteFile(file_name, fn=None, data=None, return result +def FirstFree(seq, base=0): + """Returns the first non-existing integer from seq. + + The seq argument should be a sorted list of positive integers. The + first time the index of an element is smaller than the element + value, the index will be returned. + + The base argument is used to start at a different offset, + i.e. [3, 4, 6] with offset=3 will return 5. + + Example: [0, 1, 3] will return 2. + + """ + for idx, elem in enumerate(seq): + assert elem >= base, "Passed element is higher than base offset" + if elem > idx + base: + # idx is not used + return idx + base + return None + + def all(seq, pred=bool): "Returns True if pred(x) is True for every element in the iterable" for elem in itertools.ifilterfalse(pred, seq): diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py index c5f8cfda68e95fabbda4c5941ac3be60f973a06f..1d9bd2f1a57c2655f092bb15e1ddf54ffdca1d9a 100755 --- a/test/ganeti.utils_unittest.py +++ b/test/ganeti.utils_unittest.py @@ -41,7 +41,7 @@ from ganeti.utils import IsProcessAlive, Lock, Unlock, RunCmd, \ RemoveFile, CheckDict, MatchNameComponent, FormatUnit, \ ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \ ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \ - SetEtcHostsEntry, RemoveEtcHostsEntry + SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree from ganeti.errors import LockError, UnitParseError def _ChildHandler(signal, stack): @@ -689,6 +689,16 @@ class TestUniqueSequence(unittest.TestCase): self._test(["a", "b"], ["a", "b"]) self._test(["a", "b", "a"], ["a", "b"]) +class TestFirstFree(unittest.TestCase): + """Test case for the FirstFree function""" + + def test(self): + """Test FirstFree""" + self.failUnlessEqual(FirstFree([0, 1, 3]), 2) + self.failUnlessEqual(FirstFree([]), None) + self.failUnlessEqual(FirstFree([3, 4, 6]), 0) + self.failUnlessEqual(FirstFree([3, 4, 6], base=3), 5) + self.failUnlessRaises(AssertionError, FirstFree, [0, 3, 4, 6], base=3) if __name__ == '__main__': unittest.main()