1 ##############################################################################
2 # Copyright (c) 2016 Feng Pan (fpan@redhat.com) and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
17 def get_ip_range(start_offset=None, count=None, end_offset=None,
18 cidr=None, interface=None):
20 Generate IP range for a network (cidr) or an interface.
22 If CIDR is provided, it will take precedence over interface. In this case,
23 The entire CIDR IP address space is considered usable. start_offset will be
24 calculated from the network address, and end_offset will be calculated from
25 the last address in subnet.
27 If interface is provided, the interface IP will be used to calculate
29 - If the interface IP is in the first half of the address space,
30 start_offset will be calculated from the interface IP, and end_offset
31 will be calculated from end of address space.
32 - If the interface IP is in the second half of the address space,
33 start_offset will be calculated from the network address in the address
34 space, and end_offset will be calculated from the interface IP.
36 2 of start_offset, end_offset and count options must be provided:
37 - If start_offset and end_offset are provided, a range from
38 start_offset to end_offset will be returned.
39 - If count is provided, a range from either start_offset to
40 (start_offset+count) or (end_offset-count) to end_offset will be
41 returned. The IP range returned will be of size <count>.
42 Both start_offset and end_offset must be greater than 0.
44 Returns IP range in the format of "first_addr,second_addr" or exception
48 if count and start_offset and not end_offset:
49 start_index = start_offset
50 end_index = start_offset + count - 1
51 elif count and end_offset and not start_offset:
52 end_index = -1 - end_offset
53 start_index = -1 - end_index - count + 1
54 elif start_offset and end_offset and not count:
55 start_index = start_offset
56 end_index = -1 - end_offset
58 raise IPUtilsException("Argument error: must pass in exactly 2 of"
59 " start_offset, end_offset and count")
61 start_ip = cidr[start_index]
62 end_ip = cidr[end_index]
65 network = interface.network
66 number_of_addr = network.num_addresses
67 if interface.ip < network[int(number_of_addr / 2)]:
68 if count and start_offset and not end_offset:
69 start_ip = interface.ip + start_offset
70 end_ip = start_ip + count - 1
71 elif count and end_offset and not start_offset:
72 end_ip = network[-1 - end_offset]
73 start_ip = end_ip - count + 1
74 elif start_offset and end_offset and not count:
75 start_ip = interface.ip + start_offset
76 end_ip = network[-1 - end_offset]
78 raise IPUtilsException(
79 "Argument error: must pass in exactly 2 of"
80 " start_offset, end_offset and count")
82 if count and start_offset and not end_offset:
83 start_ip = network[start_offset]
84 end_ip = start_ip + count - 1
85 elif count and end_offset and not start_offset:
86 end_ip = interface.ip - end_offset
87 start_ip = end_ip - count + 1
88 elif start_offset and end_offset and not count:
89 start_ip = network[start_offset]
90 end_ip = interface.ip - end_offset
92 raise IPUtilsException(
93 "Argument error: must pass in exactly 2 of"
94 " start_offset, end_offset and count")
97 raise IPUtilsException("Must pass in cidr or interface to generate"
100 range_result = _validate_ip_range(start_ip, end_ip, network)
102 ip_range = "{},{}".format(start_ip, end_ip)
105 raise IPUtilsException("Invalid IP range: {},{} for network {}"
106 .format(start_ip, end_ip, network))
109 def get_ip(offset, cidr=None, interface=None):
111 Returns an IP in a network given an offset.
113 Either cidr or interface must be provided, cidr takes precedence.
115 If cidr is provided, offset is calculated from network address.
116 If interface is provided, offset is calculated from interface IP.
118 offset can be positive or negative, but the resulting IP address must also
119 be contained in the same subnet, otherwise an exception will be raised.
121 returns a IP address object.
124 ip = cidr[0 + offset]
127 ip = interface.ip + offset
128 network = interface.network
130 raise IPUtilsException("Must pass in cidr or interface to generate IP")
132 if ip not in network:
133 raise IPUtilsException("IP {} not in network {}".format(ip, network))
138 def get_interface(nic, address_family=4):
140 Returns interface object for a given NIC name in the system
142 Only global address will be returned at the moment.
144 Returns interface object if an address is found for the given nic,
145 otherwise returns None.
148 logging.error("empty nic name specified")
150 output = subprocess.getoutput("ip -{} addr show {} scope global"
151 .format(address_family, nic))
152 if address_family == 4:
153 pattern = re.compile("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}")
154 elif address_family == 6:
155 pattern = re.compile("([0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}/\d{1,3}")
157 raise IPUtilsException("Invalid address family: {}"
158 .format(address_family))
159 match = re.search(pattern, output)
161 logging.info("found interface {} ip: {}".format(nic, match.group()))
162 return ipaddress.ip_interface(match.group())
164 logging.info("interface ip not found! ip address output:\n{}"
169 def find_gateway(interface):
171 Validate gateway on the system
173 Ensures that the provided interface object is in fact configured as default
176 Returns gateway IP (reachable from interface) if default route is found,
177 otherwise returns None.
180 address_family = interface.version
181 output = subprocess.getoutput("ip -{} route".format(address_family))
183 pattern = re.compile("default\s+via\s+(\S+)\s+")
184 match = re.search(pattern, output)
187 gateway_ip = match.group(1)
188 reverse_route_output = subprocess.getoutput("ip route get {}"
190 pattern = re.compile("{}.+src\s+{}".format(gateway_ip, interface.ip))
191 if not re.search(pattern, reverse_route_output):
192 logging.warning("Default route doesn't match interface specified: "
193 "{}".format(reverse_route_output))
198 logging.warning("Can't find gateway address on system")
202 def _validate_ip_range(start_ip, end_ip, cidr):
204 Validates an IP range is in good order and the range is part of cidr.
206 Returns True if validation succeeds, False otherwise.
208 ip_range = "{},{}".format(start_ip, end_ip)
209 if end_ip <= start_ip:
210 logging.warning("IP range {} is invalid: end_ip should be greater "
211 "than starting ip".format(ip_range))
213 if start_ip not in ipaddress.ip_network(cidr):
214 logging.warning('start_ip {} is not in network {}'
215 .format(start_ip, cidr))
217 if end_ip not in ipaddress.ip_network(cidr):
218 logging.warning('end_ip {} is not in network {}'.format(end_ip, cidr))
224 class IPUtilsException(Exception):
225 def __init__(self, value):