pvl/dns/serial.py
author Tero Marttila <terom@paivola.fi>
Sat, 21 Dec 2013 22:57:44 +0200
changeset 329 b3778c190aa5
parent 245 5c1c57bc510a
permissions -rw-r--r--
version 0..0:

pvl.rrd: api.report()
pvl.wlan-syslog: track openwrt hostapd syslog wlan sta activity
pvl.verkko.wlan: basic Table view
pvl.dns-serial
pvl.dns-zone
pvl.dhcp.config: nested blocks
pvl.hosts-import: import hosts from dns/dhcp
pvl.hosts-dns: generate --forward-zone=paivola.fi and --reverse-zone=194.197.235
pvl.hosts-dhcp: generate dhcp hosts conf
"""
    Generating zone serials.
"""

import datetime

import logging; log = logging.getLogger('pvl.dns.serial')

# date fmt to use in serial
DATE_FMT = '%Y%m%d'
DATE_LEN = 8

SERIAL_FMT = "{date:8}{count:02}"
SERIAL_LEN = 10

def format_serial (date, count) :
    """
        Format serial as string.

        >>> format_serial(datetime.date(2013, 9, 10), 0)
        '2013091000'
        >>> format_serial(datetime.date(2013, 9, 10), 1)
        '2013091001'
    """

    assert 0 <= count <= 99

    return SERIAL_FMT.format(date=date.strftime(DATE_FMT), count=count)

def next_serial (date, count) :
    """
        Return serial with next count.

        >>> next_serial(datetime.date(2013, 9, 10), 1)
        '2013091002'
        >>> next_serial(datetime.date(2013, 9, 10), 99)
        '2013091100'
    """


    # check
    if count < 99 :
        return format_serial(date, count + 1)
    else:
        serial = str(int(format_serial(date, count)) + 1)

        log.warn("Serial count rollover: %s, %s; fallback -> %s", date, count, serial)

        return serial

def update_serial (serial, today=None) :
    """
        Parse given serial number (string), returning an updated serial, based on date.

        Raises ValueError on invalid (non-numeric) serial.

        >>> print update_serial('', today=datetime.date(2013, 9, 10))
        2013091001
        >>> print update_serial('13', today=datetime.date(2013, 9, 10))
        14
        >>> print update_serial('2013083501', today=datetime.date(2013, 9, 10))
        2013083502
        >>> print update_serial('2013083102', today=datetime.date(2013, 9, 10))
        2013091001
        >>> print update_serial('2013091002', today=datetime.date(2013, 9, 10))
        2013091003
        >>> print update_serial('2013091102', today=datetime.date(2013, 9, 10))
        2013091103
    """
    
    if not today:
        today = datetime.date.today()

    # handle
    if not serial :
        # fresh
        log.info("Setting initial serial: %s01", today)
        
        return format_serial(today, 1)

    elif len(serial) != SERIAL_LEN :
        log.warn("Invalid serial format: %s", serial)
        
        value = int(serial)
        serial = str(value + 1)

        log.info("Incrementing serial: %d -> %s", value, serial)

        return serial

    else :
        # parse
        value = int(serial)

        try :
            date = datetime.datetime.strptime(serial[:DATE_LEN], DATE_FMT).date()
            count = int(serial[DATE_LEN:])

        except ValueError, e :
            # invalid date/count format?
            log.warn("Unable to parse serial: %s: %s", serial, e)

            serial = str(value + 1)

            log.info("Incrementing serial: %d -> %s", value, serial)

            return serial
            
        log.debug("old serial=%s, value=%d, date=%s, count=%s", serial, value, date, count)
        
    # update
    if date < today :
        log.info("Updating to today: %s -> %s", date, today)

        # update date
        return format_serial(today, 1)

    elif date == today :
        # keep date, update count
        log.info("Updating today's count: %s, %s", date, count)
        
        # handle count rollover
        return next_serial(date, count)

    elif date > today :
        # keep, update count
        serial = next_serial(date, count)

        log.warn("Serial in future; incrementing count: %s, %s -> %s", date, count, serial)

        return serial

    else :
        raise Exception("Impossible serial: %s:%s", date, count)