Switch to docopt for options parsing

Add option to not log to any file.
This commit is contained in:
Anders Ingemann 2014-04-08 21:22:34 +02:00
parent c154347849
commit 2017806d1f
3 changed files with 49 additions and 47 deletions

View file

@ -5,19 +5,6 @@ both to a file and to the console.
import logging import logging
def create_log_dir():
"""Creates the log directory
Returns:
str. The path to the logdirectory
"""
log_dir_path = '/var/log/bootstrap-vz'
import os
if not os.path.exists(log_dir_path):
os.makedirs(log_dir_path)
return log_dir_path
def get_log_filename(manifest_path): def get_log_filename(manifest_path):
"""Returns the path to a logfile given a manifest """Returns the path to a logfile given a manifest
The logfile name is constructed from the current timestamp and the basename of the manifest The logfile name is constructed from the current timestamp and the basename of the manifest
@ -49,14 +36,16 @@ def setup_logger(logfile=None, debug=False):
# Make sure all logging statements are processed by our handlers, they decide the log level # Make sure all logging statements are processed by our handlers, they decide the log level
root.setLevel(logging.NOTSET) root.setLevel(logging.NOTSET)
# Create a file log handler # Only enable logging to file if a destination was supplied
file_handler = logging.FileHandler(logfile) if logfile is not None:
# Absolute timestamps are rather useless when bootstrapping, it's much more interesting # Create a file log handler
# to see how long things take, so we log in a relative format instead file_handler = logging.FileHandler(logfile)
file_handler.setFormatter(FileFormatter('[%(relativeCreated)s] %(levelname)s: %(message)s')) # Absolute timestamps are rather useless when bootstrapping, it's much more interesting
# The file log handler always logs everything # to see how long things take, so we log in a relative format instead
file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(FileFormatter('[%(relativeCreated)s] %(levelname)s: %(message)s'))
root.addHandler(file_handler) # The file log handler always logs everything
file_handler.setLevel(logging.DEBUG)
root.addHandler(file_handler)
# Create a console log handler # Create a console log handler
import sys import sys

View file

@ -13,45 +13,57 @@ def main():
Exception Exception
""" """
# Get the commandline arguments # Get the commandline arguments
args = get_args() opts = get_opts()
# Require root privileges, except when doing a dry-run where they aren't needed # Require root privileges, except when doing a dry-run where they aren't needed
import os import os
if os.geteuid() != 0 and not args.dry_run: if os.geteuid() != 0 and not opts['--dry-run']:
raise Exception('This program requires root privileges.') raise Exception('This program requires root privileges.')
# Setup logging
import log import log
log_dir = log.create_log_dir() # Log to file unless --log is a single dash
log_filename = log.get_log_filename(args.manifest) if opts['--log'] != '-':
logfile = os.path.join(log_dir, log_filename) # Setup logging
log.setup_logger(logfile=logfile, debug=args.debug) if not os.path.exists(opts['--log']):
os.makedirs(opts['--log'])
log_filename = log.get_log_filename(opts['MANIFEST'])
logfile = os.path.join(opts['--log'], log_filename)
else:
logfile = None
log.setup_logger(logfile=logfile, debug=opts['--debug'])
# Everything has been set up, begin the bootstrapping process # Everything has been set up, begin the bootstrapping process
run(args) run(opts)
def get_args(): def get_opts():
"""Creates an argument parser and returns the arguments it has parsed """Creates an argument parser and returns the arguments it has parsed
""" """
from argparse import ArgumentParser from docopt import docopt
parser = ArgumentParser(description='Bootstrap Debian for the cloud.') usage = """bootstrap-vz
parser.add_argument('--debug', action='store_true',
help='Print debugging information') Usage: bootstrap-vz [options] MANIFEST
parser.add_argument('--pause-on-error', action='store_true',
help='Pause on error, before rollback') Options:
parser.add_argument('--dry-run', action='store_true', --log <path> Log to given directory [default: /var/log/bootstrap-vz]
help='Dont\'t actually run the tasks') If <path> is `-' file logging will be disabled.
parser.add_argument('manifest', help='Manifest file to use for bootstrapping', metavar='MANIFEST') --pause-on-error Pause on error, before rollback
return parser.parse_args() --dry-run Don't actually run the tasks
--debug Print debugging information
-h, --help show this help
"""
opts = docopt(usage)
return opts
def run(args): def run(opts):
"""Runs the bootstrapping process """Runs the bootstrapping process
Args: Args:
args (dict): Dictionary of arguments from the commandline opts (dict): Dictionary of options from the commandline
""" """
# Load the manifest # Load the manifest
from manifest import Manifest from manifest import Manifest
manifest = Manifest(args.manifest) manifest = Manifest(opts['MANIFEST'])
# Get the tasklist # Get the tasklist
from tasklist import TaskList from tasklist import TaskList
@ -61,17 +73,17 @@ def run(args):
# Create the bootstrap information object that'll be used throughout the bootstrapping process # Create the bootstrap information object that'll be used throughout the bootstrapping process
from bootstrapinfo import BootstrapInformation from bootstrapinfo import BootstrapInformation
bootstrap_info = BootstrapInformation(manifest=manifest, debug=args.debug) bootstrap_info = BootstrapInformation(manifest=manifest, debug=opts['--debug'])
try: try:
# Run all the tasks the tasklist has gathered # Run all the tasks the tasklist has gathered
tasklist.run(info=bootstrap_info, dry_run=args.dry_run) tasklist.run(info=bootstrap_info, dry_run=opts['--dry-run'])
# We're done! :-) # We're done! :-)
log.info('Successfully completed bootstrapping') log.info('Successfully completed bootstrapping')
except (Exception, KeyboardInterrupt) as e: except (Exception, KeyboardInterrupt) as e:
# When an error occurs, log it and begin rollback # When an error occurs, log it and begin rollback
log.exception(e) log.exception(e)
if args.pause_on_error: if opts['--pause-on-error']:
# The --pause-on-error is useful when the user wants to inspect the volume before rollback # The --pause-on-error is useful when the user wants to inspect the volume before rollback
raw_input('Press Enter to commence rollback') raw_input('Press Enter to commence rollback')
log.error('Rolling back') log.error('Rolling back')
@ -96,6 +108,6 @@ def run(args):
rollback_tasklist.load('resolve_rollback_tasks', manifest, counter_task) rollback_tasklist.load('resolve_rollback_tasks', manifest, counter_task)
# Run the rollback tasklist # Run the rollback tasklist
rollback_tasklist.run(info=bootstrap_info, dry_run=args.dry_run) rollback_tasklist.run(info=bootstrap_info, dry_run=opts['--dry-run'])
log.info('Successfully completed rollback') log.info('Successfully completed rollback')
raise e raise e

View file

@ -20,6 +20,7 @@ setup(name='bootstrap-vz',
'fysom >= 1.0.15', 'fysom >= 1.0.15',
'jsonschema >= 2.3.0', 'jsonschema >= 2.3.0',
'pyyaml >= 3.10', 'pyyaml >= 3.10',
'docopt >= 0.6.1',
], ],
license='Apache License, Version 2.0', license='Apache License, Version 2.0',
description='Bootstrap Debian images for virtualized environments', description='Bootstrap Debian images for virtualized environments',