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 start_offset
38 to end_offset will be returned.
39 - If count is provided, a range from either start_offset to (start_offset
40 +count) or (end_offset-count) to end_offset will be returned. The
41 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 generate_ip_range(args):
140 Generate IP range in string format for given CIDR.
141 This function works for both IPv4 and IPv6.
143 args is expected to contain the following members:
144 CIDR: any valid CIDR representation.
145 start_position: starting index, default to first address in subnet (1)
146 end_position: ending index, default to last address in subnet (-1)
148 Returns IP range in string format. A single IP is returned if start and
149 end IPs are identical.
151 cidr = ipaddress.ip_network(args.CIDR)
152 (start_index, end_index) = (args.start_position, args.end_position)
153 if cidr[start_index] == cidr[end_index]:
154 return str(cidr[start_index])
156 return ','.join(sorted([str(cidr[start_index]), str(cidr[end_index])]))
159 def get_interface(nic, address_family=4):
161 Returns interface object for a given NIC name in the system
163 Only global address will be returned at the moment.
165 Returns interface object if an address is found for the given nic,
166 otherwise returns None.
169 logging.error("empty nic name specified")
171 output = subprocess.getoutput("ip -{} addr show {} scope global"
172 .format(address_family, nic))
173 if address_family == 4:
174 pattern = re.compile("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}")
175 elif address_family == 6:
176 pattern = re.compile("([0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}/\d{1,3}")
178 raise IPUtilsException("Invalid address family: {}"
179 .format(address_family))
180 match = re.search(pattern, output)
182 logging.info("found interface {} ip: {}".format(nic, match.group()))
183 return ipaddress.ip_interface(match.group())
185 logging.info("interface ip not found! ip address output:\n{}"
190 def find_gateway(interface):
192 Validate gateway on the system
194 Ensures that the provided interface object is in fact configured as default
197 Returns gateway IP (reachable from interface) if default route is found,
198 otherwise returns None.
201 address_family = interface.version
202 output = subprocess.getoutput("ip -{} route".format(address_family))
204 pattern = re.compile("default\s+via\s+(\S+)\s+")
205 match = re.search(pattern, output)
208 gateway_ip = match.group(1)
209 reverse_route_output = subprocess.getoutput("ip route get {}"
211 pattern = re.compile("{}.+src\s+{}".format(gateway_ip, interface.ip))
212 if not re.search(pattern, reverse_route_output):
213 logging.warning("Default route doesn't match interface specified: "
214 "{}".format(reverse_route_output))
219 logging.warning("Can't find gateway address on system")
223 def _validate_ip_range(start_ip, end_ip, cidr):
225 Validates an IP range is in good order and the range is part of cidr.
227 Returns True if validation succeeds, False otherwise.
229 ip_range = "{},{}".format(start_ip, end_ip)
230 if end_ip <= start_ip:
231 logging.warning("IP range {} is invalid: end_ip should be greater than "
232 "starting ip".format(ip_range))
234 if start_ip not in ipaddress.ip_network(cidr):
235 logging.warning('start_ip {} is not in network {}'
236 .format(start_ip, cidr))
238 if end_ip not in ipaddress.ip_network(cidr):
239 logging.warning('end_ip {} is not in network {}'.format(end_ip, cidr))
245 class IPUtilsException(Exception):
246 def __init__(self, value):