Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8 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 03992c72 Christos Stavrakakis
    def get(self):
59 03992c72 Christos Stavrakakis
        """Get a value from the pool."""
60 03992c72 Christos Stavrakakis
        if self.empty():
61 03992c72 Christos Stavrakakis
            raise EmptyPool
62 03992c72 Christos Stavrakakis
        # Get the first available index
63 03992c72 Christos Stavrakakis
        index = int(self.pool.index(AVAILABLE))
64 3e7c63f8 Christos Stavrakakis
        assert(index < self.pool_size)
65 03992c72 Christos Stavrakakis
        self._reserve(index)
66 03992c72 Christos Stavrakakis
        return self.index_to_value(index)
67 03992c72 Christos Stavrakakis
68 03992c72 Christos Stavrakakis
    def put(self, value, external=False):
69 03992c72 Christos Stavrakakis
        """Return a value to the pool."""
70 03992c72 Christos Stavrakakis
        if value is None:
71 03992c72 Christos Stavrakakis
            raise ValueError
72 03992c72 Christos Stavrakakis
        index = self.value_to_index(value)
73 03992c72 Christos Stavrakakis
        self._release(index, external)
74 03992c72 Christos Stavrakakis
75 fdc94944 Christos Stavrakakis
    def reserve(self, value, external=False):
76 03992c72 Christos Stavrakakis
        """Reserve a value."""
77 03992c72 Christos Stavrakakis
        index = self.value_to_index(value)
78 03992c72 Christos Stavrakakis
        self._reserve(index, external)
79 03992c72 Christos Stavrakakis
        return True
80 03992c72 Christos Stavrakakis
81 03992c72 Christos Stavrakakis
    def save(self, db=True):
82 03992c72 Christos Stavrakakis
        """Save changes to the DB."""
83 03992c72 Christos Stavrakakis
        self.pool_table.available_map = _bitarray_to_string(self.available)
84 03992c72 Christos Stavrakakis
        self.pool_table.reserved_map = _bitarray_to_string(self.reserved)
85 03992c72 Christos Stavrakakis
        if db:
86 03992c72 Christos Stavrakakis
            self.pool_table.save()
87 03992c72 Christos Stavrakakis
88 03992c72 Christos Stavrakakis
    def empty(self):
89 03992c72 Christos Stavrakakis
        """Return True when pool is empty."""
90 03992c72 Christos Stavrakakis
        return not self.pool.any()
91 03992c72 Christos Stavrakakis
92 03992c72 Christos Stavrakakis
    def size(self):
93 3e7c63f8 Christos Stavrakakis
        """Return the size of the bitarray(original size + padding)."""
94 03992c72 Christos Stavrakakis
        return self.pool.length()
95 03992c72 Christos Stavrakakis
96 03992c72 Christos Stavrakakis
    def _reserve(self, index, external=False):
97 03992c72 Christos Stavrakakis
        if external:
98 03992c72 Christos Stavrakakis
            self.reserved[index] = UNAVAILABLE
99 03992c72 Christos Stavrakakis
        else:
100 03992c72 Christos Stavrakakis
            self.available[index] = UNAVAILABLE
101 03992c72 Christos Stavrakakis
102 03992c72 Christos Stavrakakis
    def _release(self, index, external=False):
103 03992c72 Christos Stavrakakis
        if external:
104 03992c72 Christos Stavrakakis
            self.reserved[index] = AVAILABLE
105 03992c72 Christos Stavrakakis
        else:
106 03992c72 Christos Stavrakakis
            self.available[index] = AVAILABLE
107 03992c72 Christos Stavrakakis
108 03992c72 Christos Stavrakakis
    def count_available(self):
109 03992c72 Christos Stavrakakis
        return self.pool.count(AVAILABLE)
110 03992c72 Christos Stavrakakis
111 03992c72 Christos Stavrakakis
    def count_unavailable(self):
112 fcf2479a Christos Stavrakakis
        return self.pool_size - self.count_available()
