Commit 4dd9c12e authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

Implement resizing of pools

Extend PoolManager to support extending and shrinking of Pools. Also
add extra option '--size' to pool-modify command.
parent f9259063
......@@ -27,26 +27,30 @@ class PoolManager(object):
"""
def __init__(self, pool_table):
self.pool_table = pool_table
self.pool_size = pool_table.size
if pool_table.available_map:
self.available = _bitarray_from_string(pool_table.available_map)
self.reserved = _bitarray_from_string(pool_table.reserved_map)
else:
size = self.pool_table.size
padding = find_padding(size)
size = size + padding
self.pool_size = size
self.available = self._create_empty_pool()
self.reserved = self._create_empty_pool()
for i in xrange(0, padding):
self._reserve(size - i - 1, external=True)
def _create_empty_pool(self):
assert(self.pool_size % 8 == 0)
ba = bitarray(self.pool_size)
self.available = self._create_empty_pool(self.pool_size)
self.reserved = self._create_empty_pool(self.pool_size)
self.add_padding(self.pool_size)
def _create_empty_pool(self, size):
ba = bitarray(size)
ba.setall(AVAILABLE)
return ba
def add_padding(self, pool_size):
bits = find_padding(pool_size)
self.available.extend([AVAILABLE] * bits)
self.reserved.extend([UNAVAILABLE] * bits)
def cut_padding(self, pool_size):
bits = find_padding(pool_size)
self.available = self.available[:-bits]
self.reserved = self.reserved[:-bits]
@property
def pool(self):
return (self.available & self.reserved)
......@@ -57,6 +61,7 @@ class PoolManager(object):
raise EmptyPool
# Get the first available index
index = int(self.pool.index(AVAILABLE))
assert(index < self.pool_size)
self._reserve(index)
return self.index_to_value(index)
......@@ -85,6 +90,7 @@ class PoolManager(object):
return not self.pool.any()
def size(self):
"""Return the size of the bitarray(original size + padding)."""
return self.pool.length()
def _reserve(self, index, external=False):
......@@ -106,10 +112,10 @@ class PoolManager(object):
return self.pool.count(UNAVAILABLE)
def count_reserved(self):
return self.reserved.count(UNAVAILABLE)
return self.reserved[:self.pool_size].count(UNAVAILABLE)
def count_unreserved(self):
return self.size() - self.count_reserved()
return self.pool_size - self.count_reserved()
def is_available(self, value, index=False):
if not index:
......@@ -126,11 +132,40 @@ class PoolManager(object):
return self.reserved[idx] == UNAVAILABLE
def to_01(self):
return self.pool.to01()
return self.pool[:self.pool_size].to01()
def to_map(self):
return self.to_01().replace("0", "X").replace("1", ".")
def extend(self, bits_num):
assert(bits_num >= 0)
self.resize(bits_num)
def shrink(self, bits_num):
assert(bits_num >= 0)
size = self.pool_size
tmp = self.available[(size - bits_num): size]
if tmp.count(UNAVAILABLE):
raise Exception("Can not shrink. In use")
self.resize(-bits_num)
def resize(self, bits_num):
if bits_num == 0:
return
# Cut old padding
self.cut_padding(self.pool_size)
# Do the resize
if bits_num > 0:
self.available.extend([AVAILABLE] * bits_num)
self.reserved.extend([AVAILABLE] * bits_num)
else:
self.available = self.available[:bits_num]
self.reserved = self.reserved[:bits_num]
# Add new padding
self.pool_size = self.pool_size + bits_num
self.add_padding(self.pool_size)
self.pool_table.size = self.pool_size
def index_to_value(self, index):
raise NotImplementedError
......@@ -183,7 +218,7 @@ class MacPrefixPool(PoolManager):
do_init = False if pool_table.available_map else True
super(MacPrefixPool, self).__init__(pool_table)
if do_init:
for i in xrange(1, self.size()):
for i in xrange(1, self.pool_size):
if not self.validate_mac(self.index_to_value(i)):
self._reserve(i, external=True)
# Reserve the first mac-prefix for public-networks
......@@ -221,7 +256,7 @@ class IPPool(PoolManager):
self._reserve(0, external=True)
if gateway:
self.reserve(gateway, external=True)
self._reserve(self.size() - 1, external=True)
self._reserve(self.pool_size - 1, external=True)
def value_to_index(self, value):
addr = ipaddr.IPAddress(value)
......
import sys
from synnefo.db.pools import (PoolManager, EmptyPool, BridgePool,
MacPrefixPool, IPPool)
from bitarray import bitarray
# Use backported unittest functionality if Python < 2.7
try:
......@@ -34,8 +35,10 @@ class PoolManagerTestCase(unittest.TestCase):
def test_created_pool(self):
obj = DummyObject(42)
pool = DummyPool(obj)
self.assertEqual(pool.to_01(), '1' * 42 + '0' * 6)
self.assertEqual(pool.to_map(), '.' * 42 + 'X' * 6)
self.assertEqual(pool.to_01(), '1' * 42)
self.assertEqual(pool.to_map(), '.' * 42)
self.assertEqual(pool.available, bitarray('1' * 42 + '0' * 6))
self.assertEqual(pool.reserved, bitarray('1' * 42 + '0' * 6))
def test_save_pool(self):
obj = DummyObject(42)
......@@ -89,6 +92,24 @@ class PoolManagerTestCase(unittest.TestCase):
pool.put(0)
self.assertEqual(pool.get(), 1)
def test_extend_pool(self):
obj = DummyObject(42)
pool = DummyPool(obj)
pool.extend(7)
self.assertEqual(pool.to_01(), '1' * 49)
self.assertEqual(pool.to_map(), '.' * 49)
self.assertEqual(pool.available, bitarray('1' * 49 + '0' * 7))
self.assertEqual(pool.reserved, bitarray('1' * 49 + '0' * 7))
def test_shrink_pool(self):
obj = DummyObject(42)
pool = DummyPool(obj)
pool.shrink(3)
self.assertEqual(pool.to_01(), '1' * 39)
self.assertEqual(pool.to_map(), '.' * 39)
self.assertEqual(pool.available, bitarray('1' * 39 + '0' * 1))
self.assertEqual(pool.reserved, bitarray('1' * 39 + '0' * 1))
class BridgePoolTestCase(unittest.TestCase):
def test_bridge_conversion(self):
......@@ -96,7 +117,7 @@ class BridgePoolTestCase(unittest.TestCase):
obj.base = "bridge"
pool = BridgePool(obj)
for i in range(0, 13):
self.assertEqual("bridge" + str(i), pool.get())
self.assertEqual("bridge" + str(i + 1), pool.get())
pool.put("bridge2")
pool.put("bridge6")
self.assertEqual("bridge2", pool.get())
......
......@@ -49,6 +49,7 @@ class Command(BaseCommand):
help="Type of pool"
),
make_option('--offset', dest='offset'),
make_option('--size', dest='size'),
make_option('--base', dest='base'),
make_option('--add-reserved', dest='add-reserved',
help="Comma-seperated list of values to reserve"),
......@@ -90,4 +91,23 @@ class Command(BaseCommand):
if base:
pool_row.base = base
# Save now, to avoid conflict with resizing pool.save()
pool_row.save()
if size:
try:
size = int(size)
except ValueError:
raise CommandError("Invalid size")
pool = pool_row.get_pool()
self.resize_pool(pool, pool_row.size, size)
pool.save()
def resize_pool(self, pool, old_size, new_size):
if new_size == old_size:
return
elif new_size > old_size:
pool.extend(new_size - old_size)
else:
pool.shrink(old_size - new_size)
pool.save()
......@@ -81,7 +81,7 @@ class Command(BaseCommand):
step = (type_ == 'bridge') and 64 or 80
print_map('Pool', pool.to_map(), step, self.stdout)
print_map('Reserved', bitarray_to_map(pool.reserved), step, self.stdout)
print_map('Reserved', bitarray_to_map(pool.reserved[:pool_row.size]), step, self.stdout)
def print_map(name, pool_map, step, out):
......
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