Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / db / pools / __init__.py @ f82dfec6

History | View | Annotate | Download (8.9 kB)

1 03992c72 Christos Stavrakakis
from bitarray import bitarray
2 03992c72 Christos Stavrakakis
from base64 import b64encode, b64decode
3 03992c72 Christos Stavrakakis
import ipaddr
4 03992c72 Christos Stavrakakis
5 03992c72 Christos Stavrakakis
AVAILABLE = True
6 03992c72 Christos Stavrakakis
UNAVAILABLE = False
7 03992c72 Christos Stavrakakis
8 03992c72 Christos Stavrakakis
9 03992c72 Christos Stavrakakis
class PoolManager(object):
10 03992c72 Christos Stavrakakis
    """PoolManager for DB PoolTable models.
11 03992c72 Christos Stavrakakis

12 03992c72 Christos Stavrakakis
    This class implements a persistent Pool mechanism based on rows of
13 03992c72 Christos Stavrakakis
    PoolTable objects. Values that are pooled by this class, are mapped to an
14 03992c72 Christos Stavrakakis
    index on a bitarray, which is the one that is stored on the DB.
15 03992c72 Christos Stavrakakis

16 03992c72 Christos Stavrakakis
    The object that will be used in order to initialize this pool, must have
17 03992c72 Christos Stavrakakis
    two string attributes (available_map and reserved_map) and the size of the
18 03992c72 Christos Stavrakakis
    pool.
19 03992c72 Christos Stavrakakis

20 03992c72 Christos Stavrakakis
    Subclasses of PoolManager must implement value_to_index and index_to_value
21 03992c72 Christos Stavrakakis
    method's in order to denote how the value will be mapped to the index in
22 03992c72 Christos Stavrakakis
    the bitarray.
23 03992c72 Christos Stavrakakis

24 03992c72 Christos Stavrakakis
    Important!!: Updates on a PoolManager object are not reflected to the DB,
25 03992c72 Christos Stavrakakis
    until save() method is called.
26 03992c72 Christos Stavrakakis

27 03992c72 Christos Stavrakakis
    """
28 03992c72 Christos Stavrakakis
    def __init__(self, pool_table):
29 03992c72 Christos Stavrakakis
        self.pool_table = pool_table
30 3e7c63f8 Christos Stavrakakis
        self.pool_size = pool_table.size
31 03992c72 Christos Stavrakakis
        if pool_table.available_map:
32 03992c72 Christos Stavrakakis
            self.available = _bitarray_from_string(pool_table.available_map)
33 03992c72 Christos Stavrakakis
            self.reserved = _bitarray_from_string(pool_table.reserved_map)
34 03992c72 Christos Stavrakakis
        else:
35 3e7c63f8 Christos Stavrakakis
            self.available = self._create_empty_pool(self.pool_size)
36 3e7c63f8 Christos Stavrakakis
            self.reserved = self._create_empty_pool(self.pool_size)
37 3e7c63f8 Christos Stavrakakis
            self.add_padding(self.pool_size)
38 3e7c63f8 Christos Stavrakakis
39 3e7c63f8 Christos Stavrakakis
    def _create_empty_pool(self, size):
40 3e7c63f8 Christos Stavrakakis
        ba = bitarray(size)
41 03992c72 Christos Stavrakakis
        ba.setall(AVAILABLE)
42 03992c72 Christos Stavrakakis
        return ba
43 03992c72 Christos Stavrakakis
44 3e7c63f8 Christos Stavrakakis
    def add_padding(self, pool_size):
45 3e7c63f8 Christos Stavrakakis
        bits = find_padding(pool_size)
46 35e2f2d4 Christos Stavrakakis
        self.available.extend([UNAVAILABLE] * bits)
47 3e7c63f8 Christos Stavrakakis
        self.reserved.extend([UNAVAILABLE] * bits)
48 3e7c63f8 Christos Stavrakakis
49 3e7c63f8 Christos Stavrakakis
    def cut_padding(self, pool_size):
50 3e7c63f8 Christos Stavrakakis
        bits = find_padding(pool_size)
51 3e7c63f8 Christos Stavrakakis
        self.available = self.available[:-bits]
52 3e7c63f8 Christos Stavrakakis
        self.reserved = self.reserved[:-bits]
53 3e7c63f8 Christos Stavrakakis
54 03992c72 Christos Stavrakakis
    @property
55 03992c72 Christos Stavrakakis
    def pool(self):
56 03992c72 Christos Stavrakakis
        return (self.available & self.reserved)