113 03992c72 Christos Stavrakakis
114 03992c72 Christos Stavrakakis
    def count_reserved(self):
115 3e7c63f8 Christos Stavrakakis
        return self.reserved[:self.pool_size].count(UNAVAILABLE)
116 03992c72 Christos Stavrakakis
117 03992c72 Christos Stavrakakis
    def count_unreserved(self):
118 3e7c63f8 Christos Stavrakakis
        return self.pool_size - self.count_reserved()
119 03992c72 Christos Stavrakakis
120 03992c72 Christos Stavrakakis
    def is_available(self, value, index=False):
121 03992c72 Christos Stavrakakis
        if not index:
122 03992c72 Christos Stavrakakis
            idx = self.value_to_index(value)
123 03992c72 Christos Stavrakakis
        else:
124 03992c72 Christos Stavrakakis
            idx = value
125 03992c72 Christos Stavrakakis
        return self.pool[idx] == AVAILABLE
126 03992c72 Christos Stavrakakis
127 03992c72 Christos Stavrakakis
    def is_reserved(self, value, index=False):
128 03992c72 Christos Stavrakakis
        if not index:
129 03992c72 Christos Stavrakakis
            idx = self.value_to_index(value)
130 03992c72 Christos Stavrakakis
        else:
131 03992c72 Christos Stavrakakis
            idx = value
132 03992c72 Christos Stavrakakis
        return self.reserved[idx] == UNAVAILABLE
133 03992c72 Christos Stavrakakis
134 03992c72 Christos Stavrakakis
    def to_01(self):
135 3e7c63f8 Christos Stavrakakis
        return self.pool[:self.pool_size].to01()
136 03992c72 Christos Stavrakakis
137 03992c72 Christos Stavrakakis
    def to_map(self):
138 03992c72 Christos Stavrakakis
        return self.to_01().replace("0", "X").replace("1", ".")
139 03992c72 Christos Stavrakakis
140 3e7c63f8 Christos Stavrakakis
    def extend(self, bits_num):
141 3e7c63f8 Christos Stavrakakis
        assert(bits_num >= 0)
142 3e7c63f8 Christos Stavrakakis
        self.resize(bits_num)
143 3e7c63f8 Christos Stavrakakis
144 3e7c63f8 Christos Stavrakakis
    def shrink(self, bits_num):
145 3e7c63f8 Christos Stavrakakis
        assert(bits_num >= 0)
146 3e7c63f8 Christos Stavrakakis
        size = self.pool_size
147 3e7c63f8 Christos Stavrakakis
        tmp = self.available[(size - bits_num): size]
148 3e7c63f8 Christos Stavrakakis
        if tmp.count(UNAVAILABLE):
149 3e7c63f8 Christos Stavrakakis
            raise Exception("Can not shrink. In use")
150 3e7c63f8 Christos Stavrakakis
        self.resize(-bits_num)
151 3e7c63f8 Christos Stavrakakis
152 3e7c63f8 Christos Stavrakakis
    def resize(self, bits_num):
153 3e7c63f8 Christos Stavrakakis
        if bits_num == 0:
154 3e7c63f8 Christos Stavrakakis
            return
155 3e7c63f8 Christos Stavrakakis
        # Cut old padding
156 3e7c63f8 Christos Stavrakakis
        self.cut_padding(self.pool_size)
157 3e7c63f8 Christos Stavrakakis
        # Do the resize
158 3e7c63f8 Christos Stavrakakis
        if bits_num > 0:
159 3e7c63f8 Christos Stavrakakis
            self.available.extend([AVAILABLE] * bits_num)
160 3e7c63f8 Christos Stavrakakis
            self.reserved.extend([AVAILABLE] * bits_num)
161 3e7c63f8 Christos Stavrakakis
        else:
162 3e7c63f8 Christos Stavrakakis
            self.available = self.available[:bits_num]
