From 7b4126b7c774531c8a40e291e66286ec61586fe1 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Tue, 1 Jul 2008 11:44:37 +0000
Subject: [PATCH] Add a FirstFree function to utils.py

This function will return the first unused integer based on a list of
used integers (e.g. [0, 1, 3] will return 2).

Reviewed-by: imsnah
---
 lib/utils.py                  | 21 +++++++++++++++++++++
 test/ganeti.utils_unittest.py | 12 +++++++++++-
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/lib/utils.py b/lib/utils.py
index 4e8cfe7e7..ade6b9b54 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 c5f8cfda6..1d9bd2f1a 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()
-- 
GitLab