Switch to oslo.reports

Oslo_reports enables OpenStack projects to dump Guru Meditation
Reports with useful debugging information to files or stderr.

Change-Id: I5d685c691ae519e6f027af4adf9a4690573e45ff
This commit is contained in:
Bertrand Lallau 2015-11-04 17:30:55 +01:00
parent b5b5433420
commit c68f876da9
40 changed files with 65 additions and 2093 deletions

View File

@ -17,10 +17,12 @@ import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from designate import hookpoints
from designate import service
from designate import utils
from designate import version
from designate.agent import service as agent_service
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.agent', group='service:agent')
def main():
utils.read_config('designate', sys.argv)
logging.setup(CONF, 'designate')
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
gmr.TextGuruMeditation.setup_autorun(version)
hookpoints.log_hook_setup()

View File

@ -17,10 +17,12 @@ import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from designate import hookpoints
from designate import service
from designate import utils
from designate import version
from designate.api import service as api_service
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.api', group='service:api')
def main():
utils.read_config('designate', sys.argv)
logging.setup(CONF, 'designate')
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
gmr.TextGuruMeditation.setup_autorun(version)
hookpoints.log_hook_setup()

View File

@ -17,10 +17,12 @@ import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from designate import hookpoints
from designate import service
from designate import utils
from designate import version
from designate.central import service as central
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.central', group='service:central')
def main():
utils.read_config('designate', sys.argv)
logging.setup(CONF, 'designate')
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
gmr.TextGuruMeditation.setup_autorun(version)
hookpoints.log_hook_setup()

View File

@ -21,10 +21,12 @@ import sys
import eventlet
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from stevedore.extension import ExtensionManager
from designate import hookpoints
from designate import utils
from designate import version
from designate.i18n import _
eventlet.monkey_patch(os=False)
@ -120,7 +122,7 @@ def main():
print(_('Please re-run designate-manage as root.'))
sys.exit(2)
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
gmr.TextGuruMeditation.setup_autorun(version)
hookpoints.log_hook_setup()

View File

@ -17,10 +17,12 @@ import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from designate import hookpoints
from designate import service
from designate import utils
from designate import version
from designate.mdns import service as mdns_service
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.mdns', group='service:mdns')
def main():
utils.read_config('designate', sys.argv)
logging.setup(CONF, 'designate')
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
gmr.TextGuruMeditation.setup_autorun(version)
hookpoints.log_hook_setup()

View File

@ -17,9 +17,11 @@ import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from designate import service
from designate import utils
from designate import version
from designate import hookpoints
from designate.pool_manager import service as pool_manager_service
@ -35,8 +37,7 @@ def main():
utils.read_config('designate', sys.argv)
logging.setup(CONF, 'designate')
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
gmr.TextGuruMeditation.setup_autorun(version)
server = pool_manager_service.Service(
threads=CONF['service:pool_manager'].threads

View File

@ -17,10 +17,12 @@ import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from designate import hookpoints
from designate import service
from designate import utils
from designate import version
from designate.sink import service as sink_service
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.sink', group='service:sink')
def main():
utils.read_config('designate', sys.argv)
logging.setup(CONF, 'designate')
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
gmr.TextGuruMeditation.setup_autorun(version)
hookpoints.log_hook_setup()

View File

@ -17,9 +17,11 @@ import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from designate import service
from designate import utils
from designate import version
from designate.zone_manager import service as zone_manager_service
@ -33,7 +35,7 @@ CONF.import_opt('threads', 'designate.zone_manager',
def main():
utils.read_config('designate', sys.argv)
logging.setup(CONF, 'designate')
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
gmr.TextGuruMeditation.setup_autorun(version)
server = zone_manager_service.Service(
threads=CONF['service:zone_manager'].threads)

View File

@ -1,25 +0,0 @@
# 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.
"""

View File

@ -1,21 +0,0 @@
# 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.
"""

View File

@ -1,44 +0,0 @@
# 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
from designate.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`,
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)

View File

@ -1,38 +0,0 @@
# 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 designate.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()))

View File

@ -1,86 +0,0 @@
# 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`.
"""
from __future__ import absolute_import
import sys
import threading
import greenlet
from designate.openstack.common.report.models import threading as tm
from designate.openstack.common.report.models import with_default_views as mwdv
from designate.openstack.common.report import utils as rutils
from designate.openstack.common.report.views.text import 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()` . 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 __init__(self, curr_thread_traceback=None):
self.traceback = curr_thread_traceback
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())
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)
]
return mwdv.ModelWithDefaultViews(threadModels,
text_view=text_views.MultiView())

View File

@ -1,60 +0,0 @@
# 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`.
"""
from designate.openstack.common.report.models import 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):
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)