163 3e7c63f8 Christos Stavrakakis
            self.reserved = self.reserved[:bits_num]
164 3e7c63f8 Christos Stavrakakis
        # Add new padding
165 3e7c63f8 Christos Stavrakakis
        self.pool_size = self.pool_size + bits_num
166 3e7c63f8 Christos Stavrakakis
        self.add_padding(self.pool_size)
167 3e7c63f8 Christos Stavrakakis
        self.pool_table.size = self.pool_size
168 3e7c63f8 Christos Stavrakakis
169 03992c72 Christos Stavrakakis
    def index_to_value(self, index):
170 03992c72 Christos Stavrakakis
        raise NotImplementedError
171 03992c72 Christos Stavrakakis
172 03992c72 Christos Stavrakakis
    def value_to_index(self, value):
173 03992c72 Christos Stavrakakis
        raise NotImplementedError
174 03992c72 Christos Stavrakakis
175 03992c72 Christos Stavrakakis
176 03992c72 Christos Stavrakakis
class EmptyPool(Exception):
177 03992c72 Christos Stavrakakis
    pass
178 03992c72 Christos Stavrakakis
179 03992c72 Christos Stavrakakis
180 03992c72 Christos Stavrakakis
def find_padding(size):
181 03992c72 Christos Stavrakakis
    extra = size % 8
182 03992c72 Christos Stavrakakis
    return extra and (8 - extra) or 0
183 03992c72 Christos Stavrakakis
184 03992c72 Christos Stavrakakis
185 03992c72 Christos Stavrakakis
def bitarray_to_01(bitarray_):
186 03992c72 Christos Stavrakakis
    return bitarray_.to01()
187 03992c72 Christos Stavrakakis
188 03992c72 Christos Stavrakakis
189 03992c72 Christos Stavrakakis
def bitarray_to_map(bitarray_):
190 03992c72 Christos Stavrakakis
    return bitarray_to_01(bitarray_).replace("0", "X").replace("1", ".")
191 03992c72 Christos Stavrakakis
192 03992c72 Christos Stavrakakis
193 03992c72 Christos Stavrakakis
def _bitarray_from_string(bitarray_):
194 03992c72 Christos Stavrakakis
    ba = bitarray()
195 698306b8 Christos Stavrakakis
    ba.frombytes(b64decode(bitarray_))
196 03992c72 Christos Stavrakakis
    return ba
197 03992c72 Christos Stavrakakis
198 03992c72 Christos Stavrakakis
199 03992c72 Christos Stavrakakis
def _bitarray_to_string(bitarray_):
200 698306b8 Christos Stavrakakis
    return b64encode(bitarray_.tobytes())
201 03992c72 Christos Stavrakakis
202 03992c72 Christos Stavrakakis
##
203 03992c72 Christos Stavrakakis
## Custom pools
204 03992c72 Christos Stavrakakis
##
205 03992c72 Christos Stavrakakis
206 03992c72 Christos Stavrakakis
207 03992c72 Christos Stavrakakis
class BridgePool(PoolManager):
208 03992c72 Christos Stavrakakis
    def index_to_value(self, index):
209 dd2689f9 Christos Stavrakakis
        # Bridge indexes should start from 1
210 dd2689f9 Christos Stavrakakis
        return self.pool_table.base + str(index + 1)
211 03992c72 Christos Stavrakakis
212 03992c72 Christos Stavrakakis
    def value_to_index(self, value):
213 dd2689f9 Christos Stavrakakis
        return int(value.replace(self.pool_table.base, "")) - 1
214 03992c72 Christos Stavrakakis
215 03992c72 Christos Stavrakakis
216 03992c72 Christos Stavrakakis
class MacPrefixPool(PoolManager):
217 03992c72 Christos Stavrakakis
    def __init__(self, pool_table):
218 03992c72 Christos Stavrakakis
        do_init = False if pool_table.available_map else True
219 03992c72 Christos Stavrakakis
        super(MacPrefixPool, self).__init__(pool_table)
