Package Products :: Package ZenUtils :: Module IpUtil
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenUtils.IpUtil

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, 2009 Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  __doc__ = """IpUtil 
 15   
 16  IPv4 utility functions 
 17   
 18  """ 
 19   
 20  import types 
 21  import re 
 22  from Products.ZenUtils.Exceptions import ZentinelException 
 23  from twisted.names.client import lookupPointer 
 24   
25 -class IpAddressError(ZentinelException): pass
26
27 -class InvalidIPRangeError(Exception):
28 """ 29 Attempted to parse an invalid IP range. 30 """
31 32 33 # Match if this is an IPv4 address 34 isip = re.compile("^\d+\.\d+\.\d+\.\d+$").search
35 -def checkip(ip):
36 """ 37 Check that an IPv4 address is valid. Return true 38 or raise an exception(!) 39 40 >>> checkip('10.10.20.5') 41 True 42 >>> try: checkip(10) 43 ... except IpAddressError, ex: print ex 44 10 is not a dot delimited address 45 >>> try: checkip('10') 46 ... except IpAddressError, ex: print ex 47 10 is an invalid address 48 >>> try: checkip('10.10.20.500') 49 ... except IpAddressError, ex: print ex 50 10.10.20.500 is an invalid address 51 >>> checkip('10.10.20.00') 52 True 53 >>> checkip('10.10.20.0') 54 True 55 >>> checkip('10.10.20.255') 56 True 57 """ 58 success = True 59 if ip == '': 60 success = False 61 else: 62 try: 63 octs = ip.split('.') 64 except: 65 raise IpAddressError( '%s is not a dot delimited address' % ip ) 66 if len(octs) != 4: 67 success = False 68 else: 69 for o in octs: 70 try: 71 if not (0 <= int(o) <= 255): 72 success = False 73 except: 74 success = False 75 if not success: 76 raise IpAddressError( "%s is an invalid address" % ip ) 77 return True
78 79
80 -def numbip(ip):
81 """ 82 Convert a string IP to a decimal representation easier for 83 calculating netmasks etc. 84 85 Deprecated in favour of ipToDecimal() 86 """ 87 return ipToDecimal(ip)
88
89 -def ipToDecimal(ip):
90 """ 91 Convert a string IP to a decimal representation easier for 92 calculating netmasks etc. 93 94 >>> ipToDecimal('10.10.20.5') 95 168432645L 96 >>> try: ipToDecimal('10.10.20.500') 97 ... except IpAddressError, ex: print ex 98 10.10.20.500 is an invalid address 99 """ 100 checkip(ip) 101 octs = ip.split('.') 102 octs.reverse() 103 i = 0L 104 for j in range(len(octs)): 105 i += (256l ** j) * int(octs[j]) 106 return i
107 108
109 -def ipFromIpMask(ipmask):
110 """ 111 Get just the IP address from an CIDR string like 1.1.1.1/24 112 113 >>> ipFromIpMask('1.1.1.1') 114 '1.1.1.1' 115 >>> ipFromIpMask('1.1.1.1/24') 116 '1.1.1.1' 117 """ 118 return ipmask.split("/")[0]
119 120
121 -def strip(ip):
122 """ 123 Convert a numeric IP address to a string 124 125 Deprecated in favour of decimalIpToStr() 126 """ 127 return decimalIpToStr(ip)
128
129 -def decimalIpToStr(ip):
130 """ 131 Convert a decimal IP address (as returned by ipToDecimal) 132 to a regular IPv4 dotted quad address. 133 134 >>> decimalIpToStr(ipToDecimal('10.23.44.57')) 135 '10.23.44.57' 136 """ 137 _masks = ( 138 0x000000ffL, 139 0x0000ff00L, 140 0x00ff0000L, 141 0xff000000L, 142 ) 143 o = [] 144 for i in range(len(_masks)): 145 t = ip & _masks[i] 146 s = str(t >> (i*8)) 147 o.append(s) 148 o.reverse() 149 return '.'.join(o)
150 151
152 -def hexToBits(hex):
153 """ 154 Convert hex netbits (0xff000000) to decimal netmask (8) 155 156 >>> hexToBits("0xff000000") 157 8 158 >>> hexToBits("0xffffff00") 159 24 160 """ 161 return maskToBits(hexToMask(hex))
162 163
164 -def hexToMask(hex):
165 """ 166 Converts a netmask represented in hex to octets represented in 167 decimal. 168 169 >>> hexToMask("0xffffff00") 170 '255.255.255.0' 171 >>> hexToMask("0xffffffff") 172 '255.255.255.255' 173 >>> hexToMask("0x00000000") 174 '0.0.0.0' 175 >>> hexToMask("trash") 176 '255.255.255.255' 177 """ 178 if hex.find('x') < 0: 179 return "255.255.255.255" 180 181 hex = list(hex.lower().split('x')[1]) 182 octets = [] 183 while len(hex) > 0: 184 snippit = list(hex.pop() + hex.pop()) 185 snippit.reverse() 186 decimal = int(''.join(snippit), 16) 187 octets.append(str(decimal)) 188 189 octets.reverse() 190 return '.'.join(octets)
191 192
193 -def maskToBits(netmask):
194 """ 195 Convert string rep of netmask to number of bits 196 197 >>> maskToBits('255.255.255.255') 198 32 199 >>> maskToBits('255.255.224.0') 200 19 201 >>> maskToBits('0.0.0.0') 202 0 203 """ 204 if type(netmask) == types.StringType and netmask.find('.') > -1: 205 test = 0xffffffffL 206 if netmask[0]=='0': return 0 207 masknumb = ipToDecimal(netmask) 208 for i in range(32): 209 if test == masknumb: return 32-i 210 test = test - 2 ** i 211 return None 212 else: 213 return int(netmask)
214 215
216 -def bitsToMaskNumb(netbits):
217 """ 218 Convert integer number of netbits to a decimal number 219 220 Deprecated in favour of bitsToDecimalMask() 221 """ 222 return bitsToDecimalMask(netbits)
223
224 -def bitsToDecimalMask(netbits):
225 """ 226 Convert integer number of netbits to a decimal number 227 228 >>> bitsToDecimalMask(32) 229 4294967295L 230 >>> bitsToDecimalMask(19) 231 4294959104L 232 >>> bitsToDecimalMask(0) 233 0L 234 """ 235 masknumb = 0L 236 netbits=int(netbits) 237 for i in range(32-netbits, 32): 238 masknumb += 2L ** i 239 return masknumb
240 241
242 -def bitsToMask(netbits):
243 """ 244 Convert netbits into a dotted-quad subnetmask 245 246 >>> bitsToMask(12) 247 '255.240.0.0' 248 >>> bitsToMask(0) 249 '0.0.0.0' 250 >>> bitsToMask(32) 251 '255.255.255.255' 252 """ 253 return decimalIpToStr(bitsToDecimalMask(netbits))
254 255
256 -def getnet(ip, netmask):
257 """ 258 Deprecated in favour of decimalNetFromIpAndNet() 259 """ 260 return decimalNetFromIpAndNet(ip, netmask)
261
262 -def decimalNetFromIpAndNet(ip, netmask):
263 """ 264 Get network address of IP as string netmask as in the form 255.255.255.0 265 266 >>> getnet('10.12.25.33', 24) 267 168564992L 268 >>> getnet('10.12.25.33', '255.255.255.0') 269 168564992L 270 """ 271 checkip(ip) 272 ip = ipToDecimal(ip) 273 274 try: netbits = int(netmask) 275 except ValueError: netbits = -1 276 277 if 0 < netbits <= 32: 278 netmask = bitsToDecimalMask(netbits) 279 else: 280 checkip(netmask) 281 netmask = ipToDecimal(netmask) 282 return ip & netmask
283 284
285 -def getnetstr(ip, netmask):
286 """ 287 Deprecated in favour of netFromIpAndNet() 288 """ 289 return netFromIpAndNet(ip, netmask)
290
291 -def netFromIpAndNet(ip, netmask):
292 """ 293 Return network number as string 294 295 >>> netFromIpAndNet('10.12.25.33', 24) 296 '10.12.25.0' 297 >>> netFromIpAndNet('250.12.25.33', 1) 298 '128.0.0.0' 299 >>> netFromIpAndNet('10.12.25.33', 16) 300 '10.12.0.0' 301 >>> netFromIpAndNet('10.12.25.33', 32) 302 '10.12.25.33' 303 """ 304 return decimalIpToStr(getnet(ip, netmask))
305
306 -def asyncNameLookup(address, uselibcresolver = True):
307 """ 308 Turn IP addreses into names using deferreds 309 """ 310 if uselibcresolver: 311 # This is the most reliable way to do a lookup use it 312 from twisted.internet import threads 313 import socket 314 return threads.deferToThread(lambda : socket.gethostbyaddr(address)[0]) 315 else: 316 # There is a problem with this method because it will ignore /etc/hosts 317 address = '.'.join(address.split('.')[::-1]) + '.in-addr.arpa' 318 d = lookupPointer(address, [1,2,4]) 319 def ip(result): 320 return str(result[0][0].payload.name)
321 d.addCallback(ip) 322 return d 323
324 -def asyncIpLookup(name):
325 """ 326 Look up an IP based on the name passed in. We use gethostbyname to make 327 sure that we use /etc/hosts as mentioned above. 328 329 This hasn't been tested. 330 """ 331 from twisted.internet import threads 332 import socket 333 return threads.deferToThread(lambda : socket.gethostbyname(name))
334 335
336 -def parse_iprange(iprange):
337 """ 338 Turn a string specifying an IP range into a list of IPs. 339 340 @param iprange: The range string, in the format '10.0.0.a-b' 341 @type iprange: str 342 343 >>> parse_iprange('10.0.0.1-5') 344 ['10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5'] 345 >>> parse_iprange('10.0.0.1') 346 ['10.0.0.1'] 347 >>> try: parse_iprange('10.0.0.1-2-3') 348 ... except InvalidIPRangeError: print "Invalid" 349 Invalid 350 351 """ 352 # Get the relevant octet 353 net, octet = iprange.rsplit('.', 1) 354 split = octet.split('-') 355 if len(split) > 2: # Nothing we can do about this 356 raise InvalidIPRangeError('%s is an invalid IP range.') 357 elif len(split)==1: # A single IP was passed 358 return [iprange] 359 else: 360 start, end = map(int, split) 361 return ['%s.%s' % (net, x) for x in xrange(start, end+1)]
362 363
364 -def getSubnetBounds(ip):
365 """ 366 Given a string representing the lower limit of a subnet, return decimal 367 representations of the first and last IP of that subnet. 368 369 0 is considered to define the beginning of a subnet, so x.x.x.0 represents 370 a /24, x.x.0.0 represents a /16, etc. An octet of 0 followed by a non-zero 371 octet, of course, is not considered to define a lower limit. 372 373 >>> map(decimalIpToStr, getSubnetBounds('10.1.1.0')) 374 ['10.1.1.0', '10.1.1.255'] 375 >>> map(decimalIpToStr, getSubnetBounds('10.1.1.1')) 376 ['10.1.1.1', '10.1.1.1'] 377 >>> map(decimalIpToStr, getSubnetBounds('10.0.1.0')) 378 ['10.0.1.0', '10.0.1.255'] 379 >>> map(decimalIpToStr, getSubnetBounds('0.0.0.0')) 380 ['0.0.0.0', '255.255.255.255'] 381 >>> map(decimalIpToStr, getSubnetBounds('10.0.0.0')) 382 ['10.0.0.0', '10.255.255.255'] 383 >>> map(decimalIpToStr, getSubnetBounds('100.0.0.0')) 384 ['100.0.0.0', '100.255.255.255'] 385 386 """ 387 octets = ip.split('.') 388 otherend = [] 389 while octets: 390 o = octets.pop() 391 if o=='0': 392 otherend.append('255') 393 else: 394 otherend.append(o) 395 break 396 otherend.reverse() 397 octets.extend(otherend) 398 upper = '.'.join(octets) 399 return numbip(ip), numbip(upper)
400
401 -def ensureIp(ip):
402 """ 403 Given a partially formed IP address this will return a complete Ip address 404 with four octets with the invalid or missing entries replaced by 0 405 406 @param ip partially formed ip (will strip out alpha characters) 407 @return valid IP address field 408 409 >>> from Products.ZenUtils.IpUtil import ensureIp 410 >>> ensureIp('20') 411 '20.0.0.0' 412 >>> ensureIp('2000') 413 '0.0.0.0' 414 >>> ensureIp('10.175.X') 415 '10.175.0.0' 416 >>> ensureIp('10.0.1') 417 '10.0.1.0' 418 >>> 419 """ 420 # filter out the alpha characters 421 stripped = ''.join([c for c in ip if c in '1234567890.']) 422 octets = stripped.split('.') 423 424 # make sure we always have 4 425 while (len(octets) < 4): 426 octets.append('0') 427 428 # validate each octet 429 for (idx, octet) in enumerate(octets): 430 # cast it to an integer 431 try: 432 octet = int(octet) 433 except ValueError: 434 octet = 0 435 436 # make it 0 if not in the valid ip range 437 if not (0 < octet < 255): 438 octets[idx] = '0' 439 440 return '.'.join(octets)
441