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