220 03992c72 Christos Stavrakakis
        if do_init:
221 3e7c63f8 Christos Stavrakakis
            for i in xrange(1, self.pool_size):
222 03992c72 Christos Stavrakakis
                if not self.validate_mac(self.index_to_value(i)):
223 03992c72 Christos Stavrakakis
                    self._reserve(i, external=True)
224 03992c72 Christos Stavrakakis
            # Reserve the first mac-prefix for public-networks
225 03992c72 Christos Stavrakakis
            self._reserve(0, external=True)
226 03992c72 Christos Stavrakakis
227 03992c72 Christos Stavrakakis
    def index_to_value(self, index):
228 03992c72 Christos Stavrakakis
        """Convert index to mac_prefix"""
229 03992c72 Christos Stavrakakis
        base = self.pool_table.base
230 03992c72 Christos Stavrakakis
        a = hex(int(base.replace(":", ""), 16) + index).replace("0x", '')
231 03992c72 Christos Stavrakakis
        mac_prefix = ":".join([a[x:x + 2] for x in xrange(0, len(a), 2)])
232 03992c72 Christos Stavrakakis
        return mac_prefix
233 03992c72 Christos Stavrakakis
234 03992c72 Christos Stavrakakis
    def value_to_index(self, value):
235 03992c72 Christos Stavrakakis
        base = self.pool_table.base
236 03992c72 Christos Stavrakakis
        return int(value.replace(":", ""), 16) - int(base.replace(":", ""), 16)
237 03992c72 Christos Stavrakakis
238 03992c72 Christos Stavrakakis
    @staticmethod
239 03992c72 Christos Stavrakakis
    def validate_mac(value):
240 03992c72 Christos Stavrakakis
        hex_ = value.replace(":", "")
241 03992c72 Christos Stavrakakis
        bin_ = bin(int(hex_, 16))[2:].zfill(8)
242 03992c72 Christos Stavrakakis
        return bin_[6] == '1' and bin_[7] == '0'
243 03992c72 Christos Stavrakakis
244 03992c72 Christos Stavrakakis
245 fdc94944 Christos Stavrakakis
class IPPool(PoolManager):
246 fdc94944 Christos Stavrakakis
    def __init__(self, pool_table):
247 fdc94944 Christos Stavrakakis
        do_init = False if pool_table.available_map else True
248 fdc94944 Christos Stavrakakis
        network = pool_table.network
249 fdc94944 Christos Stavrakakis
        self.net = ipaddr.IPNetwork(network.subnet)
250 fdc94944 Christos Stavrakakis
        if not pool_table.size:
251 fdc94944 Christos Stavrakakis
            pool_table.size = self.net.numhosts
252 fdc94944 Christos Stavrakakis
        super(IPPool, self).__init__(pool_table)
253 fdc94944 Christos Stavrakakis
        gateway = network.gateway
254 03992c72 Christos Stavrakakis
        self.gateway = gateway and ipaddr.IPAddress(gateway) or None
255 fdc94944 Christos Stavrakakis
        if do_init:
256 121c7507 Christos Stavrakakis
            self._reserve(0, external=True)
257 121c7507 Christos Stavrakakis
            if gateway:
258 121c7507 Christos Stavrakakis
                self.reserve(gateway, external=True)
259 3e7c63f8 Christos Stavrakakis
            self._reserve(self.pool_size - 1, external=True)
260 03992c72 Christos Stavrakakis
261 fdc94944 Christos Stavrakakis
    def value_to_index(self, value):
262 fdc94944 Christos Stavrakakis
        addr = ipaddr.IPAddress(value)
263 fdc94944 Christos Stavrakakis
        return int(addr) - int(self.net.network)
264 03992c72 Christos Stavrakakis
265 fdc94944 Christos Stavrakakis
    def index_to_value(self, index):
266 fdc94944 Christos Stavrakakis
        return str(self.net[index])