57 03992c72 Christos Stavrakakis
58 413fb1dd Christos Stavrakakis
    def get(self, value=None):
59 03992c72 Christos Stavrakakis
        """Get a value from the pool."""
60 03992c72 Christos Stavrakakis
        if self.empty():
61 03992c72 Christos Stavrakakis
            raise EmptyPool
62 413fb1dd Christos Stavrakakis
        if value is None:
63 413fb1dd Christos Stavrakakis
            # Get the first available index
64 413fb1dd Christos Stavrakakis
            index = int(self.pool.index(AVAILABLE))
65 413fb1dd Christos Stavrakakis
            assert(index < self.pool_size)
66 413fb1dd Christos Stavrakakis
            self._reserve(index)
67 413fb1dd Christos Stavrakakis
            return self.index_to_value(index)
68 413fb1dd Christos Stavrakakis
        else:
69 ae994d2e Christos Stavrakakis
            if not self.contains(value):
70 ae994d2e Christos Stavrakakis
                raise InvalidValue("Value %s does not belong to pool." % value)
71 413fb1dd Christos Stavrakakis
            if self.is_available(value):
72 413fb1dd Christos Stavrakakis
                self.reserve(value)
73 413fb1dd Christos Stavrakakis
                return value
74 413fb1dd Christos Stavrakakis
            else:
75 413fb1dd Christos Stavrakakis
                raise ValueNotAvailable("Value %s is not available" % value)
76 03992c72 Christos Stavrakakis
77 03992c72 Christos Stavrakakis
    def put(self, value, external=False):
78 03992c72 Christos Stavrakakis
        """Return a value to the pool."""
79 03992c72 Christos Stavrakakis
        if value is None:
80 03992c72 Christos Stavrakakis
            raise ValueError
81 03992c72 Christos Stavrakakis
        index = self.value_to_index(value)
82 03992c72 Christos Stavrakakis
        self._release(index, external)
83 03992c72 Christos Stavrakakis
84 fdc94944 Christos Stavrakakis
    def reserve(self, value, external=False):
85 03992c72 Christos Stavrakakis
        """Reserve a value."""
86 03992c72 Christos Stavrakakis
        index = self.value_to_index(value)
87 03992c72 Christos Stavrakakis
        self._reserve(index, external)
88 03992c72 Christos Stavrakakis
        return True
89 03992c72 Christos Stavrakakis
90 03992c72 Christos Stavrakakis
    def save(self, db=True):
91 03992c72 Christos Stavrakakis
        """Save changes to the DB."""
92 03992c72 Christos Stavrakakis
        self.pool_table.available_map = _bitarray_to_string(self.available)
93 03992c72 Christos Stavrakakis
        self.pool_table.reserved_map = _bitarray_to_string(self.reserved)
94 03992c72 Christos Stavrakakis
        if db:
95 03992c72 Christos Stavrakakis
            self.pool_table.save()
96 03992c72 Christos Stavrakakis
97 03992c72 Christos Stavrakakis
    def empty(self):
98 03992c72 Christos Stavrakakis
        """Return True when pool is empty."""
99 03992c72 Christos Stavrakakis
        return not self.pool.any()
100 03992c72 Christos Stavrakakis
101 03992c72 Christos Stavrakakis
    def size(self):
102 3e7c63f8 Christos Stavrakakis
        """Return the size of the bitarray(original size + padding)."""
103 03992c72 Christos Stavrakakis
        return self.pool.length()
104 03992c72 Christos Stavrakakis
105 03992c72 Christos Stavrakakis
    def _reserve(self, index, external=False):
106 03992c72 Christos Stavrakakis
        if external:
107 03992c72 Christos Stavrakakis
            self.reserved[index] = UNAVAILABLE
108 03992c72 Christos Stavrakakis
        else:
109 03992c72 Christos Stavrakakis
            self.available[index] = UNAVAILABLE
110 03992c72 Christos Stavrakakis
111 03992c72 Christos Stavrakakis
    def _release(self, index, external=False):
112 03992c72 Christos Stavrakakis
        if external:
113 03992c72 Christos Stavrakakis
            self.reserved[index] = AVAILABLE
114 03992c72 Christos Stavrakakis
        else:
115 03992c72 Christos Stavrakakis
            self.available[index] = AVAILABLE
116 03992c72 Christos Stavrakakis
117 ae994d2e Christos Stavrakakis
    def contains(self, value):
118 ae994d2e Christos Stavrakakis
        index = self.value_to_index(value)
