Introduce Guru Meditation Reports into Cinder
This commit integrates functionality from the `openstack.common.report` module into Cinder. This enables Cinder services to receive SIGUSR1 and print a Guru Meditation Report to stderr. The required modules were added to 'openstack-common.conf' as well. It is essentially a copy from implementation of nova side. Change-Id: I5bbdc0f97db9b0ebd7b48e50ab7869e2ca33aead Implements: blueprint guru-meditation-report
This commit is contained in:
parent
e5cd756c1a
commit
f2dc050e4d
@ -35,6 +35,7 @@ i18n.enable_lazy()
|
||||
|
||||
# Need to register global_opts
|
||||
from cinder.common import config # noqa
|
||||
from cinder.openstack.common.report import guru_meditation_report as gmr
|
||||
from cinder import rpc
|
||||
from cinder import service
|
||||
from cinder import utils
|
||||
@ -51,6 +52,8 @@ def main():
|
||||
logging.setup(CONF, "cinder")
|
||||
utils.monkey_patch()
|
||||
|
||||
gmr.TextGuruMeditation.setup_autorun(version)
|
||||
|
||||
rpc.init(CONF)
|
||||
launcher = service.process_launcher()
|
||||
server = service.WSGIService('osapi_volume')
|
||||
|
@ -33,6 +33,7 @@ i18n.enable_lazy()
|
||||
|
||||
# Need to register global_opts
|
||||
from cinder.common import config # noqa
|
||||
from cinder.openstack.common.report import guru_meditation_report as gmr
|
||||
from cinder import service
|
||||
from cinder import utils
|
||||
from cinder import version
|
||||
@ -46,6 +47,7 @@ def main():
|
||||
version=version.version_string())
|
||||
logging.setup(CONF, "cinder")
|
||||
utils.monkey_patch()
|
||||
gmr.TextGuruMeditation.setup_autorun(version)
|
||||
server = service.Service.create(binary='cinder-backup')
|
||||
service.serve(server)
|
||||
service.wait()
|
||||
|
@ -33,6 +33,7 @@ i18n.enable_lazy()
|
||||
|
||||
# Need to register global_opts
|
||||
from cinder.common import config # noqa
|
||||
from cinder.openstack.common.report import guru_meditation_report as gmr
|
||||
from cinder import service
|
||||
from cinder import utils
|
||||
from cinder import version
|
||||
@ -46,6 +47,7 @@ def main():
|
||||
version=version.version_string())
|
||||
logging.setup(CONF, "cinder")
|
||||
utils.monkey_patch()
|
||||
gmr.TextGuruMeditation.setup_autorun(version)
|
||||
server = service.Service.create(binary='cinder-scheduler')
|
||||
service.serve(server)
|
||||
service.wait()
|
||||
|
@ -44,6 +44,7 @@ i18n.enable_lazy()
|
||||
# Need to register global_opts
|
||||
from cinder.common import config # noqa
|
||||
from cinder.db import api as session
|
||||
from cinder.openstack.common.report import guru_meditation_report as gmr
|
||||
from cinder import service
|
||||
from cinder import utils
|
||||
from cinder import version
|
||||
@ -62,6 +63,7 @@ def main():
|
||||
version=version.version_string())
|
||||
logging.setup(CONF, "cinder")
|
||||
utils.monkey_patch()
|
||||
gmr.TextGuruMeditation.setup_autorun(version)
|
||||
launcher = service.get_launcher()
|
||||
if CONF.enabled_backends:
|
||||
for backend in CONF.enabled_backends:
|
||||
|
25
cinder/openstack/common/report/__init__.py
Normal file
25
cinder/openstack/common/report/__init__.py
Normal file
@ -0,0 +1,25 @@
|
||||
# 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
|
||||
# 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 way to generate serializable reports
|
||||
|
||||
This package/module provides mechanisms for defining reports
|
||||
which may then be serialized into various data types. Each
|
||||
report ( :class:`openstack.common.report.report.BasicReport` )
|
||||
is composed of one or more report sections
|
||||
( :class:`openstack.common.report.report.BasicSection` ),
|
||||
which contain generators which generate data models
|
||||
( :class:`openstack.common.report.models.base.ReportModels` ),
|
||||
which are then serialized by views.
|
||||
"""
|
21
cinder/openstack/common/report/generators/__init__.py
Normal file
21
cinder/openstack/common/report/generators/__init__.py
Normal file
@ -0,0 +1,21 @@
|
||||
# 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
|
||||
# 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 Data Model Generators
|
||||
|
||||
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.
|
||||
"""
|
44
cinder/openstack/common/report/generators/conf.py
Normal file
44
cinder/openstack/common/report/generators/conf.py
Normal file
@ -0,0 +1,44 @@
|
||||
# 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
|
||||
# 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 Openstack config generators
|
||||
|
||||
This module defines a class for configuration
|
||||
generators for generating the model in
|
||||
:mod:`openstack.common.report.models.conf`.
|
||||
"""
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
import cinder.openstack.common.report.models.conf as cm
|
||||
|
||||
|
||||
class ConfigReportGenerator(object):
|
||||
"""A Configuration Data Generator
|
||||
|
||||
This generator returns
|
||||
: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.
|
||||
|
||||
:param cnf: the configuration option object
|
||||
:type cnf: :class:`oslo.config.cfg.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, cnf=cfg.CONF):
|
||||
self.conf_obj = cnf
|
||||
|
||||
def __call__(self):
|
||||
return cm.ConfigModel(self.conf_obj)
|
73
cinder/openstack/common/report/generators/threading.py
Normal file
73
cinder/openstack/common/report/generators/threading.py
Normal file
@ -0,0 +1,73 @@
|
||||
# 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
|
||||
# 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 thread-related generators
|
||||
|
||||
This module defines classes for threading-related
|
||||
generators for generating the models in
|
||||
:mod:`openstack.common.report.models.threading`.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import greenlet
|
||||
|
||||
import cinder.openstack.common.report.models.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
|
||||
|
||||
|
||||
class ThreadReportGenerator(object):
|
||||
"""A Thread Data Generator
|
||||
|
||||
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()` .
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
threadModels = [
|
||||
tm.ThreadModel(thread_id, stack)
|
||||
for thread_id, stack in sys._current_frames().items()
|
||||
]
|
||||
|
||||
thread_pairs = dict(zip(range(len(threadModels)), threadModels))
|
||||
return mwdv.ModelWithDefaultViews(thread_pairs,
|
||||
text_view=text_views.MultiView())
|
||||
|
||||
|
||||
class GreenThreadReportGenerator(object):
|
||||
"""A Green Thread Data Generator
|
||||
|
||||
This generator returns a collection of
|
||||
:class:`openstack.common.report.models.threading.GreenThreadModel`
|
||||
objects by introspecting the current python garbage collection
|
||||
state, and sifting through for :class:`greenlet.greenlet` objects.
|
||||
|
||||
.. seealso::
|
||||
|
||||
Function :func:`openstack.common.report.utils._find_objects`
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
threadModels = [
|
||||
tm.GreenThreadModel(gr.gr_frame)
|
||||
for gr in rutils._find_objects(greenlet.greenlet)
|
||||
]
|
||||
|
||||
thread_pairs = dict(zip(range(len(threadModels)), threadModels))
|
||||
return mwdv.ModelWithDefaultViews(thread_pairs,
|
||||
text_view=text_views.MultiView())
|
46
cinder/openstack/common/report/generators/version.py
Normal file
46
cinder/openstack/common/report/generators/version.py
Normal file
@ -0,0 +1,46 @@
|
||||
# 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
|
||||
# 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 Openstack version generators
|
||||
|
||||
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
|
||||
|
||||
|
||||
class PackageReportGenerator(object):
|
||||
"""A Package Information Data Generator
|
||||
|
||||
This generator returns
|
||||
:class:`openstack.common.report.models.version.PackageModel`,
|
||||
extracting data from the given version object, which should follow
|
||||
the general format defined in Nova's version information (i.e. it
|
||||
should contain the methods vendor_string, product_string, and
|
||||
version_string_with_package).
|
||||
|
||||
:param version_object: the version information object
|
||||
"""
|
||||
|
||||
def __init__(self, version_obj):
|
||||
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())
|
179
cinder/openstack/common/report/guru_meditation_report.py
Normal file
179
cinder/openstack/common/report/guru_meditation_report.py
Normal file
@ -0,0 +1,179 @@
|
||||
# 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
|
||||
# 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 Guru Meditation Report
|
||||
|
||||
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):
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 8,9,10
|
||||
|
||||
CONF = cfg.CONF
|
||||
# maybe import some options here...
|
||||
|
||||
def main():
|
||||
config.parse_args(sys.argv)
|
||||
logging.setup('blah')
|
||||
|
||||
TextGuruMeditation.register_section('Some Special Section',
|
||||
special_section_generator)
|
||||
TextGuruMeditation.setup_autorun(version_object)
|
||||
|
||||
server = service.Service.create(binary='some-service',
|
||||
topic=CONF.some_service_topic)
|
||||
service.serve(server)
|
||||
service.wait()
|
||||
|
||||
Then, you can do
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ kill -USR1 $SERVICE_PID
|
||||
|
||||
and get a Guru Meditation Report in the file or terminal
|
||||
where stderr is logged for that given service.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import signal
|
||||
import sys
|
||||
|
||||
from cinder.openstack.common.report.generators import conf as cgen
|
||||
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
|
||||
|
||||
|
||||
class GuruMeditation(object):
|
||||
"""A Guru Meditation Report Mixin/Base Class
|
||||
|
||||
This class is a base class for Guru Meditation Reports.
|
||||
It provides facilities for registering sections and
|
||||
setting up functionality to auto-run the report on
|
||||
a certain signal.
|
||||
|
||||
This class should always be used in conjunction with
|
||||
a Report class via multiple inheritance. It should
|
||||
always come first in the class list to ensure the
|
||||
MRO is correct.
|
||||
"""
|
||||
|
||||
def __init__(self, version_obj, *args, **kwargs):
|
||||
self.version_obj = version_obj
|
||||
|
||||
super(GuruMeditation, self).__init__(*args, **kwargs)
|
||||
self.start_section_index = len(self.sections)
|
||||
|
||||
@classmethod
|
||||
def register_section(cls, section_title, generator):
|
||||
"""Register a New Section
|
||||
|
||||
This method registers a persistent section for the current
|
||||
class.
|
||||
|
||||
:param str section_title: the title of the section
|
||||
:param generator: the generator for the section
|
||||
"""
|
||||
|
||||
try:
|
||||
cls.persistent_sections.append([section_title, generator])
|
||||
except AttributeError:
|
||||
cls.persistent_sections = [[section_title, generator]]
|
||||
|
||||
@classmethod
|
||||
def setup_autorun(cls, version, signum=signal.SIGUSR1):
|
||||
"""Set Up Auto-Run
|
||||
|
||||
This method sets up the Guru Meditation Report to automatically
|
||||
get dumped to stderr when the given signal is received.
|
||||
|
||||
:param version: the version object for the current product
|
||||
:param signum: the signal to associate with running the report
|
||||
"""
|
||||
|
||||
signal.signal(signum, lambda *args: cls.handle_signal(version, *args))
|
||||
|
||||
@classmethod
|
||||
def handle_signal(cls, version, *args):
|
||||
"""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
|
||||
parameter.
|
||||
|
||||
:param version: the version object for the current product
|
||||
"""
|
||||
|
||||
try:
|
||||
res = cls(version).run()
|
||||
except Exception:
|
||||
print("Unable to run Guru Meditation Report!",
|
||||
file=sys.stderr)
|
||||
else:
|
||||
print(res, file=sys.stderr)
|
||||
|
||||
def _readd_sections(self):
|
||||
del self.sections[self.start_section_index:]
|
||||
|
||||
self.add_section('Package',
|
||||
pgen.PackageReportGenerator(self.version_obj))
|
||||
|
||||
self.add_section('Threads',
|
||||
tgen.ThreadReportGenerator())
|
||||
|
||||
self.add_section('Green Threads',
|
||||
tgen.GreenThreadReportGenerator())
|
||||
|
||||
self.add_section('Configuration',
|
||||
cgen.ConfigReportGenerator())
|
||||
|
||||
try:
|
||||
for section_title, generator in self.persistent_sections:
|
||||
self.add_section(section_title, generator)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
self._readd_sections()
|
||||
return super(GuruMeditation, self).run()
|
||||
|
||||
|
||||
# GuruMeditation must come first to get the correct MRO
|
||||
class TextGuruMeditation(GuruMeditation, report.TextReport):
|
||||
"""A Text Guru Meditation Report
|
||||
|
||||
This report is the basic human-readable Guru Meditation Report
|
||||
|
||||
It contains the following sections by default
|
||||
(in addition to any registered persistent sections):
|
||||
|
||||
- Package Information
|
||||
|
||||
- Threads List
|
||||
|
||||
- Green Threads List
|
||||
|
||||
- Configuration Options
|
||||
|
||||
:param version_obj: the version object for the current product
|
||||
"""
|
||||
|
||||
def __init__(self, version_obj):
|
||||
super(TextGuruMeditation, self).__init__(version_obj,
|
||||
'Guru Meditation')
|
20
cinder/openstack/common/report/models/__init__.py
Normal file
20
cinder/openstack/common/report/models/__init__.py
Normal file
@ -0,0 +1,20 @@
|
||||
# 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
|
||||
# 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 data models
|
||||
|
||||
This module provides both the base data model,
|
||||
as well as several predefined specific data models
|
||||
to be used in reports.
|
||||
"""
|
114
cinder/openstack/common/report/models/base.py
Normal file
114
cinder/openstack/common/report/models/base.py
Normal file
@ -0,0 +1,114 @@
|
||||
# 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
|
||||
# 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 the base report model
|
||||
|
||||
This module defines a class representing the basic report
|
||||
data model from which all data models should inherit (or
|
||||
at least implement similar functionality). Data models
|
||||
store unserialized data generated by generators during
|
||||
the report serialization process.
|
||||
"""
|
||||
|
||||
import collections as col
|
||||
import copy
|
||||
|
||||
|
||||
class ReportModel(col.MutableMapping):
|
||||
"""A Report Data Model
|
||||
|
||||
A report data model contains data generated by some
|
||||
generator method or class. Data may be read or written
|
||||
using dictionary-style access, and may be read (but not
|
||||
written) using object-member-style access. Additionally,
|
||||
a data model may have an associated view. This view is
|
||||
used to serialize the model when str() is called on the
|
||||
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
|
||||
: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 {}
|
||||
|
||||
def __str__(self):
|
||||
self_cpy = copy.deepcopy(self)
|
||||
for key in self_cpy:
|
||||
if getattr(self_cpy[key], 'attached_view', None) is not None:
|
||||
self_cpy[key] = str(self_cpy[key])
|
||||
|
||||
if self.attached_view is not None:
|
||||
return self.attached_view(self_cpy)
|
||||
else:
|
||||
raise Exception("Cannot stringify model: no attached view")
|
||||
|
||||
def __repr__(self):
|
||||
if self.attached_view is not None:
|
||||
return ("<Model {cl.__module__}.{cl.__name__} {dt}"
|
||||
" with view {vw.__module__}."
|
||||
"{vw.__name__}>").format(cl=type(self),
|
||||
dt=self.data,
|
||||
vw=type(self.attached_view))
|
||||
else:
|
||||
return ("<Model {cl.__module__}.{cl.__name__} {dt}"
|
||||
" with no view>").format(cl=type(self),
|
||||
dt=self.data)
|
||||
|
||||
def __getitem__(self, attrname):
|
||||
return self.data[attrname]
|
||||
|
||||
def __setitem__(self, attrname, attrval):
|
||||
self.data[attrname] = attrval
|
||||
|
||||
def __delitem__(self, attrname):
|
||||
del self.data[attrname]
|
||||
|
||||
def __contains__(self, key):
|
||||
return self.data.__contains__(key)
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
try:
|
||||
return self.data[attrname]
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
"'{cl}' object has no attribute '{an}'".format(
|
||||
cl=type(self).__name__, an=attrname
|
||||
)
|
||||
)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
def __iter__(self):
|
||||
return self.data.__iter__()
|
||||
|
||||
def set_current_view_type(self, tp):
|
||||
"""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)
|
||||
|
||||
:param tp: the type of the view ('text', 'json', 'xml', etc)
|
||||
"""
|
||||
|
||||
for key in self:
|
||||
try:
|
||||
self[key].set_current_view_type(tp)
|
||||
except AttributeError:
|
||||
pass
|
58
cinder/openstack/common/report/models/conf.py
Normal file
58
cinder/openstack/common/report/models/conf.py
Normal file
@ -0,0 +1,58 @@
|
||||
# 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
|
||||
# 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 Openstack Configuration Model
|
||||
|
||||
This module defines a class representing the data
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
of options and named option groups.
|
||||
|
||||
:param conf_obj: a configuration object
|
||||
:type conf_obj: :class:`oslo.config.cfg.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, conf_obj):
|
||||
kv_view = generic_text_views.KeyValueView(dict_sep=": ",
|
||||
before_dict='')
|
||||
super(ConfigModel, self).__init__(text_view=kv_view)
|
||||
|
||||
def opt_title(optname, co):
|
||||
return co._opts[optname]['opt'].name
|
||||
|
||||
self['default'] = dict(
|
||||
(opt_title(optname, conf_obj), conf_obj[optname])
|
||||
for optname in conf_obj._opts
|
||||
)
|
||||
|
||||
groups = {}
|
||||
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
|
||||
)
|
||||
groups[group_obj.name] = curr_group_opts
|
||||
|
||||
self.update(groups)
|
100
cinder/openstack/common/report/models/threading.py
Normal file
100
cinder/openstack/common/report/models/threading.py
Normal file
@ -0,0 +1,100 @@
|
||||
# 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
|
||||
# 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 threading and stack-trace models
|
||||
|
||||
This module defines classes representing thread, green
|
||||
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
|
||||
|
||||
|
||||
class StackTraceModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Stack Trace Model
|
||||
|
||||
This model holds data from a python stack trace,
|
||||
commonly extracted from running thread information
|
||||
|
||||
:param stack_state: the python stack_state object
|
||||
"""
|
||||
|
||||
def __init__(self, stack_state):
|
||||
super(StackTraceModel, self).__init__(
|
||||
text_view=text_views.StackTraceView())
|
||||
|
||||
if (stack_state is not None):
|
||||
self['lines'] = [
|
||||
{'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:
|
||||
self['root_exception'] = {
|
||||
'type': stack_state.f_exc_type,
|
||||
'value': stack_state.f_exc_value
|
||||
}
|
||||
else:
|
||||
self['root_exception'] = None
|
||||
else:
|
||||
self['lines'] = []
|
||||
self['root_exception'] = None
|
||||
|
||||
|
||||
class ThreadModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Thread Model
|
||||
|
||||
This model holds data for information about an
|
||||
individual thread. It holds both a thread id,
|
||||
as well as a stack trace for the thread
|
||||
|
||||
.. seealso::
|
||||
|
||||
Class :class:`StackTraceModel`
|
||||
|
||||
:param int thread_id: the id of the thread
|
||||
:param stack: the python stack state for the current thread
|
||||
"""
|
||||
|
||||
# threadId, stack in sys._current_frams().items()
|
||||
def __init__(self, thread_id, stack):
|
||||
super(ThreadModel, self).__init__(text_view=text_views.ThreadView())
|
||||
|
||||
self['thread_id'] = thread_id
|
||||
self['stack_trace'] = StackTraceModel(stack)
|
||||
|
||||
|
||||
class GreenThreadModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Green Thread Model
|
||||
|
||||
This model holds data for information about an
|
||||
individual thread. Unlike the thread model,
|
||||
it holds just a stack trace, since green threads
|
||||
do not have thread ids.
|
||||
|
||||
.. seealso::
|
||||
|
||||
Class :class:`StackTraceModel`
|
||||
|
||||
:param stack: the python stack state for the green thread
|
||||
"""
|
||||
|
||||
# gr in greenpool.coroutines_running --> gr.gr_frame
|
||||
def __init__(self, stack):
|
||||
super(GreenThreadModel, self).__init__(
|
||||
{'stack_trace': StackTraceModel(stack)},
|
||||
text_view=text_views.GreenThreadView())
|
44
cinder/openstack/common/report/models/version.py
Normal file
44
cinder/openstack/common/report/models/version.py
Normal file
@ -0,0 +1,44 @@
|
||||
# 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
|
||||
# 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 Openstack Version Info Model
|
||||
|
||||
This module defines a class representing the data
|
||||
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
|
||||
|
||||
|
||||
class PackageModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Package Information Model
|
||||
|
||||
This model holds information about the current
|
||||
package. It contains vendor, product, and version
|
||||
information.
|
||||
|
||||
:param str vendor: the product vendor
|
||||
:param str product: the product name
|
||||
:param str version: the product version
|
||||
"""
|
||||
|
||||
def __init__(self, vendor, product, version):
|
||||
super(PackageModel, self).__init__(
|
||||
text_view=generic_text_views.KeyValueView()
|
||||
)
|
||||
|
||||
self['vendor'] = vendor
|
||||
self['product'] = product
|
||||
self['version'] = version
|
82
cinder/openstack/common/report/models/with_default_views.py
Normal file
82
cinder/openstack/common/report/models/with_default_views.py
Normal file
@ -0,0 +1,82 @@
|
||||
# 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
|
||||
# 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 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
|
||||
|
||||
|
||||
class ModelWithDefaultViews(base_model.ReportModel):
|
||||
"""A Model With Default Views of Various Types
|
||||
|
||||
A model with default views has several predefined views,
|
||||
each associated with a given type. This is often used for
|
||||
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
|
||||
get stored as default views.
|
||||
|
||||
The default 'default views' are
|
||||
|
||||
text
|
||||
:class:`openstack.common.views.text.generic.KeyValueView`
|
||||
xml
|
||||
:class:`openstack.common.views.xml.generic.KeyValueView`
|
||||
json
|
||||
:class:`openstack.common.views.json.generic.KeyValueView`
|
||||
|
||||
.. function:: to_type()
|
||||
|
||||
('type' is one of the 'default views' defined for this model)
|
||||
Serializes this model using the default view for 'type'
|
||||
|
||||
:rtype: str
|
||||
:returns: this model serialized as 'type'
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.views = {
|
||||
'text': textviews.KeyValueView(),
|
||||
'json': jsonviews.KeyValueView(),
|
||||
'xml': xmlviews.KeyValueView()
|
||||
}
|
||||
|
||||
newargs = copy.copy(kwargs)
|
||||
for k in kwargs:
|
||||
if k.endswith('_view'):
|
||||
self.views[k[:-5]] = kwargs[k]
|
||||
del newargs[k]
|
||||
super(ModelWithDefaultViews, self).__init__(*args, **newargs)
|
||||
|
||||
def set_current_view_type(self, tp):
|
||||
self.attached_view = self.views[tp]
|
||||
super(ModelWithDefaultViews, self).set_current_view_type(tp)
|
||||
|
||||
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:]})
|
||||
else:
|
||||
return super(ModelWithDefaultViews, self).__getattr__(attrname)
|
189
cinder/openstack/common/report/report.py
Normal file
189
cinder/openstack/common/report/report.py
Normal file
@ -0,0 +1,189 @@
|
||||
# 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
|
||||
# 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 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.
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.views.text.header as header_views
|
||||
|
||||
|
||||
class BasicReport(object):
|
||||
"""A Basic Report
|
||||
|
||||
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`
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.sections = []
|
||||
self._state = 0
|
||||
|
||||
def add_section(self, view, generator, index=None):
|
||||
"""Add a section to the report
|
||||
|
||||
This method adds a section with the given view and
|
||||
generator to the report. An index may be specified to
|
||||
insert the section at a given location in the list;
|
||||
If no index is specified, the section is appended to the
|
||||
list. The view is called on the model which results from
|
||||
the generator when the report is run. A generator is simply
|
||||
a method or callable object which takes no arguments and
|
||||
returns a :class:`openstack.common.report.models.base.ReportModel`
|
||||
or similar object.
|
||||
|
||||
:param view: the top-level view for the section
|
||||
:param generator: the method or class which generates the model
|
||||
:param index: the index at which to insert the section
|
||||
(or None to append it)
|
||||
:type index: int or None
|
||||
"""
|
||||
|
||||
if index is None:
|
||||
self.sections.append(ReportSection(view, generator))
|
||||
else:
|
||||
self.sections.insert(index, ReportSection(view, generator))
|
||||
|
||||
def run(self):
|
||||
"""Run the report
|
||||
|
||||
This method runs the report, having each section generate
|
||||
its data and serialize itself before joining the sections
|
||||
together. The BasicReport accomplishes the joining
|
||||
by joining the serialized sections together with newlines.
|
||||
|
||||
:rtype: str
|
||||
:returns: the serialized report
|
||||
"""
|
||||
|
||||
return "\n".join(str(sect) for sect in self.sections)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
.. seealso::
|
||||
|
||||
Class :class:`BasicReport`
|
||||
:func:`BasicReport.add_section`
|
||||
|
||||
: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)
|
||||
"""
|
||||
|
||||
def __init__(self, view, generator):
|
||||
self.view = view
|
||||
self.generator = generator
|
||||
|
||||
def __str__(self):
|
||||
return self.view(self.generator())
|
||||
|
||||
|
||||
class ReportOfType(BasicReport):
|
||||
"""A Report of a Certain Type
|
||||
|
||||
A ReportOfType has a predefined type associated with it.
|
||||
This type is automatically propagated down to the each of
|
||||
the sections upon serialization by wrapping the generator
|
||||
for each section.
|
||||
|
||||
.. seealso::
|
||||
|
||||
Class :class:`openstack.common.report.models.with_default_view.ModelWithDefaultView` # noqa
|
||||
(the entire class)
|
||||
|
||||
Class :class:`openstack.common.report.models.base.ReportModel`
|
||||
:func:`openstack.common.report.models.base.ReportModel.set_current_view_type` # noqa
|
||||
|
||||
:param str tp: the type of the report
|
||||
"""
|
||||
|
||||
def __init__(self, tp):
|
||||
self.output_type = tp
|
||||
super(ReportOfType, self).__init__()
|
||||
|
||||
def add_section(self, view, generator, index=None):
|
||||
def with_type(gen):
|
||||
def newgen():
|
||||
res = gen()
|
||||
try:
|
||||
res.set_current_view_type(self.output_type)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return res
|
||||
return newgen
|
||||
|
||||
super(ReportOfType, self).add_section(
|
||||
view,
|
||||
with_type(generator),
|
||||
index
|
||||
)
|
||||
|
||||
|
||||
class TextReport(ReportOfType):
|
||||
"""A Human-Readable Text Report
|
||||
|
||||
This class defines a report that is designed to be read by a human
|
||||
being. It has nice section headers, and a formatted title.
|
||||
|
||||
:param str name: the title of the report
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
super(TextReport, self).__init__('text')
|
||||
self.name = name
|
||||
# add a title with a generator that creates an empty result model
|
||||
self.add_section(name, lambda: ('|' * 72) + "\n\n")
|
||||
|
||||
def add_section(self, heading, generator, index=None):
|
||||
"""Add a section to the report
|
||||
|
||||
This method adds a section with the given title, and
|
||||
generator to the report. An index may be specified to
|
||||
insert the section at a given location in the list;
|
||||
If no index is specified, the section is appended to the
|
||||
list. The view is called on the model which results from
|
||||
the generator when the report is run. A generator is simply
|
||||
a method or callable object which takes no arguments and
|
||||
returns a :class:`openstack.common.report.models.base.ReportModel`
|
||||
or similar object.
|
||||
|
||||
The model is told to serialize as text (if possible) at serialization
|
||||
time by wrapping the generator. The view model's attached view
|
||||
(if any) is wrapped in a
|
||||
:class:`openstack.common.report.views.text.header.TitledView`
|
||||
|
||||
:param str heading: the title for the section
|
||||
:param generator: the method or class which generates the model
|
||||
:param index: the index at which to insert the section
|
||||
(or None to append)
|
||||
:type index: int or None
|
||||
"""
|
||||
|
||||
super(TextReport, self).add_section(header_views.TitledView(heading),
|
||||
generator,
|
||||
index)
|
46
cinder/openstack/common/report/utils.py
Normal file
46
cinder/openstack/common/report/utils.py
Normal file
@ -0,0 +1,46 @@
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
"""Various utilities for report generation
|
||||
|
||||
This module includes various utilities
|
||||
used in generating reports.
|
||||
"""
|
||||
|
||||
import gc
|
||||
|
||||
|
||||
class StringWithAttrs(str):
|
||||
"""A String that can have arbitrary attributes
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def _find_objects(t):
|
||||
"""Find Objects in the GC State
|
||||
|
||||
This horribly hackish method locates objects of a
|
||||
given class in the current python instance's garbage
|
||||
collection state. In case you couldn't tell, this is
|
||||
horribly hackish, but is necessary for locating all
|
||||
green threads, since they don't keep track of themselves
|
||||
like normal threads do in python.
|
||||
|
||||
:param class t: the class of object to locate
|
||||
:rtype: list
|
||||
:returns: a list of objects of the given type
|
||||
"""
|
||||
|
||||
return [o for o in gc.get_objects() if isinstance(o, t)]
|
22
cinder/openstack/common/report/views/__init__.py
Normal file
22
cinder/openstack/common/report/views/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
# 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
|
||||
# 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 predefined views
|
||||
|
||||
This module provides a collection of predefined views
|
||||
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.
|
||||
"""
|
125
cinder/openstack/common/report/views/jinja_view.py
Normal file
125
cinder/openstack/common/report/views/jinja_view.py
Normal file
@ -0,0 +1,125 @@
|
||||
# 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
|
||||
# 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 Jinja Views
|
||||
|
||||
This module provides views that utilize the Jinja templating
|
||||
system for serialization. For more information on Jinja, please
|
||||
see http://jinja.pocoo.org/ .
|
||||
"""
|
||||
|
||||
import jinja2
|
||||
|
||||
|
||||
class JinjaView(object):
|
||||
"""A Jinja View
|
||||
|
||||
This view renders the given model using the provided Jinja
|
||||
template. The template can be given in various ways.
|
||||
If the `VIEw_TEXT` property is defined, that is used as template.
|
||||
Othewise, if a `path` parameter is passed to the constructor, that
|
||||
is used to load a file containing the template. If the `path`
|
||||
parameter is None, the `text` parameter is used as the template.
|
||||
|
||||
The leading newline character and trailing newline character are stripped
|
||||
from the template (provided they exist). Baseline indentation is
|
||||
also stripped from each line. The baseline indentation is determined by
|
||||
checking the indentation of the first line, after stripping off the leading
|
||||
newline (if any).
|
||||
|
||||
:param str path: the path to the Jinja template
|
||||
:param str text: the text of the Jinja template
|
||||
"""
|
||||
|
||||
def __init__(self, path=None, text=None):
|
||||
try:
|
||||
self._text = self.VIEW_TEXT
|
||||
except AttributeError:
|
||||
if path is not None:
|
||||
with open(path, 'r') as f:
|
||||
self._text = f.read()
|
||||
elif text is not None:
|
||||
self._text = text
|
||||
else:
|
||||
self._text = ""
|
||||
|
||||
if self._text[0] == "\n":
|
||||
self._text = self._text[1:]
|
||||
|
||||
newtext = self._text.lstrip()
|
||||
amt = len(self._text) - len(newtext)
|
||||
if (amt > 0):
|
||||
base_indent = self._text[0:amt]
|
||||
lines = self._text.splitlines()
|
||||
newlines = []
|
||||
for line in lines:
|
||||
if line.startswith(base_indent):
|
||||
newlines.append(line[amt:])
|
||||
else:
|
||||
newlines.append(line)
|
||||
self._text = "\n".join(newlines)
|
||||
|
||||
if self._text[-1] == "\n":
|
||||
self._text = self._text[:-1]
|
||||
|
||||
self._regentemplate = True
|
||||
self._templatecache = None
|
||||
|
||||
def __call__(self, model):
|
||||
return self.template.render(**model)
|
||||
|
||||
@property
|
||||
def template(self):
|
||||
"""Get the Compiled Template
|
||||
|
||||
Gets the compiled template, using a cached copy if possible
|
||||
(stored in attr:`_templatecache`) or otherwise recompiling
|
||||
the template if the compiled template is not present or is
|
||||
invalid (due to attr:`_regentemplate` being set to True).
|
||||
|
||||
:returns: the compiled Jinja template
|
||||
:rtype: :class:`jinja2.Template`
|
||||
"""
|
||||
|
||||
if self._templatecache is None or self._regentemplate:
|
||||
self._templatecache = jinja2.Template(self._text)
|
||||
self._regentemplate = False
|
||||
|
||||
return self._templatecache
|
||||
|
||||
def _gettext(self):
|
||||
"""Get the Template Text
|
||||
|
||||
Gets the text of the current template
|
||||
|
||||
:returns: the text of the Jinja template
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
return self._text
|
||||
|
||||
def _settext(self, textval):
|
||||
"""Set the Template Text
|
||||
|
||||
Sets the text of the current template, marking it
|
||||
for recompilation next time the compiled template
|
||||
is retrived via attr:`template` .
|
||||
|
||||
:param str textval: the new text of the Jinja template
|
||||
"""
|
||||
|
||||
self._text = textval
|
||||
self.regentemplate = True
|
||||
|
||||
text = property(_gettext, _settext)
|
19
cinder/openstack/common/report/views/json/__init__.py
Normal file
19
cinder/openstack/common/report/views/json/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
# 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
|
||||
# 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 basic JSON views
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into JSON.
|
||||
"""
|
66
cinder/openstack/common/report/views/json/generic.py
Normal file
66
cinder/openstack/common/report/views/json/generic.py
Normal file
@ -0,0 +1,66 @@
|
||||
# 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
|
||||
# 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 generic JSON views
|
||||
|
||||
This modules defines several basic views for serializing
|
||||
data to JSON. Submodels that have already been serialized
|
||||
as JSON may have their string values marked with `__is_json__
|
||||
= True` using :class:`openstack.common.report.utils.StringWithAttrs`
|
||||
(each of the classes within this module does this automatically,
|
||||
and non-naive serializers check for this attribute and handle
|
||||
such strings specially)
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
import cinder.openstack.common.report.utils as utils
|
||||
|
||||
|
||||
class BasicKeyValueView(object):
|
||||
"""A Basic Key-Value JSON View
|
||||
|
||||
This view performs a naive serialization of a model
|
||||
into JSON by simply calling :func:`json.dumps` on the model
|
||||
"""
|
||||
|
||||
def __call__(self, model):
|
||||
res = utils.StringWithAttrs(json.dumps(model.data))
|
||||
res.__is_json__ = True
|
||||
return res
|
||||
|
||||
|
||||
class KeyValueView(object):
|
||||
"""A Key-Value JSON View
|
||||
|
||||
This view performs advanced serialization to a model
|
||||
into JSON. It does so by first checking all values to
|
||||
see if they are marked as JSON. If so, they are deserialized
|
||||
using :func:`json.loads`. Then, the copy of the model with all
|
||||
JSON deserialized is reserialized into proper nested JSON using
|
||||
:func:`json.dumps`.
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
res = utils.StringWithAttrs(json.dumps(cpy.data))
|
||||
res.__is_json__ = True
|
||||
return res
|
19
cinder/openstack/common/report/views/text/__init__.py
Normal file
19
cinder/openstack/common/report/views/text/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
# 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
|
||||
# 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 basic text views
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into human-readable text.
|
||||
"""
|
202
cinder/openstack/common/report/views/text/generic.py
Normal file
202
cinder/openstack/common/report/views/text/generic.py
Normal file
@ -0,0 +1,202 @@
|
||||
# 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
|
||||
# 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 generic text views
|
||||
|
||||
This modules provides several generic views for
|
||||
serializing models into human-readable text.
|
||||
"""
|
||||
|
||||
import collections as col
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class MultiView(object):
|
||||
"""A Text View Containing Multiple Views
|
||||
|
||||
This view simply serializes each
|
||||
value in the data model, and then
|
||||
joins them with newlines (ignoring
|
||||
the key values altogether). This is
|
||||
useful for serializing lists of models
|
||||
(as array-like dicts).
|
||||
"""
|
||||
|
||||
def __call__(self, model):
|
||||
res = [str(model[key]) for key in model]
|
||||
return "\n".join(res)
|
||||
|
||||
|
||||
class BasicKeyValueView(object):
|
||||
"""A Basic Key-Value Text View
|
||||
|
||||
This view performs a naive serialization of a model into
|
||||
text using a basic key-value method, where each
|
||||
key-value pair is rendered as "key = str(value)"
|
||||
"""
|
||||
|
||||
def __call__(self, model):
|
||||
res = ""
|
||||
for key in model:
|
||||
res += "{key} = {value}\n".format(key=key, value=model[key])
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class KeyValueView(object):
|
||||
"""A Key-Value Text View
|
||||
|
||||
This view performs an advanced serialization of a model
|
||||
into text by following the following set of rules:
|
||||
|
||||
key : text
|
||||
key = text
|
||||
|
||||
rootkey : Mapping
|
||||
::
|
||||
|
||||
rootkey =
|
||||
serialize(key, value)
|
||||
|
||||
key : Sequence
|
||||
::
|
||||
|
||||
key =
|
||||
serialize(item)
|
||||
|
||||
:param str indent_str: the string used to represent one "indent"
|
||||
:param str key_sep: the separator to use between keys and values
|
||||
:param str dict_sep: the separator to use after a dictionary root key
|
||||
:param str list_sep: the separator to use after a list root key
|
||||
:param str anon_dict: the "key" to use when there is a dict in a list
|
||||
(does not automatically use the dict separator)
|
||||
:param before_dict: content to place on the line(s) before the a dict
|
||||
root key (use None to avoid inserting an extra line)
|
||||
:type before_dict: str or None
|
||||
:param before_list: content to place on the line(s) before the a list
|
||||
root key (use None to avoid inserting an extra line)
|
||||
:type before_list: str or None
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
indent_str=' ',
|
||||
key_sep=' = ',
|
||||
dict_sep=' = ',
|
||||
list_sep=' = ',
|
||||
anon_dict='[dict]',
|
||||
before_dict=None,
|
||||
before_list=None):
|
||||
self.indent_str = indent_str
|
||||
self.key_sep = key_sep
|
||||
self.dict_sep = dict_sep
|
||||
self.list_sep = list_sep
|
||||
self.anon_dict = anon_dict
|
||||
self.before_dict = before_dict
|
||||
self.before_list = before_list
|
||||
|
||||
def __call__(self, model):
|
||||
def serialize(root, rootkey, indent):
|
||||
res = []
|
||||
if rootkey is not None:
|
||||
res.append((self.indent_str * indent) + rootkey)
|
||||
|
||||
if isinstance(root, col.Mapping):
|
||||
if rootkey is None and indent > 0:
|
||||
res.append((self.indent_str * indent) + self.anon_dict)
|
||||
elif rootkey is not None:
|
||||
res[0] += self.dict_sep
|
||||
if self.before_dict is not None:
|
||||
res.insert(0, self.before_dict)
|
||||
|
||||
for key in root:
|
||||
res.extend(serialize(root[key], key, indent + 1))
|
||||
elif (isinstance(root, col.Sequence) and
|
||||
not isinstance(root, six.string_types)):
|
||||
if rootkey is not None:
|
||||
res[0] += self.list_sep
|
||||
if self.before_list is not None:
|
||||
res.insert(0, self.before_list)
|
||||
|
||||
for val in root:
|
||||
res.extend(serialize(val, None, indent + 1))
|
||||
else:
|
||||
str_root = str(root)
|
||||
if '\n' in str_root:
|
||||
# we are in a submodel
|
||||
if rootkey is not None:
|
||||
res[0] += self.dict_sep
|
||||
|
||||
list_root = [(self.indent_str * (indent + 1)) + line
|
||||
for line in str_root.split('\n')]
|
||||
res.extend(list_root)
|
||||
else:
|
||||
# just a normal key or list entry
|
||||
try:
|
||||
res[0] += self.key_sep + str_root
|
||||
except IndexError:
|
||||
res = [(self.indent_str * indent) + str_root]
|
||||
|
||||
return res
|
||||
|
||||
return "\n".join(serialize(model, None, -1))
|
||||
|
||||
|
||||
class TableView(object):
|
||||
"""A Basic Table Text View
|
||||
|
||||
This view performs serialization of data into a basic table with
|
||||
predefined column names and mappings. Column width is auto-calculated
|
||||
evenly, column values are automatically truncated accordingly. Values
|
||||
are centered in the columns.
|
||||
|
||||
:param [str] column_names: the headers for each of the columns
|
||||
:param [str] column_values: the item name to match each column to in
|
||||
each row
|
||||
:param str table_prop_name: the name of the property within the model
|
||||
containing the row models
|
||||
"""
|
||||
|
||||
def __init__(self, column_names, column_values, table_prop_name):
|
||||
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)
|
||||
|
||||
column_headers = "|".join(
|
||||
"{ch[" + str(n) + "]: ^" + str(self.column_width) + "}"
|
||||
for n in range(len(column_names))
|
||||
)
|
||||
|
||||
# correct for float-to-int roundoff error
|
||||
test_fmt = column_headers.format(ch=column_names)
|
||||
if len(test_fmt) < 72:
|
||||
column_headers += ' ' * (72 - len(test_fmt))
|
||||
|
||||
vert_divider = '-' * 72
|
||||
self.header_fmt_str = column_headers + "\n" + vert_divider + "\n"
|
||||
|
||||
self.row_fmt_str = "|".join(
|
||||
"{cv[" + str(n) + "]: ^" + str(self.column_width) + "}"
|
||||
for n in range(len(column_values))
|
||||
)
|
||||
|
||||
def __call__(self, model):
|
||||
res = self.header_fmt_str.format(ch=self.column_names)
|
||||
for raw_row in model[self.table_prop_name]:
|
||||
row = [str(raw_row[prop_name]) for prop_name in self.column_values]
|
||||
# double format is in case we have roundoff error
|
||||
res += '{0: <72}\n'.format(self.row_fmt_str.format(cv=row))
|
||||
|
||||
return res
|
52
cinder/openstack/common/report/views/text/header.py
Normal file
52
cinder/openstack/common/report/views/text/header.py
Normal file
@ -0,0 +1,52 @@
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
"""Text Views With Headers
|
||||
|
||||
This package defines several text views with headers
|
||||
"""
|
||||
|
||||
|
||||
class HeaderView(object):
|
||||
"""A Text View With a Header
|
||||
|
||||
This view simply serializes the model and places the given
|
||||
header on top.
|
||||
|
||||
:param header: the header (can be anything on which str() can be called)
|
||||
"""
|
||||
|
||||
def __init__(self, header):
|
||||
self.header = header
|
||||
|
||||
def __call__(self, model):
|
||||
return str(self.header) + "\n" + str(model)
|
||||
|
||||
|
||||
class TitledView(HeaderView):
|
||||
"""A Text View With a Title
|
||||
|
||||
This view simply serializes the model, and places
|
||||
a preformatted header containing the given title
|
||||
text on top. The title text can be up to 64 characters
|
||||
long.
|
||||
|
||||
:param str title: the title of the view
|
||||
"""
|
||||
|
||||
FORMAT_STR = ('=' * 72) + "\n===={0: ^64}====\n" + ('=' * 72)
|
||||
|
||||
def __init__(self, title):
|
||||
super(TitledView, self).__init__(self.FORMAT_STR.format(title))
|
||||
|
80
cinder/openstack/common/report/views/text/threading.py
Normal file
80
cinder/openstack/common/report/views/text/threading.py
Normal file
@ -0,0 +1,80 @@
|
||||
# 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
|
||||
# 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 thread and stack-trace views
|
||||
|
||||
This module provides a collection of views for
|
||||
visualizing threads, green threads, and stack traces
|
||||
in human-readable form.
|
||||
"""
|
||||
|
||||
import cinder.openstack.common.report.views.jinja_view as jv
|
||||
|
||||
|
||||
class StackTraceView(jv.JinjaView):
|
||||
"""A Stack Trace View
|
||||
|
||||
This view displays stack trace models defined by
|
||||
:class:`openstack.common.report.models.threading.StackTraceModel`
|
||||
"""
|
||||
|
||||
VIEW_TEXT = (
|
||||
"{% if root_exception is not none %}"
|
||||
"Exception: {{ root_exception }}\n"
|
||||
"------------------------------------\n"
|
||||
"\n"
|
||||
"{% endif %}"
|
||||
"{% for line in lines %}\n"
|
||||
"{{ line.filename }}:{{ line.line }} in {{ line.name }}\n"
|
||||
" {% if line.code is not none %}"
|
||||
"`{{ line.code }}`"
|
||||
"{% else %}"
|
||||
"(source not found)"
|
||||
"{% endif %}\n"
|
||||
"{% else %}\n"
|
||||
"No Traceback!\n"
|
||||
"{% endfor %}"
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
FORMAT_STR = "------{thread_str: ^60}------" + "\n" + "{stack_trace}"
|
||||
|
||||
def __call__(self, model):
|
||||
return self.FORMAT_STR.format(
|
||||
thread_str=" Green Thread ",
|
||||
stack_trace=model.stack_trace
|
||||
)
|
||||
|
||||
|
||||
class ThreadView(object):
|
||||
"""A Thread Collection View
|
||||
|
||||
This view displays a python thread provided by the data
|
||||
model :class:`openstack.common.report.models.threading.ThreadModel` # noqa
|
||||
"""
|
||||
|
||||
FORMAT_STR = "------{thread_str: ^60}------" + "\n" + "{stack_trace}"
|
||||
|
||||
def __call__(self, model):
|
||||
return self.FORMAT_STR.format(
|
||||
thread_str=" Thread #{0} ".format(model.thread_id),
|
||||
stack_trace=model.stack_trace
|
||||
)
|
19
cinder/openstack/common/report/views/xml/__init__.py
Normal file
19
cinder/openstack/common/report/views/xml/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
# 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
|
||||
# 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 basic XML views
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into XML.
|
||||
"""
|
85
cinder/openstack/common/report/views/xml/generic.py
Normal file
85
cinder/openstack/common/report/views/xml/generic.py
Normal file
@ -0,0 +1,85 @@
|
||||
# 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
|
||||
# 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 generic XML views
|
||||
|
||||
This modules defines several basic views for serializing
|
||||
data to XML. Submodels that have already been serialized
|
||||
as XML may have their string values marked with `__is_xml__
|
||||
= True` using :class:`openstack.common.report.utils.StringWithAttrs`
|
||||
(each of the classes within this module does this automatically,
|
||||
and non-naive serializers check for this attribute and handle
|
||||
such strings specially)
|
||||
"""
|
||||
|
||||
import collections as col
|
||||
import copy
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import six
|
||||
|
||||
import cinder.openstack.common.report.utils as utils
|
||||
|
||||
|
||||
class KeyValueView(object):
|
||||
"""A Key-Value XML View
|
||||
|
||||
This view performs advanced serialization of a data model
|
||||
into XML. It first deserializes any values marked as XML so
|
||||
that they can be properly reserialized later. It then follows
|
||||
the following rules to perform serialization:
|
||||
|
||||
key : text/xml
|
||||
The tag name is the key name, and the contents are the text or xml
|
||||
key : Sequence
|
||||
A wrapper tag is created with the key name, and each item is placed
|
||||
in an 'item' tag
|
||||
key : Mapping
|
||||
A wrapper tag is created with the key name, and the serialize is called
|
||||
on each key-value pair (such that each key gets its own tag)
|
||||
|
||||
:param str wrapper_name: the name of the top-level element
|
||||
"""
|
||||
|
||||
def __init__(self, wrapper_name="model"):
|
||||
self.wrapper_name = wrapper_name
|
||||
|
||||
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_xml__', False):
|
||||
cpy[key] = ET.fromstring(valstr)
|
||||
|
||||
def serialize(rootmodel, rootkeyname):
|
||||
res = ET.Element(rootkeyname)
|
||||
|
||||
if isinstance(rootmodel, col.Mapping):
|
||||
for key in rootmodel:
|
||||
res.append(serialize(rootmodel[key], key))
|
||||
elif (isinstance(rootmodel, col.Sequence)
|
||||
and not isinstance(rootmodel, six.string_types)):
|
||||
for val in rootmodel:
|
||||
res.append(serialize(val, 'item'))
|
||||
elif ET.iselement(rootmodel):
|
||||
res.append(rootmodel)
|
||||
else:
|
||||
res.text = str(rootmodel)
|
||||
|
||||
return res
|
||||
|
||||
res = utils.StringWithAttrs(ET.tostring(serialize(cpy,
|
||||
self.wrapper_name)))
|
||||
res.__is_xml__ = True
|
||||
return res
|
90
doc/source/devref/gmr.rst
Normal file
90
doc/source/devref/gmr.rst
Normal file
@ -0,0 +1,90 @@
|
||||
..
|
||||
Copyright (c) 2013 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.
|
||||
|
||||
Guru Meditation Reports
|
||||
=======================
|
||||
|
||||
Cinder contains a mechanism whereby developers and system administrators can
|
||||
generate a report about the state of a running Cinder executable.
|
||||
This report is called a *Guru Meditation Report* (*GMR* for short).
|
||||
|
||||
Generating a GMR
|
||||
----------------
|
||||
|
||||
A *GMR* can be generated by sending the *USR1* signal to any Cinder process
|
||||
with support (see below).
|
||||
The *GMR* will then be outputted standard error for that particular process.
|
||||
|
||||
For example, suppose that ``cinder-api`` has process id ``8675``, and was run
|
||||
with ``2>/var/log/cinder/cinder-api-err.log``.
|
||||
Then, ``kill -USR1 8675`` will trigger the Guru Meditation report to be printed
|
||||
to ``/var/log/cinder/cinder-api-err.log``.
|
||||
|
||||
Structure of a GMR
|
||||
------------------
|
||||
|
||||
The *GMR* is designed to be extensible; any particular executable may add
|
||||
its own sections. However, the base *GMR* consists of several sections:
|
||||
|
||||
Package
|
||||
Shows information about the package to which this process belongs,
|
||||
including version information
|
||||
|
||||
Threads
|
||||
Shows stack traces and thread ids for each of the threads within this process
|
||||
|
||||
Green Threads
|
||||
Shows stack traces for each of the green threads within this process
|
||||
(green threads don't have thread ids)
|
||||
|
||||
Configuration
|
||||
Lists all the configuration options currently accessible via the CONF object
|
||||
for the current process
|
||||
|
||||
Adding Support for GMRs to New Executables
|
||||
------------------------------------------
|
||||
|
||||
Adding support for a *GMR* to a given executable is fairly easy.
|
||||
|
||||
First import the module (currently residing in oslo-incubator), as well as the
|
||||
Cinder version module:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cinder.openstack.common.report import guru_meditation_report as gmr
|
||||
from cinder import version
|
||||
|
||||
Then, register any additional sections (optional):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
TextGuruMeditation.register_section('Some Special Section',
|
||||
some_section_generator)
|
||||
|
||||
Finally (under main), before running the "main loop" of the executable
|
||||
(usually ``service.server(server)`` or something similar), register the *GMR*
|
||||
hook:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
TextGuruMeditation.setup_autorun(version)
|
||||
|
||||
Extending the GMR
|
||||
-----------------
|
||||
|
||||
As mentioned above, additional sections can be added to the GMR for a
|
||||
particular executable. For more information, see the inline documentation
|
||||
under :mod:`cinder.openstack.common.report`
|
@ -30,6 +30,7 @@ Programming HowTos and Tutorials
|
||||
unit_tests
|
||||
addmethod.openstackapi
|
||||
drivers
|
||||
gmr
|
||||
|
||||
|
||||
Background Concepts for Cinder
|
||||
|
@ -16,6 +16,7 @@ module=scheduler.filters
|
||||
module=scheduler.weights
|
||||
module=service
|
||||
module=versionutils
|
||||
module=report
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=cinder
|
||||
|
Loading…
Reference in New Issue
Block a user