bin/update-serial
author Tero Marttila <terom@paivola.fi>
Fri, 16 Mar 2012 15:02:50 +0200
changeset 11 879917cebb9c
parent 3 cd10c4fe44bb
child 14 b883ef452cd8
permissions -rwxr-xr-x
update-serial: fix logging processName
#!/usr/bin/env python
# vim: set ft=python :

"""
    Update zone serials.
"""

__version__ = '0.0.1-dev'

import optparse
import codecs
import os.path
from datetime import datetime
import logging

log = logging.getLogger('main')

# command-line options, global state
options = None

def parse_options (argv) :
    """
        Parse command-line arguments.
    """
    
    prog = argv[0]

    parser = optparse.OptionParser(
            prog        = prog,
            usage       = '%prog: [options]',
            version     = __version__,

            # module docstring
            description = __doc__,
    )

    # logging
    general = optparse.OptionGroup(parser, "General Options")

    general.add_option('-q', '--quiet',     dest='loglevel', action='store_const', const=logging.ERROR, help="Less output")
    general.add_option('-v', '--verbose',   dest='loglevel', action='store_const', const=logging.INFO,  help="More output")
    general.add_option('-D', '--debug',     dest='loglevel', action='store_const', const=logging.DEBUG, help="Even more output")

    parser.add_option_group(general)

    # defaults
    parser.set_defaults(
        loglevel            = logging.WARN,
        expand              = [],
    )
    
    # parse
    options, args = parser.parse_args(argv[1:])

    # configure
    logging.basicConfig(
        format  = prog + ': %(name)s: %(levelname)s %(funcName)s : %(message)s',
        level   = options.loglevel,
    )

    return options, args

# 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) :
    return SERIAL_FMT.format(date=date.strftime(DATE_FMT), count=count)

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

    count += 1

    # check
    if count > 99 :
        serial = str(value + 1)

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

        return serial

    return format_serial(date, count)

def update_serial (serial) :
    """
        Update new serial number into given file, based on date.
    """
    
    today = datetime.now().date()

    # handle
    if not serial :
        # fresh
        log.info("Setting initial serial: %s:%s", date, count)
        
        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.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_count(value, date, count)

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

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

        return serial

    else :
        raise Exception("Invalid serial: %s:%s", old_date, old_count)
    
def process_serial (path) :
    """
        Read old serial, generate new one, and update file.
    """

    # read
    if os.path.exists(path) :
        serial = open(path).read().strip()
        log.debug("current serial: %s", serial)

    else :
        log.warn("Given .serial does not yet exist: %s", path)
        serial = None

    # update
    serial = update_serial(serial)

    # write
    open(path, 'w').write(serial)

    return serial

def main (argv) :
    global options
    
    options, args = parse_options(argv)

    # serial files to update
    for path in args :
        process_serial(path)
    
    return 0

if __name__ == '__main__':
    import sys

    sys.exit(main(sys.argv))