119 ae994d2e Christos Stavrakakis
        return index >= 0 and index < self.pool_size
120 ae994d2e Christos Stavrakakis
121 03992c72 Christos Stavrakakis
    def count_available(self):
122 03992c72 Christos Stavrakakis
        return self.pool.count(AVAILABLE)
123 03992c72 Christos Stavrakakis
124 03992c72 Christos Stavrakakis
    def count_unavailable(self):
125 fcf2479a Christos Stavrakakis
        return self.pool_size - self.count_available()
126 03992c72 Christos Stavrakakis
127 03992c72 Christos Stavrakakis
    def count_reserved(self):
128 3e7c63f8 Christos Stavrakakis
        return self.reserved[:self.pool_size].count(UNAVAILABLE)
129 03992c72 Christos Stavrakakis
130 03992c72 Christos Stavrakakis
    def count_unreserved(self):
131 3e7c63f8 Christos Stavrakakis
        return self.pool_size - self.count_reserved()
132 03992c72 Christos Stavrakakis
133 03992c72 Christos Stavrakakis
    def is_available(self, value, index=False):
134 03992c72 Christos Stavrakakis
        if not index:
135 03992c72 Christos Stavrakakis
            idx = self.value_to_index(value)
136 03992c72 Christos Stavrakakis
        else:
137 03992c72 Christos Stavrakakis
            idx = value
138 03992c72 Christos Stavrakakis
        return self.pool[idx] == AVAILABLE
139 03992c72 Christos Stavrakakis
140 03992c72 Christos Stavrakakis
    def is_reserved(self, value, index=False):
141 03992c72 Christos Stavrakakis
        if not index:
142 03992c72 Christos Stavrakakis
            idx = self.value_to_index(value)
143 03992c72 Christos Stavrakakis
        else:
144 03992c72 Christos Stavrakakis
            idx = value
145 03992c72 Christos Stavrakakis
        return self.reserved[idx] == UNAVAILABLE
146 03992c72 Christos Stavrakakis
147 03992c72 Christos Stavrakakis
    def to_01(self):
148 3e7c63f8 Christos Stavrakakis
        return self.pool[:self.pool_size].to01()
149 03992c72 Christos Stavrakakis
150 03992c72 Christos Stavrakakis
    def to_map(self):
151 03992c72 Christos Stavrakakis
        return self.to_01().replace("0", "X").replace("1", ".")
152 03992c72 Christos Stavrakakis
153 3e7c63f8 Christos Stavrakakis
    def extend(self, bits_num):
154 3e7c63f8 Christos Stavrakakis
        assert(bits_num >= 0)
155 3e7c63f8 Christos Stavrakakis
        self.resize(bits_num)
156 3e7c63f8 Christos Stavrakakis
157 3e7c63f8 Christos Stavrakakis
    def shrink(self, bits_num):
158 3e7c63f8 Christos Stavrakakis
        assert(bits_num >= 0)
159 3e7c63f8 Christos Stavrakakis
        size = self.pool_size
160 3e7c63f8 Christos Stavrakakis
        tmp = self.available[(size - bits_num): size]
161 3e7c63f8 Christos Stavrakakis
        if tmp.count(UNAVAILABLE):
162 3e7c63f8 Christos Stavrakakis
            raise Exception("Can not shrink. In use")
163 3e7c63f8 Christos Stavrakakis
        self.resize(-bits_num)
164 3e7c63f8 Christos Stavrakakis
165 3e7c63f8 Christos Stavrakakis
    def resize(self, bits_num):
166 3e7c63f8 Christos Stavrakakis
        if bits_num == 0:
167 3e7c63f8 Christos Stavrakakis
            return
168 3e7c63f8 Christos Stavrakakis
        # Cut old padding
169 3e7c63f8 Christos Stavrakakis
        self.cut_padding(self.pool_size)
170 3e7c63f8 Christos Stavrakakis
        # Do the resize
171 3e7c63f8 Christos Stavrakakis
        if bits_num > 0:
172 3e7c63f8 Christos Stavrakakis
            self.available.extend([AVAILABLE] * bits_num)
173 3e7c63f8 Christos Stavrakakis
            self.reserved.extend([AVAILABLE] * bits_num)
174 3e7c63f8 Christos Stavrakakis
        else:
175 3e7c63f8 Christos Stavrakakis
            self.available = self.available[:bits_num]
176 3e7c63f8 Christos Stavrakakis
            self.reserved = self.reserved[:bits_num]
177 3e7c63f8 Christos Stavrakakis
        # Add new padding
