low hanging fruit oslo-incubator sync
Sync modules from oslo-incubator without changing files outside of
directory 'openstack/common'.
Synced Modules:
Patch logs were generated by 'git log --abbrev-commit --pretty=oneline
{file}'
config:
0e98afd Re-raise exception of unloadable library
1a6dfb9 Sanitize FQDN in config generator
e839886 Config generator fails with lazy messages
763eedf Fix DictOpt support in config sample generator
context:
c0d357b Add model_query() to db.sqlalchemy.utils module
13eb01c Adding domain to context and log
12bcdb7 Remove vim header
bdabd51 Remove uuidutils imports in oslo modules
44e8222 Revert "Removes generate_uuid from uuidutils"
571a78a Removes generate_uuid from uuidutils
36859e5 Adding instance_uuid to context and log
b21fc56 Fix bad default for show_deleted
eventlet_backdoor:
fcf517d Update oslo log messages with translation domains
ad17a69 Fix filter() usage due to python 3 compability
8b2b0b7 Use hacking import_exceptions for gettextutils._
12bcdb7 Remove vim header
1dcc747 Fix stylistic problems with help text
excutils:
33a2cee save_and_reraise_exception: make logging respect the reraise parameter
fcf517d Update oslo log messages with translation domains
8b2b0b7 Use hacking import_exceptions for gettextutils._
6d0a6c3 Correct invalid docstrings
fileutils:
fcf517d Update oslo log messages with translation domains
e71cd1a Merge "Trivial: Make vertical white space after license header
consistent"
gettextutils:
fd33d1e Fix gettextutil.Message handling of deep copy failures
047b2e4 Change lazy translation to retain complete dict
6d55e26 Add support for translating log levels separately
importutils:
885828a Deleted duplicated method in cliutils
jsonutils:
e3a1d9c Use six.moves.xmlrpc_client instead of xmlrpclib
3a31bba Python3 support for xmlrpclib
log:
da6d713 Revert setting oslo-incubator logs to INFO
0d18381 Set default log levels for oslo.messaging and oslo-incubator
346884d Add default user_identity to logging record
3570f44 Merge "Remove None for dict.get()"
bf6d71d Merge "Rename Openstack to OpenStack"
86707cd Remove None for dict.get()
61ff7a6 Rename Openstack to OpenStack
5b5068e Merge "Fix deprecated messages sent multiple times"
dda47c9 Use ContextFormatter for imparting context info
52b6446 Fix deprecated messages sent multiple times
d6e1ba7 Merge "default connectionpool to WARN log level"
7f4ad99 Merge "Small edits on help strings"
8c3046b Merge "Backport 'ident' from python 3.3 for Oslo's
SysLogHandler"
1978114 default connectionpool to WARN log level
loopingcall:
fcf517d Update oslo log messages with translation domains
8b2b0b7 Use hacking import_exceptions for gettextutils._
12bcdb7 Remove vim header
memorycache:
12bcdb7 Remove vim header
5e765b2 python3: Fix traceback while running tests
network_utils:
897aa7c urlsplit issues with IPv6 addresses in python26
35dc1d7 py3kcompat: remove
fb738d4 Merge "Use py3kcompat urlutils functions instead of urlparse"
12bcdb7 Remove vim header
4c22556 Use py3kcompat urlutils functions instead of urlparse
processutils:
fcf517d Update oslo log messages with translation domains
service:
e2634a7 Add missing _LI for LOG.info in service module
0150ad7 Merge "Reap child processes gracefully if greenlet thread gets killed"
53e1214 notify calling process we are ready to serve
fcf517d Update oslo log messages with translation domains
1e70078 Revert "service: replace eventlet event by threading"
0644073 Simplify launch method
6b4d255 Merge "service: replace eventlet event by threading"
a4f145e Merge "Allow configurable ProcessLauncher liveness check"
8b2b0b7 Use hacking import_exceptions for gettextutils._
systemd:
53e1214 notify calling process we are ready to serve
threadgroup:
5f8ace0 Merge "threadgroup: use threading rather than greenthread"
2d06d6c Simple typo correction
4d18b57 threadgroup: use threading rather than greenthread
25ff65e Make wait & stop methods work on all threads
12bcdb7 Remove vim header
9d3c34b Add a link method to Thread
timeutils:
d815087 Merge "Fix spelling errors in comments"
63d5ba6 Merge "Fix typo in parameter documentation (timeutils)"
71208fe Fix spelling errors in comments
7013471 Fix typo in parameter documentation (timeutils)
versionutils:
8b2b0b7 Use hacking import_exceptions for gettextutils._
a5ae087 fixed typos
45658e2 Fix violations of H302:import only modules
db29de0 Merge "Adds decorator to deprecate functions and methods"
37ea814 Adds decorator to deprecate functions and methods
12bcdb7 Remove vim header
xmlutils:
12bcdb7 Remove vim header
Co-Authored-By: ChangBo Guo(gcb) <eric.guo@easystack.cn>
Change-Id: Ic8e47cb037683338f972df880b51921c1f9f98d8
This commit is contained in:
@@ -1,2 +1,17 @@
|
|||||||
|
#
|
||||||
|
# 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 six
|
import six
|
||||||
|
|
||||||
|
|
||||||
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
|
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
|||||||
WORDWRAP_WIDTH = 60
|
WORDWRAP_WIDTH = 60
|
||||||
|
|
||||||
|
|
||||||
|
def raise_extension_exception(extmanager, ep, err):
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def generate(argv):
|
def generate(argv):
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='generate sample configuration file',
|
description='generate sample configuration file',
|
||||||
@@ -107,6 +111,7 @@ def generate(argv):
|
|||||||
'oslo.config.opts',
|
'oslo.config.opts',
|
||||||
names=list(set(parsed_args.libraries)),
|
names=list(set(parsed_args.libraries)),
|
||||||
invoke_on_load=False,
|
invoke_on_load=False,
|
||||||
|
on_load_failure_callback=raise_extension_exception
|
||||||
)
|
)
|
||||||
for ext in loader:
|
for ext in loader:
|
||||||
for group, opts in ext.plugin():
|
for group, opts in ext.plugin():
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2011 OpenStack Foundation.
|
# Copyright 2011 OpenStack Foundation.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@@ -23,12 +21,11 @@ context or provide additional information in their specific WSGI pipeline.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
import uuid
|
||||||
from nova.openstack.common import uuidutils
|
|
||||||
|
|
||||||
|
|
||||||
def generate_request_id():
|
def generate_request_id():
|
||||||
return 'req-%s' % uuidutils.generate_uuid()
|
return 'req-%s' % str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
class RequestContext(object):
|
class RequestContext(object):
|
||||||
@@ -39,26 +36,46 @@ class RequestContext(object):
|
|||||||
accesses the system, as well as additional request information.
|
accesses the system, as well as additional request information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, auth_token=None, user=None, tenant=None, is_admin=False,
|
user_idt_format = '{user} {tenant} {domain} {user_domain} {p_domain}'
|
||||||
read_only=False, show_deleted=False, request_id=None):
|
|
||||||
|
def __init__(self, auth_token=None, user=None, tenant=None, domain=None,
|
||||||
|
user_domain=None, project_domain=None, is_admin=False,
|
||||||
|
read_only=False, show_deleted=False, request_id=None,
|
||||||
|
instance_uuid=None):
|
||||||
self.auth_token = auth_token
|
self.auth_token = auth_token
|
||||||
self.user = user
|
self.user = user
|
||||||
self.tenant = tenant
|
self.tenant = tenant
|
||||||
|
self.domain = domain
|
||||||
|
self.user_domain = user_domain
|
||||||
|
self.project_domain = project_domain
|
||||||
self.is_admin = is_admin
|
self.is_admin = is_admin
|
||||||
self.read_only = read_only
|
self.read_only = read_only
|
||||||
self.show_deleted = show_deleted
|
self.show_deleted = show_deleted
|
||||||
|
self.instance_uuid = instance_uuid
|
||||||
if not request_id:
|
if not request_id:
|
||||||
request_id = generate_request_id()
|
request_id = generate_request_id()
|
||||||
self.request_id = request_id
|
self.request_id = request_id
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
|
user_idt = (
|
||||||
|
self.user_idt_format.format(user=self.user or '-',
|
||||||
|
tenant=self.tenant or '-',
|
||||||
|
domain=self.domain or '-',
|
||||||
|
user_domain=self.user_domain or '-',
|
||||||
|
p_domain=self.project_domain or '-'))
|
||||||
|
|
||||||
return {'user': self.user,
|
return {'user': self.user,
|
||||||
'tenant': self.tenant,
|
'tenant': self.tenant,
|
||||||
|
'domain': self.domain,
|
||||||
|
'user_domain': self.user_domain,
|
||||||
|
'project_domain': self.project_domain,
|
||||||
'is_admin': self.is_admin,
|
'is_admin': self.is_admin,
|
||||||
'read_only': self.read_only,
|
'read_only': self.read_only,
|
||||||
'show_deleted': self.show_deleted,
|
'show_deleted': self.show_deleted,
|
||||||
'auth_token': self.auth_token,
|
'auth_token': self.auth_token,
|
||||||
'request_id': self.request_id}
|
'request_id': self.request_id,
|
||||||
|
'instance_uuid': self.instance_uuid,
|
||||||
|
'user_identity': user_idt}
|
||||||
|
|
||||||
|
|
||||||
def get_admin_context(show_deleted=False):
|
def get_admin_context(show_deleted=False):
|
||||||
@@ -81,3 +98,14 @@ def get_context_from_function_and_args(function, args, kwargs):
|
|||||||
return arg
|
return arg
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_user_context(context):
|
||||||
|
"""Indicates if the request context is a normal user."""
|
||||||
|
if not context:
|
||||||
|
return False
|
||||||
|
if context.is_admin:
|
||||||
|
return False
|
||||||
|
if not context.user_id or not context.project_id:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright (c) 2012 OpenStack Foundation.
|
# Copyright (c) 2012 OpenStack Foundation.
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
@@ -31,20 +29,20 @@ import eventlet.backdoor
|
|||||||
import greenlet
|
import greenlet
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common.gettextutils import _LI
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
help_for_backdoor_port = 'Acceptable ' + \
|
help_for_backdoor_port = (
|
||||||
'values are 0, <port> and <start>:<end>, where 0 results in ' + \
|
"Acceptable values are 0, <port>, and <start>:<end>, where 0 results "
|
||||||
'listening on a random tcp port number, <port> results in ' + \
|
"in listening on a random tcp port number; <port> results in listening "
|
||||||
'listening on the specified port number and not enabling backdoor' + \
|
"on the specified port number (and not enabling backdoor if that port "
|
||||||
'if it is in use and <start>:<end> results in listening on the ' + \
|
"is in use); and <start>:<end> results in listening on the smallest "
|
||||||
'smallest unused port number within the specified range of port ' + \
|
"unused port number within the specified range of port numbers. The "
|
||||||
'numbers. The chosen port is displayed in the service\'s log file.'
|
"chosen port is displayed in the service's log file.")
|
||||||
eventlet_backdoor_opts = [
|
eventlet_backdoor_opts = [
|
||||||
cfg.StrOpt('backdoor_port',
|
cfg.StrOpt('backdoor_port',
|
||||||
default=None,
|
default=None,
|
||||||
help='Enable eventlet backdoor. %s' % help_for_backdoor_port)
|
help="Enable eventlet backdoor. %s" % help_for_backdoor_port)
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@@ -66,7 +64,7 @@ def _dont_use_this():
|
|||||||
|
|
||||||
|
|
||||||
def _find_objects(t):
|
def _find_objects(t):
|
||||||
return filter(lambda o: isinstance(o, t), gc.get_objects())
|
return [o for o in gc.get_objects() if isinstance(o, t)]
|
||||||
|
|
||||||
|
|
||||||
def _print_greenthreads():
|
def _print_greenthreads():
|
||||||
@@ -139,8 +137,10 @@ def initialize_if_enabled():
|
|||||||
# In the case of backdoor port being zero, a port number is assigned by
|
# In the case of backdoor port being zero, a port number is assigned by
|
||||||
# listen(). In any case, pull the port number out here.
|
# listen(). In any case, pull the port number out here.
|
||||||
port = sock.getsockname()[1]
|
port = sock.getsockname()[1]
|
||||||
LOG.info(_('Eventlet backdoor listening on %(port)s for process %(pid)d') %
|
LOG.info(
|
||||||
{'port': port, 'pid': os.getpid()})
|
_LI('Eventlet backdoor listening on %(port)s for process %(pid)d') %
|
||||||
|
{'port': port, 'pid': os.getpid()}
|
||||||
|
)
|
||||||
eventlet.spawn_n(eventlet.backdoor.backdoor_server, sock,
|
eventlet.spawn_n(eventlet.backdoor.backdoor_server, sock,
|
||||||
locals=backdoor_locals)
|
locals=backdoor_locals)
|
||||||
return port
|
return port
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import traceback
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _ # noqa
|
from nova.openstack.common.gettextutils import _LE
|
||||||
|
|
||||||
|
|
||||||
class save_and_reraise_exception(object):
|
class save_and_reraise_exception(object):
|
||||||
@@ -42,16 +42,29 @@ class save_and_reraise_exception(object):
|
|||||||
|
|
||||||
In some cases the caller may not want to re-raise the exception, and
|
In some cases the caller may not want to re-raise the exception, and
|
||||||
for those circumstances this context provides a reraise flag that
|
for those circumstances this context provides a reraise flag that
|
||||||
can be used to suppress the exception. For example:
|
can be used to suppress the exception. For example::
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
with save_and_reraise_exception() as ctxt:
|
with save_and_reraise_exception() as ctxt:
|
||||||
decide_if_need_reraise()
|
decide_if_need_reraise()
|
||||||
if not should_be_reraised:
|
if not should_be_reraised:
|
||||||
ctxt.reraise = False
|
ctxt.reraise = False
|
||||||
|
|
||||||
|
If another exception occurs and reraise flag is False,
|
||||||
|
the saved exception will not be logged.
|
||||||
|
|
||||||
|
If the caller wants to raise new exception during exception handling
|
||||||
|
he/she sets reraise to False initially with an ability to set it back to
|
||||||
|
True if needed::
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
with save_and_reraise_exception(reraise=False) as ctxt:
|
||||||
|
[if statements to determine whether to raise a new exception]
|
||||||
|
# Not raising a new exception, so reraise
|
||||||
|
ctxt.reraise = True
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, reraise=True):
|
||||||
self.reraise = True
|
self.reraise = reraise
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.type_, self.value, self.tb, = sys.exc_info()
|
self.type_, self.value, self.tb, = sys.exc_info()
|
||||||
@@ -59,10 +72,11 @@ class save_and_reraise_exception(object):
|
|||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
if exc_type is not None:
|
if exc_type is not None:
|
||||||
logging.error(_('Original exception being dropped: %s'),
|
if self.reraise:
|
||||||
traceback.format_exception(self.type_,
|
logging.error(_LE('Original exception being dropped: %s'),
|
||||||
self.value,
|
traceback.format_exception(self.type_,
|
||||||
self.tb))
|
self.value,
|
||||||
|
self.tb))
|
||||||
return False
|
return False
|
||||||
if self.reraise:
|
if self.reraise:
|
||||||
six.reraise(self.type_, self.value, self.tb)
|
six.reraise(self.type_, self.value, self.tb)
|
||||||
@@ -88,8 +102,8 @@ def forever_retry_uncaught_exceptions(infunc):
|
|||||||
if (cur_time - last_log_time > 60 or
|
if (cur_time - last_log_time > 60 or
|
||||||
this_exc_message != last_exc_message):
|
this_exc_message != last_exc_message):
|
||||||
logging.exception(
|
logging.exception(
|
||||||
_('Unexpected exception occurred %d time(s)... '
|
_LE('Unexpected exception occurred %d time(s)... '
|
||||||
'retrying.') % exc_count)
|
'retrying.') % exc_count)
|
||||||
last_log_time = cur_time
|
last_log_time = cur_time
|
||||||
last_exc_message = this_exc_message
|
last_exc_message = this_exc_message
|
||||||
exc_count = 0
|
exc_count = 0
|
||||||
|
|||||||
@@ -13,14 +13,12 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from nova.openstack.common import excutils
|
from nova.openstack.common import excutils
|
||||||
from nova.openstack.common.gettextutils import _ # noqa
|
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@@ -60,7 +58,7 @@ def read_cached_file(filename, force_reload=False):
|
|||||||
cache_info = _FILE_CACHE.setdefault(filename, {})
|
cache_info = _FILE_CACHE.setdefault(filename, {})
|
||||||
|
|
||||||
if not cache_info or mtime > cache_info.get('mtime', 0):
|
if not cache_info or mtime > cache_info.get('mtime', 0):
|
||||||
LOG.debug(_("Reloading cached file %s") % filename)
|
LOG.debug("Reloading cached file %s" % filename)
|
||||||
with open(filename) as fap:
|
with open(filename) as fap:
|
||||||
cache_info['data'] = fap.read()
|
cache_info['data'] = fap.read()
|
||||||
cache_info['mtime'] = mtime
|
cache_info['mtime'] = mtime
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import gettext
|
|||||||
import locale
|
import locale
|
||||||
from logging import handlers
|
from logging import handlers
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
|
|
||||||
from babel import localedata
|
from babel import localedata
|
||||||
import six
|
import six
|
||||||
@@ -248,47 +247,22 @@ class Message(six.text_type):
|
|||||||
if other is None:
|
if other is None:
|
||||||
params = (other,)
|
params = (other,)
|
||||||
elif isinstance(other, dict):
|
elif isinstance(other, dict):
|
||||||
params = self._trim_dictionary_parameters(other)
|
# Merge the dictionaries
|
||||||
|
# Copy each item in case one does not support deep copy.
|
||||||
|
params = {}
|
||||||
|
if isinstance(self.params, dict):
|
||||||
|
for key, val in self.params.items():
|
||||||
|
params[key] = self._copy_param(val)
|
||||||
|
for key, val in other.items():
|
||||||
|
params[key] = self._copy_param(val)
|
||||||
else:
|
else:
|
||||||
params = self._copy_param(other)
|
params = self._copy_param(other)
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def _trim_dictionary_parameters(self, dict_param):
|
|
||||||
"""Return a dict that only has matching entries in the msgid."""
|
|
||||||
# NOTE(luisg): Here we trim down the dictionary passed as parameters
|
|
||||||
# to avoid carrying a lot of unnecessary weight around in the message
|
|
||||||
# object, for example if someone passes in Message() % locals() but
|
|
||||||
# only some params are used, and additionally we prevent errors for
|
|
||||||
# non-deepcopyable objects by unicoding() them.
|
|
||||||
|
|
||||||
# Look for %(param) keys in msgid;
|
|
||||||
# Skip %% and deal with the case where % is first character on the line
|
|
||||||
keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', self.msgid)
|
|
||||||
|
|
||||||
# If we don't find any %(param) keys but have a %s
|
|
||||||
if not keys and re.findall('(?:[^%]|^)%[a-z]', self.msgid):
|
|
||||||
# Apparently the full dictionary is the parameter
|
|
||||||
params = self._copy_param(dict_param)
|
|
||||||
else:
|
|
||||||
params = {}
|
|
||||||
# Save our existing parameters as defaults to protect
|
|
||||||
# ourselves from losing values if we are called through an
|
|
||||||
# (erroneous) chain that builds a valid Message with
|
|
||||||
# arguments, and then does something like "msg % kwds"
|
|
||||||
# where kwds is an empty dictionary.
|
|
||||||
src = {}
|
|
||||||
if isinstance(self.params, dict):
|
|
||||||
src.update(self.params)
|
|
||||||
src.update(dict_param)
|
|
||||||
for key in keys:
|
|
||||||
params[key] = self._copy_param(src[key])
|
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
def _copy_param(self, param):
|
def _copy_param(self, param):
|
||||||
try:
|
try:
|
||||||
return copy.deepcopy(param)
|
return copy.deepcopy(param)
|
||||||
except TypeError:
|
except Exception:
|
||||||
# Fallback to casting to unicode this will handle the
|
# Fallback to casting to unicode this will handle the
|
||||||
# python code-like objects that can't be deep-copied
|
# python code-like objects that can't be deep-copied
|
||||||
return six.text_type(param)
|
return six.text_type(param)
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ def import_module(import_str):
|
|||||||
return sys.modules[import_str]
|
return sys.modules[import_str]
|
||||||
|
|
||||||
|
|
||||||
|
def import_versioned_module(version, submodule=None):
|
||||||
|
module = 'nova.v%s' % version
|
||||||
|
if submodule:
|
||||||
|
module = '.'.join((module, submodule))
|
||||||
|
return import_module(module)
|
||||||
|
|
||||||
|
|
||||||
def try_import(import_str, default=None):
|
def try_import(import_str, default=None):
|
||||||
"""Try to import a module and if it fails return default."""
|
"""Try to import a module and if it fails return default."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -36,13 +36,9 @@ import functools
|
|||||||
import inspect
|
import inspect
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
try:
|
|
||||||
import xmlrpclib
|
|
||||||
except ImportError:
|
|
||||||
# NOTE(jd): xmlrpclib is not shipped with Python 3
|
|
||||||
xmlrpclib = None
|
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
import six.moves.xmlrpc_client as xmlrpclib
|
||||||
|
|
||||||
from nova.openstack.common import gettextutils
|
from nova.openstack.common import gettextutils
|
||||||
from nova.openstack.common import importutils
|
from nova.openstack.common import importutils
|
||||||
@@ -129,7 +125,7 @@ def to_primitive(value, convert_instances=False, convert_datetime=True,
|
|||||||
# It's not clear why xmlrpclib created their own DateTime type, but
|
# It's not clear why xmlrpclib created their own DateTime type, but
|
||||||
# for our purposes, make it a datetime type which is explicitly
|
# for our purposes, make it a datetime type which is explicitly
|
||||||
# handled
|
# handled
|
||||||
if xmlrpclib and isinstance(value, xmlrpclib.DateTime):
|
if isinstance(value, xmlrpclib.DateTime):
|
||||||
value = datetime.datetime(*tuple(value.timetuple())[:6])
|
value = datetime.datetime(*tuple(value.timetuple())[:6])
|
||||||
|
|
||||||
if convert_datetime and isinstance(value, datetime.datetime):
|
if convert_datetime and isinstance(value, datetime.datetime):
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Openstack logging handler.
|
"""OpenStack logging handler.
|
||||||
|
|
||||||
This module adds to logging functionality by adding the option to specify
|
This module adds to logging functionality by adding the option to specify
|
||||||
a context object when calling the various log methods. If the context object
|
a context object when calling the various log methods. If the context object
|
||||||
@@ -129,7 +129,7 @@ logging_cli_opts = [
|
|||||||
'and will be removed in J.'),
|
'and will be removed in J.'),
|
||||||
cfg.StrOpt('syslog-log-facility',
|
cfg.StrOpt('syslog-log-facility',
|
||||||
default='LOG_USER',
|
default='LOG_USER',
|
||||||
help='syslog facility to receive log lines')
|
help='Syslog facility to receive log lines')
|
||||||
]
|
]
|
||||||
|
|
||||||
generic_log_opts = [
|
generic_log_opts = [
|
||||||
@@ -141,20 +141,20 @@ generic_log_opts = [
|
|||||||
log_opts = [
|
log_opts = [
|
||||||
cfg.StrOpt('logging_context_format_string',
|
cfg.StrOpt('logging_context_format_string',
|
||||||
default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
|
default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
|
||||||
'%(name)s [%(request_id)s %(user)s %(tenant)s] '
|
'%(name)s [%(request_id)s %(user_identity)s] '
|
||||||
'%(instance)s%(message)s',
|
'%(instance)s%(message)s',
|
||||||
help='format string to use for log messages with context'),
|
help='Format string to use for log messages with context'),
|
||||||
cfg.StrOpt('logging_default_format_string',
|
cfg.StrOpt('logging_default_format_string',
|
||||||
default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
|
default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
|
||||||
'%(name)s [-] %(instance)s%(message)s',
|
'%(name)s [-] %(instance)s%(message)s',
|
||||||
help='format string to use for log messages without context'),
|
help='Format string to use for log messages without context'),
|
||||||
cfg.StrOpt('logging_debug_format_suffix',
|
cfg.StrOpt('logging_debug_format_suffix',
|
||||||
default='%(funcName)s %(pathname)s:%(lineno)d',
|
default='%(funcName)s %(pathname)s:%(lineno)d',
|
||||||
help='data to append to log format when level is DEBUG'),
|
help='Data to append to log format when level is DEBUG'),
|
||||||
cfg.StrOpt('logging_exception_prefix',
|
cfg.StrOpt('logging_exception_prefix',
|
||||||
default='%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s '
|
default='%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s '
|
||||||
'%(instance)s',
|
'%(instance)s',
|
||||||
help='prefix each line of exception output with this format'),
|
help='Prefix each line of exception output with this format'),
|
||||||
cfg.ListOpt('default_log_levels',
|
cfg.ListOpt('default_log_levels',
|
||||||
default=[
|
default=[
|
||||||
'amqp=WARN',
|
'amqp=WARN',
|
||||||
@@ -165,14 +165,15 @@ log_opts = [
|
|||||||
'suds=INFO',
|
'suds=INFO',
|
||||||
'oslo.messaging=INFO',
|
'oslo.messaging=INFO',
|
||||||
'iso8601=WARN',
|
'iso8601=WARN',
|
||||||
|
'requests.packages.urllib3.connectionpool=WARN'
|
||||||
],
|
],
|
||||||
help='list of logger=LEVEL pairs'),
|
help='List of logger=LEVEL pairs'),
|
||||||
cfg.BoolOpt('publish_errors',
|
cfg.BoolOpt('publish_errors',
|
||||||
default=False,
|
default=False,
|
||||||
help='publish error events'),
|
help='Publish error events'),
|
||||||
cfg.BoolOpt('fatal_deprecations',
|
cfg.BoolOpt('fatal_deprecations',
|
||||||
default=False,
|
default=False,
|
||||||
help='make deprecations fatal'),
|
help='Make deprecations fatal'),
|
||||||
|
|
||||||
# NOTE(mikal): there are two options here because sometimes we are handed
|
# NOTE(mikal): there are two options here because sometimes we are handed
|
||||||
# a full instance (and could include more information), and other times we
|
# a full instance (and could include more information), and other times we
|
||||||
@@ -304,18 +305,39 @@ class ContextAdapter(BaseLoggerAdapter):
|
|||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.project = project_name
|
self.project = project_name
|
||||||
self.version = version_string
|
self.version = version_string
|
||||||
|
self._deprecated_messages_sent = dict()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def handlers(self):
|
def handlers(self):
|
||||||
return self.logger.handlers
|
return self.logger.handlers
|
||||||
|
|
||||||
def deprecated(self, msg, *args, **kwargs):
|
def deprecated(self, msg, *args, **kwargs):
|
||||||
|
"""Call this method when a deprecated feature is used.
|
||||||
|
|
||||||
|
If the system is configured for fatal deprecations then the message
|
||||||
|
is logged at the 'critical' level and :class:`DeprecatedConfig` will
|
||||||
|
be raised.
|
||||||
|
|
||||||
|
Otherwise, the message will be logged (once) at the 'warn' level.
|
||||||
|
|
||||||
|
:raises: :class:`DeprecatedConfig` if the system is configured for
|
||||||
|
fatal deprecations.
|
||||||
|
|
||||||
|
"""
|
||||||
stdmsg = _("Deprecated: %s") % msg
|
stdmsg = _("Deprecated: %s") % msg
|
||||||
if CONF.fatal_deprecations:
|
if CONF.fatal_deprecations:
|
||||||
self.critical(stdmsg, *args, **kwargs)
|
self.critical(stdmsg, *args, **kwargs)
|
||||||
raise DeprecatedConfig(msg=stdmsg)
|
raise DeprecatedConfig(msg=stdmsg)
|
||||||
else:
|
|
||||||
self.warn(stdmsg, *args, **kwargs)
|
# Using a list because a tuple with dict can't be stored in a set.
|
||||||
|
sent_args = self._deprecated_messages_sent.setdefault(msg, list())
|
||||||
|
|
||||||
|
if args in sent_args:
|
||||||
|
# Already logged this message, so don't log it again.
|
||||||
|
return
|
||||||
|
|
||||||
|
sent_args.append(args)
|
||||||
|
self.warn(stdmsg, *args, **kwargs)
|
||||||
|
|
||||||
def process(self, msg, kwargs):
|
def process(self, msg, kwargs):
|
||||||
# NOTE(mrodden): catch any Message/other object and
|
# NOTE(mrodden): catch any Message/other object and
|
||||||
@@ -336,7 +358,7 @@ class ContextAdapter(BaseLoggerAdapter):
|
|||||||
extra.update(_dictify_context(context))
|
extra.update(_dictify_context(context))
|
||||||
|
|
||||||
instance = kwargs.pop('instance', None)
|
instance = kwargs.pop('instance', None)
|
||||||
instance_uuid = (extra.get('instance_uuid', None) or
|
instance_uuid = (extra.get('instance_uuid') or
|
||||||
kwargs.pop('instance_uuid', None))
|
kwargs.pop('instance_uuid', None))
|
||||||
instance_extra = ''
|
instance_extra = ''
|
||||||
if instance:
|
if instance:
|
||||||
@@ -344,10 +366,12 @@ class ContextAdapter(BaseLoggerAdapter):
|
|||||||
elif instance_uuid:
|
elif instance_uuid:
|
||||||
instance_extra = (CONF.instance_uuid_format
|
instance_extra = (CONF.instance_uuid_format
|
||||||
% {'uuid': instance_uuid})
|
% {'uuid': instance_uuid})
|
||||||
extra.update({'instance': instance_extra})
|
extra['instance'] = instance_extra
|
||||||
|
|
||||||
extra.update({"project": self.project})
|
extra.setdefault('user_identity', kwargs.pop('user_identity', None))
|
||||||
extra.update({"version": self.version})
|
|
||||||
|
extra['project'] = self.project
|
||||||
|
extra['version'] = self.version
|
||||||
extra['extra'] = extra.copy()
|
extra['extra'] = extra.copy()
|
||||||
return msg, kwargs
|
return msg, kwargs
|
||||||
|
|
||||||
@@ -430,12 +454,12 @@ def _load_log_config(log_config_append):
|
|||||||
raise LogConfigError(log_config_append, str(exc))
|
raise LogConfigError(log_config_append, str(exc))
|
||||||
|
|
||||||
|
|
||||||
def setup(product_name):
|
def setup(product_name, version='unknown'):
|
||||||
"""Setup logging."""
|
"""Setup logging."""
|
||||||
if CONF.log_config_append:
|
if CONF.log_config_append:
|
||||||
_load_log_config(CONF.log_config_append)
|
_load_log_config(CONF.log_config_append)
|
||||||
else:
|
else:
|
||||||
_setup_logging_from_conf()
|
_setup_logging_from_conf(product_name, version)
|
||||||
sys.excepthook = _create_logging_excepthook(product_name)
|
sys.excepthook = _create_logging_excepthook(product_name)
|
||||||
|
|
||||||
|
|
||||||
@@ -480,7 +504,7 @@ class RFCSysLogHandler(logging.handlers.SysLogHandler):
|
|||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def _setup_logging_from_conf():
|
def _setup_logging_from_conf(project, version):
|
||||||
log_root = getLogger(None).logger
|
log_root = getLogger(None).logger
|
||||||
for handler in log_root.handlers:
|
for handler in log_root.handlers:
|
||||||
log_root.removeHandler(handler)
|
log_root.removeHandler(handler)
|
||||||
@@ -528,7 +552,9 @@ def _setup_logging_from_conf():
|
|||||||
log_root.info('Deprecated: log_format is now deprecated and will '
|
log_root.info('Deprecated: log_format is now deprecated and will '
|
||||||
'be removed in the next release')
|
'be removed in the next release')
|
||||||
else:
|
else:
|
||||||
handler.setFormatter(ContextFormatter(datefmt=datefmt))
|
handler.setFormatter(ContextFormatter(project=project,
|
||||||
|
version=version,
|
||||||
|
datefmt=datefmt))
|
||||||
|
|
||||||
if CONF.debug:
|
if CONF.debug:
|
||||||
log_root.setLevel(logging.DEBUG)
|
log_root.setLevel(logging.DEBUG)
|
||||||
@@ -586,18 +612,50 @@ class ContextFormatter(logging.Formatter):
|
|||||||
For information about what variables are available for the formatter see:
|
For information about what variables are available for the formatter see:
|
||||||
http://docs.python.org/library/logging.html#formatter
|
http://docs.python.org/library/logging.html#formatter
|
||||||
|
|
||||||
|
If available, uses the context value stored in TLS - local.store.context
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Initialize ContextFormatter instance
|
||||||
|
|
||||||
|
Takes additional keyword arguments which can be used in the message
|
||||||
|
format string.
|
||||||
|
|
||||||
|
:keyword project: project name
|
||||||
|
:type project: string
|
||||||
|
:keyword version: project version
|
||||||
|
:type version: string
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.project = kwargs.pop('project', 'unknown')
|
||||||
|
self.version = kwargs.pop('version', 'unknown')
|
||||||
|
|
||||||
|
logging.Formatter.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
"""Uses contextstring if request_id is set, otherwise default."""
|
"""Uses contextstring if request_id is set, otherwise default."""
|
||||||
|
|
||||||
|
# store project info
|
||||||
|
record.project = self.project
|
||||||
|
record.version = self.version
|
||||||
|
|
||||||
|
# store request info
|
||||||
|
context = getattr(local.store, 'context', None)
|
||||||
|
if context:
|
||||||
|
d = _dictify_context(context)
|
||||||
|
for k, v in d.items():
|
||||||
|
setattr(record, k, v)
|
||||||
|
|
||||||
# NOTE(sdague): default the fancier formatting params
|
# NOTE(sdague): default the fancier formatting params
|
||||||
# to an empty string so we don't throw an exception if
|
# to an empty string so we don't throw an exception if
|
||||||
# they get used
|
# they get used
|
||||||
for key in ('instance', 'color'):
|
for key in ('instance', 'color', 'user_identity'):
|
||||||
if key not in record.__dict__:
|
if key not in record.__dict__:
|
||||||
record.__dict__[key] = ''
|
record.__dict__[key] = ''
|
||||||
|
|
||||||
if record.__dict__.get('request_id', None):
|
if record.__dict__.get('request_id'):
|
||||||
self._fmt = CONF.logging_context_format_string
|
self._fmt = CONF.logging_context_format_string
|
||||||
else:
|
else:
|
||||||
self._fmt = CONF.logging_default_format_string
|
self._fmt = CONF.logging_default_format_string
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2010 United States Government as represented by the
|
# Copyright 2010 United States Government as represented by the
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
# Copyright 2011 Justin Santa Barbara
|
# Copyright 2011 Justin Santa Barbara
|
||||||
@@ -22,7 +20,7 @@ import sys
|
|||||||
from eventlet import event
|
from eventlet import event
|
||||||
from eventlet import greenthread
|
from eventlet import greenthread
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common.gettextutils import _LE, _LW
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
from nova.openstack.common import timeutils
|
from nova.openstack.common import timeutils
|
||||||
|
|
||||||
@@ -81,14 +79,14 @@ class FixedIntervalLoopingCall(LoopingCallBase):
|
|||||||
break
|
break
|
||||||
delay = interval - timeutils.delta_seconds(start, end)
|
delay = interval - timeutils.delta_seconds(start, end)
|
||||||
if delay <= 0:
|
if delay <= 0:
|
||||||
LOG.warn(_('task run outlasted interval by %s sec') %
|
LOG.warn(_LW('task run outlasted interval by %s sec') %
|
||||||
-delay)
|
-delay)
|
||||||
greenthread.sleep(delay if delay > 0 else 0)
|
greenthread.sleep(delay if delay > 0 else 0)
|
||||||
except LoopingCallDone as e:
|
except LoopingCallDone as e:
|
||||||
self.stop()
|
self.stop()
|
||||||
done.send(e.retvalue)
|
done.send(e.retvalue)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_('in fixed duration looping call'))
|
LOG.exception(_LE('in fixed duration looping call'))
|
||||||
done.send_exception(*sys.exc_info())
|
done.send_exception(*sys.exc_info())
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@@ -128,14 +126,14 @@ class DynamicLoopingCall(LoopingCallBase):
|
|||||||
|
|
||||||
if periodic_interval_max is not None:
|
if periodic_interval_max is not None:
|
||||||
idle = min(idle, periodic_interval_max)
|
idle = min(idle, periodic_interval_max)
|
||||||
LOG.debug(_('Dynamic looping call sleeping for %.02f '
|
LOG.debug('Dynamic looping call sleeping for %.02f '
|
||||||
'seconds'), idle)
|
'seconds', idle)
|
||||||
greenthread.sleep(idle)
|
greenthread.sleep(idle)
|
||||||
except LoopingCallDone as e:
|
except LoopingCallDone as e:
|
||||||
self.stop()
|
self.stop()
|
||||||
done.send(e.retvalue)
|
done.send(e.retvalue)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_('in dynamic looping call'))
|
LOG.exception(_LE('in dynamic looping call'))
|
||||||
done.send_exception(*sys.exc_info())
|
done.send_exception(*sys.exc_info())
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2010 United States Government as represented by the
|
# Copyright 2010 United States Government as represented by the
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
@@ -61,7 +59,7 @@ class Client(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
now = timeutils.utcnow_ts()
|
now = timeutils.utcnow_ts()
|
||||||
for k in self.cache.keys():
|
for k in list(self.cache):
|
||||||
(timeout, _value) = self.cache[k]
|
(timeout, _value) = self.cache[k]
|
||||||
if timeout and now >= timeout:
|
if timeout and now >= timeout:
|
||||||
del self.cache[k]
|
del self.cache[k]
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 OpenStack Foundation.
|
# Copyright 2012 OpenStack Foundation.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@@ -19,7 +17,17 @@
|
|||||||
Network-related utilities and helper functions.
|
Network-related utilities and helper functions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import urlparse
|
# TODO(jd) Use six.moves once
|
||||||
|
# https://bitbucket.org/gutworth/six/pull-request/28
|
||||||
|
# is merged
|
||||||
|
try:
|
||||||
|
import urllib.parse
|
||||||
|
SplitResult = urllib.parse.SplitResult
|
||||||
|
except ImportError:
|
||||||
|
import urlparse
|
||||||
|
SplitResult = urlparse.SplitResult
|
||||||
|
|
||||||
|
from six.moves.urllib import parse
|
||||||
|
|
||||||
|
|
||||||
def parse_host_port(address, default_port=None):
|
def parse_host_port(address, default_port=None):
|
||||||
@@ -66,16 +74,35 @@ def parse_host_port(address, default_port=None):
|
|||||||
return (host, None if port is None else int(port))
|
return (host, None if port is None else int(port))
|
||||||
|
|
||||||
|
|
||||||
|
class ModifiedSplitResult(SplitResult):
|
||||||
|
"""Split results class for urlsplit."""
|
||||||
|
|
||||||
|
# NOTE(dims): The functions below are needed for Python 2.6.x.
|
||||||
|
# We can remove these when we drop support for 2.6.x.
|
||||||
|
@property
|
||||||
|
def hostname(self):
|
||||||
|
netloc = self.netloc.split('@', 1)[-1]
|
||||||
|
host, port = parse_host_port(netloc)
|
||||||
|
return host
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port(self):
|
||||||
|
netloc = self.netloc.split('@', 1)[-1]
|
||||||
|
host, port = parse_host_port(netloc)
|
||||||
|
return port
|
||||||
|
|
||||||
|
|
||||||
def urlsplit(url, scheme='', allow_fragments=True):
|
def urlsplit(url, scheme='', allow_fragments=True):
|
||||||
"""Parse a URL using urlparse.urlsplit(), splitting query and fragments.
|
"""Parse a URL using urlparse.urlsplit(), splitting query and fragments.
|
||||||
This function papers over Python issue9374 when needed.
|
This function papers over Python issue9374 when needed.
|
||||||
|
|
||||||
The parameters are the same as urlparse.urlsplit.
|
The parameters are the same as urlparse.urlsplit.
|
||||||
"""
|
"""
|
||||||
scheme, netloc, path, query, fragment = urlparse.urlsplit(
|
scheme, netloc, path, query, fragment = parse.urlsplit(
|
||||||
url, scheme, allow_fragments)
|
url, scheme, allow_fragments)
|
||||||
if allow_fragments and '#' in path:
|
if allow_fragments and '#' in path:
|
||||||
path, fragment = path.split('#', 1)
|
path, fragment = path.split('#', 1)
|
||||||
if '?' in path:
|
if '?' in path:
|
||||||
path, query = path.split('?', 1)
|
path, query = path.split('?', 1)
|
||||||
return urlparse.SplitResult(scheme, netloc, path, query, fragment)
|
return ModifiedSplitResult(scheme, netloc,
|
||||||
|
path, query, fragment)
|
||||||
|
|||||||
@@ -151,7 +151,8 @@ def execute(*cmd, **kwargs):
|
|||||||
while attempts > 0:
|
while attempts > 0:
|
||||||
attempts -= 1
|
attempts -= 1
|
||||||
try:
|
try:
|
||||||
LOG.log(loglevel, _('Running cmd (subprocess): %s'), ' '.join(cmd))
|
LOG.log(loglevel, 'Running cmd (subprocess): %s',
|
||||||
|
' '.join(cmd))
|
||||||
_PIPE = subprocess.PIPE # pylint: disable=E1101
|
_PIPE = subprocess.PIPE # pylint: disable=E1101
|
||||||
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
@@ -184,7 +185,7 @@ def execute(*cmd, **kwargs):
|
|||||||
break
|
break
|
||||||
obj.stdin.close() # pylint: disable=E1101
|
obj.stdin.close() # pylint: disable=E1101
|
||||||
_returncode = obj.returncode # pylint: disable=E1101
|
_returncode = obj.returncode # pylint: disable=E1101
|
||||||
LOG.log(loglevel, _('Result was %s') % _returncode)
|
LOG.log(loglevel, 'Result was %s' % _returncode)
|
||||||
if not ignore_exit_code and _returncode not in check_exit_code:
|
if not ignore_exit_code and _returncode not in check_exit_code:
|
||||||
(stdout, stderr) = result
|
(stdout, stderr) = result
|
||||||
raise ProcessExecutionError(exit_code=_returncode,
|
raise ProcessExecutionError(exit_code=_returncode,
|
||||||
@@ -196,7 +197,7 @@ def execute(*cmd, **kwargs):
|
|||||||
if not attempts:
|
if not attempts:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
LOG.log(loglevel, _('%r failed. Retrying.'), cmd)
|
LOG.log(loglevel, '%r failed. Retrying.', cmd)
|
||||||
if delay_on_retry:
|
if delay_on_retry:
|
||||||
greenthread.sleep(random.randint(20, 200) / 100.0)
|
greenthread.sleep(random.randint(20, 200) / 100.0)
|
||||||
finally:
|
finally:
|
||||||
@@ -235,7 +236,7 @@ def trycmd(*args, **kwargs):
|
|||||||
|
|
||||||
def ssh_execute(ssh, cmd, process_input=None,
|
def ssh_execute(ssh, cmd, process_input=None,
|
||||||
addl_env=None, check_exit_code=True):
|
addl_env=None, check_exit_code=True):
|
||||||
LOG.debug(_('Running cmd (SSH): %s'), cmd)
|
LOG.debug('Running cmd (SSH): %s', cmd)
|
||||||
if addl_env:
|
if addl_env:
|
||||||
raise InvalidArgumentError(_('Environment not supported over SSH'))
|
raise InvalidArgumentError(_('Environment not supported over SSH'))
|
||||||
|
|
||||||
@@ -256,7 +257,7 @@ def ssh_execute(ssh, cmd, process_input=None,
|
|||||||
|
|
||||||
# exit_status == -1 if no exit code was returned
|
# exit_status == -1 if no exit code was returned
|
||||||
if exit_status != -1:
|
if exit_status != -1:
|
||||||
LOG.debug(_('Result was %s') % exit_status)
|
LOG.debug('Result was %s' % exit_status)
|
||||||
if check_exit_code and exit_status != 0:
|
if check_exit_code and exit_status != 0:
|
||||||
raise ProcessExecutionError(exit_code=exit_status,
|
raise ProcessExecutionError(exit_code=exit_status,
|
||||||
stdout=stdout,
|
stdout=stdout,
|
||||||
|
|||||||
@@ -38,13 +38,14 @@ from eventlet import event
|
|||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova.openstack.common import eventlet_backdoor
|
from nova.openstack.common import eventlet_backdoor
|
||||||
from nova.openstack.common.gettextutils import _ # noqa
|
from nova.openstack.common.gettextutils import _LE, _LI, _LW
|
||||||
from nova.openstack.common import importutils
|
from nova.openstack.common import importutils
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
from nova.openstack.common import systemd
|
||||||
from nova.openstack.common import threadgroup
|
from nova.openstack.common import threadgroup
|
||||||
|
|
||||||
|
|
||||||
rpc = importutils.try_import('nova.rpc')
|
rpc = importutils.try_import('nova.openstack.common.rpc')
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -163,7 +164,7 @@ class ServiceLauncher(Launcher):
|
|||||||
status = None
|
status = None
|
||||||
signo = 0
|
signo = 0
|
||||||
|
|
||||||
LOG.debug(_('Full set of CONF:'))
|
LOG.debug('Full set of CONF:')
|
||||||
CONF.log_opt_values(LOG, std_logging.DEBUG)
|
CONF.log_opt_values(LOG, std_logging.DEBUG)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -172,7 +173,7 @@ class ServiceLauncher(Launcher):
|
|||||||
super(ServiceLauncher, self).wait()
|
super(ServiceLauncher, self).wait()
|
||||||
except SignalExit as exc:
|
except SignalExit as exc:
|
||||||
signame = _signo_to_signame(exc.signo)
|
signame = _signo_to_signame(exc.signo)
|
||||||
LOG.info(_('Caught %s, exiting'), signame)
|
LOG.info(_LI('Caught %s, exiting'), signame)
|
||||||
status = exc.code
|
status = exc.code
|
||||||
signo = exc.signo
|
signo = exc.signo
|
||||||
except SystemExit as exc:
|
except SystemExit as exc:
|
||||||
@@ -184,7 +185,7 @@ class ServiceLauncher(Launcher):
|
|||||||
rpc.cleanup()
|
rpc.cleanup()
|
||||||
except Exception:
|
except Exception:
|
||||||
# We're shutting down, so it doesn't matter at this point.
|
# We're shutting down, so it doesn't matter at this point.
|
||||||
LOG.exception(_('Exception during rpc cleanup.'))
|
LOG.exception(_LE('Exception during rpc cleanup.'))
|
||||||
|
|
||||||
return status, signo
|
return status, signo
|
||||||
|
|
||||||
@@ -206,10 +207,16 @@ class ServiceWrapper(object):
|
|||||||
|
|
||||||
|
|
||||||
class ProcessLauncher(object):
|
class ProcessLauncher(object):
|
||||||
def __init__(self):
|
def __init__(self, wait_interval=0.01):
|
||||||
|
"""Constructor.
|
||||||
|
|
||||||
|
:param wait_interval: The interval to sleep for between checks
|
||||||
|
of child process exit.
|
||||||
|
"""
|
||||||
self.children = {}
|
self.children = {}
|
||||||
self.sigcaught = None
|
self.sigcaught = None
|
||||||
self.running = True
|
self.running = True
|
||||||
|
self.wait_interval = wait_interval
|
||||||
rfd, self.writepipe = os.pipe()
|
rfd, self.writepipe = os.pipe()
|
||||||
self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r')
|
self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r')
|
||||||
self.handle_signal()
|
self.handle_signal()
|
||||||
@@ -229,7 +236,7 @@ class ProcessLauncher(object):
|
|||||||
# dies unexpectedly
|
# dies unexpectedly
|
||||||
self.readpipe.read()
|
self.readpipe.read()
|
||||||
|
|
||||||
LOG.info(_('Parent process has died unexpectedly, exiting'))
|
LOG.info(_LI('Parent process has died unexpectedly, exiting'))
|
||||||
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -260,13 +267,13 @@ class ProcessLauncher(object):
|
|||||||
launcher.wait()
|
launcher.wait()
|
||||||
except SignalExit as exc:
|
except SignalExit as exc:
|
||||||
signame = _signo_to_signame(exc.signo)
|
signame = _signo_to_signame(exc.signo)
|
||||||
LOG.info(_('Caught %s, exiting'), signame)
|
LOG.info(_LI('Caught %s, exiting'), signame)
|
||||||
status = exc.code
|
status = exc.code
|
||||||
signo = exc.signo
|
signo = exc.signo
|
||||||
except SystemExit as exc:
|
except SystemExit as exc:
|
||||||
status = exc.code
|
status = exc.code
|
||||||
except BaseException:
|
except BaseException:
|
||||||
LOG.exception(_('Unhandled exception'))
|
LOG.exception(_LE('Unhandled exception'))
|
||||||
status = 2
|
status = 2
|
||||||
finally:
|
finally:
|
||||||
launcher.stop()
|
launcher.stop()
|
||||||
@@ -299,7 +306,7 @@ class ProcessLauncher(object):
|
|||||||
# start up quickly but ensure we don't fork off children that
|
# start up quickly but ensure we don't fork off children that
|
||||||
# die instantly too quickly.
|
# die instantly too quickly.
|
||||||
if time.time() - wrap.forktimes[0] < wrap.workers:
|
if time.time() - wrap.forktimes[0] < wrap.workers:
|
||||||
LOG.info(_('Forking too fast, sleeping'))
|
LOG.info(_LI('Forking too fast, sleeping'))
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
wrap.forktimes.pop(0)
|
wrap.forktimes.pop(0)
|
||||||
@@ -318,7 +325,7 @@ class ProcessLauncher(object):
|
|||||||
|
|
||||||
os._exit(status)
|
os._exit(status)
|
||||||
|
|
||||||
LOG.info(_('Started child %d'), pid)
|
LOG.info(_LI('Started child %d'), pid)
|
||||||
|
|
||||||
wrap.children.add(pid)
|
wrap.children.add(pid)
|
||||||
self.children[pid] = wrap
|
self.children[pid] = wrap
|
||||||
@@ -328,7 +335,7 @@ class ProcessLauncher(object):
|
|||||||
def launch_service(self, service, workers=1):
|
def launch_service(self, service, workers=1):
|
||||||
wrap = ServiceWrapper(service, workers)
|
wrap = ServiceWrapper(service, workers)
|
||||||
|
|
||||||
LOG.info(_('Starting %d workers'), wrap.workers)
|
LOG.info(_LI('Starting %d workers'), wrap.workers)
|
||||||
while self.running and len(wrap.children) < wrap.workers:
|
while self.running and len(wrap.children) < wrap.workers:
|
||||||
self._start_child(wrap)
|
self._start_child(wrap)
|
||||||
|
|
||||||
@@ -345,15 +352,15 @@ class ProcessLauncher(object):
|
|||||||
|
|
||||||
if os.WIFSIGNALED(status):
|
if os.WIFSIGNALED(status):
|
||||||
sig = os.WTERMSIG(status)
|
sig = os.WTERMSIG(status)
|
||||||
LOG.info(_('Child %(pid)d killed by signal %(sig)d'),
|
LOG.info(_LI('Child %(pid)d killed by signal %(sig)d'),
|
||||||
dict(pid=pid, sig=sig))
|
dict(pid=pid, sig=sig))
|
||||||
else:
|
else:
|
||||||
code = os.WEXITSTATUS(status)
|
code = os.WEXITSTATUS(status)
|
||||||
LOG.info(_('Child %(pid)s exited with status %(code)d'),
|
LOG.info(_LI('Child %(pid)s exited with status %(code)d'),
|
||||||
dict(pid=pid, code=code))
|
dict(pid=pid, code=code))
|
||||||
|
|
||||||
if pid not in self.children:
|
if pid not in self.children:
|
||||||
LOG.warning(_('pid %d not in child list'), pid)
|
LOG.warning(_LW('pid %d not in child list'), pid)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
wrap = self.children.pop(pid)
|
wrap = self.children.pop(pid)
|
||||||
@@ -367,7 +374,7 @@ class ProcessLauncher(object):
|
|||||||
# Yield to other threads if no children have exited
|
# Yield to other threads if no children have exited
|
||||||
# Sleep for a short time to avoid excessive CPU usage
|
# Sleep for a short time to avoid excessive CPU usage
|
||||||
# (see bug #1095346)
|
# (see bug #1095346)
|
||||||
eventlet.greenthread.sleep(.01)
|
eventlet.greenthread.sleep(self.wait_interval)
|
||||||
continue
|
continue
|
||||||
while self.running and len(wrap.children) < wrap.workers:
|
while self.running and len(wrap.children) < wrap.workers:
|
||||||
self._start_child(wrap)
|
self._start_child(wrap)
|
||||||
@@ -375,22 +382,25 @@ class ProcessLauncher(object):
|
|||||||
def wait(self):
|
def wait(self):
|
||||||
"""Loop waiting on children to die and respawning as necessary."""
|
"""Loop waiting on children to die and respawning as necessary."""
|
||||||
|
|
||||||
LOG.debug(_('Full set of CONF:'))
|
LOG.debug('Full set of CONF:')
|
||||||
CONF.log_opt_values(LOG, std_logging.DEBUG)
|
CONF.log_opt_values(LOG, std_logging.DEBUG)
|
||||||
|
|
||||||
while True:
|
try:
|
||||||
self.handle_signal()
|
while True:
|
||||||
self._respawn_children()
|
self.handle_signal()
|
||||||
if self.sigcaught:
|
self._respawn_children()
|
||||||
signame = _signo_to_signame(self.sigcaught)
|
if self.sigcaught:
|
||||||
LOG.info(_('Caught %s, stopping children'), signame)
|
signame = _signo_to_signame(self.sigcaught)
|
||||||
if not _is_sighup_and_daemon(self.sigcaught):
|
LOG.info(_LI('Caught %s, stopping children'), signame)
|
||||||
break
|
if not _is_sighup_and_daemon(self.sigcaught):
|
||||||
|
break
|
||||||
|
|
||||||
for pid in self.children:
|
for pid in self.children:
|
||||||
os.kill(pid, signal.SIGHUP)
|
os.kill(pid, signal.SIGHUP)
|
||||||
self.running = True
|
self.running = True
|
||||||
self.sigcaught = None
|
self.sigcaught = None
|
||||||
|
except eventlet.greenlet.GreenletExit:
|
||||||
|
LOG.info(_LI("Wait called after thread killed. Cleaning up."))
|
||||||
|
|
||||||
for pid in self.children:
|
for pid in self.children:
|
||||||
try:
|
try:
|
||||||
@@ -401,7 +411,7 @@ class ProcessLauncher(object):
|
|||||||
|
|
||||||
# Wait for children to die
|
# Wait for children to die
|
||||||
if self.children:
|
if self.children:
|
||||||
LOG.info(_('Waiting on %d children to exit'), len(self.children))
|
LOG.info(_LI('Waiting on %d children to exit'), len(self.children))
|
||||||
while self.children:
|
while self.children:
|
||||||
self._wait_child()
|
self._wait_child()
|
||||||
|
|
||||||
@@ -478,14 +488,16 @@ class Services(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
service.start()
|
service.start()
|
||||||
|
systemd.notify_once()
|
||||||
done.wait()
|
done.wait()
|
||||||
|
|
||||||
|
|
||||||
def launch(service, workers=None):
|
def launch(service, workers=1):
|
||||||
if workers:
|
if workers is None or workers == 1:
|
||||||
launcher = ProcessLauncher()
|
|
||||||
launcher.launch_service(service, workers=workers)
|
|
||||||
else:
|
|
||||||
launcher = ServiceLauncher()
|
launcher = ServiceLauncher()
|
||||||
launcher.launch_service(service)
|
launcher.launch_service(service)
|
||||||
|
else:
|
||||||
|
launcher = ProcessLauncher()
|
||||||
|
launcher.launch_service(service, workers=workers)
|
||||||
|
|
||||||
return launcher
|
return launcher
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Copyright 2012-2014 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Helper module for systemd service readiness notification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _abstractify(socket_name):
|
||||||
|
if socket_name.startswith('@'):
|
||||||
|
# abstract namespace socket
|
||||||
|
socket_name = '\0%s' % socket_name[1:]
|
||||||
|
return socket_name
|
||||||
|
|
||||||
|
|
||||||
|
def _sd_notify(unset_env, msg):
|
||||||
|
notify_socket = os.getenv('NOTIFY_SOCKET')
|
||||||
|
if notify_socket:
|
||||||
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||||
|
try:
|
||||||
|
sock.connect(_abstractify(notify_socket))
|
||||||
|
sock.sendall(msg)
|
||||||
|
if unset_env:
|
||||||
|
del os.environ['NOTIFY_SOCKET']
|
||||||
|
except EnvironmentError:
|
||||||
|
LOG.debug("Systemd notification failed", exc_info=True)
|
||||||
|
finally:
|
||||||
|
sock.close()
|
||||||
|
|
||||||
|
|
||||||
|
def notify():
|
||||||
|
"""Send notification to Systemd that service is ready.
|
||||||
|
For details see
|
||||||
|
http://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||||
|
"""
|
||||||
|
_sd_notify(False, 'READY=1')
|
||||||
|
|
||||||
|
|
||||||
|
def notify_once():
|
||||||
|
"""Send notification once to Systemd that service is ready.
|
||||||
|
Systemd sets NOTIFY_SOCKET environment variable with the name of the
|
||||||
|
socket listening for notifications from services.
|
||||||
|
This method removes the NOTIFY_SOCKET environment variable to ensure
|
||||||
|
notification is sent only once.
|
||||||
|
"""
|
||||||
|
_sd_notify(True, 'READY=1')
|
||||||
|
|
||||||
|
|
||||||
|
def onready(notify_socket, timeout):
|
||||||
|
"""Wait for systemd style notification on the socket.
|
||||||
|
|
||||||
|
:param notify_socket: local socket address
|
||||||
|
:type notify_socket: string
|
||||||
|
:param timeout: socket timeout
|
||||||
|
:type timeout: float
|
||||||
|
:returns: 0 service ready
|
||||||
|
1 service not ready
|
||||||
|
2 timeout occured
|
||||||
|
"""
|
||||||
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||||
|
sock.settimeout(timeout)
|
||||||
|
sock.bind(_abstractify(notify_socket))
|
||||||
|
try:
|
||||||
|
msg = sock.recv(512)
|
||||||
|
except socket.timeout:
|
||||||
|
return 2
|
||||||
|
finally:
|
||||||
|
sock.close()
|
||||||
|
if 'READY=1' in msg:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# simple CLI for testing
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
notify()
|
||||||
|
elif len(sys.argv) >= 2:
|
||||||
|
timeout = float(sys.argv[1])
|
||||||
|
notify_socket = os.getenv('NOTIFY_SOCKET')
|
||||||
|
if notify_socket:
|
||||||
|
retval = onready(notify_socket, timeout)
|
||||||
|
sys.exit(retval)
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2012 Red Hat, Inc.
|
# Copyright 2012 Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@@ -13,10 +11,10 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import threading
|
||||||
|
|
||||||
from eventlet import greenlet
|
import eventlet
|
||||||
from eventlet import greenpool
|
from eventlet import greenpool
|
||||||
from eventlet import greenthread
|
|
||||||
|
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
from nova.openstack.common import loopingcall
|
from nova.openstack.common import loopingcall
|
||||||
@@ -48,9 +46,12 @@ class Thread(object):
|
|||||||
def wait(self):
|
def wait(self):
|
||||||
return self.thread.wait()
|
return self.thread.wait()
|
||||||
|
|
||||||
|
def link(self, func, *args, **kwargs):
|
||||||
|
self.thread.link(func, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ThreadGroup(object):
|
class ThreadGroup(object):
|
||||||
"""The point of the ThreadGroup classis to:
|
"""The point of the ThreadGroup class is to:
|
||||||
|
|
||||||
* keep track of timers and greenthreads (making it easier to stop them
|
* keep track of timers and greenthreads (making it easier to stop them
|
||||||
when need be).
|
when need be).
|
||||||
@@ -79,13 +80,17 @@ class ThreadGroup(object):
|
|||||||
gt = self.pool.spawn(callback, *args, **kwargs)
|
gt = self.pool.spawn(callback, *args, **kwargs)
|
||||||
th = Thread(gt, self)
|
th = Thread(gt, self)
|
||||||
self.threads.append(th)
|
self.threads.append(th)
|
||||||
|
return th
|
||||||
|
|
||||||
def thread_done(self, thread):
|
def thread_done(self, thread):
|
||||||
self.threads.remove(thread)
|
self.threads.remove(thread)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
current = greenthread.getcurrent()
|
current = threading.current_thread()
|
||||||
for x in self.threads:
|
|
||||||
|
# Iterate over a copy of self.threads so thread_done doesn't
|
||||||
|
# modify the list while we're iterating
|
||||||
|
for x in self.threads[:]:
|
||||||
if x is current:
|
if x is current:
|
||||||
# don't kill the current thread.
|
# don't kill the current thread.
|
||||||
continue
|
continue
|
||||||
@@ -105,17 +110,20 @@ class ThreadGroup(object):
|
|||||||
for x in self.timers:
|
for x in self.timers:
|
||||||
try:
|
try:
|
||||||
x.wait()
|
x.wait()
|
||||||
except greenlet.GreenletExit:
|
except eventlet.greenlet.GreenletExit:
|
||||||
pass
|
pass
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.exception(ex)
|
LOG.exception(ex)
|
||||||
current = greenthread.getcurrent()
|
current = threading.current_thread()
|
||||||
for x in self.threads:
|
|
||||||
|
# Iterate over a copy of self.threads so thread_done doesn't
|
||||||
|
# modify the list while we're iterating
|
||||||
|
for x in self.threads[:]:
|
||||||
if x is current:
|
if x is current:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
x.wait()
|
x.wait()
|
||||||
except greenlet.GreenletExit:
|
except eventlet.greenlet.GreenletExit:
|
||||||
pass
|
pass
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.exception(ex)
|
LOG.exception(ex)
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ def utcnow():
|
|||||||
|
|
||||||
|
|
||||||
def iso8601_from_timestamp(timestamp):
|
def iso8601_from_timestamp(timestamp):
|
||||||
"""Returns a iso8601 formated date from timestamp."""
|
"""Returns a iso8601 formatted date from timestamp."""
|
||||||
return isotime(datetime.datetime.utcfromtimestamp(timestamp))
|
return isotime(datetime.datetime.utcfromtimestamp(timestamp))
|
||||||
|
|
||||||
|
|
||||||
@@ -201,8 +201,8 @@ def total_seconds(delta):
|
|||||||
def is_soon(dt, window):
|
def is_soon(dt, window):
|
||||||
"""Determines if time is going to happen in the next window seconds.
|
"""Determines if time is going to happen in the next window seconds.
|
||||||
|
|
||||||
:params dt: the time
|
:param dt: the time
|
||||||
:params window: minimum seconds to remain to consider the time not soon
|
:param window: minimum seconds to remain to consider the time not soon
|
||||||
|
|
||||||
:return: True if expiration is within the given duration
|
:return: True if expiration is within the given duration
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright (c) 2013 OpenStack Foundation
|
# Copyright (c) 2013 OpenStack Foundation
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@@ -19,8 +17,113 @@
|
|||||||
Helpers for comparing version strings.
|
Helpers for comparing version strings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import functools
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
from nova.openstack.common.gettextutils import _
|
||||||
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class deprecated(object):
|
||||||
|
"""A decorator to mark callables as deprecated.
|
||||||
|
|
||||||
|
This decorator logs a deprecation message when the callable it decorates is
|
||||||
|
used. The message will include the release where the callable was
|
||||||
|
deprecated, the release where it may be removed and possibly an optional
|
||||||
|
replacement.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
1. Specifying the required deprecated release
|
||||||
|
|
||||||
|
>>> @deprecated(as_of=deprecated.ICEHOUSE)
|
||||||
|
... def a(): pass
|
||||||
|
|
||||||
|
2. Specifying a replacement:
|
||||||
|
|
||||||
|
>>> @deprecated(as_of=deprecated.ICEHOUSE, in_favor_of='f()')
|
||||||
|
... def b(): pass
|
||||||
|
|
||||||
|
3. Specifying the release where the functionality may be removed:
|
||||||
|
|
||||||
|
>>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=+1)
|
||||||
|
... def c(): pass
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
FOLSOM = 'F'
|
||||||
|
GRIZZLY = 'G'
|
||||||
|
HAVANA = 'H'
|
||||||
|
ICEHOUSE = 'I'
|
||||||
|
|
||||||
|
_RELEASES = {
|
||||||
|
'F': 'Folsom',
|
||||||
|
'G': 'Grizzly',
|
||||||
|
'H': 'Havana',
|
||||||
|
'I': 'Icehouse',
|
||||||
|
}
|
||||||
|
|
||||||
|
_deprecated_msg_with_alternative = _(
|
||||||
|
'%(what)s is deprecated as of %(as_of)s in favor of '
|
||||||
|
'%(in_favor_of)s and may be removed in %(remove_in)s.')
|
||||||
|
|
||||||
|
_deprecated_msg_no_alternative = _(
|
||||||
|
'%(what)s is deprecated as of %(as_of)s and may be '
|
||||||
|
'removed in %(remove_in)s. It will not be superseded.')
|
||||||
|
|
||||||
|
def __init__(self, as_of, in_favor_of=None, remove_in=2, what=None):
|
||||||
|
"""Initialize decorator
|
||||||
|
|
||||||
|
:param as_of: the release deprecating the callable. Constants
|
||||||
|
are define in this class for convenience.
|
||||||
|
:param in_favor_of: the replacement for the callable (optional)
|
||||||
|
:param remove_in: an integer specifying how many releases to wait
|
||||||
|
before removing (default: 2)
|
||||||
|
:param what: name of the thing being deprecated (default: the
|
||||||
|
callable's name)
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.as_of = as_of
|
||||||
|
self.in_favor_of = in_favor_of
|
||||||
|
self.remove_in = remove_in
|
||||||
|
self.what = what
|
||||||
|
|
||||||
|
def __call__(self, func):
|
||||||
|
if not self.what:
|
||||||
|
self.what = func.__name__ + '()'
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
msg, details = self._build_message()
|
||||||
|
LOG.deprecated(msg, details)
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
def _get_safe_to_remove_release(self, release):
|
||||||
|
# TODO(dstanek): this method will have to be reimplemented once
|
||||||
|
# when we get to the X release because once we get to the Y
|
||||||
|
# release, what is Y+2?
|
||||||
|
new_release = chr(ord(release) + self.remove_in)
|
||||||
|
if new_release in self._RELEASES:
|
||||||
|
return self._RELEASES[new_release]
|
||||||
|
else:
|
||||||
|
return new_release
|
||||||
|
|
||||||
|
def _build_message(self):
|
||||||
|
details = dict(what=self.what,
|
||||||
|
as_of=self._RELEASES[self.as_of],
|
||||||
|
remove_in=self._get_safe_to_remove_release(self.as_of))
|
||||||
|
|
||||||
|
if self.in_favor_of:
|
||||||
|
details['in_favor_of'] = self.in_favor_of
|
||||||
|
msg = self._deprecated_msg_with_alternative
|
||||||
|
else:
|
||||||
|
msg = self._deprecated_msg_no_alternative
|
||||||
|
return msg, details
|
||||||
|
|
||||||
|
|
||||||
def is_compatible(requested_version, current_version, same_major=True):
|
def is_compatible(requested_version, current_version, same_major=True):
|
||||||
"""Determine whether `requested_version` is satisfied by
|
"""Determine whether `requested_version` is satisfied by
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2013 IBM Corp.
|
# Copyright 2013 IBM Corp.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ module=report.views.text
|
|||||||
module=service
|
module=service
|
||||||
module=sslutils
|
module=sslutils
|
||||||
module=strutils
|
module=strutils
|
||||||
|
module=systemd
|
||||||
module=threadgroup
|
module=threadgroup
|
||||||
module=timeutils
|
module=timeutils
|
||||||
module=units
|
module=units
|
||||||
|
|||||||
Reference in New Issue
Block a user