migration to python3 (#1)
[osm/vim-emu.git] / src / emuvim / api / openstack / resources / net.py
1 # Copyright (c) 2015 SONATA-NFV and Paderborn University
2 # ALL RIGHTS RESERVED.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16 # Neither the name of the SONATA-NFV, Paderborn University
17 # nor the names of its contributors may be used to endorse or promote
18 # products derived from this software without specific prior written
19 # permission.
20 #
21 # This work has been performed in the framework of the SONATA project,
22 # funded by the European Commission under Grant number 671517 through
23 # the Horizon 2020 and 5G-PPP programmes. The authors would like to
24 # acknowledge the contributions of their colleagues of the SONATA
25 # partner consortium (www.sonata-nfv.eu).
26 import re
27 from json import dumps
28
29
30 class Net:
31 def __init__(self, name):
32 self.name = name
33 self.id = None
34 self.subnet_name = None
35 self.subnet_id = None
36 self.subnet_creation_time = None
37 self.subnet_update_time = None
38 self.gateway_ip = None
39 self.segmentation_id = None # not set
40 self._cidr = None
41 self.start_end_dict = None
42 self._issued_ip_addresses = dict()
43
44 def get_short_id(self):
45 """
46 Returns a shortened UUID, with only the first 6 characters.
47
48 :return: First 6 characters of the UUID
49 :rtype: ``str``
50 """
51 return str(self.id)[:6]
52
53 def get_new_ip_address(self, port_name):
54 """
55 Calculates the next unused IP Address which belongs to the subnet.
56
57 :param port_name: Specifies the port.
58 :type port_name: ``str``
59 :return: Returns a unused IP Address or none if all are in use.
60 :rtype: ``str``
61 """
62 if self.start_end_dict is None:
63 return None
64
65 # First address as network address not usable
66 int_start_ip = Net.ip_2_int(self.start_end_dict['start']) + 2
67 # Second one is for gateways only
68 # Last address for broadcasts
69 int_end_ip = Net.ip_2_int(self.start_end_dict['end']) - 1
70 while int_start_ip in self._issued_ip_addresses and int_start_ip <= int_end_ip:
71 int_start_ip += 1
72
73 if int_start_ip > int_end_ip:
74 return None
75
76 self._issued_ip_addresses[int_start_ip] = port_name
77 return Net.int_2_ip(int_start_ip) + '/' + self._cidr.rsplit('/', 1)[1]
78
79 def assign_ip_address(self, cidr, port_name):
80 """
81 Assigns the IP address to the port if it is currently NOT used.
82
83 :param cidr: The cidr used by the port - e.g. 10.0.0.1/24
84 :type cidr: ``str``
85 :param port_name: The port name
86 :type port_name: ``str``
87 :return: * *False*: If the IP address is already issued or if it is not within this subnet mask.
88 * *True*: Else
89 """
90 int_ip = Net.cidr_2_int(cidr)
91 if int_ip in self._issued_ip_addresses:
92 return False
93
94 # First address as network address not usable
95 int_start_ip = Net.ip_2_int(self.start_end_dict['start']) + 1
96 # Last address for broadcasts
97 int_end_ip = Net.ip_2_int(self.start_end_dict['end']) - 1
98 if int_ip < int_start_ip or int_ip > int_end_ip:
99 return False
100
101 self._issued_ip_addresses[int_ip] = port_name
102 return True
103
104 def is_my_ip(self, cidr, port_name):
105 """
106 Checks if the IP is registered for this port name.
107
108 :param cidr: The cidr used by the port - e.g. 10.0.0.1/24
109 :type cidr: ``str``
110 :param port_name: The port name
111 :type port_name: ``str``
112 :return: Returns true if the IP address belongs to the port name. Else it returns false.
113 """
114 int_ip = Net.cidr_2_int(cidr)
115
116 if int_ip not in self._issued_ip_addresses:
117 return False
118
119 if self._issued_ip_addresses[int_ip] == port_name:
120 return True
121 return False
122
123 def withdraw_ip_address(self, ip_address):
124 """
125 Removes the IP address from the list of issued addresses, thus other ports can use it.
126
127 :param ip_address: The issued IP address.
128 :type ip_address: ``str``
129 """
130 if ip_address is None:
131 return
132
133 if "/" in ip_address:
134 address, suffix = ip_address.rsplit('/', 1)
135 else:
136 address = ip_address
137 int_ip_address = Net.ip_2_int(address)
138 if int_ip_address in self._issued_ip_addresses.keys():
139 del self._issued_ip_addresses[int_ip_address]
140
141 def reset_issued_ip_addresses(self):
142 """
143 Resets all issued IP addresses.
144 """
145 self._issued_ip_addresses = dict()
146
147 def update_port_name_for_ip_address(self, ip_address, port_name):
148 """
149 Updates the port name of the issued IP address.
150
151 :param ip_address: The already issued IP address.
152 :type ip_address: ``str``
153 :param port_name: The new port name
154 :type port_name: ``str``
155 """
156 address, suffix = ip_address.rsplit('/', 1)
157 int_ip_address = Net.ip_2_int(address)
158 self._issued_ip_addresses[int_ip_address] = port_name
159
160 def set_cidr(self, cidr):
161 """
162 Sets the CIDR for the subnet. It previously checks for the correct CIDR format.
163
164 :param cidr: The new CIDR for the subnet.
165 :type cidr: ``str``
166 :return: * *True*: When the new CIDR was set successfully.
167 * *False*: If the CIDR format was wrong.
168 :rtype: ``bool``
169 """
170 if cidr is None:
171 if self._cidr is not None:
172 import emuvim.api.openstack.ip_handler as IP
173 IP.free_cidr(self._cidr, self.subnet_id)
174 self._cidr = None
175 self.reset_issued_ip_addresses()
176 self.start_end_dict = dict()
177 return True
178 if not Net.check_cidr_format(cidr):
179 return False
180
181 self.reset_issued_ip_addresses()
182 self.start_end_dict = Net.calculate_start_and_end_dict(cidr)
183 self._cidr = cidr
184 return True
185
186 def get_cidr(self):
187 """
188 Gets the CIDR.
189
190 :return: The CIDR
191 :rtype: ``str``
192 """
193 return self._cidr
194
195 def clear_cidr(self):
196 self._cidr = None
197 self.start_end_dict = dict()
198 self.reset_issued_ip_addresses()
199
200 def delete_subnet(self):
201 self.subnet_id = None
202 self.subnet_name = None
203 self.subnet_creation_time = None
204 self.subnet_update_time = None
205 self.set_cidr(None)
206
207 @staticmethod
208 def calculate_start_and_end_dict(cidr):
209 """
210 Calculates the start and end IP address for the subnet.
211
212 :param cidr: The CIDR for the subnet.
213 :type cidr: ``str``
214 :return: Dict with start and end ip address
215 :rtype: ``dict``
216 """
217 address, suffix = cidr.rsplit('/', 1)
218 int_suffix = int(suffix)
219 int_address = Net.ip_2_int(address)
220 address_space = 2 ** 32 - 1
221
222 for x in range(0, 31 - int_suffix):
223 address_space = ~(~address_space | (1 << x))
224
225 start = int_address & address_space
226 end = start + (2 ** (32 - int_suffix) - 1)
227
228 return {'start': Net.int_2_ip(start), 'end': Net.int_2_ip(end)}
229
230 @staticmethod
231 def cidr_2_int(cidr):
232 if cidr is None:
233 return None
234 ip = cidr.rsplit('/', 1)[0]
235 return Net.ip_2_int(ip)
236
237 @staticmethod
238 def ip_2_int(ip):
239 """
240 Converts a IP address to int.
241
242 :param ip: IP address
243 :type ip: ``str``
244 :return: IP address as int.
245 :rtype: ``int``
246 """
247 o = list(map(int, ip.split('.')))
248 res = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3]
249 return res
250
251 @staticmethod
252 def int_2_ip(int_ip):
253 """
254 Converts a int IP address to string.
255
256 :param int_ip: Int IP address.
257 :type int_ip: ``int``
258 :return: IP address
259 :rtype: ``str``
260 """
261 o1 = int(int_ip / 16777216) % 256
262 o2 = int(int_ip / 65536) % 256
263 o3 = int(int_ip / 256) % 256
264 o4 = int(int_ip) % 256
265 return '%(o1)s.%(o2)s.%(o3)s.%(o4)s' % locals()
266
267 @staticmethod
268 def check_cidr_format(cidr):
269 """
270 Checks the CIDR format. An valid example is: 192.168.0.0/29
271
272 :param cidr: CIDR to be checked.
273 :type cidr: ``str``
274 :return: * *True*: If the Format is correct.
275 * *False*: If it is not correct.
276 :rtype: ``bool``
277 """
278 r = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{2}')
279 if r.match(cidr):
280 return True
281 return False
282
283 def create_network_dict(self):
284 """
285 Creates the network description dictionary.
286
287 :return: Network description.
288 :rtype: ``dict``
289 """
290 network_dict = dict()
291 # TODO do we support inactive networks?
292 network_dict["status"] = "ACTIVE"
293 if self.subnet_id is None:
294 network_dict["subnets"] = []
295 else:
296 network_dict["subnets"] = [self.subnet_id]
297 network_dict["name"] = self.name
298 network_dict["admin_state_up"] = True # TODO is it always true?
299 # TODO what should go in here
300 network_dict["tenant_id"] = "abcdefghijklmnopqrstuvwxyz123456"
301 network_dict["id"] = self.id
302 network_dict["shared"] = False # TODO is it always false?
303 return network_dict
304
305 def create_subnet_dict(self):
306 """
307 Creates the subnet description dictionary.
308
309 :return: Subnet description.
310 :rtype: ``dict``
311 """
312 subnet_dict = dict()
313 subnet_dict["name"] = self.subnet_name
314 subnet_dict["network_id"] = self.id
315 # TODO what should go in here?
316 subnet_dict["tenant_id"] = "abcdefghijklmnopqrstuvwxyz123456"
317 subnet_dict["created_at"] = self.subnet_creation_time
318 subnet_dict["dns_nameservers"] = []
319 subnet_dict["allocation_pools"] = [self.start_end_dict]
320 subnet_dict["host_routers"] = []
321 subnet_dict["gateway_ip"] = self.gateway_ip
322 subnet_dict["ip_version"] = "4"
323 subnet_dict["cidr"] = self.get_cidr()
324 subnet_dict["updated_at"] = self.subnet_update_time
325 subnet_dict["id"] = self.subnet_id
326 subnet_dict["enable_dhcp"] = False # TODO do we support DHCP?
327 return subnet_dict
328
329 def __eq__(self, other):
330 if self.name == other.name and self.subnet_name == other.subnet_name and \
331 self.gateway_ip == other.gateway_ip and \
332 self.segmentation_id == other.segmentation_id and \
333 self._cidr == other._cidr and \
334 self.start_end_dict == other.start_end_dict:
335 return True
336 return False
337
338 def __hash__(self):
339 return hash((self.name,
340 self.subnet_name,
341 self.gateway_ip,
342 self.segmentation_id,
343 self._cidr,
344 dumps(self.start_end_dict)))