Merge "Sync with latest oslo-incubator"
This commit is contained in:
commit
fc024b290f
@ -20,6 +20,7 @@ import time
|
|||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
from oslo.utils import strutils
|
||||||
import six
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
@ -905,7 +906,7 @@ class Resource(wsgi.Application):
|
|||||||
"%(body)s") % {'action': action,
|
"%(body)s") % {'action': action,
|
||||||
'body': unicode(body, 'utf-8'),
|
'body': unicode(body, 'utf-8'),
|
||||||
'meth': str(meth)}
|
'meth': str(meth)}
|
||||||
LOG.debug(logging.mask_password(msg))
|
LOG.debug(strutils.mask_password(msg))
|
||||||
else:
|
else:
|
||||||
LOG.debug("Calling method '%(meth)s'",
|
LOG.debug("Calling method '%(meth)s'",
|
||||||
{'meth': str(meth)})
|
{'meth': str(meth)})
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
|
|
40
nova/openstack/common/_i18n.py
Normal file
40
nova/openstack/common/_i18n.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""oslo.i18n integration module.
|
||||||
|
|
||||||
|
See http://docs.openstack.org/developer/oslo.i18n/usage.html
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import oslo.i18n
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
|
||||||
|
# application name when this module is synced into the separate
|
||||||
|
# repository. It is OK to have more than one translation function
|
||||||
|
# using the same domain, since there will still only be one message
|
||||||
|
# catalog.
|
||||||
|
_translators = oslo.i18n.TranslatorFactory(domain='nova')
|
||||||
|
|
||||||
|
# The primary translation function using the well-known name "_"
|
||||||
|
_ = _translators.primary
|
||||||
|
|
||||||
|
# Translators for log levels.
|
||||||
|
#
|
||||||
|
# The abbreviated names are meant to reflect the usual use of a short
|
||||||
|
# name like '_'. The "L" is for "log" and the other letter comes from
|
||||||
|
# the level.
|
||||||
|
_LI = _translators.log_info
|
||||||
|
_LW = _translators.log_warning
|
||||||
|
_LE = _translators.log_error
|
||||||
|
_LC = _translators.log_critical
|
@ -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
|
||||||
@ -14,20 +12,33 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
# W0603: Using the global statement
|
||||||
|
# W0621: Redefining name %s from outer scope
|
||||||
|
# pylint: disable=W0603,W0621
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import getpass
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from oslo.utils import encodeutils
|
||||||
|
from oslo.utils import strutils
|
||||||
|
import prettytable
|
||||||
|
import six
|
||||||
|
from six import moves
|
||||||
|
|
||||||
|
from nova.openstack.common._i18n import _
|
||||||
|
|
||||||
|
|
||||||
class MissingArgs(Exception):
|
class MissingArgs(Exception):
|
||||||
|
"""Supplied arguments are not sufficient for calling a function."""
|
||||||
def __init__(self, missing):
|
def __init__(self, missing):
|
||||||
self.missing = missing
|
self.missing = missing
|
||||||
|
msg = _("Missing arguments: %s") % ", ".join(missing)
|
||||||
def __str__(self):
|
super(MissingArgs, self).__init__(msg)
|
||||||
if len(self.missing) == 1:
|
|
||||||
return "An argument is missing"
|
|
||||||
else:
|
|
||||||
return ("%(num)d arguments are missing" %
|
|
||||||
dict(num=len(self.missing)))
|
|
||||||
|
|
||||||
|
|
||||||
def validate_args(fn, *args, **kwargs):
|
def validate_args(fn, *args, **kwargs):
|
||||||
@ -36,11 +47,11 @@ def validate_args(fn, *args, **kwargs):
|
|||||||
>>> validate_args(lambda a: None)
|
>>> validate_args(lambda a: None)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
MissingArgs: An argument is missing
|
MissingArgs: Missing argument(s): a
|
||||||
>>> validate_args(lambda a, b, c, d: None, 0, c=1)
|
>>> validate_args(lambda a, b, c, d: None, 0, c=1)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
MissingArgs: 2 arguments are missing
|
MissingArgs: Missing argument(s): b, d
|
||||||
|
|
||||||
:param fn: the function to check
|
:param fn: the function to check
|
||||||
:param arg: the positional arguments supplied
|
:param arg: the positional arguments supplied
|
||||||
@ -52,7 +63,7 @@ def validate_args(fn, *args, **kwargs):
|
|||||||
required_args = argspec.args[:len(argspec.args) - num_defaults]
|
required_args = argspec.args[:len(argspec.args) - num_defaults]
|
||||||
|
|
||||||
def isbound(method):
|
def isbound(method):
|
||||||
return getattr(method, 'im_self', None) is not None
|
return getattr(method, '__self__', None) is not None
|
||||||
|
|
||||||
if isbound(fn):
|
if isbound(fn):
|
||||||
required_args.pop(0)
|
required_args.pop(0)
|
||||||
@ -61,3 +72,193 @@ def validate_args(fn, *args, **kwargs):
|
|||||||
missing = missing[len(args):]
|
missing = missing[len(args):]
|
||||||
if missing:
|
if missing:
|
||||||
raise MissingArgs(missing)
|
raise MissingArgs(missing)
|
||||||
|
|
||||||
|
|
||||||
|
def arg(*args, **kwargs):
|
||||||
|
"""Decorator for CLI args.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> @arg("name", help="Name of the new entity")
|
||||||
|
... def entity_create(args):
|
||||||
|
... pass
|
||||||
|
"""
|
||||||
|
def _decorator(func):
|
||||||
|
add_arg(func, *args, **kwargs)
|
||||||
|
return func
|
||||||
|
return _decorator
|
||||||
|
|
||||||
|
|
||||||
|
def env(*args, **kwargs):
|
||||||
|
"""Returns the first environment variable set.
|
||||||
|
|
||||||
|
If all are empty, defaults to '' or keyword arg `default`.
|
||||||
|
"""
|
||||||
|
for arg in args:
|
||||||
|
value = os.environ.get(arg)
|
||||||
|
if value:
|
||||||
|
return value
|
||||||
|
return kwargs.get('default', '')
|
||||||
|
|
||||||
|
|
||||||
|
def add_arg(func, *args, **kwargs):
|
||||||
|
"""Bind CLI arguments to a shell.py `do_foo` function."""
|
||||||
|
|
||||||
|
if not hasattr(func, 'arguments'):
|
||||||
|
func.arguments = []
|
||||||
|
|
||||||
|
# NOTE(sirp): avoid dups that can occur when the module is shared across
|
||||||
|
# tests.
|
||||||
|
if (args, kwargs) not in func.arguments:
|
||||||
|
# Because of the semantics of decorator composition if we just append
|
||||||
|
# to the options list positional options will appear to be backwards.
|
||||||
|
func.arguments.insert(0, (args, kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
def unauthenticated(func):
|
||||||
|
"""Adds 'unauthenticated' attribute to decorated function.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
>>> @unauthenticated
|
||||||
|
... def mymethod(f):
|
||||||
|
... pass
|
||||||
|
"""
|
||||||
|
func.unauthenticated = True
|
||||||
|
return func
|
||||||
|
|
||||||
|
|
||||||
|
def isunauthenticated(func):
|
||||||
|
"""Checks if the function does not require authentication.
|
||||||
|
|
||||||
|
Mark such functions with the `@unauthenticated` decorator.
|
||||||
|
|
||||||
|
:returns: bool
|
||||||
|
"""
|
||||||
|
return getattr(func, 'unauthenticated', False)
|
||||||
|
|
||||||
|
|
||||||
|
def print_list(objs, fields, formatters=None, sortby_index=0,
|
||||||
|
mixed_case_fields=None, field_labels=None):
|
||||||
|
"""Print a list or objects as a table, one row per object.
|
||||||
|
|
||||||
|
:param objs: iterable of :class:`Resource`
|
||||||
|
:param fields: attributes that correspond to columns, in order
|
||||||
|
:param formatters: `dict` of callables for field formatting
|
||||||
|
:param sortby_index: index of the field for sorting table rows
|
||||||
|
:param mixed_case_fields: fields corresponding to object attributes that
|
||||||
|
have mixed case names (e.g., 'serverId')
|
||||||
|
:param field_labels: Labels to use in the heading of the table, default to
|
||||||
|
fields.
|
||||||
|
"""
|
||||||
|
formatters = formatters or {}
|
||||||
|
mixed_case_fields = mixed_case_fields or []
|
||||||
|
field_labels = field_labels or fields
|
||||||
|
if len(field_labels) != len(fields):
|
||||||
|
raise ValueError(_("Field labels list %(labels)s has different number "
|
||||||
|
"of elements than fields list %(fields)s"),
|
||||||
|
{'labels': field_labels, 'fields': fields})
|
||||||
|
|
||||||
|
if sortby_index is None:
|
||||||
|
kwargs = {}
|
||||||
|
else:
|
||||||
|
kwargs = {'sortby': field_labels[sortby_index]}
|
||||||
|
pt = prettytable.PrettyTable(field_labels)
|
||||||
|
pt.align = 'l'
|
||||||
|
|
||||||
|
for o in objs:
|
||||||
|
row = []
|
||||||
|
for field in fields:
|
||||||
|
if field in formatters:
|
||||||
|
row.append(formatters[field](o))
|
||||||
|
else:
|
||||||
|
if field in mixed_case_fields:
|
||||||
|
field_name = field.replace(' ', '_')
|
||||||
|
else:
|
||||||
|
field_name = field.lower().replace(' ', '_')
|
||||||
|
data = getattr(o, field_name, '')
|
||||||
|
row.append(data)
|
||||||
|
pt.add_row(row)
|
||||||
|
|
||||||
|
print(encodeutils.safe_encode(pt.get_string(**kwargs)))
|
||||||
|
|
||||||
|
|
||||||
|
def print_dict(dct, dict_property="Property", wrap=0):
|
||||||
|
"""Print a `dict` as a table of two columns.
|
||||||
|
|
||||||
|
:param dct: `dict` to print
|
||||||
|
:param dict_property: name of the first column
|
||||||
|
:param wrap: wrapping for the second column
|
||||||
|
"""
|
||||||
|
pt = prettytable.PrettyTable([dict_property, 'Value'])
|
||||||
|
pt.align = 'l'
|
||||||
|
for k, v in six.iteritems(dct):
|
||||||
|
# convert dict to str to check length
|
||||||
|
if isinstance(v, dict):
|
||||||
|
v = six.text_type(v)
|
||||||
|
if wrap > 0:
|
||||||
|
v = textwrap.fill(six.text_type(v), wrap)
|
||||||
|
# if value has a newline, add in multiple rows
|
||||||
|
# e.g. fault with stacktrace
|
||||||
|
if v and isinstance(v, six.string_types) and r'\n' in v:
|
||||||
|
lines = v.strip().split(r'\n')
|
||||||
|
col1 = k
|
||||||
|
for line in lines:
|
||||||
|
pt.add_row([col1, line])
|
||||||
|
col1 = ''
|
||||||
|
else:
|
||||||
|
pt.add_row([k, v])
|
||||||
|
print(encodeutils.safe_encode(pt.get_string()))
|
||||||
|
|
||||||
|
|
||||||
|
def get_password(max_password_prompts=3):
|
||||||
|
"""Read password from TTY."""
|
||||||
|
verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD"))
|
||||||
|
pw = None
|
||||||
|
if hasattr(sys.stdin, "isatty") and sys.stdin.isatty():
|
||||||
|
# Check for Ctrl-D
|
||||||
|
try:
|
||||||
|
for __ in moves.range(max_password_prompts):
|
||||||
|
pw1 = getpass.getpass("OS Password: ")
|
||||||
|
if verify:
|
||||||
|
pw2 = getpass.getpass("Please verify: ")
|
||||||
|
else:
|
||||||
|
pw2 = pw1
|
||||||
|
if pw1 == pw2 and pw1:
|
||||||
|
pw = pw1
|
||||||
|
break
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
|
return pw
|
||||||
|
|
||||||
|
|
||||||
|
def service_type(stype):
|
||||||
|
"""Adds 'service_type' attribute to decorated function.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@service_type('volume')
|
||||||
|
def mymethod(f):
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
def inner(f):
|
||||||
|
f.service_type = stype
|
||||||
|
return f
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
def get_service_type(f):
|
||||||
|
"""Retrieves service type from function."""
|
||||||
|
return getattr(f, 'service_type', None)
|
||||||
|
|
||||||
|
|
||||||
|
def pretty_choice_list(l):
|
||||||
|
return ', '.join("'%s'" % i for i in l)
|
||||||
|
|
||||||
|
|
||||||
|
def exit(msg=''):
|
||||||
|
if msg:
|
||||||
|
print (msg, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
@ -29,7 +29,7 @@ import eventlet.backdoor
|
|||||||
import greenlet
|
import greenlet
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _LI
|
from nova.openstack.common._i18n import _LI
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
help_for_backdoor_port = (
|
help_for_backdoor_port = (
|
||||||
@ -41,7 +41,6 @@ help_for_backdoor_port = (
|
|||||||
"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,
|
|
||||||
help="Enable eventlet backdoor. %s" % help_for_backdoor_port)
|
help="Enable eventlet backdoor. %s" % help_for_backdoor_port)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ import errno
|
|||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from nova.openstack.common import excutils
|
from oslo.utils import excutils
|
||||||
|
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -0,0 +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
|
||||||
|
|
||||||
|
|
||||||
|
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
|
@ -48,4 +48,4 @@ class LockFixture(fixtures.Fixture):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(LockFixture, self).setUp()
|
super(LockFixture, self).setUp()
|
||||||
self.addCleanup(self.mgr.__exit__, None, None, None)
|
self.addCleanup(self.mgr.__exit__, None, None, None)
|
||||||
self.mgr.__enter__()
|
self.lock = self.mgr.__enter__()
|
||||||
|
@ -21,8 +21,9 @@ Helper methods to deal with images.
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from oslo.utils import strutils
|
||||||
from nova.openstack.common import strutils
|
|
||||||
|
from nova.openstack.common._i18n import _
|
||||||
|
|
||||||
|
|
||||||
class QemuImgInfo(object):
|
class QemuImgInfo(object):
|
||||||
@ -62,7 +63,7 @@ class QemuImgInfo(object):
|
|||||||
# Standardize on underscores/lc/no dash and no spaces
|
# Standardize on underscores/lc/no dash and no spaces
|
||||||
# since qemu seems to have mixed outputs here... and
|
# since qemu seems to have mixed outputs here... and
|
||||||
# this format allows for better integration with python
|
# this format allows for better integration with python
|
||||||
# - ie for usage in kwargs and such...
|
# - i.e. for usage in kwargs and such...
|
||||||
field = field.lower().strip()
|
field = field.lower().strip()
|
||||||
for c in (" ", "-"):
|
for c in (" ", "-"):
|
||||||
field = field.replace(c, '_')
|
field = field.replace(c, '_')
|
||||||
|
@ -29,7 +29,7 @@ import weakref
|
|||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova.openstack.common import fileutils
|
from nova.openstack.common import fileutils
|
||||||
from nova.openstack.common.gettextutils import _, _LE, _LI
|
from nova.openstack.common._i18n import _, _LE, _LI
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -38,18 +38,15 @@ import sys
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
from oslo.serialization import jsonutils
|
||||||
|
from oslo.utils import importutils
|
||||||
import six
|
import six
|
||||||
from six import moves
|
from six import moves
|
||||||
|
|
||||||
_PY26 = sys.version_info[0:2] == (2, 6)
|
_PY26 = sys.version_info[0:2] == (2, 6)
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common._i18n import _
|
||||||
from nova.openstack.common import importutils
|
|
||||||
from nova.openstack.common import jsonutils
|
|
||||||
from nova.openstack.common import local
|
from nova.openstack.common import local
|
||||||
# NOTE(flaper87): Pls, remove when graduating this module
|
|
||||||
# from the incubator.
|
|
||||||
from nova.openstack.common.strutils import mask_password # noqa
|
|
||||||
|
|
||||||
|
|
||||||
_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||||
|
@ -21,7 +21,7 @@ import time
|
|||||||
from eventlet import event
|
from eventlet import event
|
||||||
from eventlet import greenthread
|
from eventlet import greenthread
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _LE, _LW
|
from nova.openstack.common._i18n import _LE, _LW
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
"""Super simple fake memcache client."""
|
"""Super simple fake memcache client."""
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from oslo.utils import timeutils
|
from oslo.utils import timeutils
|
||||||
|
|
||||||
memcache_opts = [
|
memcache_opts = [
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
"""Base class(es) for WSGI Middleware."""
|
"""Base class(es) for WSGI Middleware."""
|
||||||
|
|
||||||
import webob.dec
|
import webob.dec
|
||||||
|
46
nova/openstack/common/middleware/catch_errors.py
Normal file
46
nova/openstack/common/middleware/catch_errors.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Copyright (c) 2013 NEC Corporation
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Middleware that provides high-level error handling.
|
||||||
|
|
||||||
|
It catches all exceptions from subsequent applications in WSGI pipeline
|
||||||
|
to hide internal errors from API response.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import webob.dec
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
|
from nova.openstack.common._i18n import _LE
|
||||||
|
from nova.openstack.common.middleware import base
|
||||||
|
from nova.openstack.common import versionutils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@versionutils.deprecated(as_of=versionutils.deprecated.JUNO,
|
||||||
|
in_favor_of='oslo.middleware.CatchErrors')
|
||||||
|
class CatchErrorsMiddleware(base.Middleware):
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
try:
|
||||||
|
response = req.get_response(self.application)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(_LE('An error occurred during '
|
||||||
|
'processing the request: %s'))
|
||||||
|
response = webob.exc.HTTPInternalServerError()
|
||||||
|
return response
|
31
nova/openstack/common/middleware/correlation_id.py
Normal file
31
nova/openstack/common/middleware/correlation_id.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Copyright (c) 2013 Rackspace Hosting
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Middleware that attaches a correlation id to WSGI request"""
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from nova.openstack.common.middleware import base
|
||||||
|
from nova.openstack.common import versionutils
|
||||||
|
|
||||||
|
|
||||||
|
@versionutils.deprecated(as_of=versionutils.deprecated.JUNO,
|
||||||
|
in_favor_of='oslo.middleware.CorrelationId')
|
||||||
|
class CorrelationIdMiddleware(base.Middleware):
|
||||||
|
|
||||||
|
def process_request(self, req):
|
||||||
|
correlation_id = (req.headers.get("X_CORRELATION_ID") or
|
||||||
|
str(uuid.uuid4()))
|
||||||
|
req.headers['X_CORRELATION_ID'] = correlation_id
|
63
nova/openstack/common/middleware/debug.py
Normal file
63
nova/openstack/common/middleware/debug.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Debug middleware"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import six
|
||||||
|
import webob.dec
|
||||||
|
|
||||||
|
from nova.openstack.common.middleware import base
|
||||||
|
from nova.openstack.common import versionutils
|
||||||
|
|
||||||
|
|
||||||
|
@versionutils.deprecated(as_of=versionutils.deprecated.JUNO,
|
||||||
|
in_favor_of='oslo.middleware.Debug')
|
||||||
|
class Debug(base.Middleware):
|
||||||
|
"""Helper class that returns debug information.
|
||||||
|
|
||||||
|
Can be inserted into any WSGI application chain to get information about
|
||||||
|
the request and response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
print(("*" * 40) + " REQUEST ENVIRON")
|
||||||
|
for key, value in req.environ.items():
|
||||||
|
print(key, "=", value)
|
||||||
|
print()
|
||||||
|
resp = req.get_response(self.application)
|
||||||
|
|
||||||
|
print(("*" * 40) + " RESPONSE HEADERS")
|
||||||
|
for (key, value) in six.iteritems(resp.headers):
|
||||||
|
print(key, "=", value)
|
||||||
|
print()
|
||||||
|
|
||||||
|
resp.app_iter = self.print_generator(resp.app_iter)
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def print_generator(app_iter):
|
||||||
|
"""Prints the contents of a wrapper string iterator when iterated."""
|
||||||
|
print(("*" * 40) + " BODY")
|
||||||
|
for part in app_iter:
|
||||||
|
sys.stdout.write(part)
|
||||||
|
sys.stdout.flush()
|
||||||
|
yield part
|
||||||
|
print()
|
@ -23,12 +23,15 @@ import webob.dec
|
|||||||
|
|
||||||
from nova.openstack.common import context
|
from nova.openstack.common import context
|
||||||
from nova.openstack.common.middleware import base
|
from nova.openstack.common.middleware import base
|
||||||
|
from nova.openstack.common import versionutils
|
||||||
|
|
||||||
|
|
||||||
ENV_REQUEST_ID = 'openstack.request_id'
|
ENV_REQUEST_ID = 'openstack.request_id'
|
||||||
HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
|
HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
|
||||||
|
|
||||||
|
|
||||||
|
@versionutils.deprecated(as_of=versionutils.deprecated.JUNO,
|
||||||
|
in_favor_of='oslo.middleware.RequestId')
|
||||||
class RequestIdMiddleware(base.Middleware):
|
class RequestIdMiddleware(base.Middleware):
|
||||||
|
|
||||||
@webob.dec.wsgify
|
@webob.dec.wsgify
|
||||||
|
85
nova/openstack/common/middleware/sizelimit.py
Normal file
85
nova/openstack/common/middleware/sizelimit.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# Copyright (c) 2012 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Request Body limiting middleware.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
import webob.dec
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
|
from nova.openstack.common._i18n import _
|
||||||
|
from nova.openstack.common.middleware import base
|
||||||
|
from nova.openstack.common import versionutils
|
||||||
|
|
||||||
|
|
||||||
|
# default request size is 112k
|
||||||
|
max_req_body_size = cfg.IntOpt('max_request_body_size',
|
||||||
|
deprecated_name='osapi_max_request_body_size',
|
||||||
|
default=114688,
|
||||||
|
help='The maximum body size for each '
|
||||||
|
' request, in bytes.')
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF.register_opt(max_req_body_size)
|
||||||
|
|
||||||
|
|
||||||
|
class LimitingReader(object):
|
||||||
|
"""Reader to limit the size of an incoming request."""
|
||||||
|
def __init__(self, data, limit):
|
||||||
|
"""Initiates LimitingReader object.
|
||||||
|
|
||||||
|
:param data: Underlying data object
|
||||||
|
:param limit: maximum number of bytes the reader should allow
|
||||||
|
"""
|
||||||
|
self.data = data
|
||||||
|
self.limit = limit
|
||||||
|
self.bytes_read = 0
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for chunk in self.data:
|
||||||
|
self.bytes_read += len(chunk)
|
||||||
|
if self.bytes_read > self.limit:
|
||||||
|
msg = _("Request is too large.")
|
||||||
|
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||||
|
else:
|
||||||
|
yield chunk
|
||||||
|
|
||||||
|
def read(self, i=None):
|
||||||
|
result = self.data.read(i)
|
||||||
|
self.bytes_read += len(result)
|
||||||
|
if self.bytes_read > self.limit:
|
||||||
|
msg = _("Request is too large.")
|
||||||
|
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@versionutils.deprecated(as_of=versionutils.deprecated.JUNO,
|
||||||
|
in_favor_of='oslo.middleware.RequestBodySizeLimiter')
|
||||||
|
class RequestBodySizeLimiter(base.Middleware):
|
||||||
|
"""Limit the size of incoming requests."""
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
if (req.content_length is not None and
|
||||||
|
req.content_length > CONF.max_request_body_size):
|
||||||
|
msg = _("Request is too large.")
|
||||||
|
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||||
|
if req.content_length is None and req.is_body_readable:
|
||||||
|
limiter = LimitingReader(req.body_file,
|
||||||
|
CONF.max_request_body_size)
|
||||||
|
req.body_file = limiter
|
||||||
|
return self.application
|
@ -17,7 +17,7 @@ import time
|
|||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _, _LE, _LI
|
from nova.openstack.common._i18n import _, _LE, _LI
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,16 +77,17 @@ as it allows particular rules to be explicitly disabled.
|
|||||||
|
|
||||||
import abc
|
import abc
|
||||||
import ast
|
import ast
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
from oslo.serialization import jsonutils
|
||||||
import six
|
import six
|
||||||
import six.moves.urllib.parse as urlparse
|
import six.moves.urllib.parse as urlparse
|
||||||
import six.moves.urllib.request as urlrequest
|
import six.moves.urllib.request as urlrequest
|
||||||
|
|
||||||
from nova.openstack.common import fileutils
|
from nova.openstack.common import fileutils
|
||||||
from nova.openstack.common.gettextutils import _, _LE
|
from nova.openstack.common._i18n import _, _LE, _LW
|
||||||
from nova.openstack.common import jsonutils
|
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
|
||||||
@ -98,6 +99,10 @@ policy_opts = [
|
|||||||
default='default',
|
default='default',
|
||||||
help=_('Default rule. Enforced when a requested rule is not '
|
help=_('Default rule. Enforced when a requested rule is not '
|
||||||
'found.')),
|
'found.')),
|
||||||
|
cfg.MultiStrOpt('policy_dirs',
|
||||||
|
default=['policy.d'],
|
||||||
|
help=_('The directories of policy configuration files is '
|
||||||
|
'stored')),
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -216,6 +221,7 @@ class Enforcer(object):
|
|||||||
def clear(self):
|
def clear(self):
|
||||||
"""Clears Enforcer rules, policy's cache and policy's path."""
|
"""Clears Enforcer rules, policy's cache and policy's path."""
|
||||||
self.set_rules({})
|
self.set_rules({})
|
||||||
|
fileutils.delete_cached_file(self.policy_path)
|
||||||
self.default_rule = None
|
self.default_rule = None
|
||||||
self.policy_path = None
|
self.policy_path = None
|
||||||
|
|
||||||
@ -232,31 +238,53 @@ class Enforcer(object):
|
|||||||
|
|
||||||
if self.use_conf:
|
if self.use_conf:
|
||||||
if not self.policy_path:
|
if not self.policy_path:
|
||||||
self.policy_path = self._get_policy_path()
|
self.policy_path = self._get_policy_path(self.policy_file)
|
||||||
|
|
||||||
|
self._load_policy_file(self.policy_path, force_reload)
|
||||||
|
for path in CONF.policy_dirs:
|
||||||
|
try:
|
||||||
|
path = self._get_policy_path(path)
|
||||||
|
except cfg.ConfigFilesNotFoundError:
|
||||||
|
LOG.warn(_LW("Can not find policy directories %s"), path)
|
||||||
|
continue
|
||||||
|
self._walk_through_policy_directory(path,
|
||||||
|
self._load_policy_file,
|
||||||
|
force_reload, False)
|
||||||
|
|
||||||
|
def _walk_through_policy_directory(self, path, func, *args):
|
||||||
|
# We do not iterate over sub-directories.
|
||||||
|
policy_files = next(os.walk(path))[2]
|
||||||
|
policy_files.sort()
|
||||||
|
for policy_file in [p for p in policy_files if not p.startswith('.')]:
|
||||||
|
func(os.path.join(path, policy_file), *args)
|
||||||
|
|
||||||
|
def _load_policy_file(self, path, force_reload, overwrite=True):
|
||||||
reloaded, data = fileutils.read_cached_file(
|
reloaded, data = fileutils.read_cached_file(
|
||||||
self.policy_path, force_reload=force_reload)
|
path, force_reload=force_reload)
|
||||||
if reloaded or not self.rules:
|
if reloaded or not self.rules:
|
||||||
rules = Rules.load_json(data, self.default_rule)
|
rules = Rules.load_json(data, self.default_rule)
|
||||||
self.set_rules(rules)
|
self.set_rules(rules, overwrite)
|
||||||
LOG.debug("Rules successfully reloaded")
|
LOG.debug("Rules successfully reloaded")
|
||||||
|
|
||||||
def _get_policy_path(self):
|
def _get_policy_path(self, path):
|
||||||
"""Locate the policy json data file.
|
"""Locate the policy json data file/path.
|
||||||
|
|
||||||
:param policy_file: Custom policy file to locate.
|
:param path: It's value can be a full path or related path. When
|
||||||
|
full path specified, this function just returns the full
|
||||||
|
path. When related path specified, this function will
|
||||||
|
search configuration directories to find one that exists.
|
||||||
|
|
||||||
:returns: The policy path
|
:returns: The policy path
|
||||||
|
|
||||||
:raises: ConfigFilesNotFoundError if the file couldn't
|
:raises: ConfigFilesNotFoundError if the file/path couldn't
|
||||||
be located.
|
be located.
|
||||||
"""
|
"""
|
||||||
policy_file = CONF.find_file(self.policy_file)
|
policy_path = CONF.find_file(path)
|
||||||
|
|
||||||
if policy_file:
|
if policy_path:
|
||||||
return policy_file
|
return policy_path
|
||||||
|
|
||||||
raise cfg.ConfigFilesNotFoundError((self.policy_file,))
|
raise cfg.ConfigFilesNotFoundError((path,))
|
||||||
|
|
||||||
def enforce(self, rule, target, creds, do_raise=False,
|
def enforce(self, rule, target, creds, do_raise=False,
|
||||||
exc=None, *args, **kwargs):
|
exc=None, *args, **kwargs):
|
||||||
@ -784,7 +812,7 @@ def _parse_text_rule(rule):
|
|||||||
return state.result
|
return state.result
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Couldn't parse the rule
|
# Couldn't parse the rule
|
||||||
LOG.exception(_LE("Failed to understand rule %r") % rule)
|
LOG.exception(_LE("Failed to understand rule %s") % rule)
|
||||||
|
|
||||||
# Fail closed
|
# Fail closed
|
||||||
return FalseCheck()
|
return FalseCheck()
|
||||||
@ -875,7 +903,6 @@ class GenericCheck(Check):
|
|||||||
'Member':%(role.name)s
|
'Member':%(role.name)s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO(termie): do dict inspection via dot syntax
|
|
||||||
try:
|
try:
|
||||||
match = self.match % target
|
match = self.match % target
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -888,7 +915,10 @@ class GenericCheck(Check):
|
|||||||
leftval = ast.literal_eval(self.kind)
|
leftval = ast.literal_eval(self.kind)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
try:
|
try:
|
||||||
leftval = creds[self.kind]
|
kind_parts = self.kind.split('.')
|
||||||
|
leftval = creds
|
||||||
|
for kind_part in kind_parts:
|
||||||
|
leftval = leftval[kind_part]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return False
|
||||||
return match == six.text_type(leftval)
|
return match == six.text_type(leftval)
|
||||||
|
@ -18,7 +18,7 @@ System-level utilities and helper functions.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
import logging as stdlib_logging
|
import logging
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
@ -27,11 +27,10 @@ import signal
|
|||||||
|
|
||||||
from eventlet.green import subprocess
|
from eventlet.green import subprocess
|
||||||
from eventlet import greenthread
|
from eventlet import greenthread
|
||||||
|
from oslo.utils import strutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common._i18n import _
|
||||||
from nova.openstack.common import log as logging
|
|
||||||
from nova.openstack.common import strutils
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -116,8 +115,7 @@ def execute(*cmd, **kwargs):
|
|||||||
execute this command. Defaults to false.
|
execute this command. Defaults to false.
|
||||||
:type shell: boolean
|
:type shell: boolean
|
||||||
:param loglevel: log level for execute commands.
|
:param loglevel: log level for execute commands.
|
||||||
:type loglevel: int. (Should be stdlib_logging.DEBUG or
|
:type loglevel: int. (Should be logging.DEBUG or logging.INFO)
|
||||||
stdlib_logging.INFO)
|
|
||||||
:returns: (stdout, stderr) from process execution
|
:returns: (stdout, stderr) from process execution
|
||||||
:raises: :class:`UnknownArgumentError` on
|
:raises: :class:`UnknownArgumentError` on
|
||||||
receiving unknown arguments
|
receiving unknown arguments
|
||||||
@ -133,7 +131,7 @@ def execute(*cmd, **kwargs):
|
|||||||
run_as_root = kwargs.pop('run_as_root', False)
|
run_as_root = kwargs.pop('run_as_root', False)
|
||||||
root_helper = kwargs.pop('root_helper', '')
|
root_helper = kwargs.pop('root_helper', '')
|
||||||
shell = kwargs.pop('shell', False)
|
shell = kwargs.pop('shell', False)
|
||||||
loglevel = kwargs.pop('loglevel', stdlib_logging.DEBUG)
|
loglevel = kwargs.pop('loglevel', logging.DEBUG)
|
||||||
|
|
||||||
if isinstance(check_exit_code, bool):
|
if isinstance(check_exit_code, bool):
|
||||||
ignore_exit_code = not check_exit_code
|
ignore_exit_code = not check_exit_code
|
||||||
@ -142,8 +140,7 @@ def execute(*cmd, **kwargs):
|
|||||||
check_exit_code = [check_exit_code]
|
check_exit_code = [check_exit_code]
|
||||||
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
raise UnknownArgumentError(_('Got unknown keyword args '
|
raise UnknownArgumentError(_('Got unknown keyword args: %r') % kwargs)
|
||||||
'to utils.execute: %r') % kwargs)
|
|
||||||
|
|
||||||
if run_as_root and hasattr(os, 'geteuid') and os.geteuid() != 0:
|
if run_as_root and hasattr(os, 'geteuid') and os.geteuid() != 0:
|
||||||
if not root_helper:
|
if not root_helper:
|
||||||
|
@ -12,7 +12,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.
|
||||||
|
|
||||||
"""Provides Openstack config generators
|
"""Provides OpenStack config generators
|
||||||
|
|
||||||
This module defines a class for configuration
|
This module defines a class for configuration
|
||||||
generators for generating the model in
|
generators for generating the model in
|
||||||
@ -21,7 +21,7 @@ generators for generating the model in
|
|||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
import nova.openstack.common.report.models.conf as cm
|
from nova.openstack.common.report.models import conf as cm
|
||||||
|
|
||||||
|
|
||||||
class ConfigReportGenerator(object):
|
class ConfigReportGenerator(object):
|
||||||
@ -31,7 +31,7 @@ class ConfigReportGenerator(object):
|
|||||||
:class:`openstack.common.report.models.conf.ConfigModel`,
|
:class:`openstack.common.report.models.conf.ConfigModel`,
|
||||||
by default using the configuration options stored
|
by default using the configuration options stored
|
||||||
in :attr:`oslo.config.cfg.CONF`, which is where
|
in :attr:`oslo.config.cfg.CONF`, which is where
|
||||||
Openstack stores everything.
|
OpenStack stores everything.
|
||||||
|
|
||||||
:param cnf: the configuration option object
|
:param cnf: the configuration option object
|
||||||
:type cnf: :class:`oslo.config.cfg.ConfigOpts`
|
:type cnf: :class:`oslo.config.cfg.ConfigOpts`
|
||||||
|
@ -23,10 +23,10 @@ import sys
|
|||||||
|
|
||||||
import greenlet
|
import greenlet
|
||||||
|
|
||||||
import nova.openstack.common.report.models.threading as tm
|
from nova.openstack.common.report.models import threading as tm
|
||||||
from nova.openstack.common.report.models import with_default_views as mwdv
|
from nova.openstack.common.report.models import with_default_views as mwdv
|
||||||
import nova.openstack.common.report.utils as rutils
|
from nova.openstack.common.report import utils as rutils
|
||||||
import nova.openstack.common.report.views.text.generic as text_views
|
from nova.openstack.common.report.views.text import generic as text_views
|
||||||
|
|
||||||
|
|
||||||
class ThreadReportGenerator(object):
|
class ThreadReportGenerator(object):
|
||||||
|
@ -12,15 +12,15 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Provides Openstack version generators
|
"""Provides OpenStack version generators
|
||||||
|
|
||||||
This module defines a class for Openstack
|
This module defines a class for OpenStack
|
||||||
version and package information
|
version and package information
|
||||||
generators for generating the model in
|
generators for generating the model in
|
||||||
:mod:`openstack.common.report.models.version`.
|
:mod:`openstack.common.report.models.version`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import nova.openstack.common.report.models.version as vm
|
from nova.openstack.common.report.models import version as vm
|
||||||
|
|
||||||
|
|
||||||
class PackageReportGenerator(object):
|
class PackageReportGenerator(object):
|
||||||
|
@ -51,9 +51,13 @@ where stderr is logged for that given service.
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from oslo.utils import timeutils
|
||||||
|
|
||||||
from nova.openstack.common.report.generators import conf as cgen
|
from nova.openstack.common.report.generators import conf as cgen
|
||||||
from nova.openstack.common.report.generators import threading as tgen
|
from nova.openstack.common.report.generators import threading as tgen
|
||||||
from nova.openstack.common.report.generators import version as pgen
|
from nova.openstack.common.report.generators import version as pgen
|
||||||
@ -74,6 +78,8 @@ class GuruMeditation(object):
|
|||||||
MRO is correct.
|
MRO is correct.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
timestamp_fmt = "%Y%m%d%H%M%S"
|
||||||
|
|
||||||
def __init__(self, version_obj, *args, **kwargs):
|
def __init__(self, version_obj, *args, **kwargs):
|
||||||
self.version_obj = version_obj
|
self.version_obj = version_obj
|
||||||
|
|
||||||
@ -97,13 +103,17 @@ class GuruMeditation(object):
|
|||||||
cls.persistent_sections = [[section_title, generator]]
|
cls.persistent_sections = [[section_title, generator]]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_autorun(cls, version, signum=None):
|
def setup_autorun(cls, version, service_name=None,
|
||||||
|
log_dir=None, signum=None):
|
||||||
"""Set Up Auto-Run
|
"""Set Up Auto-Run
|
||||||
|
|
||||||
This method sets up the Guru Meditation Report to automatically
|
This method sets up the Guru Meditation Report to automatically
|
||||||
get dumped to stderr when the given signal is received.
|
get dumped to stderr or a file in a given dir when the given signal
|
||||||
|
is received.
|
||||||
|
|
||||||
:param version: the version object for the current product
|
:param version: the version object for the current product
|
||||||
|
:param service_name: this program name used to construct logfile name
|
||||||
|
:param logdir: path to a log directory where to create a file
|
||||||
:param signum: the signal to associate with running the report
|
:param signum: the signal to associate with running the report
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -113,18 +123,25 @@ class GuruMeditation(object):
|
|||||||
|
|
||||||
if signum:
|
if signum:
|
||||||
signal.signal(signum,
|
signal.signal(signum,
|
||||||
lambda *args: cls.handle_signal(version, *args))
|
lambda *args: cls.handle_signal(
|
||||||
|
version, service_name, log_dir, *args))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_signal(cls, version, *args):
|
def handle_signal(cls, version, service_name, log_dir, *args):
|
||||||
"""The Signal Handler
|
"""The Signal Handler
|
||||||
|
|
||||||
This method (indirectly) handles receiving a registered signal and
|
This method (indirectly) handles receiving a registered signal and
|
||||||
dumping the Guru Meditation Report to stderr. This method is designed
|
dumping the Guru Meditation Report to stderr or a file in a given dir.
|
||||||
to be curried into a proper signal handler by currying out the version
|
If service name and log dir are not None, the report will be dumped to
|
||||||
|
a file named $service_name_gurumeditation_$current_time in the log_dir
|
||||||
|
directory.
|
||||||
|
This method is designed to be curried into a proper signal handler by
|
||||||
|
currying out the version
|
||||||
parameter.
|
parameter.
|
||||||
|
|
||||||
:param version: the version object for the current product
|
:param version: the version object for the current product
|
||||||
|
:param service_name: this program name used to construct logfile name
|
||||||
|
:param logdir: path to a log directory where to create a file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -132,6 +149,19 @@ class GuruMeditation(object):
|
|||||||
except Exception:
|
except Exception:
|
||||||
print("Unable to run Guru Meditation Report!",
|
print("Unable to run Guru Meditation Report!",
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
|
else:
|
||||||
|
if log_dir:
|
||||||
|
service_name = service_name or os.path.basename(
|
||||||
|
inspect.stack()[-1][1])
|
||||||
|
filename = "%s_gurumeditation_%s" % (
|
||||||
|
service_name, timeutils.strtime(fmt=cls.timestamp_fmt))
|
||||||
|
filepath = os.path.join(log_dir, filename)
|
||||||
|
try:
|
||||||
|
with open(filepath, "w") as dumpfile:
|
||||||
|
dumpfile.write(res)
|
||||||
|
except Exception:
|
||||||
|
print("Unable to dump Guru Meditation Report to file %s" %
|
||||||
|
(filepath,), file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
print(res, file=sys.stderr)
|
print(res, file=sys.stderr)
|
||||||
|
|
||||||
|
@ -81,6 +81,11 @@ class ReportModel(col.MutableMapping):
|
|||||||
return self.data.__contains__(key)
|
return self.data.__contains__(key)
|
||||||
|
|
||||||
def __getattr__(self, attrname):
|
def __getattr__(self, attrname):
|
||||||
|
# Needed for deepcopy in Python3. That will avoid an infinite loop
|
||||||
|
# in __getattr__ .
|
||||||
|
if 'data' not in self.__dict__:
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.data[attrname]
|
return self.data[attrname]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -12,14 +12,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Provides Openstack Configuration Model
|
"""Provides OpenStack Configuration Model
|
||||||
|
|
||||||
This module defines a class representing the data
|
This module defines a class representing the data
|
||||||
model for :mod:`oslo.config` configuration options
|
model for :mod:`oslo.config` configuration options
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import nova.openstack.common.report.models.with_default_views as mwdv
|
from nova.openstack.common.report.models import with_default_views as mwdv
|
||||||
import nova.openstack.common.report.views.text.generic as generic_text_views
|
from nova.openstack.common.report.views.text import generic as generic_text_views
|
||||||
|
|
||||||
|
|
||||||
class ConfigModel(mwdv.ModelWithDefaultViews):
|
class ConfigModel(mwdv.ModelWithDefaultViews):
|
||||||
|
@ -20,8 +20,8 @@ thread, and stack trace data models
|
|||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import nova.openstack.common.report.models.with_default_views as mwdv
|
from nova.openstack.common.report.models import with_default_views as mwdv
|
||||||
import nova.openstack.common.report.views.text.threading as text_views
|
from nova.openstack.common.report.views.text import threading as text_views
|
||||||
|
|
||||||
|
|
||||||
class StackTraceModel(mwdv.ModelWithDefaultViews):
|
class StackTraceModel(mwdv.ModelWithDefaultViews):
|
||||||
@ -42,12 +42,12 @@ class StackTraceModel(mwdv.ModelWithDefaultViews):
|
|||||||
{'filename': fn, 'line': ln, 'name': nm, 'code': cd}
|
{'filename': fn, 'line': ln, 'name': nm, 'code': cd}
|
||||||
for fn, ln, nm, cd in traceback.extract_stack(stack_state)
|
for fn, ln, nm, cd in traceback.extract_stack(stack_state)
|
||||||
]
|
]
|
||||||
|
# FIXME(flepied): under Python3 f_exc_type doesn't exist
|
||||||
if stack_state.f_exc_type is not None:
|
# anymore so we lose information about exceptions
|
||||||
|
if getattr(stack_state, 'f_exc_type', None) is not None:
|
||||||
self['root_exception'] = {
|
self['root_exception'] = {
|
||||||
'type': stack_state.f_exc_type,
|
'type': stack_state.f_exc_type,
|
||||||
'value': stack_state.f_exc_value
|
'value': stack_state.f_exc_value}
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
self['root_exception'] = None
|
self['root_exception'] = None
|
||||||
else:
|
else:
|
||||||
|
@ -12,14 +12,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Provides Openstack Version Info Model
|
"""Provides OpenStack Version Info Model
|
||||||
|
|
||||||
This module defines a class representing the data
|
This module defines a class representing the data
|
||||||
model for Openstack package and version information
|
model for OpenStack package and version information
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import nova.openstack.common.report.models.with_default_views as mwdv
|
from nova.openstack.common.report.models import with_default_views as mwdv
|
||||||
import nova.openstack.common.report.views.text.generic as generic_text_views
|
from nova.openstack.common.report.views.text import generic as generic_text_views
|
||||||
|
|
||||||
|
|
||||||
class PackageModel(mwdv.ModelWithDefaultViews):
|
class PackageModel(mwdv.ModelWithDefaultViews):
|
||||||
|
@ -14,10 +14,10 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
import nova.openstack.common.report.models.base as base_model
|
from nova.openstack.common.report.models import base as base_model
|
||||||
import nova.openstack.common.report.views.json.generic as jsonviews
|
from nova.openstack.common.report.views.json import generic as jsonviews
|
||||||
import nova.openstack.common.report.views.text.generic as textviews
|
from nova.openstack.common.report.views.text import generic as textviews
|
||||||
import nova.openstack.common.report.views.xml.generic as xmlviews
|
from nova.openstack.common.report.views.xml import generic as xmlviews
|
||||||
|
|
||||||
|
|
||||||
class ModelWithDefaultViews(base_model.ReportModel):
|
class ModelWithDefaultViews(base_model.ReportModel):
|
||||||
@ -28,18 +28,18 @@ class ModelWithDefaultViews(base_model.ReportModel):
|
|||||||
when a submodel should have an attached view, but the view
|
when a submodel should have an attached view, but the view
|
||||||
differs depending on the serialization format
|
differs depending on the serialization format
|
||||||
|
|
||||||
Paramaters are as the superclass, with the exception
|
Parameters are as the superclass, except for any
|
||||||
of any parameters ending in '_view': these parameters
|
parameters ending in '_view': these parameters
|
||||||
get stored as default views.
|
get stored as default views.
|
||||||
|
|
||||||
The default 'default views' are
|
The default 'default views' are
|
||||||
|
|
||||||
text
|
text
|
||||||
:class:`openstack.common.views.text.generic.KeyValueView`
|
:class:`openstack.common.report.views.text.generic.KeyValueView`
|
||||||
xml
|
xml
|
||||||
:class:`openstack.common.views.xml.generic.KeyValueView`
|
:class:`openstack.common.report.views.xml.generic.KeyValueView`
|
||||||
json
|
json
|
||||||
:class:`openstack.common.views.json.generic.KeyValueView`
|
:class:`openstack.common.report.views.json.generic.KeyValueView`
|
||||||
|
|
||||||
.. function:: to_type()
|
.. function:: to_type()
|
||||||
|
|
||||||
|
@ -14,12 +14,12 @@
|
|||||||
|
|
||||||
"""Provides Report classes
|
"""Provides Report classes
|
||||||
|
|
||||||
This module defines various classes representing
|
This module defines various classes representing reports and report sections.
|
||||||
reports and report sections. All reports take the
|
All reports take the form of a report class containing various report
|
||||||
form of a report class containing various report sections.
|
sections.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import nova.openstack.common.report.views.text.header as header_views
|
from nova.openstack.common.report.views.text import header as header_views
|
||||||
|
|
||||||
|
|
||||||
class BasicReport(object):
|
class BasicReport(object):
|
||||||
@ -28,7 +28,7 @@ class BasicReport(object):
|
|||||||
A Basic Report consists of a collection of :class:`ReportSection`
|
A Basic Report consists of a collection of :class:`ReportSection`
|
||||||
objects, each of which contains a top-level model and generator.
|
objects, each of which contains a top-level model and generator.
|
||||||
It collects these sections into a cohesive report which may then
|
It collects these sections into a cohesive report which may then
|
||||||
be serialized by calling :func:`run`
|
be serialized by calling :func:`run`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -78,10 +78,9 @@ class BasicReport(object):
|
|||||||
class ReportSection(object):
|
class ReportSection(object):
|
||||||
"""A Report Section
|
"""A Report Section
|
||||||
|
|
||||||
A report section contains a generator and a top-level view.
|
A report section contains a generator and a top-level view. When something
|
||||||
When something attempts to serialize the section by calling
|
attempts to serialize the section by calling str() on it, the section runs
|
||||||
str() on it, the section runs the generator and calls the view
|
the generator and calls the view on the resulting model.
|
||||||
on the resulting model.
|
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
@ -89,9 +88,8 @@ class ReportSection(object):
|
|||||||
:func:`BasicReport.add_section`
|
:func:`BasicReport.add_section`
|
||||||
|
|
||||||
:param view: the top-level view for this section
|
:param view: the top-level view for this section
|
||||||
:param generator: the generator for this section which could be
|
:param generator: the generator for this section
|
||||||
any callable object which takes
|
(any callable object which takes no parameters and returns a data model)
|
||||||
no parameters and returns a data model
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, view, generator):
|
def __init__(self, view, generator):
|
||||||
|
@ -19,6 +19,8 @@ system for serialization. For more information on Jinja, please
|
|||||||
see http://jinja.pocoo.org/ .
|
see http://jinja.pocoo.org/ .
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
|
|
||||||
@ -79,6 +81,16 @@ class JinjaView(object):
|
|||||||
def __call__(self, model):
|
def __call__(self, model):
|
||||||
return self.template.render(**model)
|
return self.template.render(**model)
|
||||||
|
|
||||||
|
def __deepcopy__(self, memodict):
|
||||||
|
res = object.__new__(JinjaView)
|
||||||
|
res._text = copy.deepcopy(self._text, memodict)
|
||||||
|
|
||||||
|
# regenerate the template on a deepcopy
|
||||||
|
res._regentemplate = True
|
||||||
|
res._templatecache = None
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def template(self):
|
def template(self):
|
||||||
"""Get the Compiled Template
|
"""Get the Compiled Template
|
||||||
|
@ -25,8 +25,9 @@ such strings specially)
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from nova.openstack.common import jsonutils as json
|
from oslo.serialization import jsonutils as json
|
||||||
import nova.openstack.common.report.utils as utils
|
|
||||||
|
from nova.openstack.common.report import utils as utils
|
||||||
|
|
||||||
|
|
||||||
class BasicKeyValueView(object):
|
class BasicKeyValueView(object):
|
||||||
@ -56,10 +57,10 @@ class KeyValueView(object):
|
|||||||
def __call__(self, model):
|
def __call__(self, model):
|
||||||
# this part deals with subviews that were already serialized
|
# this part deals with subviews that were already serialized
|
||||||
cpy = copy.deepcopy(model)
|
cpy = copy.deepcopy(model)
|
||||||
for key, valstr in model.items():
|
for key in model.keys():
|
||||||
if getattr(valstr, '__is_json__', False):
|
if getattr(model[key], '__is_json__', False):
|
||||||
cpy[key] = json.loads(valstr)
|
cpy[key] = json.loads(model[key])
|
||||||
|
|
||||||
res = utils.StringWithAttrs(json.dumps(cpy.data))
|
res = utils.StringWithAttrs(json.dumps(cpy.data, sort_keys=True))
|
||||||
res.__is_json__ = True
|
res.__is_json__ = True
|
||||||
return res
|
return res
|
||||||
|
@ -120,7 +120,7 @@ class KeyValueView(object):
|
|||||||
if self.before_dict is not None:
|
if self.before_dict is not None:
|
||||||
res.insert(0, self.before_dict)
|
res.insert(0, self.before_dict)
|
||||||
|
|
||||||
for key in root:
|
for key in sorted(root):
|
||||||
res.extend(serialize(root[key], key, indent + 1))
|
res.extend(serialize(root[key], key, indent + 1))
|
||||||
elif (isinstance(root, col.Sequence) and
|
elif (isinstance(root, col.Sequence) and
|
||||||
not isinstance(root, six.string_types)):
|
not isinstance(root, six.string_types)):
|
||||||
@ -129,7 +129,7 @@ class KeyValueView(object):
|
|||||||
if self.before_list is not None:
|
if self.before_list is not None:
|
||||||
res.insert(0, self.before_list)
|
res.insert(0, self.before_list)
|
||||||
|
|
||||||
for val in root:
|
for val in sorted(root, key=str):
|
||||||
res.extend(serialize(val, None, indent + 1))
|
res.extend(serialize(val, None, indent + 1))
|
||||||
else:
|
else:
|
||||||
str_root = str(root)
|
str_root = str(root)
|
||||||
@ -172,7 +172,7 @@ class TableView(object):
|
|||||||
self.table_prop_name = table_prop_name
|
self.table_prop_name = table_prop_name
|
||||||
self.column_names = column_names
|
self.column_names = column_names
|
||||||
self.column_values = column_values
|
self.column_values = column_values
|
||||||
self.column_width = (72 - len(column_names) + 1) / len(column_names)
|
self.column_width = (72 - len(column_names) + 1) // len(column_names)
|
||||||
|
|
||||||
column_headers = "|".join(
|
column_headers = "|".join(
|
||||||
"{ch[" + str(n) + "]: ^" + str(self.column_width) + "}"
|
"{ch[" + str(n) + "]: ^" + str(self.column_width) + "}"
|
||||||
|
@ -19,7 +19,7 @@ visualizing threads, green threads, and stack traces
|
|||||||
in human-readable form.
|
in human-readable form.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import nova.openstack.common.report.views.jinja_view as jv
|
from nova.openstack.common.report.views import jinja_view as jv
|
||||||
|
|
||||||
|
|
||||||
class StackTraceView(jv.JinjaView):
|
class StackTraceView(jv.JinjaView):
|
||||||
@ -52,7 +52,7 @@ class GreenThreadView(object):
|
|||||||
"""A Green Thread View
|
"""A Green Thread View
|
||||||
|
|
||||||
This view displays a green thread provided by the data
|
This view displays a green thread provided by the data
|
||||||
model :class:`openstack.common.report.models.threading.GreenThreadModel` # noqa
|
model :class:`openstack.common.report.models.threading.GreenThreadModel`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
FORMAT_STR = "------{thread_str: ^60}------" + "\n" + "{stack_trace}"
|
FORMAT_STR = "------{thread_str: ^60}------" + "\n" + "{stack_trace}"
|
||||||
|
@ -29,7 +29,7 @@ import xml.etree.ElementTree as ET
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
import nova.openstack.common.report.utils as utils
|
from nova.openstack.common.report import utils as utils
|
||||||
|
|
||||||
|
|
||||||
class KeyValueView(object):
|
class KeyValueView(object):
|
||||||
@ -66,11 +66,11 @@ class KeyValueView(object):
|
|||||||
res = ET.Element(rootkeyname)
|
res = ET.Element(rootkeyname)
|
||||||
|
|
||||||
if isinstance(rootmodel, col.Mapping):
|
if isinstance(rootmodel, col.Mapping):
|
||||||
for key in rootmodel:
|
for key in sorted(rootmodel):
|
||||||
res.append(serialize(rootmodel[key], key))
|
res.append(serialize(rootmodel[key], key))
|
||||||
elif (isinstance(rootmodel, col.Sequence)
|
elif (isinstance(rootmodel, col.Sequence)
|
||||||
and not isinstance(rootmodel, six.string_types)):
|
and not isinstance(rootmodel, six.string_types)):
|
||||||
for val in rootmodel:
|
for val in sorted(rootmodel, key=str):
|
||||||
res.append(serialize(val, 'item'))
|
res.append(serialize(val, 'item'))
|
||||||
elif ET.iselement(rootmodel):
|
elif ET.iselement(rootmodel):
|
||||||
res.append(rootmodel)
|
res.append(rootmodel)
|
||||||
@ -79,7 +79,9 @@ class KeyValueView(object):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
res = utils.StringWithAttrs(ET.tostring(serialize(cpy,
|
str_ = ET.tostring(serialize(cpy,
|
||||||
self.wrapper_name)))
|
self.wrapper_name),
|
||||||
|
encoding="utf-8").decode("utf-8")
|
||||||
|
res = utils.StringWithAttrs(str_)
|
||||||
res.__is_xml__ = True
|
res.__is_xml__ = True
|
||||||
return res
|
return res
|
||||||
|
@ -38,14 +38,12 @@ 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 _LE, _LI, _LW
|
from nova.openstack.common._i18n import _LE, _LI, _LW
|
||||||
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 systemd
|
||||||
from nova.openstack.common import threadgroup
|
from nova.openstack.common import threadgroup
|
||||||
|
|
||||||
|
|
||||||
rpc = importutils.try_import('nova.openstack.common.rpc')
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -180,16 +178,11 @@ class ServiceLauncher(Launcher):
|
|||||||
status = exc.code
|
status = exc.code
|
||||||
finally:
|
finally:
|
||||||
self.stop()
|
self.stop()
|
||||||
if rpc:
|
|
||||||
try:
|
|
||||||
rpc.cleanup()
|
|
||||||
except Exception:
|
|
||||||
# We're shutting down, so it doesn't matter at this point.
|
|
||||||
LOG.exception(_LE('Exception during rpc cleanup.'))
|
|
||||||
|
|
||||||
return status, signo
|
return status, signo
|
||||||
|
|
||||||
def wait(self, ready_callback=None):
|
def wait(self, ready_callback=None):
|
||||||
|
systemd.notify_once()
|
||||||
while True:
|
while True:
|
||||||
self.handle_signal()
|
self.handle_signal()
|
||||||
status, signo = self._wait_for_exit_or_signal(ready_callback)
|
status, signo = self._wait_for_exit_or_signal(ready_callback)
|
||||||
@ -267,7 +260,7 @@ 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(_LI('Caught %s, exiting'), signame)
|
LOG.info(_LI('Child caught %s, exiting'), signame)
|
||||||
status = exc.code
|
status = exc.code
|
||||||
signo = exc.signo
|
signo = exc.signo
|
||||||
except SystemExit as exc:
|
except SystemExit as exc:
|
||||||
@ -382,6 +375,7 @@ 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."""
|
||||||
|
|
||||||
|
systemd.notify_once()
|
||||||
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)
|
||||||
|
|
||||||
@ -389,7 +383,10 @@ class ProcessLauncher(object):
|
|||||||
while True:
|
while True:
|
||||||
self.handle_signal()
|
self.handle_signal()
|
||||||
self._respawn_children()
|
self._respawn_children()
|
||||||
if self.sigcaught:
|
# No signal means that stop was called. Don't clean up here.
|
||||||
|
if not self.sigcaught:
|
||||||
|
return
|
||||||
|
|
||||||
signame = _signo_to_signame(self.sigcaught)
|
signame = _signo_to_signame(self.sigcaught)
|
||||||
LOG.info(_LI('Caught %s, stopping children'), signame)
|
LOG.info(_LI('Caught %s, stopping children'), signame)
|
||||||
if not _is_sighup_and_daemon(self.sigcaught):
|
if not _is_sighup_and_daemon(self.sigcaught):
|
||||||
@ -402,6 +399,11 @@ class ProcessLauncher(object):
|
|||||||
except eventlet.greenlet.GreenletExit:
|
except eventlet.greenlet.GreenletExit:
|
||||||
LOG.info(_LI("Wait called after thread killed. Cleaning up."))
|
LOG.info(_LI("Wait called after thread killed. Cleaning up."))
|
||||||
|
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""Terminate child processes and wait on each."""
|
||||||
|
self.running = False
|
||||||
for pid in self.children:
|
for pid in self.children:
|
||||||
try:
|
try:
|
||||||
os.kill(pid, signal.SIGTERM)
|
os.kill(pid, signal.SIGTERM)
|
||||||
@ -488,7 +490,6 @@ class Services(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
service.start()
|
service.start()
|
||||||
systemd.notify_once()
|
|
||||||
done.wait()
|
done.wait()
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import ssl
|
|||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common._i18n import _
|
||||||
|
|
||||||
|
|
||||||
ssl_opts = [
|
ssl_opts = [
|
||||||
|
@ -85,7 +85,7 @@ class ThreadGroup(object):
|
|||||||
def thread_done(self, thread):
|
def thread_done(self, thread):
|
||||||
self.threads.remove(thread)
|
self.threads.remove(thread)
|
||||||
|
|
||||||
def stop(self):
|
def _stop_threads(self):
|
||||||
current = threading.current_thread()
|
current = threading.current_thread()
|
||||||
|
|
||||||
# Iterate over a copy of self.threads so thread_done doesn't
|
# Iterate over a copy of self.threads so thread_done doesn't
|
||||||
@ -99,6 +99,7 @@ class ThreadGroup(object):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.exception(ex)
|
LOG.exception(ex)
|
||||||
|
|
||||||
|
def stop_timers(self):
|
||||||
for x in self.timers:
|
for x in self.timers:
|
||||||
try:
|
try:
|
||||||
x.stop()
|
x.stop()
|
||||||
@ -106,6 +107,23 @@ class ThreadGroup(object):
|
|||||||
LOG.exception(ex)
|
LOG.exception(ex)
|
||||||
self.timers = []
|
self.timers = []
|
||||||
|
|
||||||
|
def stop(self, graceful=False):
|
||||||
|
"""stop function has the option of graceful=True/False.
|
||||||
|
|
||||||
|
* In case of graceful=True, wait for all threads to be finished.
|
||||||
|
Never kill threads.
|
||||||
|
* In case of graceful=False, kill threads immediately.
|
||||||
|
"""
|
||||||
|
self.stop_timers()
|
||||||
|
if graceful:
|
||||||
|
# In case of graceful=True, wait for all threads to be
|
||||||
|
# finished, never kill threads
|
||||||
|
self.wait()
|
||||||
|
else:
|
||||||
|
# In case of graceful=False(Default), kill threads
|
||||||
|
# immediately
|
||||||
|
self._stop_threads()
|
||||||
|
|
||||||
def wait(self):
|
def wait(self):
|
||||||
for x in self.timers:
|
for x in self.timers:
|
||||||
try:
|
try:
|
||||||
|
@ -18,10 +18,12 @@ Helpers for comparing version strings.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
import inspect
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
import six
|
||||||
|
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common._i18n import _
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
|
||||||
|
|
||||||
@ -71,6 +73,7 @@ class deprecated(object):
|
|||||||
HAVANA = 'H'
|
HAVANA = 'H'
|
||||||
ICEHOUSE = 'I'
|
ICEHOUSE = 'I'
|
||||||
JUNO = 'J'
|
JUNO = 'J'
|
||||||
|
KILO = 'K'
|
||||||
|
|
||||||
_RELEASES = {
|
_RELEASES = {
|
||||||
# NOTE(morganfainberg): Bexar is used for unit test purposes, it is
|
# NOTE(morganfainberg): Bexar is used for unit test purposes, it is
|
||||||
@ -81,6 +84,7 @@ class deprecated(object):
|
|||||||
'H': 'Havana',
|
'H': 'Havana',
|
||||||
'I': 'Icehouse',
|
'I': 'Icehouse',
|
||||||
'J': 'Juno',
|
'J': 'Juno',
|
||||||
|
'K': 'Kilo',
|
||||||
}
|
}
|
||||||
|
|
||||||
_deprecated_msg_with_alternative = _(
|
_deprecated_msg_with_alternative = _(
|
||||||
@ -114,16 +118,34 @@ class deprecated(object):
|
|||||||
self.remove_in = remove_in
|
self.remove_in = remove_in
|
||||||
self.what = what
|
self.what = what
|
||||||
|
|
||||||
def __call__(self, func):
|
def __call__(self, func_or_cls):
|
||||||
if not self.what:
|
if not self.what:
|
||||||
self.what = func.__name__ + '()'
|
self.what = func_or_cls.__name__ + '()'
|
||||||
|
|
||||||
@functools.wraps(func)
|
|
||||||
def wrapped(*args, **kwargs):
|
|
||||||
msg, details = self._build_message()
|
msg, details = self._build_message()
|
||||||
|
|
||||||
|
if inspect.isfunction(func_or_cls):
|
||||||
|
|
||||||
|
@six.wraps(func_or_cls)
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
LOG.deprecated(msg, details)
|
LOG.deprecated(msg, details)
|
||||||
return func(*args, **kwargs)
|
return func_or_cls(*args, **kwargs)
|
||||||
return wrapped
|
return wrapped
|
||||||
|
elif inspect.isclass(func_or_cls):
|
||||||
|
orig_init = func_or_cls.__init__
|
||||||
|
|
||||||
|
# TODO(tsufiev): change `functools` module to `six` as
|
||||||
|
# soon as six 1.7.4 (with fix for passing `assigned`
|
||||||
|
# argument to underlying `functools.wraps`) is released
|
||||||
|
# and added to the nova-incubator requrements
|
||||||
|
@functools.wraps(orig_init, assigned=('__name__', '__doc__'))
|
||||||
|
def new_init(self, *args, **kwargs):
|
||||||
|
LOG.deprecated(msg, details)
|
||||||
|
orig_init(self, *args, **kwargs)
|
||||||
|
func_or_cls.__init__ = new_init
|
||||||
|
return func_or_cls
|
||||||
|
else:
|
||||||
|
raise TypeError('deprecated can be used only with functions or '
|
||||||
|
'classes')
|
||||||
|
|
||||||
def _get_safe_to_remove_release(self, release):
|
def _get_safe_to_remove_release(self, release):
|
||||||
# TODO(dstanek): this method will have to be reimplemented once
|
# TODO(dstanek): this method will have to be reimplemented once
|
||||||
|
@ -11044,6 +11044,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||||||
_fake_network_info(self.stubs, 1))
|
_fake_network_info(self.stubs, 1))
|
||||||
|
|
||||||
def test_cleanup_resize_same_host(self):
|
def test_cleanup_resize_same_host(self):
|
||||||
|
CONF.set_override('policy_dirs', [])
|
||||||
ins_ref = self._create_instance({'host': CONF.host})
|
ins_ref = self._create_instance({'host': CONF.host})
|
||||||
|
|
||||||
def fake_os_path_exists(path):
|
def fake_os_path_exists(path):
|
||||||
@ -11064,6 +11065,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||||||
_fake_network_info(self.stubs, 1))
|
_fake_network_info(self.stubs, 1))
|
||||||
|
|
||||||
def test_cleanup_resize_not_same_host(self):
|
def test_cleanup_resize_not_same_host(self):
|
||||||
|
CONF.set_override('policy_dirs', [])
|
||||||
host = 'not' + CONF.host
|
host = 'not' + CONF.host
|
||||||
ins_ref = self._create_instance({'host': host})
|
ins_ref = self._create_instance({'host': host})
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ from lxml import etree
|
|||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
from oslo.utils import excutils
|
from oslo.utils import excutils
|
||||||
from oslo.utils import importutils
|
from oslo.utils import importutils
|
||||||
|
from oslo.utils import strutils
|
||||||
from oslo.utils import timeutils
|
from oslo.utils import timeutils
|
||||||
from oslo.utils import units
|
from oslo.utils import units
|
||||||
import six
|
import six
|
||||||
@ -4133,7 +4134,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
'block_device_info': block_device_info})
|
'block_device_info': block_device_info})
|
||||||
# NOTE(mriedem): block_device_info can contain auth_password so we
|
# NOTE(mriedem): block_device_info can contain auth_password so we
|
||||||
# need to sanitize the password in the message.
|
# need to sanitize the password in the message.
|
||||||
LOG.debug(logging.mask_password(msg), instance=instance)
|
LOG.debug(strutils.mask_password(msg), instance=instance)
|
||||||
conf = self._get_guest_config(instance, network_info, image_meta,
|
conf = self._get_guest_config(instance, network_info, image_meta,
|
||||||
disk_info, rescue, block_device_info,
|
disk_info, rescue, block_device_info,
|
||||||
context)
|
context)
|
||||||
|
@ -22,6 +22,7 @@ import time
|
|||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
from oslo.utils import strutils
|
||||||
import six
|
import six
|
||||||
import six.moves.urllib.parse as urlparse
|
import six.moves.urllib.parse as urlparse
|
||||||
|
|
||||||
@ -238,7 +239,7 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||||||
{'command': iscsi_command, 'out': out, 'err': err})
|
{'command': iscsi_command, 'out': out, 'err': err})
|
||||||
# NOTE(bpokorny): iscsi_command can contain passwords so we need to
|
# NOTE(bpokorny): iscsi_command can contain passwords so we need to
|
||||||
# sanitize the password in the message.
|
# sanitize the password in the message.
|
||||||
LOG.debug(logging.mask_password(msg))
|
LOG.debug(strutils.mask_password(msg))
|
||||||
return (out, err)
|
return (out, err)
|
||||||
|
|
||||||
def _iscsiadm_update(self, iscsi_properties, property_key, property_value,
|
def _iscsiadm_update(self, iscsi_properties, property_key, property_value,
|
||||||
|
@ -26,6 +26,7 @@ import time
|
|||||||
import decorator
|
import decorator
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
from oslo.utils import excutils
|
from oslo.utils import excutils
|
||||||
|
from oslo.utils import strutils
|
||||||
from oslo.utils import units
|
from oslo.utils import units
|
||||||
from oslo.vmware import exceptions as vexc
|
from oslo.vmware import exceptions as vexc
|
||||||
|
|
||||||
@ -467,7 +468,7 @@ class VMwareVMOps(object):
|
|||||||
msg = "Block device information present: %s" % block_device_info
|
msg = "Block device information present: %s" % block_device_info
|
||||||
# NOTE(mriedem): block_device_info can contain an auth_password
|
# NOTE(mriedem): block_device_info can contain an auth_password
|
||||||
# so we have to scrub the message before logging it.
|
# so we have to scrub the message before logging it.
|
||||||
LOG.debug(logging.mask_password(msg), instance=instance)
|
LOG.debug(strutils.mask_password(msg), instance=instance)
|
||||||
|
|
||||||
for root_disk in block_device_mapping:
|
for root_disk in block_device_mapping:
|
||||||
connection_info = root_disk['connection_info']
|
connection_info = root_disk['connection_info']
|
||||||
|
@ -345,7 +345,7 @@ class VMOps(object):
|
|||||||
msg = "block device info: %s" % block_device_info
|
msg = "block device info: %s" % block_device_info
|
||||||
# NOTE(mriedem): block_device_info can contain an auth_password
|
# NOTE(mriedem): block_device_info can contain an auth_password
|
||||||
# so we have to scrub the message before logging it.
|
# so we have to scrub the message before logging it.
|
||||||
LOG.debug(logging.mask_password(msg), instance=instance)
|
LOG.debug(strutils.mask_password(msg), instance=instance)
|
||||||
root_device_name = block_device_info['root_device_name']
|
root_device_name = block_device_info['root_device_name']
|
||||||
|
|
||||||
for bdm in block_device_info['block_device_mapping']:
|
for bdm in block_device_info['block_device_mapping']:
|
||||||
|
@ -35,12 +35,13 @@ stevedore>=1.0.0 # Apache-2.0
|
|||||||
websockify>=0.6.0,<0.7
|
websockify>=0.6.0,<0.7
|
||||||
wsgiref>=0.1.2
|
wsgiref>=0.1.2
|
||||||
oslo.config>=1.4.0 # Apache-2.0
|
oslo.config>=1.4.0 # Apache-2.0
|
||||||
|
oslo.serialization>=1.0.0 # Apache-2.0
|
||||||
|
oslo.utils>=1.0.0 # Apache-2.0
|
||||||
oslo.db>=1.0.0 # Apache-2.0
|
oslo.db>=1.0.0 # Apache-2.0
|
||||||
oslo.rootwrap>=1.3.0
|
oslo.rootwrap>=1.3.0
|
||||||
pycadf>=0.6.0
|
pycadf>=0.6.0
|
||||||
oslo.messaging>=1.4.0
|
oslo.messaging>=1.4.0
|
||||||
oslo.i18n>=1.0.0 # Apache-2.0
|
oslo.i18n>=1.0.0 # Apache-2.0
|
||||||
oslo.utils>=1.0.0 # Apache-2.0
|
|
||||||
lockfile>=0.8
|
lockfile>=0.8
|
||||||
simplejson>=2.2.0
|
simplejson>=2.2.0
|
||||||
rfc3986>=0.2.0 # Apache-2.0
|
rfc3986>=0.2.0 # Apache-2.0
|
||||||
|
@ -125,7 +125,7 @@ class InstallVenv(object):
|
|||||||
parser.add_option('-n', '--no-site-packages',
|
parser.add_option('-n', '--no-site-packages',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help="Do not inherit packages from global Python "
|
help="Do not inherit packages from global Python "
|
||||||
"install")
|
"install.")
|
||||||
return parser.parse_args(argv[1:])[0]
|
return parser.parse_args(argv[1:])[0]
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user