| peusterm | 0019978 | 2017-05-17 08:48:12 +0200 | [diff] [blame^] | 1 | import re |
| 2 | |
| 3 | |
| 4 | class Net: |
| 5 | def __init__(self, name): |
| 6 | self.name = name |
| 7 | self.id = None |
| 8 | self.subnet_name = None |
| 9 | self.subnet_id = None |
| 10 | self.subnet_creation_time = None |
| 11 | self.subnet_update_time = None |
| 12 | self.gateway_ip = None |
| 13 | self.segmentation_id = None # not set |
| 14 | self._cidr = None |
| 15 | self.start_end_dict = None |
| 16 | self._issued_ip_addresses = dict() |
| 17 | |
| 18 | def get_short_id(self): |
| 19 | """ |
| 20 | Returns a shortened UUID, with only the first 6 characters. |
| 21 | |
| 22 | :return: First 6 characters of the UUID |
| 23 | :rtype: ``str`` |
| 24 | """ |
| 25 | return str(self.id)[:6] |
| 26 | |
| 27 | def get_new_ip_address(self, port_name): |
| 28 | """ |
| 29 | Calculates the next unused IP Address which belongs to the subnet. |
| 30 | |
| 31 | :param port_name: Specifies the port. |
| 32 | :type port_name: ``str`` |
| 33 | :return: Returns a unused IP Address or none if all are in use. |
| 34 | :rtype: ``str`` |
| 35 | """ |
| 36 | if self.start_end_dict is None: |
| 37 | return None |
| 38 | |
| 39 | int_start_ip = Net.ip_2_int(self.start_end_dict['start']) + 2 # First address as network address not usable |
| 40 | # Second one is for gateways only |
| 41 | int_end_ip = Net.ip_2_int(self.start_end_dict['end']) - 1 # Last address for broadcasts |
| 42 | while int_start_ip in self._issued_ip_addresses and int_start_ip <= int_end_ip: |
| 43 | int_start_ip += 1 |
| 44 | |
| 45 | if int_start_ip > int_end_ip: |
| 46 | return None |
| 47 | |
| 48 | self._issued_ip_addresses[int_start_ip] = port_name |
| 49 | return Net.int_2_ip(int_start_ip) + '/' + self._cidr.rsplit('/', 1)[1] |
| 50 | |
| 51 | def assign_ip_address(self, cidr, port_name): |
| 52 | """ |
| 53 | Assigns the IP address to the port if it is currently NOT used. |
| 54 | |
| 55 | :param cidr: The cidr used by the port - e.g. 10.0.0.1/24 |
| 56 | :type cidr: ``str`` |
| 57 | :param port_name: The port name |
| 58 | :type port_name: ``str`` |
| 59 | :return: * *False*: If the IP address is already issued or if it is not within this subnet mask. |
| 60 | * *True*: Else |
| 61 | """ |
| 62 | int_ip = Net.cidr_2_int(cidr) |
| 63 | if int_ip in self._issued_ip_addresses: |
| 64 | return False |
| 65 | |
| 66 | int_start_ip = Net.ip_2_int(self.start_end_dict['start']) + 1 # First address as network address not usable |
| 67 | int_end_ip = Net.ip_2_int(self.start_end_dict['end']) - 1 # Last address for broadcasts |
| 68 | if int_ip < int_start_ip or int_ip > int_end_ip: |
| 69 | return False |
| 70 | |
| 71 | self._issued_ip_addresses[int_ip] = port_name |
| 72 | return True |
| 73 | |
| 74 | def is_my_ip(self, cidr, port_name): |
| 75 | """ |
| 76 | Checks if the IP is registered for this port name. |
| 77 | |
| 78 | :param cidr: The cidr used by the port - e.g. 10.0.0.1/24 |
| 79 | :type cidr: ``str`` |
| 80 | :param port_name: The port name |
| 81 | :type port_name: ``str`` |
| 82 | :return: Returns true if the IP address belongs to the port name. Else it returns false. |
| 83 | """ |
| 84 | int_ip = Net.cidr_2_int(cidr) |
| 85 | |
| 86 | if not int_ip in self._issued_ip_addresses: |
| 87 | return False |
| 88 | |
| 89 | if self._issued_ip_addresses[int_ip] == port_name: |
| 90 | return True |
| 91 | return False |
| 92 | |
| 93 | def withdraw_ip_address(self, ip_address): |
| 94 | """ |
| 95 | Removes the IP address from the list of issued addresses, thus other ports can use it. |
| 96 | |
| 97 | :param ip_address: The issued IP address. |
| 98 | :type ip_address: ``str`` |
| 99 | """ |
| 100 | if ip_address is None: |
| 101 | return |
| 102 | |
| 103 | if "/" in ip_address: |
| 104 | address, suffix = ip_address.rsplit('/', 1) |
| 105 | else: |
| 106 | address = ip_address |
| 107 | int_ip_address = Net.ip_2_int(address) |
| 108 | if int_ip_address in self._issued_ip_addresses.keys(): |
| 109 | del self._issued_ip_addresses[int_ip_address] |
| 110 | |
| 111 | def reset_issued_ip_addresses(self): |
| 112 | """ |
| 113 | Resets all issued IP addresses. |
| 114 | """ |
| 115 | self._issued_ip_addresses = dict() |
| 116 | |
| 117 | def update_port_name_for_ip_address(self, ip_address, port_name): |
| 118 | """ |
| 119 | Updates the port name of the issued IP address. |
| 120 | |
| 121 | :param ip_address: The already issued IP address. |
| 122 | :type ip_address: ``str`` |
| 123 | :param port_name: The new port name |
| 124 | :type port_name: ``str`` |
| 125 | """ |
| 126 | address, suffix = ip_address.rsplit('/', 1) |
| 127 | int_ip_address = Net.ip_2_int(address) |
| 128 | self._issued_ip_addresses[int_ip_address] = port_name |
| 129 | |
| 130 | def set_cidr(self, cidr): |
| 131 | """ |
| 132 | Sets the CIDR for the subnet. It previously checks for the correct CIDR format. |
| 133 | |
| 134 | :param cidr: The new CIDR for the subnet. |
| 135 | :type cidr: ``str`` |
| 136 | :return: * *True*: When the new CIDR was set successfully. |
| 137 | * *False*: If the CIDR format was wrong. |
| 138 | :rtype: ``bool`` |
| 139 | """ |
| 140 | if cidr is None: |
| 141 | if self._cidr is not None: |
| 142 | import emuvim.api.heat.ip_handler as IP |
| 143 | IP.free_cidr(self._cidr, self.subnet_id) |
| 144 | self._cidr = None |
| 145 | self.reset_issued_ip_addresses() |
| 146 | self.start_end_dict = dict() |
| 147 | return True |
| 148 | if not Net.check_cidr_format(cidr): |
| 149 | return False |
| 150 | |
| 151 | self.reset_issued_ip_addresses() |
| 152 | self.start_end_dict = Net.calculate_start_and_end_dict(cidr) |
| 153 | self._cidr = cidr |
| 154 | return True |
| 155 | |
| 156 | def get_cidr(self): |
| 157 | """ |
| 158 | Gets the CIDR. |
| 159 | |
| 160 | :return: The CIDR |
| 161 | :rtype: ``str`` |
| 162 | """ |
| 163 | return self._cidr |
| 164 | |
| 165 | def clear_cidr(self): |
| 166 | self._cidr = None |
| 167 | self.start_end_dict = dict() |
| 168 | self.reset_issued_ip_addresses() |
| 169 | |
| 170 | def delete_subnet(self): |
| 171 | self.subnet_id = None |
| 172 | self.subnet_name = None |
| 173 | self.subnet_creation_time = None |
| 174 | self.subnet_update_time = None |
| 175 | self.set_cidr(None) |
| 176 | |
| 177 | @staticmethod |
| 178 | def calculate_start_and_end_dict(cidr): |
| 179 | """ |
| 180 | Calculates the start and end IP address for the subnet. |
| 181 | |
| 182 | :param cidr: The CIDR for the subnet. |
| 183 | :type cidr: ``str`` |
| 184 | :return: Dict with start and end ip address |
| 185 | :rtype: ``dict`` |
| 186 | """ |
| 187 | address, suffix = cidr.rsplit('/', 1) |
| 188 | int_suffix = int(suffix) |
| 189 | int_address = Net.ip_2_int(address) |
| 190 | address_space = 2 ** 32 - 1 |
| 191 | |
| 192 | for x in range(0, 31 - int_suffix): |
| 193 | address_space = ~(~address_space | (1 << x)) |
| 194 | |
| 195 | start = int_address & address_space |
| 196 | end = start + (2 ** (32 - int_suffix) - 1) |
| 197 | |
| 198 | return {'start': Net.int_2_ip(start), 'end': Net.int_2_ip(end)} |
| 199 | |
| 200 | @staticmethod |
| 201 | def cidr_2_int(cidr): |
| 202 | if cidr is None: |
| 203 | return None |
| 204 | ip = cidr.rsplit('/', 1)[0] |
| 205 | return Net.ip_2_int(ip) |
| 206 | |
| 207 | @staticmethod |
| 208 | def ip_2_int(ip): |
| 209 | """ |
| 210 | Converts a IP address to int. |
| 211 | |
| 212 | :param ip: IP address |
| 213 | :type ip: ``str`` |
| 214 | :return: IP address as int. |
| 215 | :rtype: ``int`` |
| 216 | """ |
| 217 | o = map(int, ip.split('.')) |
| 218 | res = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3] |
| 219 | return res |
| 220 | |
| 221 | @staticmethod |
| 222 | def int_2_ip(int_ip): |
| 223 | """ |
| 224 | Converts a int IP address to string. |
| 225 | |
| 226 | :param int_ip: Int IP address. |
| 227 | :type int_ip: ``int`` |
| 228 | :return: IP address |
| 229 | :rtype: ``str`` |
| 230 | """ |
| 231 | o1 = int(int_ip / 16777216) % 256 |
| 232 | o2 = int(int_ip / 65536) % 256 |
| 233 | o3 = int(int_ip / 256) % 256 |
| 234 | o4 = int(int_ip) % 256 |
| 235 | return '%(o1)s.%(o2)s.%(o3)s.%(o4)s' % locals() |
| 236 | |
| 237 | @staticmethod |
| 238 | def check_cidr_format(cidr): |
| 239 | """ |
| 240 | Checks the CIDR format. An valid example is: 192.168.0.0/29 |
| 241 | |
| 242 | :param cidr: CIDR to be checked. |
| 243 | :type cidr: ``str`` |
| 244 | :return: * *True*: If the Format is correct. |
| 245 | * *False*: If it is not correct. |
| 246 | :rtype: ``bool`` |
| 247 | """ |
| 248 | r = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{2}') |
| 249 | if r.match(cidr): |
| 250 | return True |
| 251 | return False |
| 252 | |
| 253 | def create_network_dict(self): |
| 254 | """ |
| 255 | Creates the network description dictionary. |
| 256 | |
| 257 | :return: Network description. |
| 258 | :rtype: ``dict`` |
| 259 | """ |
| 260 | network_dict = dict() |
| 261 | network_dict["status"] = "ACTIVE" # TODO do we support inactive networks? |
| 262 | if self.subnet_id == None: |
| 263 | network_dict["subnets"] = [] |
| 264 | else: |
| 265 | network_dict["subnets"] = [self.subnet_id] |
| 266 | network_dict["name"] = self.name |
| 267 | network_dict["admin_state_up"] = True # TODO is it always true? |
| 268 | network_dict["tenant_id"] = "abcdefghijklmnopqrstuvwxyz123456" # TODO what should go in here |
| 269 | network_dict["id"] = self.id |
| 270 | network_dict["shared"] = False # TODO is it always false? |
| 271 | return network_dict |
| 272 | |
| 273 | def create_subnet_dict(self): |
| 274 | """ |
| 275 | Creates the subnet description dictionary. |
| 276 | |
| 277 | :return: Subnet description. |
| 278 | :rtype: ``dict`` |
| 279 | """ |
| 280 | subnet_dict = dict() |
| 281 | subnet_dict["name"] = self.subnet_name |
| 282 | subnet_dict["network_id"] = self.id |
| 283 | subnet_dict["tenant_id"] = "abcdefghijklmnopqrstuvwxyz123456" # TODO what should go in here? |
| 284 | subnet_dict["created_at"] = self.subnet_creation_time |
| 285 | subnet_dict["dns_nameservers"] = [] |
| 286 | subnet_dict["allocation_pools"] = [self.start_end_dict] |
| 287 | subnet_dict["host_routers"] = [] |
| 288 | subnet_dict["gateway_ip"] = self.gateway_ip |
| 289 | subnet_dict["ip_version"] = "4" |
| 290 | subnet_dict["cidr"] = self.get_cidr() |
| 291 | subnet_dict["updated_at"] = self.subnet_update_time |
| 292 | subnet_dict["id"] = self.subnet_id |
| 293 | subnet_dict["enable_dhcp"] = False # TODO do we support DHCP? |
| 294 | return subnet_dict |
| 295 | |
| 296 | def __eq__(self, other): |
| 297 | if self.name == other.name and self.subnet_name == other.subnet_name and \ |
| 298 | self.gateway_ip == other.gateway_ip and \ |
| 299 | self.segmentation_id == other.segmentation_id and \ |
| 300 | self._cidr == other._cidr and \ |
| 301 | self.start_end_dict == other.start_end_dict: |
| 302 | return True |
| 303 | return False |
| 304 | |
| 305 | def __hash__(self): |
| 306 | return hash((self.name, |
| 307 | self.subnet_name, |
| 308 | self.gateway_ip, |
| 309 | self.segmentation_id, |
| 310 | self._cidr, |
| 311 | self.start_end_dict)) |