View File

@ -1,227 +0,0 @@
# 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 inspect
import os
import signal
import sys
from oslo_utils import timeutils
from designate.openstack.common.report.generators import conf as cgen
from designate.openstack.common.report.generators import process as prgen
from designate.openstack.common.report.generators import threading as tgen
from designate.openstack.common.report.generators import version as pgen
from designate.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.
"""
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)
@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, 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 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
"""
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, service_name, log_dir, traceback):
"""The Signal Handler
This method (indirectly) handles receiving a registered signal and
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, traceback).run()
except Exception:
print("Unable to run Guru Meditation Report!",
file=sys.stderr)
else:
if log_dir:
service_name = service_name or os.path.basename(
inspect.stack()[-1][1])
filename = "%s_gurumeditation_%s" % (
service_name, timeutils.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:]
self.add_section('Package',
pgen.PackageReportGenerator(self.version_obj))
self.add_section('Threads',
tgen.ThreadReportGenerator(self.traceback))
self.add_section('Green Threads',
tgen.GreenThreadReportGenerator())
self.add_section('Processes',
prgen.ProcessReportGenerator())
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
- 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, traceback=None):
super(TextGuruMeditation, self).__init__(version_obj, traceback,
'Guru Meditation')

View File

@ -1,20 +0,0 @@
# 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.
"""

View File

@ -1,162 +0,0 @@
# 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
import six
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.
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
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)
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):
# 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
)
)
def __len__(self):
return len(self.data)
def __iter__(self):
return self.data.__iter__()
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, 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
"""
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)

View File

@ -1,66 +0,0 @@
# 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
"""
from designate.openstack.common.report.models import with_default_views as mwdv
from designate.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
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
def opt_value(opt_obj, value):
if opt_obj['opt'].secret:
return '***'
else:
return value
self['default'] = dict(
(opt_title(optname, conf_obj),
opt_value(conf_obj._opts[optname], 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),
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)

View File

@ -1,62 +0,0 @@
# 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 designate.openstack.common.report.models.with_default_views as mwdv
import designate.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()]

View File

@ -1,100 +0,0 @@
# 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
from designate.openstack.common.report.models import with_default_views as mwdv
from designate.openstack.common.report.views.text import 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)
]
# 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}
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())

View File

@ -1,44 +0,0 @@
# 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
"""
from designate.openstack.common.report.models import with_default_views as mwdv
from designate.openstack.common.report.views.text import 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

View File

@ -1,81 +0,0 @@
# 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
from designate.openstack.common.report.models import base as base_model
from designate.openstack.common.report.views.json import generic as jsonviews
from designate.openstack.common.report.views.text import generic as textviews
from designate.openstack.common.report.views.xml import 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
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.report.views.text.generic.KeyValueView`
xml
:class:`openstack.common.report.views.xml.generic.KeyValueView`
json
:class:`openstack.common.report.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, visited=None):
self.attached_view = self.views[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 {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)

View File

@ -1,187 +0,0 @@
# 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.
"""
from designate.openstack.common.report.views.text import 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):