Update openstack-common in prep for pulling in common.rpc

Change-Id: Ib3444d97967c807cb96175ce23d4b670a028e9a7
Signed-off-by: Steven Dake <sdake@redhat.com>
This commit is contained in:
Steven Dake 2012-07-13 15:30:03 -07:00
parent fba2ed3921
commit 5d5d8ba5dd
11 changed files with 366 additions and 96 deletions

View File

@ -1,14 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# 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.

View File

@ -42,8 +42,8 @@ Options can be strings, integers, floats, booleans, lists or 'multi strings'::
osapi_compute_extension_opt = cfg.MultiStrOpt('osapi_compute_extension', osapi_compute_extension_opt = cfg.MultiStrOpt('osapi_compute_extension',
default=DEFAULT_EXTENSIONS) default=DEFAULT_EXTENSIONS)
Option schemas are registered with with the config manager at runtime, but Option schemas are registered with the config manager at runtime, but before
before the option is referenced:: the option is referenced::
class ExtensionManager(object): class ExtensionManager(object):
@ -391,7 +391,7 @@ def _get_config_dirs(project=None):
fix_path('~'), fix_path('~'),
os.path.join('/etc', project) if project else None, os.path.join('/etc', project) if project else None,
'/etc' '/etc'
] ]
return filter(bool, cfg_dirs) return filter(bool, cfg_dirs)
@ -494,7 +494,8 @@ class Opt(object):
multi = False multi = False
def __init__(self, name, dest=None, short=None, default=None, def __init__(self, name, dest=None, short=None, default=None,
metavar=None, help=None, secret=False, required=False): metavar=None, help=None, secret=False, required=False,
deprecated_name=None):
"""Construct an Opt object. """Construct an Opt object.
The only required parameter is the option's name. However, it is The only required parameter is the option's name. However, it is
@ -508,6 +509,7 @@ class Opt(object):
:param help: an explanation of how the option is used :param help: an explanation of how the option is used
:param secret: true iff the value should be obfuscated in log output :param secret: true iff the value should be obfuscated in log output
:param required: true iff a value must be supplied for this option :param required: true iff a value must be supplied for this option
:param deprecated_name: deprecated name option. Acts like an alias
""" """
self.name = name self.name = name
if dest is None: if dest is None:
@ -520,6 +522,10 @@ class Opt(object):
self.help = help self.help = help
self.secret = secret self.secret = secret
self.required = required self.required = required
if deprecated_name is not None:
self.deprecated_name = deprecated_name.replace('-', '_')
else:
self.deprecated_name = None
def _get_from_config_parser(self, cparser, section): def _get_from_config_parser(self, cparser, section):
"""Retrieves the option value from a MultiConfigParser object. """Retrieves the option value from a MultiConfigParser object.
@ -531,7 +537,13 @@ class Opt(object):
:param cparser: a ConfigParser object :param cparser: a ConfigParser object
:param section: a section name :param section: a section name
""" """
return cparser.get(section, self.dest) return self._cparser_get_with_deprecated(cparser, section)
def _cparser_get_with_deprecated(self, cparser, section):
"""If cannot find option as dest try deprecated_name alias."""
if self.deprecated_name is not None:
return cparser.get(section, [self.dest, self.deprecated_name])
return cparser.get(section, [self.dest])
def _add_to_cli(self, parser, group=None): def _add_to_cli(self, parser, group=None):
"""Makes the option available in the command line interface. """Makes the option available in the command line interface.
@ -546,9 +558,11 @@ class Opt(object):
container = self._get_optparse_container(parser, group) container = self._get_optparse_container(parser, group)
kwargs = self._get_optparse_kwargs(group) kwargs = self._get_optparse_kwargs(group)
prefix = self._get_optparse_prefix('', group) prefix = self._get_optparse_prefix('', group)
self._add_to_optparse(container, self.name, self.short, kwargs, prefix) self._add_to_optparse(container, self.name, self.short, kwargs, prefix,
self.deprecated_name)
def _add_to_optparse(self, container, name, short, kwargs, prefix=''): def _add_to_optparse(self, container, name, short, kwargs, prefix='',
deprecated_name=None):
"""Add an option to an optparse parser or group. """Add an option to an optparse parser or group.
:param container: an optparse.OptionContainer object :param container: an optparse.OptionContainer object
@ -561,6 +575,8 @@ class Opt(object):
args = ['--' + prefix + name] args = ['--' + prefix + name]
if short: if short:
args += ['-' + short] args += ['-' + short]
if deprecated_name:
args += ['--' + prefix + deprecated_name]
for a in args: for a in args:
if container.has_option(a): if container.has_option(a):
raise DuplicateOptError(a) raise DuplicateOptError(a)
@ -591,11 +607,9 @@ class Opt(object):
dest = self.dest dest = self.dest
if group is not None: if group is not None:
dest = group.name + '_' + dest dest = group.name + '_' + dest
kwargs.update({ kwargs.update({'dest': dest,
'dest': dest, 'metavar': self.metavar,
'metavar': self.metavar, 'help': self.help, })
'help': self.help,
})
return kwargs return kwargs
def _get_optparse_prefix(self, prefix, group): def _get_optparse_prefix(self, prefix, group):
@ -645,7 +659,8 @@ class BoolOpt(Opt):
return value return value
return [convert_bool(v) for v in cparser.get(section, self.dest)] return [convert_bool(v) for v in
self._cparser_get_with_deprecated(cparser, section)]
def _add_to_cli(self, parser, group=None): def _add_to_cli(self, parser, group=None):
"""Extends the base class method to add the --nooptname option.""" """Extends the base class method to add the --nooptname option."""
@ -658,7 +673,8 @@ class BoolOpt(Opt):
kwargs = self._get_optparse_kwargs(group, action='store_false') kwargs = self._get_optparse_kwargs(group, action='store_false')
prefix = self._get_optparse_prefix('no', group) prefix = self._get_optparse_prefix('no', group)
kwargs["help"] = "The inverse of --" + self.name kwargs["help"] = "The inverse of --" + self.name
self._add_to_optparse(container, self.name, None, kwargs, prefix) self._add_to_optparse(container, self.name, None, kwargs, prefix,
self.deprecated_name)
def _get_optparse_kwargs(self, group, action='store_true', **kwargs): def _get_optparse_kwargs(self, group, action='store_true', **kwargs):
"""Extends the base optparse keyword dict for boolean options.""" """Extends the base optparse keyword dict for boolean options."""
@ -672,7 +688,8 @@ class IntOpt(Opt):
def _get_from_config_parser(self, cparser, section): def _get_from_config_parser(self, cparser, section):
"""Retrieve the opt value as a integer from ConfigParser.""" """Retrieve the opt value as a integer from ConfigParser."""
return [int(v) for v in cparser.get(section, self.dest)] return [int(v) for v in self._cparser_get_with_deprecated(cparser,
section)]
def _get_optparse_kwargs(self, group, **kwargs): def _get_optparse_kwargs(self, group, **kwargs):
"""Extends the base optparse keyword dict for integer options.""" """Extends the base optparse keyword dict for integer options."""
@ -686,7 +703,8 @@ class FloatOpt(Opt):
def _get_from_config_parser(self, cparser, section): def _get_from_config_parser(self, cparser, section):
"""Retrieve the opt value as a float from ConfigParser.""" """Retrieve the opt value as a float from ConfigParser."""
return [float(v) for v in cparser.get(section, self.dest)] return [float(v) for v in
self._cparser_get_with_deprecated(cparser, section)]
def _get_optparse_kwargs(self, group, **kwargs): def _get_optparse_kwargs(self, group, **kwargs):
"""Extends the base optparse keyword dict for float options.""" """Extends the base optparse keyword dict for float options."""
@ -703,7 +721,8 @@ class ListOpt(Opt):
def _get_from_config_parser(self, cparser, section): def _get_from_config_parser(self, cparser, section):
"""Retrieve the opt value as a list from ConfigParser.""" """Retrieve the opt value as a list from ConfigParser."""
return [v.split(',') for v in cparser.get(section, self.dest)] return [v.split(',') for v in
self._cparser_get_with_deprecated(cparser, section)]
def _get_optparse_kwargs(self, group, **kwargs): def _get_optparse_kwargs(self, group, **kwargs):
"""Extends the base optparse keyword dict for list options.""" """Extends the base optparse keyword dict for list options."""
@ -732,6 +751,13 @@ class MultiStrOpt(Opt):
return super(MultiStrOpt, return super(MultiStrOpt,
self)._get_optparse_kwargs(group, action='append') self)._get_optparse_kwargs(group, action='append')
def _cparser_get_with_deprecated(self, cparser, section):
"""If cannot find option as dest try deprecated_name alias."""
if self.deprecated_name is not None:
return cparser.get(section, [self.dest, self.deprecated_name],
multi=True)
return cparser.get(section, [self.dest], multi=True)
class OptGroup(object): class OptGroup(object):
@ -846,25 +872,38 @@ class ConfigParser(iniparser.BaseParser):
class MultiConfigParser(object): class MultiConfigParser(object):
def __init__(self): def __init__(self):
self.sections = {} self.parsed = []
def read(self, config_files): def read(self, config_files):
read_ok = [] read_ok = []
for filename in config_files: for filename in config_files:
parser = ConfigParser(filename, self.sections) sections = {}
parser = ConfigParser(filename, sections)
try: try:
parser.parse() parser.parse()
except IOError: except IOError:
continue continue
self.parsed.insert(0, sections)
read_ok.append(filename) read_ok.append(filename)
return read_ok return read_ok
def get(self, section, name): def get(self, section, names, multi=False):
return self.sections[section][name] rvalue = []
for sections in self.parsed:
if section not in sections:
continue
for name in names:
if name in sections[section]:
if multi:
rvalue = sections[section][name] + rvalue
else:
return sections[section][name]
if multi and rvalue != []:
return rvalue
raise KeyError
class ConfigOpts(collections.Mapping): class ConfigOpts(collections.Mapping):
@ -905,13 +944,13 @@ class ConfigOpts(collections.Mapping):
self._oparser.disable_interspersed_args() self._oparser.disable_interspersed_args()
self._config_opts = [ self._config_opts = [
MultiStrOpt('config-file', MultiStrOpt('config-file',
default=default_config_files, default=default_config_files,
metavar='PATH', metavar='PATH',
help='Path to a config file to use. Multiple config ' help='Path to a config file to use. Multiple config '
'files can be specified, with values in later ' 'files can be specified, with values in later '
'files taking precedence. The default files ' 'files taking precedence. The default files '
' used are: %s' % (default_config_files, )), ' used are: %s' % (default_config_files, )),
StrOpt('config-dir', StrOpt('config-dir',
metavar='DIR', metavar='DIR',
help='Path to a config directory to pull *.conf ' help='Path to a config directory to pull *.conf '
@ -921,7 +960,7 @@ class ConfigOpts(collections.Mapping):
'the file(s), if any, specified via --config-file, ' 'the file(s), if any, specified via --config-file, '
'hence over-ridden options in the directory take ' 'hence over-ridden options in the directory take '
'precedence.'), 'precedence.'),
] ]
self.register_cli_opts(self._config_opts) self.register_cli_opts(self._config_opts)
self.project = project self.project = project
@ -1323,7 +1362,7 @@ class ConfigOpts(collections.Mapping):
def _substitute(self, value): def _substitute(self, value):
"""Perform string template substitution. """Perform string template substitution.
Substititue any template variables (e.g. $foo, ${bar}) in the supplied Substitute any template variables (e.g. $foo, ${bar}) in the supplied
string value(s) with opt values. string value(s) with opt values.
:param value: the string value, or list of string values :param value: the string value, or list of string values
@ -1411,8 +1450,7 @@ class ConfigOpts(collections.Mapping):
default, opt, override = [info[k] for k in sorted(info.keys())] default, opt, override = [info[k] for k in sorted(info.keys())]
if opt.required: if opt.required:
if (default is not None or if (default is not None or override is not None):
override is not None):
continue continue
if self._get(opt.name, group) is None: if self._get(opt.name, group) is None:
@ -1516,7 +1554,7 @@ class CommonConfigOpts(ConfigOpts):
short='v', short='v',
default=False, default=False,
help='Print more verbose output'), help='Print more verbose output'),
] ]
logging_cli_opts = [ logging_cli_opts = [
StrOpt('log-config', StrOpt('log-config',
@ -1550,7 +1588,7 @@ class CommonConfigOpts(ConfigOpts):
StrOpt('syslog-log-facility', StrOpt('syslog-log-facility',
default='LOG_USER', default='LOG_USER',
help='syslog facility to receive log lines') help='syslog facility to receive log lines')
] ]
def __init__(self): def __init__(self):
super(CommonConfigOpts, self).__init__() super(CommonConfigOpts, self).__init__()

View File

@ -19,6 +19,7 @@
Exceptions common to OpenStack projects Exceptions common to OpenStack projects
""" """
import itertools
import logging import logging

View File

@ -0,0 +1,33 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Red Hat, Inc.
# All Rights Reserved.
#
# 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.
"""
gettext for openstack-common modules.
Usual usage in an openstack.common module:
from openstack.common.gettextutils import _
"""
import gettext
t = gettext.translation('openstack-common', 'locale', fallback=True)
def _(msg):
return t.ugettext(msg)

View File

@ -20,6 +20,7 @@ Import related utilities and helper functions.
""" """
import sys import sys
import traceback
def import_class(import_str): def import_class(import_str):
@ -30,7 +31,8 @@ def import_class(import_str):
return getattr(sys.modules[mod_str], class_str) return getattr(sys.modules[mod_str], class_str)
except (ImportError, ValueError, AttributeError), exc: except (ImportError, ValueError, AttributeError), exc:
raise ImportError('Class %s cannot be found (%s)' % raise ImportError('Class %s cannot be found (%s)' %
(class_str, str(exc))) (class_str,
traceback.format_exception(*sys.exc_info())))
def import_object(import_str, *args, **kwargs): def import_object(import_str, *args, **kwargs):
@ -38,6 +40,19 @@ def import_object(import_str, *args, **kwargs):
return import_class(import_str)(*args, **kwargs) return import_class(import_str)(*args, **kwargs)
def import_object_ns(name_space, import_str, *args, **kwargs):
"""
Import a class and return an instance of it, first by trying
to find the class in a default namespace, then failing back to
a full path if not found in the default namespace.
"""
import_value = "%s.%s" % (name_space, import_str)
try:
return import_class(import_value)(*args, **kwargs)
except ImportError:
return import_class(import_str)(*args, **kwargs)
def import_module(import_str): def import_module(import_str):
"""Import a module.""" """Import a module."""
__import__(import_str) __import__(import_str)

View File

@ -52,7 +52,10 @@ class BaseParser(object):
else: else:
key, value = line[:colon], line[colon + 1:] key, value = line[:colon], line[colon + 1:]
return key.strip(), [value.strip()] value = value.strip()
if value[0] == value[-1] and value[0] == "\"" or value[0] == "'":
value = value[1:-1]
return key.strip(), [value]
def parse(self, lineiter): def parse(self, lineiter):
key = None key = None

View File

@ -19,9 +19,11 @@
Utilities with minimum-depends for use in setup.py Utilities with minimum-depends for use in setup.py
""" """
import datetime
import os import os
import re import re
import subprocess import subprocess
import sys
from setuptools.command import sdist from setuptools.command import sdist
@ -76,6 +78,10 @@ def parse_requirements(requirements_files=['requirements.txt',
# -f lines are for index locations, and don't get used here # -f lines are for index locations, and don't get used here
elif re.match(r'\s*-f\s+', line): elif re.match(r'\s*-f\s+', line):
pass pass
# argparse is part of the standard library starting with 2.7
# adding it to the requirements list screws distro installs
elif line == 'argparse' and sys.version_info >= (2, 7):
pass
else: else:
requirements.append(line) requirements.append(line)
@ -113,38 +119,75 @@ def write_requirements():
def _run_shell_command(cmd): def _run_shell_command(cmd):
output = subprocess.Popen(["/bin/sh", "-c", cmd], output = subprocess.Popen(["/bin/sh", "-c", cmd],
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
return output.communicate()[0].strip() out = output.communicate()
if len(out) == 0:
return None
if len(out[0].strip()) == 0:
return None
return out[0].strip()
def write_vcsversion(location): def _get_git_next_version_suffix(branch_name):
"""Produce a vcsversion dict that mimics the old one produced by bzr. datestamp = datetime.datetime.now().strftime('%Y%m%d')
""" if branch_name == 'milestone-proposed':
if os.path.isdir('.git'): revno_prefix = "r"
branch_nick_cmd = 'git branch | grep -Ei "\* (.*)" | cut -f2 -d" "' else:
branch_nick = _run_shell_command(branch_nick_cmd) revno_prefix = ""
revid_cmd = "git rev-parse HEAD" _run_shell_command("git fetch origin +refs/meta/*:refs/remotes/meta/*")
revid = _run_shell_command(revid_cmd).split()[0] milestone_cmd = "git show meta/openstack/release:%s" % branch_name
revno_cmd = "git log --oneline | wc -l" milestonever = _run_shell_command(milestone_cmd)
revno = _run_shell_command(revno_cmd) if not milestonever:
with open(location, 'w') as version_file: milestonever = ""
version_file.write(""" post_version = _get_git_post_version()
# This file is automatically generated by setup.py, So don't edit it. :) # post version should look like:
version_info = { # 0.1.1.4.gcc9e28a
'branch_nick': '%s', # where the bit after the last . is the short sha, and the bit between
'revision_id': '%s', # the last and second to last is the revno count
'revno': %s (revno, sha) = post_version.split(".")[-2:]
} first_half = "%(milestonever)s~%(datestamp)s" % locals()
""" % (branch_nick, revid, revno)) second_half = "%(revno_prefix)s%(revno)s.%(sha)s" % locals()
return ".".join((first_half, second_half))
def _get_git_current_tag():
return _run_shell_command("git tag --contains HEAD")
def _get_git_tag_info():
return _run_shell_command("git describe --tags")
def _get_git_post_version():
current_tag = _get_git_current_tag()
if current_tag is not None:
return current_tag
else:
tag_info = _get_git_tag_info()
if tag_info is None:
base_version = "0.0"
cmd = "git --no-pager log --oneline"
out = _run_shell_command(cmd)
revno = len(out.split("\n"))
sha = _run_shell_command("git describe --always")
else:
tag_infos = tag_info.split("-")
base_version = "-".join(tag_infos[:-2])
(revno, sha) = tag_infos[-2:]
return "%s.%s.%s" % (base_version, revno, sha)
def write_git_changelog(): def write_git_changelog():
"""Write a changelog based on the git changelog.""" """Write a changelog based on the git changelog."""
if os.path.isdir('.git'): new_changelog = 'ChangeLog'
git_log_cmd = 'git log --stat' if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'):
changelog = _run_shell_command(git_log_cmd) if os.path.isdir('.git'):
mailmap = parse_mailmap() git_log_cmd = 'git log --stat'
with open("ChangeLog", "w") as changelog_file: changelog = _run_shell_command(git_log_cmd)
changelog_file.write(canonicalize_emails(changelog, mailmap)) mailmap = parse_mailmap()
with open(new_changelog, "w") as changelog_file:
changelog_file.write(canonicalize_emails(changelog, mailmap))
else:
open(new_changelog, 'w').close()
def generate_authors(): def generate_authors():
@ -152,17 +195,49 @@ def generate_authors():
jenkins_email = 'jenkins@review.openstack.org' jenkins_email = 'jenkins@review.openstack.org'
old_authors = 'AUTHORS.in' old_authors = 'AUTHORS.in'
new_authors = 'AUTHORS' new_authors = 'AUTHORS'
if os.path.isdir('.git'): if not os.getenv('SKIP_GENERATE_AUTHORS'):
# don't include jenkins email address in AUTHORS file if os.path.isdir('.git'):
git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | " # don't include jenkins email address in AUTHORS file
"grep -v " + jenkins_email) git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | "
changelog = _run_shell_command(git_log_cmd) "grep -v " + jenkins_email)
mailmap = parse_mailmap() changelog = _run_shell_command(git_log_cmd)
with open(new_authors, 'w') as new_authors_fh: mailmap = parse_mailmap()
new_authors_fh.write(canonicalize_emails(changelog, mailmap)) with open(new_authors, 'w') as new_authors_fh:
if os.path.exists(old_authors): new_authors_fh.write(canonicalize_emails(changelog, mailmap))
with open(old_authors, "r") as old_authors_fh: if os.path.exists(old_authors):
new_authors_fh.write('\n' + old_authors_fh.read()) with open(old_authors, "r") as old_authors_fh:
new_authors_fh.write('\n' + old_authors_fh.read())
else:
open(new_authors, 'w').close()
_rst_template = """%(heading)s
%(underline)s
.. automodule:: %(module)s
:members:
:undoc-members:
:show-inheritance:
"""
def read_versioninfo(project):
"""Read the versioninfo file. If it doesn't exist, we're in a github
zipball, and there's really no way to know what version we really
are, but that should be ok, because the utility of that should be
just about nil if this code path is in use in the first place."""
versioninfo_path = os.path.join(project, 'versioninfo')
if os.path.exists(versioninfo_path):
with open(versioninfo_path, 'r') as vinfo:
version = vinfo.read().strip()
else:
version = "0.0.0"
return version
def write_versioninfo(project, version):
"""Write a simple file containing the version of the package."""
open(os.path.join(project, 'versioninfo'), 'w').write("%s\n" % version)
def get_cmdclass(): def get_cmdclass():
@ -170,6 +245,12 @@ def get_cmdclass():
cmdclass = dict() cmdclass = dict()
def _find_modules(arg, dirname, files):
for filename in files:
if filename.endswith('.py') and filename != '__init__.py':
arg["%s.%s" % (dirname.replace('/', '.'),
filename[:-3])] = True
class LocalSDist(sdist.sdist): class LocalSDist(sdist.sdist):
"""Builds the ChangeLog and Authors files from VC first.""" """Builds the ChangeLog and Authors files from VC first."""
@ -188,13 +269,91 @@ def get_cmdclass():
from sphinx.setup_command import BuildDoc from sphinx.setup_command import BuildDoc
class LocalBuildDoc(BuildDoc): class LocalBuildDoc(BuildDoc):
def generate_autoindex(self):
print "**Autodocumenting from %s" % os.path.abspath(os.curdir)
modules = {}
option_dict = self.distribution.get_option_dict('build_sphinx')
source_dir = os.path.join(option_dict['source_dir'][1], 'api')
if not os.path.exists(source_dir):
os.makedirs(source_dir)
for pkg in self.distribution.packages:
if '.' not in pkg:
os.path.walk(pkg, _find_modules, modules)
module_list = modules.keys()
module_list.sort()
autoindex_filename = os.path.join(source_dir, 'autoindex.rst')
with open(autoindex_filename, 'w') as autoindex:
autoindex.write(""".. toctree::
:maxdepth: 1
""")
for module in module_list:
output_filename = os.path.join(source_dir,
"%s.rst" % module)
heading = "The :mod:`%s` Module" % module
underline = "=" * len(heading)
values = dict(module=module, heading=heading,
underline=underline)
print "Generating %s" % output_filename
with open(output_filename, 'w') as output_file:
output_file.write(_rst_template % values)
autoindex.write(" %s.rst\n" % module)
def run(self): def run(self):
if not os.getenv('SPHINX_DEBUG'):
self.generate_autoindex()
for builder in ['html', 'man']: for builder in ['html', 'man']:
self.builder = builder self.builder = builder
self.finalize_options() self.finalize_options()
self.project = self.distribution.get_name()
self.version = self.distribution.get_version()
self.release = self.distribution.get_version()
BuildDoc.run(self) BuildDoc.run(self)
cmdclass['build_sphinx'] = LocalBuildDoc cmdclass['build_sphinx'] = LocalBuildDoc
except ImportError: except ImportError:
pass pass
return cmdclass return cmdclass
def get_git_branchname():
for branch in _run_shell_command("git branch --color=never").split("\n"):
if branch.startswith('*'):
_branch_name = branch.split()[1].strip()
if _branch_name == "(no":
_branch_name = "no-branch"
return _branch_name
def get_pre_version(projectname, base_version):
"""Return a version which is leading up to a version that will
be released in the future."""
if os.path.isdir('.git'):
current_tag = _get_git_current_tag()
if current_tag is not None:
version = current_tag
else:
branch_name = os.getenv('BRANCHNAME',
os.getenv('GERRIT_REFNAME',
get_git_branchname()))
version_suffix = _get_git_next_version_suffix(branch_name)
version = "%s~%s" % (base_version, version_suffix)
write_versioninfo(projectname, version)
return version
else:
version = read_versioninfo(projectname)
return version
def get_post_version(projectname):
"""Return a version which is equal to the tag that's on the current
revision if there is one, or tag plus number of additional revisions
if the current revision has no tag."""
if os.path.isdir('.git'):
version = _get_git_post_version()
write_versioninfo(projectname, version)
return version
return read_versioninfo(projectname)

View File

@ -19,12 +19,15 @@
Time related utilities and helper functions. Time related utilities and helper functions.
""" """
import calendar
import datetime import datetime
import time
import iso8601 import iso8601
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S" TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
def isotime(at=None): def isotime(at=None):
@ -47,12 +50,34 @@ def parse_isotime(timestr):
raise ValueError(e.message) raise ValueError(e.message)
def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
"""Returns formatted utcnow."""
if not at:
at = utcnow()
return at.strftime(fmt)
def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
"""Turn a formatted time back into a datetime."""
return datetime.datetime.strptime(timestr, fmt)
def normalize_time(timestamp): def normalize_time(timestamp):
"""Normalize time in arbitrary timezone to UTC""" """Normalize time in arbitrary timezone to UTC"""
offset = timestamp.utcoffset() offset = timestamp.utcoffset()
return timestamp.replace(tzinfo=None) - offset if offset else timestamp return timestamp.replace(tzinfo=None) - offset if offset else timestamp
def is_older_than(before, seconds):
"""Return True if before is older than seconds."""
return utcnow() - before > datetime.timedelta(seconds=seconds)
def utcnow_ts():
"""Timestamp version of our utcnow function."""
return calendar.timegm(utcnow().timetuple())
def utcnow(): def utcnow():
"""Overridable version of utils.utcnow.""" """Overridable version of utils.utcnow."""
if utcnow.override_time: if utcnow.override_time:
@ -68,6 +93,17 @@ def set_time_override(override_time=datetime.datetime.utcnow()):
utcnow.override_time = override_time utcnow.override_time = override_time
def advance_time_delta(timedelta):
"""Advance overriden time using a datetime.timedelta."""
assert(not utcnow.override_time is None)
utcnow.override_time += timedelta
def advance_time_seconds(seconds):
"""Advance overriden time by seconds."""
advance_time_delta(datetime.timedelta(0, seconds))
def clear_time_override(): def clear_time_override():
"""Remove the overridden time.""" """Remove the overridden time."""
utcnow.override_time = None utcnow.override_time = None

View File

@ -28,6 +28,7 @@ from eventlet import greenthread
from eventlet.green import subprocess from eventlet.green import subprocess
from heat.openstack.common import exception from heat.openstack.common import exception
from heat.openstack.common.gettextutils import _
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -118,13 +119,13 @@ def execute(*cmd, **kwargs):
LOG.debug(_('Result was %s') % _returncode) LOG.debug(_('Result was %s') % _returncode)
if (isinstance(check_exit_code, int) and if (isinstance(check_exit_code, int) and
not isinstance(check_exit_code, bool) and not isinstance(check_exit_code, bool) and
_returncode != check_exit_code): _returncode != check_exit_code):
(stdout, stderr) = result (stdout, stderr) = result
raise exception.ProcessExecutionError( raise exception.ProcessExecutionError(
exit_code=_returncode, exit_code=_returncode,
stdout=stdout, stdout=stdout,
stderr=stderr, stderr=stderr,
cmd=' '.join(cmd)) cmd=' '.join(cmd))
return result return result
except exception.ProcessExecutionError: except exception.ProcessExecutionError:
if not attempts: if not attempts:

View File

@ -1,7 +1,7 @@
[DEFAULT] [DEFAULT]
# The list of modules to copy from openstack-common # The list of modules to copy from openstack-common
modules=cfg,local,iniparser,utils,exception,timeutils,importutils,setup modules=gettextutils,cfg,local,iniparser,utils,exception,timeutils,importutils,setup
# The base module to hold the copy of openstack.common # The base module to hold the copy of openstack.common
base=heat base=heat

View File

@ -21,8 +21,6 @@ import setuptools
from heat.openstack.common import setup from heat.openstack.common import setup
setup.write_vcsversion('heat/vcsversion.py')
# import this after write_vcsversion because version imports vcsversion # import this after write_vcsversion because version imports vcsversion
from heat import version from heat import version