Friday, February 10, 2012

Cisco CDR Timestamp and IP Address Conversion

The call detail records (CDRs) used by Cisco Unified Communications Manager (aka Call Manager) can be tricky to interpret. Two fields that are frequently confusing are the IP address fields and the timestamp field.

Continuing my theme of using Python to help with Cisco product administration, here's how to convert timestamps and IP addresses to human-readable format.

Timestamps are pretty easy. They're recorded in UNIX epoch time and there are lots of examples available showing how to convert them using Excel or various scripting tools. In Python:

import time

def time_to_string(time_value):
    """convert Unix epoch time to a human readable string"""
    return time.strftime("%m/%d/%Y %H:%M:%S",time.localtime(float(time_value)))

It took me a while to figure out how to convert IPv4 addresses, since CUCM uses signed 32-bit integers to represent IP addresses and Python's long integers can be of infinite length. First, a review of how to do the conversion manually is in order. Let's say that a CUCM CDR lists an IP address as "-2126438902". First, we convert this signed 32-bit integer to hex using Windows calculator:

-2126438902 = 0x81411E0A

Next, we break the hex number into 1-byte chunks, and reverse the order:

0x0A
0x1E
0x41
0x81

Finally, we convert each byte to decimal and put them together into an IP address:

10.30.65.129

Here's the Python function to do the conversion:

def int_to_ip(signed_int):
""" convert a 32-bit signed integer to an IP address"""
# do preventative type checking because I didn't want to check inputs
try:
if type(signed_int) == str or type(signed_int) == int:
signed_int = long(signed_int)
except ValueError:
return "err_ip"
# CUCM occasionally creates CDRs with an IP of '0'. Bug or feature? Beats me.
if signed_int == 0:
return "err_ip"
# hex conversion for 32-bit signed int;
# the slice at the end removes the '0x' and 'L' in the result
h = hex(signed_int & 0xffffffff)[2:-1]
if len(h) == 7: #pad initial zero if required
h = '0' + h
hex_ip = [h[6:8],h[4:6],h[2:4],h[:2]] # reverse the octets
#put them back together in IPv4 format
ip = '.'.join([str(int(n,16)) for n in hex_ip])
return ip


And here it is in the interpreter showing that it works for reasonable input types:

>>> int_to_ip('-2126438902')
'10.30.65.129'
>>> int_to_ip(-2126438902)
'10.30.65.129'
>>> int_to_ip(-2126438902L)
'10.30.65.129'

 I don't have access to an IPv6-aware CUCM install, so you're on your own for that!