189 lines
5.7 KiB
Python
189 lines
5.7 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright (c) 2012 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import contextlib
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
|
|
'''
|
|
Compatibility code for handling the deprecated --flagfile option.
|
|
|
|
gflags style configuration files are deprecated and will be removed in future.
|
|
|
|
The code in this module transles --flagfile options into --config-file and can
|
|
be removed when support for --flagfile is removed.
|
|
'''
|
|
|
|
|
|
def _get_flagfile(argp):
|
|
'''Parse the filename from a --flagfile argument.
|
|
|
|
The current and next arguments are passed as a 2 item list. If the
|
|
flagfile filename is in the next argument, the two arguments are
|
|
joined into the first item while the second item is set to None.
|
|
'''
|
|
i = argp[0].find('-flagfile')
|
|
if i < 0:
|
|
return None
|
|
|
|
# Accept -flagfile or -flagfile
|
|
if i != 0 and (i != 1 or argp[0][i] != '-'):
|
|
return None
|
|
|
|
i += len('-flagfile')
|
|
if i == len(argp[0]): # Accept [-]-flagfile foo
|
|
argp[0] += '=' + argp[1]
|
|
argp[1] = None
|
|
|
|
if argp[0][i] != '=': # Accept [-]-flagfile=foo
|
|
return None
|
|
|
|
return argp[0][i + 1:]
|
|
|
|
|
|
def _open_file_for_reading(path):
|
|
'''Helper method which test code may stub out.'''
|
|
return open(path, 'r')
|
|
|
|
|
|
def _open_fd_for_writing(fd, _path):
|
|
'''Helper method which test code may stub out.'''
|
|
return os.fdopen(fd, 'w')
|
|
|
|
|
|
def _read_lines(flagfile):
|
|
'''Read a flag file, returning all lines with comments stripped.'''
|
|
with _open_file_for_reading(flagfile) as f:
|
|
lines = f.readlines()
|
|
ret = []
|
|
for l in lines:
|
|
if l.isspace() or l.startswith('#') or l.startswith('//'):
|
|
continue
|
|
ret.append(l.strip())
|
|
return ret
|
|
|
|
|
|
def _read_flagfile(arg, next_arg, tempdir=None):
|
|
'''Convert a --flagfile argument to --config-file.
|
|
|
|
If the supplied argument is a --flagfile argument, read the contents
|
|
of the file and convert it to a .ini format config file. Return a
|
|
--config-file argument with the converted file.
|
|
|
|
If the flag file contains more --flagfile arguments, multiple
|
|
--config-file arguments will be returned.
|
|
|
|
The returned argument list may also contain None values which should
|
|
be filtered out later.
|
|
'''
|
|
argp = [arg, next_arg]
|
|
flagfile = _get_flagfile(argp)
|
|
if not flagfile:
|
|
return argp
|
|
|
|
args = _read_lines(flagfile)
|
|
|
|
if args and not args[0].startswith('--'):
|
|
# This is a config file, not a flagfile, so return it.
|
|
return ['--config-file=' + flagfile] + argp[1:]
|
|
|
|
#
|
|
# We're recursing here to convert any --flagfile arguments
|
|
# read from this flagfile into --config-file arguments
|
|
#
|
|
# We don't actually include those --config-file arguments
|
|
# in the generated config file; instead we include all those
|
|
# --config-file args in the final command line
|
|
#
|
|
args = _iterate_args(args, _read_flagfile, tempdir=tempdir)
|
|
|
|
config_file_args = []
|
|
|
|
(fd, tmpconf) = tempfile.mkstemp(suffix='.conf', dir=tempdir)
|
|
|
|
with _open_fd_for_writing(fd, tmpconf) as f:
|
|
f.write('[DEFAULT]\n')
|
|
for arg in args:
|
|
if arg.startswith('--config-file='):
|
|
config_file_args.append(arg)
|
|
continue
|
|
if '=' in arg:
|
|
f.write(arg[2:] + '\n')
|
|
elif arg[2:].startswith('no'):
|
|
f.write(arg[4:] + '=false\n')
|
|
else:
|
|
f.write(arg[2:] + '=true\n')
|
|
|
|
return ['--config-file=' + tmpconf] + argp[1:] + config_file_args
|
|
|
|
|
|
def _iterate_args(args, iterator, **kwargs):
|
|
'''Run an iterator function on the supplied args list.
|
|
|
|
The iterator is passed the current arg and next arg and returns a
|
|
list of args. The returned args replace the suppied args in the
|
|
resulting args list.
|
|
|
|
The iterator will be passed None for the next arg when processing
|
|
the last arg.
|
|
'''
|
|
args.append(None)
|
|
|
|
ret = []
|
|
for i in range(len(args)):
|
|
if args[i] is None: # last item, or consumed file name
|
|
continue
|
|
|
|
modified = iterator(args[i], args[i + 1], **kwargs)
|
|
args[i], args[i + 1] = modified[:2]
|
|
|
|
ret.extend(modified[:1] + modified[2:]) # don't append next arg
|
|
|
|
return filter(None, ret)
|
|
|
|
|
|
def handle_flagfiles(args, tempdir=None):
|
|
'''Replace --flagfile arguments with --config-file arguments.
|
|
|
|
Replace any --flagfile argument in the supplied list with a --config-file
|
|
argument containing a temporary config file with the contents of the flag
|
|
file translated to .ini format.
|
|
|
|
The tempdir argument is a directory which will be used to create temporary
|
|
files.
|
|
'''
|
|
return _iterate_args(args[:], _read_flagfile, tempdir=tempdir)
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def handle_flagfiles_managed(args):
|
|
'''A context manager for handle_flagfiles() which removes temp files.
|
|
|
|
For use with the 'with' statement, i.e.::
|
|
|
|
with handle_flagfiles_managed(args) as args:
|
|
# Do stuff
|
|
# Any temporary fils have been removed
|
|
'''
|
|
# NOTE(johannes): Would be nice to use utils.tempdir(), but it
|
|
# causes an import loop
|
|
tempdir = tempfile.mkdtemp(prefix='nova-conf-')
|
|
try:
|
|
yield handle_flagfiles(args, tempdir=tempdir)
|
|
finally:
|
|
shutil.rmtree(tempdir)
|