178 3e7c63f8 Christos Stavrakakis
        self.pool_size = self.pool_size + bits_num
179 3e7c63f8 Christos Stavrakakis
        self.add_padding(self.pool_size)
180 3e7c63f8 Christos Stavrakakis
        self.pool_table.size = self.pool_size
181 3e7c63f8 Christos Stavrakakis
182 03992c72 Christos Stavrakakis
    def index_to_value(self, index):
183 03992c72 Christos Stavrakakis
        raise NotImplementedError
184 03992c72 Christos Stavrakakis
185 03992c72 Christos Stavrakakis
    def value_to_index(self, value):
186 03992c72 Christos Stavrakakis
        raise NotImplementedError
187 03992c72 Christos Stavrakakis
188 d793786b Christos Stavrakakis
    def __repr__(self):
189 d793786b Christos Stavrakakis
        return repr(self.pool_table)
190 d793786b Christos Stavrakakis
191 03992c72 Christos Stavrakakis
192 03992c72 Christos Stavrakakis
class EmptyPool(Exception):
193 03992c72 Christos Stavrakakis
    pass
194 03992c72 Christos Stavrakakis
195 03992c72 Christos Stavrakakis
196 413fb1dd Christos Stavrakakis
class ValueNotAvailable(Exception):
197 413fb1dd Christos Stavrakakis
    pass
198 413fb1dd Christos Stavrakakis
199 413fb1dd Christos Stavrakakis
200 ae994d2e Christos Stavrakakis
class InvalidValue(Exception):
201 ae994d2e Christos Stavrakakis
    pass
202 ae994d2e Christos Stavrakakis
203 ae994d2e Christos Stavrakakis
204 03992c72 Christos Stavrakakis
def find_padding(size):
205 03992c72 Christos Stavrakakis
    extra = size % 8
206 03992c72 Christos Stavrakakis
    return extra and (8 - extra) or 0
207 03992c72 Christos Stavrakakis
208 03992c72 Christos Stavrakakis
209 03992c72 Christos Stavrakakis
def bitarray_to_01(bitarray_):
210 03992c72 Christos Stavrakakis
    return bitarray_.to01()
211 03992c72 Christos Stavrakakis
212 03992c72 Christos Stavrakakis
213 03992c72 Christos Stavrakakis
def bitarray_to_map(bitarray_):
214 03992c72 Christos Stavrakakis
    return bitarray_to_01(bitarray_).replace("0", "X").replace("1", ".")
215 03992c72 Christos Stavrakakis
216 03992c72 Christos Stavrakakis
217 03992c72 Christos Stavrakakis
def _bitarray_from_string(bitarray_):
218 03992c72 Christos Stavrakakis
    ba = bitarray()
219 698306b8 Christos Stavrakakis
    ba.frombytes(b64decode(bitarray_))
220 03992c72 Christos Stavrakakis
    return ba
221 03992c72 Christos Stavrakakis
222 03992c72 Christos Stavrakakis
223 03992c72 Christos Stavrakakis
def _bitarray_to_string(bitarray_):
224 698306b8 Christos Stavrakakis
    return b64encode(bitarray_.tobytes())
225 03992c72 Christos Stavrakakis
226 03992c72 Christos Stavrakakis
##
227 03992c72 Christos Stavrakakis
## Custom pools
228 03992c72 Christos Stavrakakis
##
229 03992c72 Christos Stavrakakis
230 03992c72 Christos Stavrakakis
231 03992c72 Christos Stavrakakis
class BridgePool(PoolManager):
232 03992c72 Christos Stavrakakis
    def index_to_value(self, index):
233 dd2689f9 Christos Stavrakakis
        # Bridge indexes should start from 1
234 dd2689f9 Christos Stavrakakis
        return self.pool_table.base + str(index + 1)
235 03992c72 Christos Stavrakakis
236 03992c72 Christos Stavrakakis
    def value_to_index(self, value):
237 dd2689f9 Christos Stavrakakis
        return int(value.replace(self.pool_table.base, "")) - 1
238 03992c72 Christos Stavrakakis
239 03992c72 Christos Stavrakakis
240 03992c72 Christos Stavrakakis
class MacPrefixPool(PoolManager):
241 03992c72 Christos Stavrakakis
    def __init__(self, pool_table):
242 03992c72 Christos Stavrakakis
        do_init = False if pool_table.available_map else True
243 03992c72 Christos Stavrakakis
        super(MacPrefixPool, self).__init__(pool_table)
244 03992c72 Christos Stavrakakis
        if do_init:
