| peusterm | d7cbd21 | 2017-09-07 08:55:14 +0200 | [diff] [blame] | 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 | """ |
| peusterm | 0019978 | 2017-05-17 08:48:12 +0200 | [diff] [blame] | 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: |
| peusterm | e46ab60 | 2017-05-17 10:08:50 +0200 | [diff] [blame] | 169 | import emuvim.api.openstack.ip_handler as IP |
| peusterm | 0019978 | 2017-05-17 08:48:12 +0200 | [diff] [blame] | 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)) |