Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert snmp octet string to human readable date format

Using the pysnmp framework i get some values doing a snmp walk. Unfortunately for the oid

1.3.6.1.21.69.1.5.8.1.2 (DOCS-CABLE-DEVICE-MIB)

i get a weird result which i cant correctly print here since it contains ascii chars like BEL ACK

When doing a repr i get:

OctetString('\x07\xd8\t\x17\x03\x184\x00')

But the output should look like:

2008-9-23,3:24:52.0

the format is called "DateAndTime". How can i translate the OctetString output to a "human readable" date/time ?

like image 904
Michael Lang Avatar asked Dec 31 '10 19:12

Michael Lang


3 Answers

You can find the format specification here.

A date-time specification. 
            field  octets  contents                  range
            -----  ------  --------                  -----
              1      1-2   year*                     0..65536
              2       3    month                     1..12
              3       4    day                       1..31
              4       5    hour                      0..23
              5       6    minutes                   0..59
              6       7    seconds                   0..60
                           (use 60 for leap-second)
              7       8    deci-seconds              0..9
              8       9    direction from UTC        '+' / '-'
              9      10    hours from UTC*           0..13
             10      11    minutes from UTC          0..59
* Notes:
            - the value of year is in network-byte order
            - daylight saving time in New Zealand is +13 For example, 
              Tuesday May 26, 1992 at 1:30:15 PM EDT would be displayed as:
                 1992-5-26,13:30:15.0,-4:0 
              Note that if only local time is known, then timezone
              information (fields 8-10) is not present.

In order to decode your sample data you can use this quick-and-dirty one-liner:

>>> import struct, datetime
>>> s = '\x07\xd8\t\x17\x03\x184\x00'
>>> datetime.datetime(*struct.unpack('>HBBBBBB', s))
datetime.datetime(2008, 9, 23, 3, 24, 52)

The example above is far from perfect, it does not account for size (this object has variable size) and is missing timezone information. Also note that the field 7 is deci-seconds (0..9) while timetuple[6] is microseconds (0 <= x < 1000000); the correct implementations is left as an exercise for the reader.

[update]

8 years later, lets try to fix this answer (am I lazy or what?):

import struct, pytz, datetime

def decode_snmp_date(octetstr: bytes) -> datetime.datetime:
    size = len(octetstr)
    if size == 8:
        (year, month, day, hour, minutes, 
         seconds, deci_seconds,
        ) = struct.unpack('>HBBBBBB', octetstr)
        return datetime.datetime(
            year, month, day, hour, minutes, seconds, 
            deci_seconds * 100_000, tzinfo=pytz.utc)
    elif size == 11:
        (year, month, day, hour, minutes, 
         seconds, deci_seconds, direction, 
         hours_from_utc, minutes_from_utc,
        ) = struct.unpack('>HBBBBBBcBB', octetstr)
        offset = datetime.timedelta(
            hours=hours_from_utc, minutes=minutes_from_utc)
        if direction == b'-':
            offset = -offset 
        return datetime.datetime(
            year, month, day, hour, minutes, seconds, 
            deci_seconds * 100_000, tzinfo=pytz.utc) + offset
    raise ValueError("The provided OCTETSTR is not a valid SNMP date")

I'm not sure I got the timezone offset right but I don't have sample data to test, feel free to amend the answer or ping me in the comments.

like image 180
Paulo Scardine Avatar answered Nov 20 '22 09:11

Paulo Scardine


@Paulo Scardine: This was the best answer I found online when working to resolve a very similar problem. It still took me a little while to resolve my issue even with this answer, so I wanted to post a follow up answer that may add more clarity. (specifically the issue with the date having different length options).

The following piece of code connects to a server and grabs the system time and then outputs it as a string to illustrate the method.

import netsnmp
import struct
oid = netsnmp.Varbind('hrSystemDate.0')
resp = netsnmp.snmpget(oid, Version=1, DestHost='<ip>', Community='public')
oct = str(resp[0])
# hrSystemDate can be either 8 or 11 units in length.
oct_len = len(oct)
fmt_mapping = dict({8:'>HBBBBBB', 11:'>HBBBBBBcBB'})
if oct_len == 8 or oct_len == 11:
    t = struct.unpack(fmt_mapping[oct_len], oct)
    print 'date tuple: %s' % (repr(t))
else:
    print 'invalid date format'

I hope this helps other people who are having similar issues trying to work with this type of data.

like image 4
swill Avatar answered Nov 20 '22 09:11

swill


Shameless plug here: The Pycopia SNMP and SMI modules correctly handle this object, and others as well. Pycopia is installed from source, and dont forget the mibs file if you try it.

like image 2
Keith Avatar answered Nov 20 '22 11:11

Keith