bin/update-serial
branchdns-new
changeset 604 9a23fca9167a
parent 603 b58236f9ea7b
child 605 26a307558602
equal deleted inserted replaced
603:b58236f9ea7b 604:9a23fca9167a
     1 #!/usr/bin/env python
       
     2 # vim: set ft=python :
       
     3 
       
     4 """
       
     5     Update zone serials.
       
     6 """
       
     7 
       
     8 __version__ = '0.0.1-dev'
       
     9 
       
    10 import optparse
       
    11 import codecs
       
    12 import os.path
       
    13 from datetime import datetime
       
    14 import logging
       
    15 
       
    16 log = logging.getLogger('main')
       
    17 
       
    18 # command-line options, global state
       
    19 options = None
       
    20 
       
    21 def parse_options (argv) :
       
    22     """
       
    23         Parse command-line arguments.
       
    24     """
       
    25     
       
    26     prog = argv[0]
       
    27 
       
    28     parser = optparse.OptionParser(
       
    29             prog        = prog,
       
    30             usage       = '%prog: [options]',
       
    31             version     = __version__,
       
    32 
       
    33             # module docstring
       
    34             description = __doc__,
       
    35     )
       
    36 
       
    37     # logging
       
    38     general = optparse.OptionGroup(parser, "General Options")
       
    39 
       
    40     general.add_option('-q', '--quiet',     dest='loglevel', action='store_const', const=logging.ERROR, help="Less output")
       
    41     general.add_option('-v', '--verbose',   dest='loglevel', action='store_const', const=logging.INFO,  help="More output")
       
    42     general.add_option('-D', '--debug',     dest='loglevel', action='store_const', const=logging.DEBUG, help="Even more output")
       
    43 
       
    44     parser.add_option_group(general)
       
    45 
       
    46     # defaults
       
    47     parser.set_defaults(
       
    48         loglevel            = logging.WARN,
       
    49         expand              = [],
       
    50     )
       
    51     
       
    52     # parse
       
    53     options, args = parser.parse_args(argv[1:])
       
    54 
       
    55     # configure
       
    56     logging.basicConfig(
       
    57         format  = prog + ': %(name)s: %(levelname)s %(funcName)s : %(message)s',
       
    58         level   = options.loglevel,
       
    59     )
       
    60 
       
    61     return options, args
       
    62 
       
    63 # date fmt to use in serial
       
    64 DATE_FMT = '%Y%m%d'
       
    65 DATE_LEN = 8
       
    66 
       
    67 SERIAL_FMT = "{date:8}{count:02}"
       
    68 SERIAL_LEN = 10
       
    69 
       
    70 def format_serial (date, count) :
       
    71     return SERIAL_FMT.format(date=date.strftime(DATE_FMT), count=count)
       
    72 
       
    73 def next_count (value, date, count) :
       
    74     """
       
    75         Return serial with next count.
       
    76     """
       
    77 
       
    78     count += 1
       
    79 
       
    80     # check
       
    81     if count > 99 :
       
    82         serial = str(value + 1)
       
    83 
       
    84         log.warn("Serial count rollover: %s, %s; fallback -> %s", date, count, serial)
       
    85 
       
    86         return serial
       
    87 
       
    88     return format_serial(date, count)
       
    89 
       
    90 def update_serial (serial) :
       
    91     """
       
    92         Update new serial number into given file, based on date.
       
    93     """
       
    94     
       
    95     today = datetime.now().date()
       
    96 
       
    97     # handle
       
    98     if not serial :
       
    99         # fresh
       
   100         log.info("Setting initial serial: %s01", today)
       
   101         
       
   102         return format_serial(today, 1)
       
   103 
       
   104     elif len(serial) != SERIAL_LEN :
       
   105         log.warn("Invalid serial format: %s", serial)
       
   106         
       
   107         value = int(serial)
       
   108         serial = str(value + 1)
       
   109 
       
   110         log.info("Incrementing serial: %d -> %s", value, serial)
       
   111 
       
   112         return serial
       
   113 
       
   114     else :
       
   115         # parse
       
   116         value = int(serial)
       
   117 
       
   118         try :
       
   119             date = datetime.strptime(serial[:DATE_LEN], DATE_FMT).date()
       
   120             count = int(serial[DATE_LEN:])
       
   121 
       
   122         except ValueError, e :
       
   123             # invalid date/count format?
       
   124             log.warn("Unable to parse serial: %s: %s", serial, e)
       
   125 
       
   126             serial = str(value + 1)
       
   127 
       
   128             log.info("Incrementing serial: %d -> %s", value, serial)
       
   129 
       
   130             return serial
       
   131             
       
   132         log.debug("old serial=%s, value=%d, date=%s, count=%s", serial, value, date, count)
       
   133         
       
   134     # update
       
   135     if date < today :
       
   136         log.info("Updating to today: %s -> %s", date, today)
       
   137 
       
   138         # update date
       
   139         return format_serial(today, 1)
       
   140 
       
   141     elif date == today :
       
   142         # keep date, update count
       
   143         log.info("Updating today's count: %s, %s", date, count)
       
   144         
       
   145         # handle count rollover
       
   146         return next_count(value, date, count)
       
   147 
       
   148     elif date > today :
       
   149         # keep, update count
       
   150         serial = next_count(value, date, count)
       
   151 
       
   152         log.warn("Serial in future; incrementing count: %s, %s -> %s", date, count, serial)
       
   153 
       
   154         return serial
       
   155 
       
   156     else :
       
   157         raise Exception("Invalid serial: %s:%s", old_date, old_count)
       
   158     
       
   159 def process_serial (path) :
       
   160     """
       
   161         Read old serial, generate new one, and update file.
       
   162     """
       
   163 
       
   164     # read
       
   165     if os.path.exists(path) :
       
   166         serial = open(path).read().strip()
       
   167         log.debug("current serial: %s", serial)
       
   168 
       
   169     else :
       
   170         log.warn("Given .serial does not yet exist: %s", path)
       
   171         serial = None
       
   172 
       
   173     # update
       
   174     serial = update_serial(serial)
       
   175 
       
   176     # write
       
   177     open(path, 'w').write(serial + '\n')
       
   178 
       
   179     return serial
       
   180 
       
   181 def main (argv) :
       
   182     global options
       
   183     
       
   184     options, args = parse_options(argv)
       
   185 
       
   186     # serial files to update
       
   187     for path in args :
       
   188         process_serial(path)
       
   189     
       
   190     return 0
       
   191 
       
   192 if __name__ == '__main__':
       
   193     import sys
       
   194 
       
   195     sys.exit(main(sys.argv))