Sync 'report' from oslo-incubator
Needed to fix guru meditation report for Windows. We landed an outdated 'report' module recently. Add report.[generators,models,views] to openstack-common.conf. Oslo source: c4c7dd28 Updated from global requirements Closes-Bug: #1286528 Change-Id: Ib33542718176241663c0e61605acbf4e941e2ca9
This commit is contained in:
parent
8b581e0b01
commit
7dae430e81
@ -22,4 +22,4 @@ is composed of one or more report sections
|
||||
which contain generators which generate data models
|
||||
( :class:`openstack.common.report.models.base.ReportModels` ),
|
||||
which are then serialized by views.
|
||||
"""
|
||||
"""
|
||||
|
@ -18,4 +18,4 @@ This module defines classes for generating data models
|
||||
( :class:`openstack.common.report.models.base.ReportModel` ).
|
||||
A generator is any object which is callable with no parameters
|
||||
and returns a data model.
|
||||
"""
|
||||
"""
|
||||
|
@ -12,7 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides Openstack config generators
|
||||
"""Provides OpenStack config generators
|
||||
|
||||
This module defines a class for configuration
|
||||
generators for generating the model in
|
||||
@ -21,20 +21,20 @@ generators for generating the model in
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
import cinder.openstack.common.report.models.conf as cm
|
||||
from cinder.openstack.common.report.models import conf as cm
|
||||
|
||||
|
||||
class ConfigReportGenerator(object):
|
||||
"""A Configuration Data Generator
|
||||
|
||||
This generator returns
|
||||
:class:`openstack.common.report.models.conf.ConfigModel` ,
|
||||
:class:`openstack.common.report.models.conf.ConfigModel`,
|
||||
by default using the configuration options stored
|
||||
in :attr:`oslo_config.cfg.CONF`, which is where
|
||||
Openstack stores everything.
|
||||
OpenStack stores everything.
|
||||
|
||||
:param cnf: the configuration option object
|
||||
:type cnf: :class:`oslo.config.cfg.ConfigOpts`
|
||||
:type cnf: :class:`oslo_config.cfg.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, cnf=cfg.CONF):
|
||||
|
38
cinder/openstack/common/report/generators/process.py
Normal file
38
cinder/openstack/common/report/generators/process.py
Normal file
@ -0,0 +1,38 @@
|
||||
# Copyright 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.
|
||||
|
||||
"""Provides process-data generators
|
||||
|
||||
This modules defines a class for generating
|
||||
process data by way of the psutil package.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import psutil
|
||||
|
||||
from cinder.openstack.common.report.models import process as pm
|
||||
|
||||
|
||||
class ProcessReportGenerator(object):
|
||||
"""A Process Data Generator
|
||||
|
||||
This generator returns a
|
||||
:class:`openstack.common.report.models.process.ProcessModel`
|
||||
based on the current process (which will also include
|
||||
all subprocesses, recursively) using the :class:`psutil.Process` class`.
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
return pm.ProcessModel(psutil.Process(os.getpid()))
|
@ -19,14 +19,17 @@ generators for generating the models in
|
||||
:mod:`openstack.common.report.models.threading`.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
import threading
|
||||
|
||||
import greenlet
|
||||
|
||||
import cinder.openstack.common.report.models.threading as tm
|
||||
from cinder.openstack.common.report.models import threading as tm
|
||||
from cinder.openstack.common.report.models import with_default_views as mwdv
|
||||
import cinder.openstack.common.report.utils as rutils
|
||||
import cinder.openstack.common.report.views.text.generic as text_views
|
||||
from cinder.openstack.common.report import utils as rutils
|
||||
from cinder.openstack.common.report.views.text import generic as text_views
|
||||
|
||||
|
||||
class ThreadReportGenerator(object):
|
||||
@ -35,17 +38,28 @@ class ThreadReportGenerator(object):
|
||||
This generator returns a collection of
|
||||
:class:`openstack.common.report.models.threading.ThreadModel`
|
||||
objects by introspecting the current python state using
|
||||
:func:`sys._current_frames()` .
|
||||
:func:`sys._current_frames()` . Its constructor may optionally
|
||||
be passed a frame object. This frame object will be interpreted
|
||||
as the actual stack trace for the current thread, and, come generation
|
||||
time, will be used to replace the stack trace of the thread in which
|
||||
this code is running.
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
threadModels = [
|
||||
tm.ThreadModel(thread_id, stack)
|
||||
for thread_id, stack in sys._current_frames().items()
|
||||
]
|
||||
def __init__(self, curr_thread_traceback=None):
|
||||
self.traceback = curr_thread_traceback
|
||||
|
||||
thread_pairs = dict(zip(range(len(threadModels)), threadModels))
|
||||
return mwdv.ModelWithDefaultViews(thread_pairs,
|
||||
def __call__(self):
|
||||
threadModels = dict(
|
||||
(thread_id, tm.ThreadModel(thread_id, stack))
|
||||
for thread_id, stack in sys._current_frames().items()
|
||||
)
|
||||
|
||||
if self.traceback is not None:
|
||||
curr_thread_id = threading.current_thread().ident
|
||||
threadModels[curr_thread_id] = tm.ThreadModel(curr_thread_id,
|
||||
self.traceback)
|
||||
|
||||
return mwdv.ModelWithDefaultViews(threadModels,
|
||||
text_view=text_views.MultiView())
|
||||
|
||||
|
||||
@ -68,6 +82,5 @@ class GreenThreadReportGenerator(object):
|
||||
for gr in rutils._find_objects(greenlet.greenlet)
|
||||
]
|
||||
|
||||
thread_pairs = dict(zip(range(len(threadModels)), threadModels))
|
||||
return mwdv.ModelWithDefaultViews(thread_pairs,
|
||||
return mwdv.ModelWithDefaultViews(threadModels,
|
||||
text_view=text_views.MultiView())
|
||||
|
@ -12,15 +12,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# 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
|
||||
generators for generating the model in
|
||||
:mod:`openstack.common.report.models.version`.
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.models.version as vm
|
||||
from cinder.openstack.common.report.models import version as vm
|
||||
|
||||
|
||||
class PackageReportGenerator(object):
|
||||
@ -40,7 +40,21 @@ class PackageReportGenerator(object):
|
||||
self.version_obj = version_obj
|
||||
|
||||
def __call__(self):
|
||||
return vm.PackageModel(
|
||||
self.version_obj.vendor_string(),
|
||||
self.version_obj.product_string(),
|
||||
self.version_obj.version_string_with_package())
|
||||
if hasattr(self.version_obj, "vendor_string"):
|
||||
vendor_string = self.version_obj.vendor_string()
|
||||
else:
|
||||
vendor_string = None
|
||||
|
||||
if hasattr(self.version_obj, "product_string"):
|
||||
product_string = self.version_obj.product_string()
|
||||
else:
|
||||
product_string = None
|
||||
|
||||
if hasattr(self.version_obj, "version_string_with_package"):
|
||||
version_string_with_package = self.version_obj.\
|
||||
version_string_with_package()
|
||||
else:
|
||||
version_string_with_package = None
|
||||
|
||||
return vm.PackageModel(vendor_string, product_string,
|
||||
version_string_with_package)
|
||||
|
@ -14,7 +14,8 @@
|
||||
|
||||
"""Provides Guru Meditation Report
|
||||
|
||||
This module defines the actual OpenStack Guru Meditation Report class.
|
||||
This module defines the actual OpenStack Guru Meditation
|
||||
Report class.
|
||||
|
||||
This can be used in the OpenStack command definition files.
|
||||
For example, in a nova command module (under nova/cmd):
|
||||
@ -50,10 +51,15 @@ where stderr is logged for that given service.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from cinder.openstack.common.report.generators import conf as cgen
|
||||
from cinder.openstack.common.report.generators import process as prgen
|
||||
from cinder.openstack.common.report.generators import threading as tgen
|
||||
from cinder.openstack.common.report.generators import version as pgen
|
||||
from cinder.openstack.common.report import report
|
||||
@ -73,8 +79,11 @@ class GuruMeditation(object):
|
||||
MRO is correct.
|
||||
"""
|
||||
|
||||
def __init__(self, version_obj, *args, **kwargs):
|
||||
timestamp_fmt = "%Y%m%d%H%M%S"
|
||||
|
||||
def __init__(self, version_obj, sig_handler_tb=None, *args, **kwargs):
|
||||
self.version_obj = version_obj
|
||||
self.traceback = sig_handler_tb
|
||||
|
||||
super(GuruMeditation, self).__init__(*args, **kwargs)
|
||||
self.start_section_index = len(self.sections)
|
||||
@ -96,37 +105,69 @@ class GuruMeditation(object):
|
||||
cls.persistent_sections = [[section_title, generator]]
|
||||
|
||||
@classmethod
|
||||
def setup_autorun(cls, version, signum=signal.SIGUSR1):
|
||||
def setup_autorun(cls, version, service_name=None,
|
||||
log_dir=None, signum=None):
|
||||
"""Set Up Auto-Run
|
||||
|
||||
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 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
|
||||
"""
|
||||
|
||||
signal.signal(signum, lambda *args: cls.handle_signal(version, *args))
|
||||
if not signum and hasattr(signal, 'SIGUSR1'):
|
||||
# SIGUSR1 is not supported on all platforms
|
||||
signum = signal.SIGUSR1
|
||||
|
||||
if signum:
|
||||
signal.signal(signum,
|
||||
lambda sn, tb: cls.handle_signal(
|
||||
version, service_name, log_dir, tb))
|
||||
|
||||
@classmethod
|
||||
def handle_signal(cls, version, *args):
|
||||
def handle_signal(cls, version, service_name, log_dir, traceback):
|
||||
"""The Signal Handler
|
||||
|
||||
This method (indirectly) handles receiving a registered signal and
|
||||
dumping the Guru Meditation Report to stderr. This method is designed
|
||||
to be curried into a proper signal handler by currying out the version
|
||||
dumping the Guru Meditation Report to stderr or a file in a given dir.
|
||||
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.
|
||||
|
||||
: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 traceback: the traceback provided to the signal handler
|
||||
"""
|
||||
|
||||
try:
|
||||
res = cls(version).run()
|
||||
res = cls(version, traceback).run()
|
||||
except Exception:
|
||||
print("Unable to run Guru Meditation Report!",
|
||||
file=sys.stderr)
|
||||
else:
|
||||
print(res, file=sys.stderr)
|
||||
if log_dir:
|
||||
service_name = service_name or os.path.basename(
|
||||
inspect.stack()[-1][1])
|
||||
filename = "%s_gurumeditation_%s" % (
|
||||
service_name, timeutils.utcnow().strftime(
|
||||
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:
|
||||
print(res, file=sys.stderr)
|
||||
|
||||
def _readd_sections(self):
|
||||
del self.sections[self.start_section_index:]
|
||||
@ -135,11 +176,14 @@ class GuruMeditation(object):
|
||||
pgen.PackageReportGenerator(self.version_obj))
|
||||
|
||||
self.add_section('Threads',
|
||||
tgen.ThreadReportGenerator())
|
||||
tgen.ThreadReportGenerator(self.traceback))
|
||||
|
||||
self.add_section('Green Threads',
|
||||
tgen.GreenThreadReportGenerator())
|
||||
|
||||
self.add_section('Processes',
|
||||
prgen.ProcessReportGenerator())
|
||||
|
||||
self.add_section('Configuration',
|
||||
cgen.ConfigReportGenerator())
|
||||
|
||||
@ -169,11 +213,15 @@ class TextGuruMeditation(GuruMeditation, report.TextReport):
|
||||
|
||||
- Green Threads List
|
||||
|
||||
- Process List
|
||||
|
||||
- Configuration Options
|
||||
|
||||
:param version_obj: the version object for the current product
|
||||
:param traceback: an (optional) frame object providing the actual
|
||||
traceback for the current thread
|
||||
"""
|
||||
|
||||
def __init__(self, version_obj):
|
||||
super(TextGuruMeditation, self).__init__(version_obj,
|
||||
def __init__(self, version_obj, traceback=None):
|
||||
super(TextGuruMeditation, self).__init__(version_obj, traceback,
|
||||
'Guru Meditation')
|
||||
|
@ -17,4 +17,4 @@
|
||||
This module provides both the base data model,
|
||||
as well as several predefined specific data models
|
||||
to be used in reports.
|
||||
"""
|
||||
"""
|
||||
|
@ -24,6 +24,8 @@ the report serialization process.
|
||||
import collections as col
|
||||
import copy
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class ReportModel(col.MutableMapping):
|
||||
"""A Report Data Model
|
||||
@ -37,13 +39,29 @@ class ReportModel(col.MutableMapping):
|
||||
model. An appropriate object for a view is callable with
|
||||
a single parameter: the model to be serialized.
|
||||
|
||||
:param data: a dictionary of data to initially associate with the model
|
||||
If present, the object passed in as data will be transformed
|
||||
into a standard python dict. For mappings, this is fairly
|
||||
straightforward. For sequences, the indices become keys
|
||||
and the items become values.
|
||||
|
||||
:param data: a sequence or mapping of data to associate with the model
|
||||
:param attached_view: a view object to attach to this model
|
||||
"""
|
||||
|
||||
def __init__(self, data=None, attached_view=None):
|
||||
self.attached_view = attached_view
|
||||
self.data = data or {}
|
||||
|
||||
if data is not None:
|
||||
if isinstance(data, col.Mapping):
|
||||
self.data = dict(data)
|
||||
elif isinstance(data, col.Sequence):
|
||||
# convert a list [a, b, c] to a dict {0: a, 1: b, 2: c}
|
||||
self.data = dict(enumerate(data))
|
||||
else:
|
||||
raise TypeError('Data for the model must be a sequence '
|
||||
'or mapping.')
|
||||
else:
|
||||
self.data = {}
|
||||
|
||||
def __str__(self):
|
||||
self_cpy = copy.deepcopy(self)
|
||||
@ -81,9 +99,16 @@ class ReportModel(col.MutableMapping):
|
||||
return self.data.__contains__(key)
|
||||
|
||||
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:
|
||||
return self.data[attrname]
|
||||
except KeyError:
|
||||
# we don't have that key in data, and the
|
||||
# model class doesn't have that attribute
|
||||
raise AttributeError(
|
||||
"'{cl}' object has no attribute '{an}'".format(
|
||||
cl=type(self).__name__, an=attrname
|
||||
@ -96,19 +121,42 @@ class ReportModel(col.MutableMapping):
|
||||
def __iter__(self):
|
||||
return self.data.__iter__()
|
||||
|
||||
def set_current_view_type(self, tp):
|
||||
def set_current_view_type(self, tp, visited=None):
|
||||
"""Set the current view type
|
||||
|
||||
This method attempts to set the current view
|
||||
type for this model and all submodels by calling
|
||||
itself recursively on all values (and ignoring the
|
||||
ones that are not themselves models)
|
||||
itself recursively on all values, traversing
|
||||
intervening sequences and mappings when possible,
|
||||
and ignoring all other objects.
|
||||
|
||||
:param tp: the type of the view ('text', 'json', 'xml', etc)
|
||||
:param visited: a set of object ids for which the corresponding objects
|
||||
have already had their view type set
|
||||
"""
|
||||
|
||||
for key in self:
|
||||
try:
|
||||
self[key].set_current_view_type(tp)
|
||||
except AttributeError:
|
||||
pass
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
def traverse_obj(obj):
|
||||
oid = id(obj)
|
||||
|
||||
# don't die on recursive structures,
|
||||
# and don't treat strings like sequences
|
||||
if oid in visited or isinstance(obj, six.string_types):
|
||||
return
|
||||
|
||||
visited.add(oid)
|
||||
|
||||
if hasattr(obj, 'set_current_view_type'):
|
||||
obj.set_current_view_type(tp, visited=visited)
|
||||
|
||||
if isinstance(obj, col.Sequence):
|
||||
for item in obj:
|
||||
traverse_obj(item)
|
||||
|
||||
elif isinstance(obj, col.Mapping):
|
||||
for val in six.itervalues(obj):
|
||||
traverse_obj(val)
|
||||
|
||||
traverse_obj(self)
|
||||
|
@ -12,25 +12,25 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides Openstack Configuration Model
|
||||
"""Provides OpenStack Configuration Model
|
||||
|
||||
This module defines a class representing the data
|
||||
model for :mod:`oslo.config` configuration options
|
||||
model for :mod:`oslo_config` configuration options
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.models.with_default_views as mwdv
|
||||
import cinder.openstack.common.report.views.text.generic as generic_text_views
|
||||
from cinder.openstack.common.report.models import with_default_views as mwdv
|
||||
from cinder.openstack.common.report.views.text import generic as generic_text_views
|
||||
|
||||
|
||||
class ConfigModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Configuration Options Model
|
||||
|
||||
This model holds data about a set of configuration options
|
||||
from :mod:`oslo.config`. It supports both the default group
|
||||
from :mod:`oslo_config`. It supports both the default group
|
||||
of options and named option groups.
|
||||
|
||||
:param conf_obj: a configuration object
|
||||
:type conf_obj: :class:`oslo.config.cfg.ConfigOpts`
|
||||
:type conf_obj: :class:`oslo_config.cfg.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, conf_obj):
|
||||
@ -41,8 +41,15 @@ class ConfigModel(mwdv.ModelWithDefaultViews):
|
||||
def opt_title(optname, co):
|
||||
return co._opts[optname]['opt'].name
|
||||
|
||||
def opt_value(opt_obj, value):
|
||||
if opt_obj['opt'].secret:
|
||||
return '***'
|
||||
else:
|
||||
return value
|
||||
|
||||
self['default'] = dict(
|
||||
(opt_title(optname, conf_obj), conf_obj[optname])
|
||||
(opt_title(optname, conf_obj),
|
||||
opt_value(conf_obj._opts[optname], conf_obj[optname]))
|
||||
for optname in conf_obj._opts
|
||||
)
|
||||
|
||||
@ -50,9 +57,10 @@ class ConfigModel(mwdv.ModelWithDefaultViews):
|
||||
for groupname in conf_obj._groups:
|
||||
group_obj = conf_obj._groups[groupname]
|
||||
curr_group_opts = dict(
|
||||
(opt_title(optname, group_obj), conf_obj[groupname][optname])
|
||||
for optname in group_obj._opts
|
||||
)
|
||||
(opt_title(optname, group_obj),
|
||||
opt_value(group_obj._opts[optname],
|
||||
conf_obj[groupname][optname]))
|
||||
for optname in group_obj._opts)
|
||||
groups[group_obj.name] = curr_group_opts
|
||||
|
||||
self.update(groups)
|
||||
|
62
cinder/openstack/common/report/models/process.py
Normal file
62
cinder/openstack/common/report/models/process.py
Normal file
@ -0,0 +1,62 @@
|
||||
# Copyright 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.
|
||||
|
||||
"""Provides a process model
|
||||
|
||||
This module defines a class representing a process,
|
||||
potentially with subprocesses.
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.models.with_default_views as mwdv
|
||||
import cinder.openstack.common.report.views.text.process as text_views
|
||||
|
||||
|
||||
class ProcessModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Process Model
|
||||
|
||||
This model holds data about a process,
|
||||
including references to any subprocesses
|
||||
|
||||
:param process: a :class:`psutil.Process` object
|
||||
"""
|
||||
|
||||
def __init__(self, process):
|
||||
super(ProcessModel, self).__init__(
|
||||
text_view=text_views.ProcessView())
|
||||
|
||||
self['pid'] = process.pid
|
||||
self['parent_pid'] = process.ppid
|
||||
if hasattr(process, 'uids'):
|
||||
self['uids'] = {'real': process.uids.real,
|
||||
'effective': process.uids.effective,
|
||||
'saved': process.uids.saved}
|
||||
else:
|
||||
self['uids'] = {'real': None,
|
||||
'effective': None,
|
||||
'saved': None}
|
||||
|
||||
if hasattr(process, 'gids'):
|
||||
self['gids'] = {'real': process.gids.real,
|
||||
'effective': process.gids.effective,
|
||||
'saved': process.gids.saved}
|
||||
else:
|
||||
self['gids'] = {'real': None,
|
||||
'effective': None,
|
||||
'saved': None}
|
||||
|
||||
self['username'] = process.username
|
||||
self['command'] = process.cmdline
|
||||
self['state'] = process.status
|
||||
|
||||
self['children'] = [ProcessModel(pr) for pr in process.get_children()]
|
@ -20,8 +20,8 @@ thread, and stack trace data models
|
||||
|
||||
import traceback
|
||||
|
||||
import cinder.openstack.common.report.models.with_default_views as mwdv
|
||||
import cinder.openstack.common.report.views.text.threading as text_views
|
||||
from cinder.openstack.common.report.models import with_default_views as mwdv
|
||||
from cinder.openstack.common.report.views.text import threading as text_views
|
||||
|
||||
|
||||
class StackTraceModel(mwdv.ModelWithDefaultViews):
|
||||
@ -42,12 +42,12 @@ class StackTraceModel(mwdv.ModelWithDefaultViews):
|
||||
{'filename': fn, 'line': ln, 'name': nm, 'code': cd}
|
||||
for fn, ln, nm, cd in traceback.extract_stack(stack_state)
|
||||
]
|
||||
|
||||
if stack_state.f_exc_type is not None:
|
||||
# FIXME(flepied): under Python3 f_exc_type doesn't exist
|
||||
# anymore so we lose information about exceptions
|
||||
if getattr(stack_state, 'f_exc_type', None) is not None:
|
||||
self['root_exception'] = {
|
||||
'type': stack_state.f_exc_type,
|
||||
'value': stack_state.f_exc_value
|
||||
}
|
||||
'value': stack_state.f_exc_value}
|
||||
else:
|
||||
self['root_exception'] = None
|
||||
else:
|
||||
|
@ -12,14 +12,14 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides Openstack Version Info Model
|
||||
"""Provides OpenStack Version Info Model
|
||||
|
||||
This module defines a class representing the data
|
||||
model for Openstack package and version information
|
||||
model for OpenStack package and version information
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.models.with_default_views as mwdv
|
||||
import cinder.openstack.common.report.views.text.generic as generic_text_views
|
||||
from cinder.openstack.common.report.models import with_default_views as mwdv
|
||||
from cinder.openstack.common.report.views.text import generic as generic_text_views
|
||||
|
||||
|
||||
class PackageModel(mwdv.ModelWithDefaultViews):
|
||||
|
@ -14,10 +14,10 @@
|
||||
|
||||
import copy
|
||||
|
||||
import cinder.openstack.common.report.models.base as base_model
|
||||
import cinder.openstack.common.report.views.json.generic as jsonviews
|
||||
import cinder.openstack.common.report.views.text.generic as textviews
|
||||
import cinder.openstack.common.report.views.xml.generic as xmlviews
|
||||
from cinder.openstack.common.report.models import base as base_model
|
||||
from cinder.openstack.common.report.views.json import generic as jsonviews
|
||||
from cinder.openstack.common.report.views.text import generic as textviews
|
||||
from cinder.openstack.common.report.views.xml import generic as xmlviews
|
||||
|
||||
|
||||
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
|
||||
differs depending on the serialization format
|
||||
|
||||
Paramaters are as the superclass, with the exception
|
||||
of any parameters ending in '_view': these parameters
|
||||
Parameters are as the superclass, except for any
|
||||
parameters ending in '_view': these parameters
|
||||
get stored as default views.
|
||||
|
||||
The default 'default views' are
|
||||
|
||||
text
|
||||
:class:`openstack.common.views.text.generic.KeyValueView`
|
||||
:class:`openstack.common.report.views.text.generic.KeyValueView`
|
||||
xml
|
||||
:class:`openstack.common.views.xml.generic.KeyValueView`
|
||||
:class:`openstack.common.report.views.xml.generic.KeyValueView`
|
||||
json
|
||||
:class:`openstack.common.views.json.generic.KeyValueView`
|
||||
:class:`openstack.common.report.views.json.generic.KeyValueView`
|
||||
|
||||
.. function:: to_type()
|
||||
|
||||
@ -64,19 +64,18 @@ class ModelWithDefaultViews(base_model.ReportModel):
|
||||
del newargs[k]
|
||||
super(ModelWithDefaultViews, self).__init__(*args, **newargs)
|
||||
|
||||
def set_current_view_type(self, tp):
|
||||
def set_current_view_type(self, tp, visited=None):
|
||||
self.attached_view = self.views[tp]
|
||||
super(ModelWithDefaultViews, self).set_current_view_type(tp)
|
||||
super(ModelWithDefaultViews, self).set_current_view_type(tp, visited)
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
if attrname[:3] == 'to_':
|
||||
if self.views[attrname[3:]] is not None:
|
||||
return lambda: self.views[attrname[3:]](self)
|
||||
else:
|
||||
raise NotImplementedError(_(
|
||||
"Model %(module)s.%(name)s does not have a default view "
|
||||
"for %(tp)s"), {'module': type(self).__module__,
|
||||
'name': type(self).__name__,
|
||||
'tp': attrname[3:]})
|
||||
raise NotImplementedError((
|
||||
"Model {cn.__module__}.{cn.__name__} does not have" +
|
||||
" a default view for "
|
||||
"{tp}").format(cn=type(self), tp=attrname[3:]))
|
||||
else:
|
||||
return super(ModelWithDefaultViews, self).__getattr__(attrname)
|
||||
|
@ -14,12 +14,12 @@
|
||||
|
||||
"""Provides Report classes
|
||||
|
||||
This module defines various classes representing
|
||||
reports and report sections. All reports take the
|
||||
form of a report class containing various report sections.
|
||||
This module defines various classes representing reports and report sections.
|
||||
All reports take the form of a report class containing various report
|
||||
sections.
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.views.text.header as header_views
|
||||
from cinder.openstack.common.report.views.text import header as header_views
|
||||
|
||||
|
||||
class BasicReport(object):
|
||||
@ -28,7 +28,7 @@ class BasicReport(object):
|
||||
A Basic Report consists of a collection of :class:`ReportSection`
|
||||
objects, each of which contains a top-level model and generator.
|
||||
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):
|
||||
@ -78,10 +78,9 @@ class BasicReport(object):
|
||||
class ReportSection(object):
|
||||
"""A Report Section
|
||||
|
||||
A report section contains a generator and a top-level view.
|
||||
When something attempts to serialize the section by calling
|
||||
str() on it, the section runs the generator and calls the view
|
||||
on the resulting model.
|
||||
A report section contains a generator and a top-level view. When something
|
||||
attempts to serialize the section by calling str() on it, the section runs
|
||||
the generator and calls the view on the resulting model.
|
||||
|
||||
.. seealso::
|
||||
|
||||
@ -90,8 +89,7 @@ class ReportSection(object):
|
||||
|
||||
:param view: the top-level view for this section
|
||||
:param generator: the generator for this section
|
||||
(any callable object which takes
|
||||
no parameters and returns a data model)
|
||||
(any callable object which takes no parameters and returns a data model)
|
||||
"""
|
||||
|
||||
def __init__(self, view, generator):
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
# Copyright 2013 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
|
||||
|
@ -19,4 +19,4 @@ for use in reports. It is separated by type (xml, json, or text).
|
||||
Each type contains a submodule called 'generic' containing
|
||||
several basic, universal views for that type. There is also
|
||||
a predefined view that utilizes Jinja.
|
||||
"""
|
||||
"""
|
||||
|
@ -19,6 +19,8 @@ system for serialization. For more information on Jinja, please
|
||||
see http://jinja.pocoo.org/ .
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
import jinja2
|
||||
|
||||
|
||||
@ -79,6 +81,16 @@ class JinjaView(object):
|
||||
def __call__(self, 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
|
||||
def template(self):
|
||||
"""Get the Compiled Template
|
||||
|
@ -16,4 +16,4 @@
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into JSON.
|
||||
"""
|
||||
"""
|
||||
|
@ -27,7 +27,7 @@ import copy
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
import cinder.openstack.common.report.utils as utils
|
||||
from cinder.openstack.common.report import utils as utils
|
||||
|
||||
|
||||
class BasicKeyValueView(object):
|
||||
@ -57,10 +57,10 @@ class KeyValueView(object):
|
||||
def __call__(self, model):
|
||||
# this part deals with subviews that were already serialized
|
||||
cpy = copy.deepcopy(model)
|
||||
for key, valstr in model.items():
|
||||
if getattr(valstr, '__is_json__', False):
|
||||
cpy[key] = json.loads(valstr)
|
||||
for key in model.keys():
|
||||
if getattr(model[key], '__is_json__', False):
|
||||
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
|
||||
return res
|
||||
|
@ -16,4 +16,4 @@
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into human-readable text.
|
||||
"""
|
||||
"""
|
||||
|
@ -120,7 +120,7 @@ class KeyValueView(object):
|
||||
if self.before_dict is not None:
|
||||
res.insert(0, self.before_dict)
|
||||
|
||||
for key in root:
|
||||
for key in sorted(root):
|
||||
res.extend(serialize(root[key], key, indent + 1))
|
||||
elif (isinstance(root, col.Sequence) and
|
||||
not isinstance(root, six.string_types)):
|
||||
@ -129,7 +129,7 @@ class KeyValueView(object):
|
||||
if self.before_list is not None:
|
||||
res.insert(0, self.before_list)
|
||||
|
||||
for val in root:
|
||||
for val in sorted(root, key=str):
|
||||
res.extend(serialize(val, None, indent + 1))
|
||||
else:
|
||||
str_root = str(root)
|
||||
@ -172,7 +172,7 @@ class TableView(object):
|
||||
self.table_prop_name = table_prop_name
|
||||
self.column_names = column_names
|
||||
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(
|
||||
"{ch[" + str(n) + "]: ^" + str(self.column_width) + "}"
|
||||
|
@ -49,4 +49,3 @@ class TitledView(HeaderView):
|
||||
|
||||
def __init__(self, title):
|
||||
super(TitledView, self).__init__(self.FORMAT_STR.format(title))
|
||||
|
||||
|
38
cinder/openstack/common/report/views/text/process.py
Normal file
38
cinder/openstack/common/report/views/text/process.py
Normal file
@ -0,0 +1,38 @@
|
||||
# Copyright 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.
|
||||
|
||||
"""Provides process view
|
||||
|
||||
This module provides a view for
|
||||
visualizing processes in human-readable formm
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.views.jinja_view as jv
|
||||
|
||||
|
||||
class ProcessView(jv.JinjaView):
|
||||
"""A Process View
|
||||
|
||||
This view displays process models defined by
|
||||
:class:`openstack.common.report.models.process.ProcessModel`
|
||||
"""
|
||||
|
||||
VIEW_TEXT = (
|
||||
"Process {{ pid }} (under {{ parent_pid }}) "
|
||||
"[ run by: {{ username }} ({{ uids.real|default('unknown uid') }}),"
|
||||
" state: {{ state }} ]\n"
|
||||
"{% for child in children %}"
|
||||
" {{ child }}"
|
||||
"{% endfor %}"
|
||||
)
|
@ -19,7 +19,7 @@ visualizing threads, green threads, and stack traces
|
||||
in human-readable form.
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.views.jinja_view as jv
|
||||
from cinder.openstack.common.report.views import jinja_view as jv
|
||||
|
||||
|
||||
class StackTraceView(jv.JinjaView):
|
||||
@ -52,7 +52,7 @@ class GreenThreadView(object):
|
||||
"""A Green Thread View
|
||||
|
||||
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}"
|
||||
|
@ -16,4 +16,4 @@
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into XML.
|
||||
"""
|
||||
"""
|
||||
|
@ -29,7 +29,7 @@ import xml.etree.ElementTree as ET
|
||||
|
||||
import six
|
||||
|
||||
import cinder.openstack.common.report.utils as utils
|
||||
from cinder.openstack.common.report import utils as utils
|
||||
|
||||
|
||||
class KeyValueView(object):
|
||||
@ -66,11 +66,11 @@ class KeyValueView(object):
|
||||
res = ET.Element(rootkeyname)
|
||||
|
||||
if isinstance(rootmodel, col.Mapping):
|
||||
for key in rootmodel:
|
||||
for key in sorted(rootmodel):
|
||||
res.append(serialize(rootmodel[key], key))
|
||||
elif (isinstance(rootmodel, col.Sequence)
|
||||
and not isinstance(rootmodel, six.string_types)):
|
||||
for val in rootmodel:
|
||||
for val in sorted(rootmodel, key=str):
|
||||
res.append(serialize(val, 'item'))
|
||||
elif ET.iselement(rootmodel):
|
||||
res.append(rootmodel)
|
||||
@ -79,7 +79,9 @@ class KeyValueView(object):
|
||||
|
||||
return res
|
||||
|
||||
res = utils.StringWithAttrs(ET.tostring(serialize(cpy,
|
||||
self.wrapper_name)))
|
||||
str_ = ET.tostring(serialize(cpy,
|
||||
self.wrapper_name),
|
||||
encoding="utf-8").decode("utf-8")
|
||||
res = utils.StringWithAttrs(str_)
|
||||
res.__is_xml__ = True
|
||||
return res
|
||||
|
@ -15,6 +15,12 @@ module=scheduler.weights
|
||||
module=service
|
||||
module=versionutils
|
||||
module=report
|
||||
module=report.generators
|
||||
module=report.models
|
||||
module=report.views
|
||||
module=report.views.json
|
||||
module=report.views.text
|
||||
module=report.views.xml
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=cinder
|
||||
|
@ -28,6 +28,7 @@ osprofiler>=0.3.0 # Apache-2.0
|
||||
paramiko>=1.13.0
|
||||
Paste
|
||||
PasteDeploy>=1.5.0
|
||||
psutil>=1.1.1,<2.0.0
|
||||
pycrypto>=2.6
|
||||
pyparsing>=2.0.1
|
||||
python-barbicanclient>=3.0.1
|
||||
|
Loading…
x
Reference in New Issue
Block a user