1 # Copyright (c) 2015 SONATA-NFV and Paderborn University
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
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
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).
27 from json
import dumps
31 def __init__(self
, name
):
34 self
.subnet_name
= 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
41 self
.start_end_dict
= None
42 self
._issued
_ip
_addresses
= dict()
44 def get_short_id(self
):
46 Returns a shortened UUID, with only the first 6 characters.
48 :return: First 6 characters of the UUID
51 return str(self
.id)[:6]
53 def get_new_ip_address(self
, port_name
):
55 Calculates the next unused IP Address which belongs to the subnet.
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.
62 if self
.start_end_dict
is None:
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
:
73 if int_start_ip
> int_end_ip
:
76 self
._issued
_ip
_addresses
[int_start_ip
] = port_name
77 return Net
.int_2_ip(int_start_ip
) + '/' + self
._cidr
.rsplit('/', 1)[1]
79 def assign_ip_address(self
, cidr
, port_name
):
81 Assigns the IP address to the port if it is currently NOT used.
83 :param cidr: The cidr used by the port - e.g. 10.0.0.1/24
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.
90 int_ip
= Net
.cidr_2_int(cidr
)
91 if int_ip
in self
._issued
_ip
_addresses
:
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
:
101 self
._issued
_ip
_addresses
[int_ip
] = port_name
104 def is_my_ip(self
, cidr
, port_name
):
106 Checks if the IP is registered for this port name.
108 :param cidr: The cidr used by the port - e.g. 10.0.0.1/24
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.
114 int_ip
= Net
.cidr_2_int(cidr
)
116 if int_ip
not in self
._issued
_ip
_addresses
:
119 if self
._issued
_ip
_addresses
[int_ip
] == port_name
:
123 def withdraw_ip_address(self
, ip_address
):
125 Removes the IP address from the list of issued addresses, thus other ports can use it.
127 :param ip_address: The issued IP address.
128 :type ip_address: ``str``
130 if ip_address
is None:
133 if "/" in ip_address
:
134 address
, suffix
= ip_address
.rsplit('/', 1)
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
]
141 def reset_issued_ip_addresses(self
):
143 Resets all issued IP addresses.
145 self
._issued
_ip
_addresses
= dict()
147 def update_port_name_for_ip_address(self
, ip_address
, port_name
):
149 Updates the port name of the issued IP address.
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``
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
160 def set_cidr(self
, cidr
):
162 Sets the CIDR for the subnet. It previously checks for the correct CIDR format.
164 :param cidr: The new CIDR for the subnet.
166 :return: * *True*: When the new CIDR was set successfully.
167 * *False*: If the CIDR format was wrong.
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
)
175 self
.reset_issued_ip_addresses()
176 self
.start_end_dict
= dict()
178 if not Net
.check_cidr_format(cidr
):
181 self
.reset_issued_ip_addresses()
182 self
.start_end_dict
= Net
.calculate_start_and_end_dict(cidr
)
195 def clear_cidr(self
):
197 self
.start_end_dict
= dict()
198 self
.reset_issued_ip_addresses()
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
208 def calculate_start_and_end_dict(cidr
):
210 Calculates the start and end IP address for the subnet.
212 :param cidr: The CIDR for the subnet.
214 :return: Dict with start and end ip address
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
222 for x
in range(0, 31 - int_suffix
):
223 address_space
= ~
(~address_space |
(1 << x
))
225 start
= int_address
& address_space
226 end
= start
+ (2 ** (32 - int_suffix
) - 1)
228 return {'start': Net
.int_2_ip(start
), 'end': Net
.int_2_ip(end
)}
231 def cidr_2_int(cidr
):
234 ip
= cidr
.rsplit('/', 1)[0]
235 return Net
.ip_2_int(ip
)
240 Converts a IP address to int.
242 :param ip: IP address
244 :return: IP address as int.
247 o
= list(map(int, ip
.split('.')))
248 res
= (16777216 * o
[0]) + (65536 * o
[1]) + (256 * o
[2]) + o
[3]
252 def int_2_ip(int_ip
):
254 Converts a int IP address to string.
256 :param int_ip: Int IP address.
257 :type int_ip: ``int``
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()
268 def check_cidr_format(cidr
):
270 Checks the CIDR format. An valid example is: 192.168.0.0/29
272 :param cidr: CIDR to be checked.
274 :return: * *True*: If the Format is correct.
275 * *False*: If it is not correct.
278 r
= re
.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{2}')
283 def create_network_dict(self
):
285 Creates the network description dictionary.
287 :return: Network description.
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"] = []
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?
305 def create_subnet_dict(self
):
307 Creates the subnet description dictionary.
309 :return: Subnet description.
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?
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
:
339 return hash((self
.name
,
342 self
.segmentation_id
,
344 dumps(self
.start_end_dict
)))