53493f82ff69524ae172970892a0c80abd8e5b64
[pyi2ncommon] / src / v4_addr_range.py
1 # The software in this package is distributed under the GNU General
2 # Public License version 2 (with a special exception described below).
3 #
4 # A copy of GNU General Public License (GPL) is included in this distribution,
5 # in the file COPYING.GPL.
6 #
7 # As a special exception, if other files instantiate templates or use macros
8 # or inline functions from this file, or you compile this file and link it
9 # with other works to produce a work based on this file, this file
10 # does not by itself cause the resulting work to be covered
11 # by the GNU General Public License.
12 #
13 # However the source code for this file must still be made available
14 # in accordance with section (3) of the GNU General Public License.
15 #
16 # This exception does not invalidate any other reasons why a work based
17 # on this file might be covered by the GNU General Public License.
18 #
19 # Copyright (c) 2016-2018 Intra2net AG <info@intra2net.com>
20
21 """
22
23 SUMMARY
24 ------------------------------------------------------
25 V4_addr_range class
26
27 Copyright: Intra2net AG
28
29
30 INTERFACE
31 ------------------------------------------------------
32
33 """
34
35 default_addr_lo = 42
36 default_addr_range = 42
37 uint32_max = 0xffffffff
38
39
40 class V4_addr_range:
41
42     def __init__(self, lo=None, addr_range=None):
43         self.val_lo = default_addr_lo     # : int, initial offset for allocation
44         self.val_range = default_addr_range  # : int, allocation block size
45         self.val_hi = 0                   # : int, lo + range
46         self.var_alloc = {}                  # : mutable int, bool allocated values
47
48         if lo is not None:
49             if isinstance(lo, int) is False:
50                 raise TypeError("Expected value of integer type, got \"%s\" : %s."
51                                 % (lo, type(lo)))
52             self.val_lo = lo
53         if addr_range is not None:
54             if isinstance(addr_range, int) is False:
55                 raise TypeError("Expected value of integer type, got \"%s\" : %s."
56                                 % (lo, type(lo)))
57             self.val_range = addr_range
58         self.fix_range()
59         for val in range(self.val_lo, self.val_hi):
60             # we use ints as keys since the types are checked elsewhere
61             self.var_alloc[val] = False
62
63     def __len__(self):
64         length = 0
65         for v in self.var_alloc.values():
66             length += 1 if v is True else 0
67         return length
68
69     def __eq__(self, other):
70         if isinstance(other, int):
71             return other == len(self)
72         if isinstance(other, self.__class__):
73             return len(self) == len(other)
74         raise TypeError("Equality comparison of %s with type %s is undefined."
75                         % (self.__class__.__name__, type(other)))
76
77     def __getitem__(self, k):
78         try:
79             return self.var_alloc[k]
80         except KeyError:
81             return False
82
83     def fix_range(self):
84         if self.val_range <= 0:
85             raise TypeError("IP address ranges need to be natural numbers > 0."
86                             % (self.val_lo, self.val_range))
87         hi = self.val_lo + self.val_range
88         if hi <= self.val_lo or hi > uint32_max:
89             raise ValueError("Invalid IP address range: %d+%d."
90                              % (self.val_lo, self.val_range))
91         self.val_hi = hi
92
93     def lo(self):
94         return self.val_lo
95
96     def range(self):
97         return self.val_range
98
99     def hi(self):
100         return self.val_hi
101
102     def get(self):
103         """
104         .get -- Return lowest unallocated number in range and insert as a
105         Python integer.
106         """
107         curlen = len(self)
108
109         if curlen == self.val_range:
110             raise IndexError("Address range (%d) exhausted."
111                              % self.val_range)
112         for val in range(self.val_lo, self.val_hi):
113             if self.var_alloc[val] is False:
114                 self.var_alloc[val] = True
115                 return val
116
117     def rm(self, val):
118         """
119         Remove allocated number from range.
120         """
121         if isinstance(val, int) is False:
122             raise TypeError("Expected int or short, got %s." % type(val))
123         val = val
124         vali = val
125         if val < self.val_lo or self.val_hi < val:
126             raise IndexError("Address %d out of bounds ([%d, %d])."
127                              % (vali, self.val_lo, self.val_hi))
128         if self.var_alloc[vali] is False:
129             raise ValueError("Address %d was never allocated." % vali)
130         self.var_alloc[vali] = True
131
132     def __str__(self):
133         return "v4 address range: [%d, %d] = %d+%d, at %d (%.2f%%)" \
134                % (self.val_lo,
135                   self.val_hi,
136                   self.val_lo,
137                   self.val_range,
138                   len(self.var_alloc),
139                   (100.0 * (float(len(self.var_alloc)) / float(self.val_range))))
140
141     def __repr__(self):
142         return "(%d, %d, %s)" \
143                % (self.val_lo,
144                   self.val_range,
145                   self.var_alloc.__repr__())