4cd94cd763
When updating the openstack.common.config module, the shutil module raises an exception when attemting to copy a directory. Fixed to skip directories as copy targets and handle directories of target module names. Fixed problem of wrong import path of the generator module. Change-Id: Ie8336b070573c74691ab5e7304dd05c540826c53
311 lines
9.6 KiB
Python
311 lines
9.6 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 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.
|
|
|
|
r"""
|
|
A simple script to update openstack-common modules which have been copied
|
|
into other projects. See:
|
|
|
|
http://wiki.openstack.org/CommonLibrary#Incubation
|
|
|
|
The script can be called the following ways:
|
|
|
|
$> python update.py ../myproj
|
|
$> python update.py --config-file ../myproj/openstack-common.conf
|
|
|
|
Where ../myproj is a project directory containing openstack-common.conf which
|
|
might look like:
|
|
|
|
[DEFAULT]
|
|
modules = wsgi,utils
|
|
base = myproj
|
|
|
|
Or:
|
|
|
|
$> python update.py ../myproj/myconf.conf
|
|
$> python update.py --config-file ../myproj/myconf.conf
|
|
|
|
Where ../myproj is a project directory which contains a differently named
|
|
configuration file, or:
|
|
|
|
$> python update.py --config-file ../myproj/myproj/openstack/common.conf
|
|
--dest-dir ../myproj
|
|
|
|
Where ../myproject is a project directory, but the configuration file is
|
|
stored in a sub-directory, or:
|
|
|
|
$> python update.py --modules wsgi,utils --base myproj ../myproj
|
|
$> python update.py --modules wsgi,utils --base myproj --dest-dir ../myproj
|
|
|
|
Where ../myproject is a project directory, but we explicitly specify
|
|
the modules to copy and the base destination module
|
|
|
|
Obviously, the first way is the easiest!
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import functools
|
|
import glob
|
|
import os
|
|
import os.path
|
|
import re
|
|
import shutil
|
|
import sys
|
|
|
|
from oslo.config import cfg
|
|
|
|
opts = [
|
|
cfg.ListOpt('modules',
|
|
default=[],
|
|
help='The list of modules to copy from oslo-incubator '
|
|
'(deprecated in favor of --module)'),
|
|
cfg.MultiStrOpt('module',
|
|
default=[],
|
|
help='The list of modules to copy from oslo-incubator'),
|
|
cfg.StrOpt('base',
|
|
default=None,
|
|
help='The base module to hold the copy of openstack.common'),
|
|
cfg.StrOpt('dest-dir',
|
|
default=None,
|
|
help='Destination project directory'),
|
|
cfg.StrOpt('configfile_or_destdir',
|
|
default=None,
|
|
help='A config file or destination project directory',
|
|
positional=True),
|
|
cfg.BoolOpt('nodeps',
|
|
default=False,
|
|
help='Discard dependencies of configured modules'),
|
|
]
|
|
|
|
|
|
def _parse_args(argv):
|
|
conf = cfg.ConfigOpts()
|
|
conf.register_cli_opts(opts)
|
|
conf(argv, usage='Usage: %(prog)s [config-file|dest-dir]')
|
|
|
|
if conf.configfile_or_destdir:
|
|
def def_config_file(dest_dir):
|
|
return os.path.join(dest_dir, 'openstack-common.conf')
|
|
|
|
config_file = None
|
|
if os.path.isfile(conf.configfile_or_destdir):
|
|
config_file = conf.configfile_or_destdir
|
|
elif (os.path.isdir(conf.configfile_or_destdir)
|
|
and os.path.isfile(def_config_file(conf.configfile_or_destdir))):
|
|
config_file = def_config_file(conf.configfile_or_destdir)
|
|
|
|
if config_file:
|
|
conf(argv + ['--config-file', config_file])
|
|
|
|
return conf
|
|
|
|
|
|
def _mod_to_path(mod):
|
|
return os.path.join(*mod.split('.'))
|
|
|
|
|
|
def _dest_path(path, base, dest_dir):
|
|
return os.path.join(dest_dir, _mod_to_path(base), path)
|
|
|
|
|
|
def _replace(path, pattern, replacement):
|
|
with open(path, "rb+") as f:
|
|
lines = f.readlines()
|
|
f.seek(0)
|
|
f.truncate()
|
|
for line in lines:
|
|
f.write(re.sub(pattern, replacement, line))
|
|
|
|
|
|
def _make_dirs(path):
|
|
if not os.path.isdir(os.path.dirname(path)):
|
|
os.makedirs(os.path.dirname(path))
|
|
|
|
|
|
def _copy_file(path, dest, base):
|
|
_make_dirs(dest)
|
|
if not os.path.isdir(os.path.dirname(dest)):
|
|
os.makedirs(os.path.dirname(dest))
|
|
|
|
shutil.copy2(path, dest)
|
|
|
|
if 'rpc/' not in dest:
|
|
_replace(dest, 'oslo', base)
|
|
|
|
# Restore the imports for modules that are part of the oslo
|
|
# namespace package. We can't just do something like 'oslo\..+'
|
|
# because there are default configuration settings like
|
|
# "oslo.sqlite" that we want to have changed to "nova.sqlite" by
|
|
# the above call.
|
|
for oslo_module in ['config']:
|
|
_replace(dest, base + '.' + oslo_module,
|
|
'oslo.' + oslo_module)
|
|
|
|
_replace(dest,
|
|
'^( *)from openstack.common',
|
|
r'\1from ' + base + '.openstack.common')
|
|
|
|
_replace(dest,
|
|
'\'openstack\.common',
|
|
'\'' + base + '.openstack.common')
|
|
|
|
_replace(dest,
|
|
'\"openstack\.common',
|
|
'\"' + base + '.openstack.common')
|
|
|
|
_replace(dest,
|
|
'=openstack\.common',
|
|
'=' + base + '.openstack.common')
|
|
|
|
_replace(dest,
|
|
'possible_topdir, "oslo",$',
|
|
'possible_topdir, "' + base + '",')
|
|
|
|
|
|
def _copy_pyfile(path, base, dest_dir):
|
|
_copy_file(path, _dest_path(path, base, dest_dir), base)
|
|
|
|
|
|
def _copy_module(mod, base, dest_dir):
|
|
print("Copying %s under the %s module in %s" % (mod, base, dest_dir))
|
|
|
|
copy_pyfile = functools.partial(_copy_pyfile,
|
|
base=base, dest_dir=dest_dir)
|
|
|
|
if '.' in mod:
|
|
path = _mod_to_path('openstack.common')
|
|
for d in mod.split('.')[:-1]:
|
|
path = os.path.join(path, d)
|
|
if os.path.isdir(path):
|
|
copy_pyfile(os.path.join(path, '__init__.py'))
|
|
|
|
mod_path = _mod_to_path('openstack.common.%s' % mod)
|
|
mod_file = '%s.py' % mod_path
|
|
if os.path.isfile(mod_file):
|
|
copy_pyfile(mod_file)
|
|
elif os.path.isdir(mod_path):
|
|
dest = os.path.join(dest_dir, _mod_to_path(base),
|
|
'openstack', 'common', mod)
|
|
_make_dirs(dest)
|
|
sources = filter(lambda x: x[-3:] == '.py', os.listdir(mod_path))
|
|
for s in sources:
|
|
copy_pyfile(os.path.join(mod_path, s))
|
|
|
|
globs_to_copy = [
|
|
os.path.join('tools', mod, '*.sh'),
|
|
os.path.join('tools', mod + '*'),
|
|
os.path.join('etc', 'oslo', mod + '*.conf'),
|
|
os.path.join('contrib', mod + '*'),
|
|
]
|
|
|
|
for matches in [glob.glob(g) for g in globs_to_copy]:
|
|
for match in [x for x in matches if not os.path.isdir(x)]:
|
|
dest = os.path.join(dest_dir, match.replace('oslo', base))
|
|
print("Copying %s to %s" % (match, dest))
|
|
_copy_file(match, dest, base)
|
|
|
|
|
|
def _create_module_init(base, dest_dir, *sub_paths):
|
|
"""Create module __init__ files."""
|
|
init_path = _dest_path('openstack', base, dest_dir)
|
|
|
|
if sub_paths:
|
|
init_path = os.path.join(init_path, *sub_paths)
|
|
|
|
init_path = os.path.join(init_path, '__init__.py')
|
|
|
|
if not os.path.exists(init_path):
|
|
_make_dirs(init_path)
|
|
open(init_path, 'w').close()
|
|
|
|
|
|
def _find_import_modules(srcfile):
|
|
oslo_import_pattern = re.compile(r"\s*from\sopenstack\.common"
|
|
"(\simport\s|\.)(\w+)($|.+)")
|
|
with open(srcfile, 'r') as f:
|
|
for line in f:
|
|
result = oslo_import_pattern.match(line)
|
|
if result:
|
|
yield result.group(2)
|
|
|
|
|
|
def _build_dependency_tree():
|
|
dep_tree = {}
|
|
base_path = os.path.join('openstack', 'common')
|
|
for dirpath, _, filenames in os.walk(base_path):
|
|
for filename in [x for x in filenames if x.endswith('.py')]:
|
|
if dirpath == base_path:
|
|
mod_name = filename.split('.')[0]
|
|
else:
|
|
mod_name = dirpath.split(os.sep)[2]
|
|
if mod_name == '__init__':
|
|
continue
|
|
filepath = os.path.join(dirpath, filename)
|
|
dep_list = dep_tree.setdefault(mod_name, [])
|
|
dep_list.extend([x for x in _find_import_modules(filepath)
|
|
if x != mod_name and x not in dep_list])
|
|
return dep_tree
|
|
|
|
|
|
def _dfs_dependency_tree(dep_tree, mod_name, mod_list=[]):
|
|
mod_list.append(mod_name)
|
|
for mod in dep_tree.get(mod_name, []):
|
|
if mod not in mod_list:
|
|
mod_list = _dfs_dependency_tree(dep_tree, mod, mod_list)
|
|
return mod_list
|
|
|
|
|
|
def _complete_module_list(mod_list, nodeps):
|
|
if nodeps:
|
|
return mod_list
|
|
addons = []
|
|
dep_tree = _build_dependency_tree()
|
|
for mod in mod_list:
|
|
addons.extend([x for x in _dfs_dependency_tree(dep_tree, mod)
|
|
if x not in mod_list and x not in addons])
|
|
mod_list.extend(addons)
|
|
return mod_list
|
|
|
|
|
|
def main(argv):
|
|
conf = _parse_args(argv)
|
|
|
|
dest_dir = conf.dest_dir
|
|
if not dest_dir and conf.config_file:
|
|
dest_dir = os.path.dirname(conf.config_file[-1])
|
|
|
|
if not dest_dir or not os.path.isdir(dest_dir):
|
|
print("A valid destination dir is required", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if not conf.module and not conf.modules:
|
|
print("A list of modules to copy is required", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if not conf.base:
|
|
print("A destination base module is required", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
_create_module_init(conf.base, dest_dir)
|
|
_create_module_init(conf.base, dest_dir, 'common')
|
|
|
|
for mod in _complete_module_list(conf.module + conf.modules, conf.nodeps):
|
|
_copy_module(mod, conf.base, dest_dir)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv[1:])
|