Adam Tengler | 82074b0 | 2018-08-10 15:58:06 +0000 | [diff] [blame] | 1 | from itertools import chain |
Alexandr Lovtsov | 42037e9 | 2019-02-22 18:49:50 +0300 | [diff] [blame^] | 2 | |
| 3 | try: |
| 4 | from netaddr import iter_iprange, IPAddress, IPNetwork |
| 5 | HAS_NETADDR = True |
| 6 | except ImportError: |
| 7 | HAS_NETADDR = False |
Adam Tengler | a2f260a | 2018-07-12 17:35:28 +0000 | [diff] [blame] | 8 | |
| 9 | __virtualname__ = 'netutils' |
| 10 | |
| 11 | |
| 12 | def __virtual__(): |
Alexandr Lovtsov | 42037e9 | 2019-02-22 18:49:50 +0300 | [diff] [blame^] | 13 | if not HAS_NETADDR: |
| 14 | return False, "'netaddr' python library is unavailable" |
Adam Tengler | a2f260a | 2018-07-12 17:35:28 +0000 | [diff] [blame] | 15 | return __virtualname__ |
| 16 | |
| 17 | |
Adam Tengler | 82074b0 | 2018-08-10 15:58:06 +0000 | [diff] [blame] | 18 | def parse_ip_ranges(ranges, needed): |
Adam Tengler | a2f260a | 2018-07-12 17:35:28 +0000 | [diff] [blame] | 19 | ''' |
| 20 | Takes comma seprated list of IP ranges and returns full list of IP addresses in these ranges. |
| 21 | Second argument is used to check if there is enough IP addresses to cover the required number of nodes. |
| 22 | |
Adam Tengler | 82074b0 | 2018-08-10 15:58:06 +0000 | [diff] [blame] | 23 | >>> parse_ip_ranges("192.168.1.101-192.168.1.103,192.168.2.101-192.168.2.103") |
Adam Tengler | a2f260a | 2018-07-12 17:35:28 +0000 | [diff] [blame] | 24 | ["192.168.1.101", "192.168.1.102", "192.168.1.103", "192.168.2.101", "192.168.2.102", "192.168.2.103"] |
| 25 | ''' |
| 26 | range_list = ranges.split(',') |
| 27 | ip_obj_list = [] |
| 28 | for _range in ranges.split(','): |
| 29 | ip_obj_list += list(iter_iprange(*_range.split('-'))) |
| 30 | ip_list = [str(ip) for ip in ip_obj_list] |
| 31 | if len(ip_list) < needed: |
| 32 | raise ValueError('There is not enough IP addresses in ranges: "{}". {} available, {} required.'.format(ranges, len(ip_list), needed)) |
| 33 | return ip_list |
Adam Tengler | 82074b0 | 2018-08-10 15:58:06 +0000 | [diff] [blame] | 34 | |
| 35 | |
| 36 | def parse_network_ranges(ranges, iterate=False): |
| 37 | ''' |
| 38 | Takes comma separated list of network ranges and returns full list of first IP addresses in every given subnet. |
| 39 | Second argument is used to check if there is enough IP addresses to cover the required number of nodes. |
| 40 | |
| 41 | >>> parse_network_ranges("10.10.0.1/24-10.10.10.1/24,192.168.0.1/24-192.168.10.1/24") |
| 42 | ['10.10.0.1', '10.10.1.1', '10.10.2.1', '10.10.3.1', '10.10.4.1', '10.10.5.1', '10.10.6.1', '10.10.7.1', '10.10.8.1', '10.10.9.1', '10.10.10.1', '192.168.0.1', '192.168.1.1', '192.168.2.1', '192.168.3.1', '192.168.4.1', '192.168.5.1', '192.168.6.1', '192.168.7.1', '192.168.8.1', '192.168.9.1', '192.168.10.1'] |
| 43 | ''' |
| 44 | def _iter_subnet_list(start, end): |
| 45 | yield str(IPAddress(start.first) + 1) |
| 46 | if start != end: |
| 47 | for ip in _iter_subnet_list(start.next(), end): |
| 48 | yield ip |
| 49 | |
| 50 | generators = tuple() |
| 51 | |
| 52 | for _range in ranges.split(','): |
| 53 | start_str, end_str = _range.split('-') |
| 54 | start, end = IPNetwork(start_str), IPNetwork(end_str) |
| 55 | if start > end: |
| 56 | raise ValueError('Invalid network range, start address is higher than end address') |
| 57 | generators += (_iter_subnet_list(start, end),) |
| 58 | |
| 59 | if iterate: |
| 60 | return [ip for ip in chain(*generators)] |
| 61 | return chain(*generators) |