Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (6.7 kB)

1
from bitarray import bitarray
2
from base64 import b64encode, b64decode
3
import ipaddr
4

    
5
AVAILABLE = True
6
UNAVAILABLE = False
7

    
8

    
9
class PoolManager(object):
10
    """PoolManager for DB PoolTable models.
11

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

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

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

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

27
    """
28
    def __init__(self, pool_table):
29
        self.pool_table = pool_table
30

    
31
        if pool_table.available_map:
32
            self.available = _bitarray_from_string(pool_table.available_map)
33
            self.reserved = _bitarray_from_string(pool_table.reserved_map)
34
        else:
35
            size = self.pool_table.size
36
            padding = find_padding(size)
37
            size = size + padding
38
            self.pool_size = size
39
            self.available = self._create_empty_pool()
40
            self.reserved = self._create_empty_pool()
41
            for i in xrange(0, padding):
42
                self._reserve(size - i - 1, external=True)
43

    
44
    def _create_empty_pool(self):
45
        assert(self.pool_size % 8 == 0)
46
        ba = bitarray(self.pool_size)
47
        ba.setall(AVAILABLE)
48
        return ba
49

    
50
    @property
51
    def pool(self):
52
        return (self.available & self.reserved)
53

    
54
    def get(self):
55
        """Get a value from the pool."""
56
        if self.empty():
57
            raise EmptyPool
58
        # Get the first available index
59
        index = int(self.pool.index(AVAILABLE))
60
        self._reserve(index)
61
        return self.index_to_value(index)
62

    
63
    def put(self, value, external=False):
64
        """Return a value to the pool."""
65
        if value is None:
66
            raise ValueError
67
        index = self.value_to_index(value)
68
        self._release(index, external)
69

    
70
    def reserve(self, value, external=False):
71
        """Reserve a value."""
72
        index = self.value_to_index(value)
73
        self._reserve(index, external)
74
        return True
75

    
76
    def save(self, db=True):
77
        """Save changes to the DB."""
78
        self.pool_table.available_map = _bitarray_to_string(self.available)
79
        self.pool_table.reserved_map = _bitarray_to_string(self.reserved)
80
        if db:
81
            self.pool_table.save()
82

    
83
    def empty(self):
84
        """Return True when pool is empty."""
85
        return not self.pool.any()
86

    
87
    def size(self):
88
        return self.pool.length()
89

    
90
    def _reserve(self, index, external=False):
91
        if external:
92
            self.reserved[index] = UNAVAILABLE
93
        else:
94
            self.available[index] = UNAVAILABLE
95

    
96
    def _release(self, index, external=False):
97
        if external:
98
            self.reserved[index] = AVAILABLE
99
        else:
100
            self.available[index] = AVAILABLE
101

    
102
    def count_available(self):
103
        return self.pool.count(AVAILABLE)
104

    
105
    def count_unavailable(self):
106
        return self.pool.count(UNAVAILABLE)
107

    
108
    def count_reserved(self):
109
        return self.reserved.count(UNAVAILABLE)
110

    
111
    def count_unreserved(self):
112
        return self.size() - self.count_reserved()
113

    
114
    def is_available(self, value, index=False):
115
        if not index:
116
            idx = self.value_to_index(value)
117
        else:
118
            idx = value
119
        return self.pool[idx] == AVAILABLE
120

    
121
    def is_reserved(self, value, index=False):
122
        if not index:
123
            idx = self.value_to_index(value)
124
        else:
125
            idx = value
126
        return self.reserved[idx] == UNAVAILABLE
127

    
128
    def to_01(self):
129
        return self.pool.to01()
130

    
131
    def to_map(self):
132
        return self.to_01().replace("0", "X").replace("1", ".")
133

    
134
    def index_to_value(self, index):
135
        raise NotImplementedError
136

    
137
    def value_to_index(self, value):
138
        raise NotImplementedError
139

    
140

    
141
class EmptyPool(Exception):
142
    pass
143

    
144

    
145
def find_padding(size):
146
    extra = size % 8
147
    return extra and (8 - extra) or 0
148

    
149

    
150
def bitarray_to_01(bitarray_):
151
    return bitarray_.to01()
152

    
153

    
154
def bitarray_to_map(bitarray_):
155
    return bitarray_to_01(bitarray_).replace("0", "X").replace("1", ".")
156

    
157

    
158
def _bitarray_from_string(bitarray_):
159
    ba = bitarray()
160
    ba.fromstring(b64decode(bitarray_))
161
    return ba
162

    
163

    
164
def _bitarray_to_string(bitarray_):
165
    return b64encode(bitarray_.tostring())
166

    
167
##
168
## Custom pools
169
##
170

    
171

    
172
class BridgePool(PoolManager):
173
    def index_to_value(self, index):
174
        return self.pool_table.base + str(index)
175

    
176
    def value_to_index(self, value):
177
        return int(value.replace(self.pool_table.base, ""))
178

    
179

    
180
class MacPrefixPool(PoolManager):
181
    def __init__(self, pool_table):
182
        do_init = False if pool_table.available_map else True
183
        super(MacPrefixPool, self).__init__(pool_table)
184
        if do_init:
185
            for i in xrange(1, self.size()):
186
                if not self.validate_mac(self.index_to_value(i)):
187
                    self._reserve(i, external=True)
188
            # Reserve the first mac-prefix for public-networks
189
            self._reserve(0, external=True)
190

    
191
    def index_to_value(self, index):
192
        """Convert index to mac_prefix"""
193
        base = self.pool_table.base
194
        a = hex(int(base.replace(":", ""), 16) + index).replace("0x", '')
195
        mac_prefix = ":".join([a[x:x + 2] for x in xrange(0, len(a), 2)])
196
        return mac_prefix
197

    
198
    def value_to_index(self, value):
199
        base = self.pool_table.base
200
        return int(value.replace(":", ""), 16) - int(base.replace(":", ""), 16)
201

    
202
    @staticmethod
203
    def validate_mac(value):
204
        hex_ = value.replace(":", "")
205
        bin_ = bin(int(hex_, 16))[2:].zfill(8)
206
        return bin_[6] == '1' and bin_[7] == '0'
207

    
208

    
209
class IPPool(PoolManager):
210
    def __init__(self, pool_table):
211
        do_init = False if pool_table.available_map else True
212
        network = pool_table.network
213
        self.net = ipaddr.IPNetwork(network.subnet)
214
        if not pool_table.size:
215
            pool_table.size = self.net.numhosts
216
        super(IPPool, self).__init__(pool_table)
217
        gateway = network.gateway
218
        self.gateway = gateway and ipaddr.IPAddress(gateway) or None
219
        if do_init:
220
            self._reserve(0, external=True)
221
            if gateway:
222
                self.reserve(gateway, external=True)
223
            self._reserve(self.size() - 1, external=True)
224

    
225
    def value_to_index(self, value):
226
        addr = ipaddr.IPAddress(value)
227
        return int(addr) - int(self.net.network)
228

    
229
    def index_to_value(self, index):
230
        return str(self.net[index])