245 3e7c63f8 Christos Stavrakakis
            for i in xrange(1, self.pool_size):
246 03992c72 Christos Stavrakakis
                if not self.validate_mac(self.index_to_value(i)):
247 03992c72 Christos Stavrakakis
                    self._reserve(i, external=True)
248 03992c72 Christos Stavrakakis
            # Reserve the first mac-prefix for public-networks
249 03992c72 Christos Stavrakakis
            self._reserve(0, external=True)
250 03992c72 Christos Stavrakakis
251 03992c72 Christos Stavrakakis
    def index_to_value(self, index):
252 03992c72 Christos Stavrakakis
        """Convert index to mac_prefix"""
253 03992c72 Christos Stavrakakis
        base = self.pool_table.base
254 03992c72 Christos Stavrakakis
        a = hex(int(base.replace(":", ""), 16) + index).replace("0x", '')
255 03992c72 Christos Stavrakakis
        mac_prefix = ":".join([a[x:x + 2] for x in xrange(0, len(a), 2)])
256 03992c72 Christos Stavrakakis
        return mac_prefix
257 03992c72 Christos Stavrakakis
258 03992c72 Christos Stavrakakis
    def value_to_index(self, value):
259 03992c72 Christos Stavrakakis
        base = self.pool_table.base
260 03992c72 Christos Stavrakakis
        return int(value.replace(":", ""), 16) - int(base.replace(":", ""), 16)
261 03992c72 Christos Stavrakakis
262 03992c72 Christos Stavrakakis
    @staticmethod
263 03992c72 Christos Stavrakakis
    def validate_mac(value):
264 03992c72 Christos Stavrakakis
        hex_ = value.replace(":", "")
265 03992c72 Christos Stavrakakis
        bin_ = bin(int(hex_, 16))[2:].zfill(8)
266 03992c72 Christos Stavrakakis
        return bin_[6] == '1' and bin_[7] == '0'
267 03992c72 Christos Stavrakakis
268 03992c72 Christos Stavrakakis
269 fdc94944 Christos Stavrakakis
class IPPool(PoolManager):
270 fdc94944 Christos Stavrakakis
    def __init__(self, pool_table):
271 b4695420 Christos Stavrakakis
        subnet = pool_table.subnet
272 b4695420 Christos Stavrakakis
        self.net = ipaddr.IPNetwork(subnet.cidr)
273 a1d3bc8a Christos Stavrakakis
        if pool_table.base is None:
274 a1d3bc8a Christos Stavrakakis
            self.base = pool_table.subnet.cidr
275 a1d3bc8a Christos Stavrakakis
        else:
276 a1d3bc8a Christos Stavrakakis
            self.base = pool_table.base
277 a1d3bc8a Christos Stavrakakis
        if pool_table.offset is None:
278 a1d3bc8a Christos Stavrakakis
            self.offset = 0
279 a1d3bc8a Christos Stavrakakis
        else:
280 a1d3bc8a Christos Stavrakakis
            self.offset = pool_table.offset
281 fdc94944 Christos Stavrakakis
        super(IPPool, self).__init__(pool_table)
282 03992c72 Christos Stavrakakis
283 fdc94944 Christos Stavrakakis
    def value_to_index(self, value):
284 fdc94944 Christos Stavrakakis
        addr = ipaddr.IPAddress(value)
285 f82dfec6 Christos Stavrakakis
        return int(addr) - int(self.net.network) - int(self.offset)
286 03992c72 Christos Stavrakakis
287 fdc94944 Christos Stavrakakis
    def index_to_value(self, index):
288 f82dfec6 Christos Stavrakakis
        return str(self.net[index + int(self.offset)])
289 a3d99af0 Christos Stavrakakis
290 a3d99af0 Christos Stavrakakis
    def contains(self, address):
291 a3d99af0 Christos Stavrakakis
        addr = ipaddr.IPAddress(address)
292 f82dfec6 Christos Stavrakakis
        return addr in self.net and super(IPPool, self).contains(address)
293 4445f97a Dionysis Grigoropoulos
294 4445f97a Dionysis Grigoropoulos
    def return_start(self):
295 4445f97a Dionysis Grigoropoulos
        return str(ipaddr.IPAddress(self.base) + self.offset)
296 4445f97a Dionysis Grigoropoulos
297 4445f97a Dionysis Grigoropoulos
    def return_end(self):
298 4445f97a Dionysis Grigoropoulos
        return str(ipaddr.IPAddress(self.base) + self.offset + self.size - 1)