#!/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))