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:
parent
b5b5433420
commit
c68f876da9
@ -17,10 +17,12 @@ import sys
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
|
|
||||||
from designate import hookpoints
|
from designate import hookpoints
|
||||||
from designate import service
|
from designate import service
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate import version
|
||||||
from designate.agent import service as agent_service
|
from designate.agent import service as agent_service
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.agent', group='service:agent')
|
|||||||
def main():
|
def main():
|
||||||
utils.read_config('designate', sys.argv)
|
utils.read_config('designate', sys.argv)
|
||||||
logging.setup(CONF, 'designate')
|
logging.setup(CONF, 'designate')
|
||||||
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
|
|
||||||
hookpoints.log_hook_setup()
|
hookpoints.log_hook_setup()
|
||||||
|
|
||||||
|
@ -17,10 +17,12 @@ import sys
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
|
|
||||||
from designate import hookpoints
|
from designate import hookpoints
|
||||||
from designate import service
|
from designate import service
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate import version
|
||||||
from designate.api import service as api_service
|
from designate.api import service as api_service
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.api', group='service:api')
|
|||||||
def main():
|
def main():
|
||||||
utils.read_config('designate', sys.argv)
|
utils.read_config('designate', sys.argv)
|
||||||
logging.setup(CONF, 'designate')
|
logging.setup(CONF, 'designate')
|
||||||
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
|
|
||||||
hookpoints.log_hook_setup()
|
hookpoints.log_hook_setup()
|
||||||
|
|
||||||
|
@ -17,10 +17,12 @@ import sys
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
|
|
||||||
from designate import hookpoints
|
from designate import hookpoints
|
||||||
from designate import service
|
from designate import service
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate import version
|
||||||
from designate.central import service as central
|
from designate.central import service as central
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.central', group='service:central')
|
|||||||
def main():
|
def main():
|
||||||
utils.read_config('designate', sys.argv)
|
utils.read_config('designate', sys.argv)
|
||||||
logging.setup(CONF, 'designate')
|
logging.setup(CONF, 'designate')
|
||||||
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
|
|
||||||
hookpoints.log_hook_setup()
|
hookpoints.log_hook_setup()
|
||||||
|
|
||||||
|
@ -21,10 +21,12 @@ import sys
|
|||||||
import eventlet
|
import eventlet
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
from stevedore.extension import ExtensionManager
|
from stevedore.extension import ExtensionManager
|
||||||
|
|
||||||
from designate import hookpoints
|
from designate import hookpoints
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate import version
|
||||||
from designate.i18n import _
|
from designate.i18n import _
|
||||||
|
|
||||||
eventlet.monkey_patch(os=False)
|
eventlet.monkey_patch(os=False)
|
||||||
@ -120,7 +122,7 @@ def main():
|
|||||||
print(_('Please re-run designate-manage as root.'))
|
print(_('Please re-run designate-manage as root.'))
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
|
|
||||||
hookpoints.log_hook_setup()
|
hookpoints.log_hook_setup()
|
||||||
|
|
||||||
|
@ -17,10 +17,12 @@ import sys
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
|
|
||||||
from designate import hookpoints
|
from designate import hookpoints
|
||||||
from designate import service
|
from designate import service
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate import version
|
||||||
from designate.mdns import service as mdns_service
|
from designate.mdns import service as mdns_service
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.mdns', group='service:mdns')
|
|||||||
def main():
|
def main():
|
||||||
utils.read_config('designate', sys.argv)
|
utils.read_config('designate', sys.argv)
|
||||||
logging.setup(CONF, 'designate')
|
logging.setup(CONF, 'designate')
|
||||||
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
|
|
||||||
hookpoints.log_hook_setup()
|
hookpoints.log_hook_setup()
|
||||||
|
|
||||||
|
@ -17,9 +17,11 @@ import sys
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
|
|
||||||
from designate import service
|
from designate import service
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate import version
|
||||||
from designate import hookpoints
|
from designate import hookpoints
|
||||||
from designate.pool_manager import service as pool_manager_service
|
from designate.pool_manager import service as pool_manager_service
|
||||||
|
|
||||||
@ -35,8 +37,7 @@ def main():
|
|||||||
utils.read_config('designate', sys.argv)
|
utils.read_config('designate', sys.argv)
|
||||||
|
|
||||||
logging.setup(CONF, 'designate')
|
logging.setup(CONF, 'designate')
|
||||||
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
|
|
||||||
|
|
||||||
server = pool_manager_service.Service(
|
server = pool_manager_service.Service(
|
||||||
threads=CONF['service:pool_manager'].threads
|
threads=CONF['service:pool_manager'].threads
|
||||||
|
@ -17,10 +17,12 @@ import sys
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
|
|
||||||
from designate import hookpoints
|
from designate import hookpoints
|
||||||
from designate import service
|
from designate import service
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate import version
|
||||||
from designate.sink import service as sink_service
|
from designate.sink import service as sink_service
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ CONF.import_opt('threads', 'designate.sink', group='service:sink')
|
|||||||
def main():
|
def main():
|
||||||
utils.read_config('designate', sys.argv)
|
utils.read_config('designate', sys.argv)
|
||||||
logging.setup(CONF, 'designate')
|
logging.setup(CONF, 'designate')
|
||||||
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
|
|
||||||
hookpoints.log_hook_setup()
|
hookpoints.log_hook_setup()
|
||||||
|
|
||||||
|
@ -17,9 +17,11 @@ import sys
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
|
|
||||||
from designate import service
|
from designate import service
|
||||||
from designate import utils
|
from designate import utils
|
||||||
|
from designate import version
|
||||||
from designate.zone_manager import service as zone_manager_service
|
from designate.zone_manager import service as zone_manager_service
|
||||||
|
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ CONF.import_opt('threads', 'designate.zone_manager',
|
|||||||
def main():
|
def main():
|
||||||
utils.read_config('designate', sys.argv)
|
utils.read_config('designate', sys.argv)
|
||||||
logging.setup(CONF, 'designate')
|
logging.setup(CONF, 'designate')
|
||||||
utils.setup_gmr(log_dir=cfg.CONF.log_dir)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
|
|
||||||
server = zone_manager_service.Service(
|
server = zone_manager_service.Service(
|
||||||
threads=CONF['service:zone_manager'].threads)
|
threads=CONF['service:zone_manager'].threads)
|
||||||
|
@ -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.
|
|
||||||
"""
|
|
@ -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.
|
|
||||||
"""
|
|
@ -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)
|
|
@ -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()))
|
|
@ -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())
|
|
@ -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)
|
|
@ -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')
|
|
@ -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.
|
|
||||||
"""
|
|
@ -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)
|
|
@ -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)
|
|
@ -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()]
|
|
@ -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())
|
|
@ -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
|
|
@ -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)
|
|
@ -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):
|
|
||||||
"""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)
|
|
@ -1,46 +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.
|
|
||||||
|
|
||||||
"""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)]
|
|
@ -1,22 +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 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.
|
|
||||||
"""
|
|
@ -1,137 +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 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 copy
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def __deepcopy__(self, memodict):
|
|
||||||
res = object.__new__(JinjaView)
|
|
||||||
res._text = copy.deepcopy(self._text, memodict)
|
|
||||||
|
|
||||||
# regenerate the template on a deepcopy
|
|
||||||
res._regentemplate = True
|
|
||||||
res._templatecache = None
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
@property
|
|
||||||
def template(self):
|
|
||||||
"""Get the Compiled Template
|
|
||||||
|
|
||||||
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)
|
|
@ -1,19 +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 basic JSON views
|
|
||||||
|
|
||||||
This module provides several basic views which serialize
|
|
||||||
models into JSON.
|
|
||||||
"""
|
|
@ -1,67 +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 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
|
|
||||||
|
|
||||||
import six
|
|
||||||
from oslo_serialization import jsonutils as json
|
|
||||||
|
|
||||||
from designate.openstack.common.report import 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 in six.iterkeys(model):
|
|
||||||
if getattr(model[key], '__is_json__', False):
|
|
||||||
cpy[key] = json.loads(model[key])
|
|
||||||
|
|
||||||
res = utils.StringWithAttrs(json.dumps(cpy.data, sort_keys=True))
|
|
||||||
res.__is_json__ = True
|
|
||||||
return res
|
|
@ -1,19 +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 basic text views
|
|
||||||
|
|
||||||
This module provides several basic views which serialize
|
|
||||||
models into human-readable text.
|
|
||||||
"""
|
|
@ -1,202 +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 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 sorted(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 sorted(root, key=str):
|
|
||||||
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
|
|
@ -1,51 +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.
|
|
||||||
|
|
||||||
"""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))
|
|
@ -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 view
|
|
||||||
|
|
||||||
This module provides a view for
|
|
||||||
visualizing processes in human-readable formm
|
|
||||||
"""
|
|
||||||
|
|
||||||
import designate.openstack.common.report.views.jinja_view as jv
|
|
||||||
|
|
||||||
|
|
||||||
class ProcessView(jv.JinjaView):
|
|
||||||
"""A Process View
|
|
||||||
|
|
||||||
This view displays process models defined by
|
|
||||||
:class:`openstack.common.report.models.process.ProcessModel`
|
|
||||||
"""
|
|
||||||
|
|
||||||
VIEW_TEXT = (
|
|
||||||
"Process {{ pid }} (under {{ parent_pid }}) "
|
|
||||||
"[ run by: {{ username }} ({{ uids.real|default('unknown uid') }}),"
|
|
||||||
" state: {{ state }} ]\n"
|
|
||||||
"{% for child in children %}"
|
|
||||||
" {{ child }}"
|
|
||||||
"{% endfor %}"
|
|
||||||
)
|
|
@ -1,80 +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 and stack-trace views
|
|
||||||
|
|
||||||
This module provides a collection of views for
|
|
||||||
visualizing threads, green threads, and stack traces
|
|
||||||
in human-readable form.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from designate.openstack.common.report.views import 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`
|
|
||||||
"""
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
@ -1,19 +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 basic XML views
|
|
||||||
|
|
||||||
This module provides several basic views which serialize
|
|
||||||
models into XML.
|
|
||||||
"""
|
|
@ -1,87 +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 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
|
|
||||||
|
|
||||||
from designate.openstack.common.report import 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 sorted(rootmodel):
|
|
||||||
res.append(serialize(rootmodel[key], key))
|
|
||||||
elif (isinstance(rootmodel, col.Sequence)
|
|
||||||
and not isinstance(rootmodel, six.string_types)):
|
|
||||||
for val in sorted(rootmodel, key=str):
|
|
||||||
res.append(serialize(val, 'item'))
|
|
||||||
elif ET.iselement(rootmodel):
|
|
||||||
res.append(rootmodel)
|
|
||||||
else:
|
|
||||||
res.text = str(rootmodel)
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
str_ = ET.tostring(serialize(cpy,
|
|
||||||
self.wrapper_name),
|
|
||||||
encoding="utf-8").decode("utf-8")
|
|
||||||
res = utils.StringWithAttrs(str_)
|
|
||||||
res.__is_xml__ = True
|
|
||||||
return res
|
|
@ -32,8 +32,6 @@ from oslo_utils import timeutils
|
|||||||
from designate import exceptions
|
from designate import exceptions
|
||||||
from designate.i18n import _
|
from designate.i18n import _
|
||||||
from designate.i18n import _LI
|
from designate.i18n import _LI
|
||||||
from designate.openstack.common.report import guru_meditation_report as gmr
|
|
||||||
from designate import version as designate_version
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -394,10 +392,6 @@ def cache_result(function):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def setup_gmr(log_dir=None):
|
|
||||||
gmr.TextGuruMeditation.setup_autorun(designate_version, log_dir=log_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def split_host_port(string, default_port=53):
|
def split_host_port(string, default_port=53):
|
||||||
try:
|
try:
|
||||||
(host, port) = string.split(':', 1)
|
(host, port) = string.split(':', 1)
|
||||||
|
@ -15,26 +15,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
import pbr.version
|
import pbr.version
|
||||||
|
|
||||||
DESIGNATE_VENDOR = "OpenStack Foundation"
|
|
||||||
DESIGNATE_PRODUCT = "OpenStack Designate"
|
|
||||||
|
|
||||||
version_info = pbr.version.VersionInfo('designate')
|
version_info = pbr.version.VersionInfo('designate')
|
||||||
|
version_string = version_info.version_string
|
||||||
|
|
||||||
def vendor_string():
|
|
||||||
return DESIGNATE_VENDOR
|
|
||||||
|
|
||||||
|
|
||||||
def product_string():
|
|
||||||
return DESIGNATE_PRODUCT
|
|
||||||
|
|
||||||
|
|
||||||
def package_string():
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def version_string_with_package():
|
|
||||||
if package_string() is None:
|
|
||||||
return version_info.version_string()
|
|
||||||
else:
|
|
||||||
return "%s-%s" % (version_info.version_string(), package_string())
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
=========================
|
=========================
|
||||||
|
|
||||||
A Guru Meditation Reports(GMR) is generated by the Designate services when
|
A Guru Meditation Reports(GMR) is generated by the Designate services when
|
||||||
service processes receiving SIGUSR1 signal. The report is a general-purpose
|
service processes receiving SIGUSR2 signal. The report is a general-purpose
|
||||||
debug report for developers and system admins which contains the current state
|
debug report for developers and system admins which contains the current state
|
||||||
of a running Designate service process.
|
of a running Designate service process.
|
||||||
|
|
||||||
@ -34,9 +34,9 @@ Configuration
|
|||||||
Generate a GMR
|
Generate a GMR
|
||||||
==============
|
==============
|
||||||
|
|
||||||
A GMR can be generated by sending the USR1 signal to any Designate processes.
|
A GMR can be generated by sending the USR2 signal to any Designate processes.
|
||||||
|
|
||||||
For example, suppose ``designate-central`` has pid ``15097``, ``kill -USR1
|
For example, suppose ``designate-central`` has pid ``15097``, ``kill -USR2
|
||||||
15097`` will trigger a GMR.
|
15097`` will trigger a GMR.
|
||||||
|
|
||||||
If option ``logdir`` has been set in ``designate.conf``, the GMR will be saved
|
If option ``logdir`` has been set in ``designate.conf``, the GMR will be saved
|
||||||
@ -223,7 +223,7 @@ GMR Example
|
|||||||
==== Configuration ====
|
==== Configuration ====
|
||||||
========================================================================
|
========================================================================
|
||||||
|
|
||||||
backend:agent:bind9:
|
backend:agent:bind9:
|
||||||
query-destination = 127.0.0.1
|
query-destination = 127.0.0.1
|
||||||
rndc-config-file = None
|
rndc-config-file = None
|
||||||
rndc-host = 127.0.0.1
|
rndc-host = 127.0.0.1
|
||||||
@ -231,21 +231,21 @@ GMR Example
|
|||||||
rndc-port = 953
|
rndc-port = 953
|
||||||
zone-file-path = /opt/stack/data/designate/zones
|
zone-file-path = /opt/stack/data/designate/zones
|
||||||
|
|
||||||
backend:bind9:
|
backend:bind9:
|
||||||
masters =
|
masters =
|
||||||
127.0.0.1:5354
|
127.0.0.1:5354
|
||||||
rndc-config-file = None
|
rndc-config-file = None
|
||||||
rndc-host = 127.0.0.1
|
rndc-host = 127.0.0.1
|
||||||
rndc-key-file = None
|
rndc-key-file = None
|
||||||
rndc-port = 953
|
rndc-port = 953
|
||||||
server_ids =
|
server_ids =
|
||||||
|
|
||||||
backend:fake:
|
backend:fake:
|
||||||
masters =
|
masters =
|
||||||
127.0.0.1:5354
|
127.0.0.1:5354
|
||||||
server_ids =
|
server_ids =
|
||||||
|
|
||||||
backend:powerdns:
|
backend:powerdns:
|
||||||
backend = sqlalchemy
|
backend = sqlalchemy
|
||||||
connection = ***
|
connection = ***
|
||||||
connection_debug = 0
|
connection_debug = 0
|
||||||
@ -255,7 +255,7 @@ GMR Example
|
|||||||
db_max_retry_interval = 10
|
db_max_retry_interval = 10
|
||||||
db_retry_interval = 1
|
db_retry_interval = 1
|
||||||
idle_timeout = 3600
|
idle_timeout = 3600
|
||||||
masters =
|
masters =
|
||||||
10.180.64.117:5354
|
10.180.64.117:5354
|
||||||
max_overflow = None
|
max_overflow = None
|
||||||
max_pool_size = None
|
max_pool_size = None
|
||||||
@ -264,14 +264,14 @@ GMR Example
|
|||||||
mysql_sql_mode = TRADITIONAL
|
mysql_sql_mode = TRADITIONAL
|
||||||
pool_timeout = None
|
pool_timeout = None
|
||||||
retry_interval = 10
|
retry_interval = 10
|
||||||
server_ids =
|
server_ids =
|
||||||
f26e0b32-736f-4f0a-831b-039a415c481e
|
f26e0b32-736f-4f0a-831b-039a415c481e
|
||||||
slave_connection = ***
|
slave_connection = ***
|
||||||
sqlite_db = oslo.sqlite
|
sqlite_db = oslo.sqlite
|
||||||
sqlite_synchronous = True
|
sqlite_synchronous = True
|
||||||
use_db_reconnect = False
|
use_db_reconnect = False
|
||||||
|
|
||||||
backend:powerdns:f26e0b32-736f-4f0a-831b-039a415c481e:
|
backend:powerdns:f26e0b32-736f-4f0a-831b-039a415c481e:
|
||||||
backend = None
|
backend = None
|
||||||
connection = ***
|
connection = ***
|
||||||
connection_debug = None
|
connection_debug = None
|
||||||
@ -297,13 +297,13 @@ GMR Example
|
|||||||
tsig-key = None
|
tsig-key = None
|
||||||
use_db_reconnect = None
|
use_db_reconnect = None
|
||||||
|
|
||||||
default:
|
default:
|
||||||
allowed_remote_exmods =
|
allowed_remote_exmods =
|
||||||
backdoor_port = None
|
backdoor_port = None
|
||||||
backlog = 4096
|
backlog = 4096
|
||||||
central-topic = central
|
central-topic = central
|
||||||
config-dir = None
|
config-dir = None
|
||||||
config-file =
|
config-file =
|
||||||
/etc/designate/designate.conf
|
/etc/designate/designate.conf
|
||||||
control_exchange = designate
|
control_exchange = designate
|
||||||
debug = True
|
debug = True
|
||||||
@ -312,7 +312,7 @@ GMR Example
|
|||||||
default-soa-refresh = 3600
|
default-soa-refresh = 3600
|
||||||
default-soa-retry = 600
|
default-soa-retry = 600
|
||||||
default-ttl = 3600
|
default-ttl = 3600
|
||||||
default_log_levels =
|
default_log_levels =
|
||||||
amqp=WARN
|
amqp=WARN
|
||||||
amqplib=WARN
|
amqplib=WARN
|
||||||
boto=WARN
|
boto=WARN
|
||||||
@ -325,8 +325,8 @@ GMR Example
|
|||||||
suds=INFO
|
suds=INFO
|
||||||
fatal_deprecations = False
|
fatal_deprecations = False
|
||||||
host = cns-dev2
|
host = cns-dev2
|
||||||
instance_format = [instance: %(uuid)s]
|
instance_format = [instance: %(uuid)s]
|
||||||
instance_uuid_format = [instance: %(uuid)s]
|
instance_uuid_format = [instance: %(uuid)s]
|
||||||
log-config-append = None
|
log-config-append = None
|
||||||
log-date-format = %Y-%m-%d %H:%M:%S
|
log-date-format = %Y-%m-%d %H:%M:%S
|
||||||
log-dir = /opt/stack/logs/designate
|
log-dir = /opt/stack/logs/designate
|
||||||
@ -338,11 +338,11 @@ GMR Example
|
|||||||
logging_exception_prefix = %(color)s%(asctime)s.%(msecs)03d TRACE %(name)s [01;35m%(instance)s[00m
|
logging_exception_prefix = %(color)s%(asctime)s.%(msecs)03d TRACE %(name)s [01;35m%(instance)s[00m
|
||||||
mdns-topic = mdns
|
mdns-topic = mdns
|
||||||
network_api = neutron
|
network_api = neutron
|
||||||
notification_driver =
|
notification_driver =
|
||||||
notification_topics =
|
notification_topics =
|
||||||
notifications
|
notifications
|
||||||
policy_default_rule = default
|
policy_default_rule = default
|
||||||
policy_dirs =
|
policy_dirs =
|
||||||
policy.d
|
policy.d
|
||||||
policy_file = /etc/designate/policy.json
|
policy_file = /etc/designate/policy.json
|
||||||
pool-manager-topic = pool_manager
|
pool-manager-topic = pool_manager
|
||||||
@ -365,7 +365,7 @@ GMR Example
|
|||||||
use_stderr = True
|
use_stderr = True
|
||||||
verbose = True
|
verbose = True
|
||||||
|
|
||||||
network_api:neutron:
|
network_api:neutron:
|
||||||
admin_password = ***
|
admin_password = ***
|
||||||
admin_tenant_name = None
|
admin_tenant_name = None
|
||||||
admin_username = None
|
admin_username = None
|
||||||
@ -377,22 +377,22 @@ GMR Example
|
|||||||
insecure = False
|
insecure = False
|
||||||
timeout = 30
|
timeout = 30
|
||||||
|
|
||||||
oslo_concurrency:
|
oslo_concurrency:
|
||||||
disable_process_locking = False
|
disable_process_locking = False
|
||||||
lock_path = None
|
lock_path = None
|
||||||
|
|
||||||
oslo_messaging_rabbit:
|
oslo_messaging_rabbit:
|
||||||
amqp_auto_delete = False
|
amqp_auto_delete = False
|
||||||
amqp_durable_queues = False
|
amqp_durable_queues = False
|
||||||
fake_rabbit = False
|
fake_rabbit = False
|
||||||
kombu_reconnect_delay = 1.0
|
kombu_reconnect_delay = 1.0
|
||||||
kombu_ssl_ca_certs =
|
kombu_ssl_ca_certs =
|
||||||
kombu_ssl_certfile =
|
kombu_ssl_certfile =
|
||||||
kombu_ssl_keyfile =
|
kombu_ssl_keyfile =
|
||||||
kombu_ssl_version =
|
kombu_ssl_version =
|
||||||
rabbit_ha_queues = False
|
rabbit_ha_queues = False
|
||||||
rabbit_host = localhost
|
rabbit_host = localhost
|
||||||
rabbit_hosts =
|
rabbit_hosts =
|
||||||
127.0.0.1
|
127.0.0.1
|
||||||
rabbit_login_method = AMQPLAIN
|
rabbit_login_method = AMQPLAIN
|
||||||
rabbit_max_retries = 0
|
rabbit_max_retries = 0
|
||||||
@ -405,14 +405,14 @@ GMR Example
|
|||||||
rabbit_virtual_host = /
|
rabbit_virtual_host = /
|
||||||
rpc_conn_pool_size = 30
|
rpc_conn_pool_size = 30
|
||||||
|
|
||||||
proxy:
|
proxy:
|
||||||
http_proxy = None
|
http_proxy = None
|
||||||
https_proxy = None
|
https_proxy = None
|
||||||
no_proxy =
|
no_proxy =
|
||||||
|
|
||||||
service:central:
|
service:central:
|
||||||
default_pool_id = 794ccc2c-d751-44fe-b57f-8894c9f5c842
|
default_pool_id = 794ccc2c-d751-44fe-b57f-8894c9f5c842
|
||||||
enabled-notification-handlers =
|
enabled-notification-handlers =
|
||||||
managed_resource_email = hostmaster@example.com
|
managed_resource_email = hostmaster@example.com
|
||||||
managed_resource_tenant_id = None
|
managed_resource_tenant_id = None
|
||||||
max_domain_name_len = 255
|
max_domain_name_len = 255
|
||||||
@ -421,8 +421,8 @@ GMR Example
|
|||||||
storage-driver = sqlalchemy
|
storage-driver = sqlalchemy
|
||||||
workers = None
|
workers = None
|
||||||
|
|
||||||
service:pool_manager:
|
service:pool_manager:
|
||||||
backends =
|
backends =
|
||||||
powerdns
|
powerdns
|
||||||
cache-driver = sqlalchemy
|
cache-driver = sqlalchemy
|
||||||
enable-recovery-timer = True
|
enable-recovery-timer = True
|
||||||
@ -438,12 +438,12 @@ GMR Example
|
|||||||
threshold-percentage = 100
|
threshold-percentage = 100
|
||||||
workers = None
|
workers = None
|
||||||
|
|
||||||
ssl:
|
ssl:
|
||||||
ca_file = None
|
ca_file = None
|
||||||
cert_file = None
|
cert_file = None
|
||||||
key_file = None
|
key_file = None
|
||||||
|
|
||||||
storage:sqlalchemy:
|
storage:sqlalchemy:
|
||||||
backend = sqlalchemy
|
backend = sqlalchemy
|
||||||
connection = ***
|
connection = ***
|
||||||
connection_debug = 0
|
connection_debug = 0
|
||||||
|
@ -5,13 +5,6 @@ script=tools/install_venv_common.py
|
|||||||
|
|
||||||
# The list of modules to copy from oslo-incubator.git
|
# The list of modules to copy from oslo-incubator.git
|
||||||
module=memorycache
|
module=memorycache
|
||||||
module=report
|
|
||||||
module=report.generators
|
|
||||||
module=report.models
|
|
||||||
module=report.views
|
|
||||||
module=report.views.xml
|
|
||||||
module=report.views.json
|
|
||||||
module=report.views.text
|
|
||||||
|
|
||||||
# The base module to hold the copy of openstack.common
|
# The base module to hold the copy of openstack.common
|
||||||
base=designate
|
base=designate
|
||||||
|
@ -16,6 +16,7 @@ oslo.concurrency>=2.3.0 # Apache-2.0
|
|||||||
oslo.messaging>=1.16.0,!=1.17.0,!=1.17.1,!=2.6.0 # Apache-2.0
|
oslo.messaging>=1.16.0,!=1.17.0,!=1.17.1,!=2.6.0 # Apache-2.0
|
||||||
oslo.middleware>=2.8.0 # Apache-2.0
|
oslo.middleware>=2.8.0 # Apache-2.0
|
||||||
oslo.log>=1.8.0 # Apache-2.0
|
oslo.log>=1.8.0 # Apache-2.0
|
||||||
|
oslo.reports>=0.6.0 # Apache-2.0
|
||||||
oslo.rootwrap>=2.0.0 # Apache-2.0
|
oslo.rootwrap>=2.0.0 # Apache-2.0
|
||||||
oslo.serialization>=1.4.0 # Apache-2.0
|
oslo.serialization>=1.4.0 # Apache-2.0
|
||||||
oslo.service>=0.7.0 # Apache-2.0
|
oslo.service>=0.7.0 # Apache-2.0
|
||||||
@ -44,5 +45,4 @@ oslo.context>=0.2.0 # Apache-2.0
|
|||||||
oslo.policy>=0.5.0 # Apache-2.0
|
oslo.policy>=0.5.0 # Apache-2.0
|
||||||
Werkzeug>=0.7 # BSD License
|
Werkzeug>=0.7 # BSD License
|
||||||
python-memcached>=1.56
|
python-memcached>=1.56
|
||||||
psutil<2.0.0,>=1.1.1
|
|
||||||
tooz>=1.19.0 # Apache-2.0
|
tooz>=1.19.0 # Apache-2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user