which contain generators which generate data models
( :class:`openstack.common.report.models.base.ReportModels` ),
which are then serialized by views.
-"""
\ No newline at end of file
+"""
( :class:`openstack.common.report.models.base.ReportModel` ).
A generator is any object which is callable with no parameters
and returns a data model.
-"""
\ No newline at end of file
+"""
# License for the specific language governing permissions and limitations
# under the License.
-"""Provides Openstack config generators
+"""Provides OpenStack config generators
This module defines a class for configuration
generators for generating the model in
from oslo_config import cfg
-import cinder.openstack.common.report.models.conf as cm
+from cinder.openstack.common.report.models import conf as cm
class ConfigReportGenerator(object):
"""A Configuration Data Generator
This generator returns
- :class:`openstack.common.report.models.conf.ConfigModel` ,
+ :class:`openstack.common.report.models.conf.ConfigModel`,
by default using the configuration options stored
in :attr:`oslo_config.cfg.CONF`, which is where
- Openstack stores everything.
+ OpenStack stores everything.
:param cnf: the configuration option object
- :type cnf: :class:`oslo.config.cfg.ConfigOpts`
+ :type cnf: :class:`oslo_config.cfg.ConfigOpts`
"""
def __init__(self, cnf=cfg.CONF):
--- /dev/null
+# Copyright 2014 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Provides process-data generators
+
+This modules defines a class for generating
+process data by way of the psutil package.
+"""
+
+import os
+
+import psutil
+
+from cinder.openstack.common.report.models import process as pm
+
+
+class ProcessReportGenerator(object):
+ """A Process Data Generator
+
+ This generator returns a
+ :class:`openstack.common.report.models.process.ProcessModel`
+ based on the current process (which will also include
+ all subprocesses, recursively) using the :class:`psutil.Process` class`.
+ """
+
+ def __call__(self):
+ return pm.ProcessModel(psutil.Process(os.getpid()))
:mod:`openstack.common.report.models.threading`.
"""
+from __future__ import absolute_import
+
import sys
+import threading
import greenlet
-import cinder.openstack.common.report.models.threading as tm
+from cinder.openstack.common.report.models import threading as tm
from cinder.openstack.common.report.models import with_default_views as mwdv
-import cinder.openstack.common.report.utils as rutils
-import cinder.openstack.common.report.views.text.generic as text_views
+from cinder.openstack.common.report import utils as rutils
+from cinder.openstack.common.report.views.text import generic as text_views
class ThreadReportGenerator(object):
This generator returns a collection of
:class:`openstack.common.report.models.threading.ThreadModel`
objects by introspecting the current python state using
- :func:`sys._current_frames()` .
+ :func:`sys._current_frames()` . Its constructor may optionally
+ be passed a frame object. This frame object will be interpreted
+ as the actual stack trace for the current thread, and, come generation
+ time, will be used to replace the stack trace of the thread in which
+ this code is running.
"""
+ def __init__(self, curr_thread_traceback=None):
+ self.traceback = curr_thread_traceback
+
def __call__(self):
- threadModels = [
- tm.ThreadModel(thread_id, stack)
+ 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)
- thread_pairs = dict(zip(range(len(threadModels)), threadModels))
- return mwdv.ModelWithDefaultViews(thread_pairs,
+ return mwdv.ModelWithDefaultViews(threadModels,
text_view=text_views.MultiView())
for gr in rutils._find_objects(greenlet.greenlet)
]
- thread_pairs = dict(zip(range(len(threadModels)), threadModels))
- return mwdv.ModelWithDefaultViews(thread_pairs,
+ return mwdv.ModelWithDefaultViews(threadModels,
text_view=text_views.MultiView())
# License for the specific language governing permissions and limitations
# under the License.
-"""Provides Openstack version generators
+"""Provides OpenStack version generators
-This module defines a class for Openstack
+This module defines a class for OpenStack
version and package information
generators for generating the model in
:mod:`openstack.common.report.models.version`.
"""
-import cinder.openstack.common.report.models.version as vm
+from cinder.openstack.common.report.models import version as vm
class PackageReportGenerator(object):
self.version_obj = version_obj
def __call__(self):
- return vm.PackageModel(
- self.version_obj.vendor_string(),
- self.version_obj.product_string(),
- self.version_obj.version_string_with_package())
+ if hasattr(self.version_obj, "vendor_string"):
+ vendor_string = self.version_obj.vendor_string()
+ else:
+ vendor_string = None
+
+ if hasattr(self.version_obj, "product_string"):
+ product_string = self.version_obj.product_string()
+ else:
+ product_string = None
+
+ if hasattr(self.version_obj, "version_string_with_package"):
+ version_string_with_package = self.version_obj.\
+ version_string_with_package()
+ else:
+ version_string_with_package = None
+
+ return vm.PackageModel(vendor_string, product_string,
+ version_string_with_package)
"""Provides Guru Meditation Report
-This module defines the actual OpenStack Guru Meditation Report class.
+This module defines the actual OpenStack Guru Meditation
+Report class.
This can be used in the OpenStack command definition files.
For example, in a nova command module (under nova/cmd):
from __future__ import print_function
+import inspect
+import os
import signal
import sys
+from oslo_utils import timeutils
+
from cinder.openstack.common.report.generators import conf as cgen
+from cinder.openstack.common.report.generators import process as prgen
from cinder.openstack.common.report.generators import threading as tgen
from cinder.openstack.common.report.generators import version as pgen
from cinder.openstack.common.report import report
MRO is correct.
"""
- def __init__(self, version_obj, *args, **kwargs):
+ timestamp_fmt = "%Y%m%d%H%M%S"
+
+ def __init__(self, version_obj, sig_handler_tb=None, *args, **kwargs):
self.version_obj = version_obj
+ self.traceback = sig_handler_tb
super(GuruMeditation, self).__init__(*args, **kwargs)
self.start_section_index = len(self.sections)
cls.persistent_sections = [[section_title, generator]]
@classmethod
- def setup_autorun(cls, version, signum=signal.SIGUSR1):
+ def setup_autorun(cls, version, service_name=None,
+ log_dir=None, signum=None):
"""Set Up Auto-Run
This method sets up the Guru Meditation Report to automatically
- get dumped to stderr when the given signal is received.
+ get dumped to stderr or a file in a given dir when the given signal
+ is received.
:param version: the version object for the current product
+ :param service_name: this program name used to construct logfile name
+ :param logdir: path to a log directory where to create a file
:param signum: the signal to associate with running the report
"""
- signal.signal(signum, lambda *args: cls.handle_signal(version, *args))
+ if not signum and hasattr(signal, 'SIGUSR1'):
+ # SIGUSR1 is not supported on all platforms
+ signum = signal.SIGUSR1
+
+ if signum:
+ signal.signal(signum,
+ lambda sn, tb: cls.handle_signal(
+ version, service_name, log_dir, tb))
@classmethod
- def handle_signal(cls, version, *args):
+ def handle_signal(cls, version, service_name, log_dir, traceback):
"""The Signal Handler
This method (indirectly) handles receiving a registered signal and
- dumping the Guru Meditation Report to stderr. This method is designed
- to be curried into a proper signal handler by currying out the version
+ dumping the Guru Meditation Report to stderr or a file in a given dir.
+ If service name and log dir are not None, the report will be dumped to
+ a file named $service_name_gurumeditation_$current_time in the log_dir
+ directory.
+ This method is designed to be curried into a proper signal handler by
+ currying out the version
parameter.
:param version: the version object for the current product
+ :param service_name: this program name used to construct logfile name
+ :param logdir: path to a log directory where to create a file
+ :param traceback: the traceback provided to the signal handler
"""
try:
- res = cls(version).run()
+ res = cls(version, traceback).run()
except Exception:
print("Unable to run Guru Meditation Report!",
file=sys.stderr)
else:
- print(res, file=sys.stderr)
+ if log_dir:
+ service_name = service_name or os.path.basename(
+ inspect.stack()[-1][1])
+ filename = "%s_gurumeditation_%s" % (
+ service_name, timeutils.utcnow().strftime(
+ cls.timestamp_fmt))
+ filepath = os.path.join(log_dir, filename)
+ try:
+ with open(filepath, "w") as dumpfile:
+ dumpfile.write(res)
+ except Exception:
+ print("Unable to dump Guru Meditation Report to file %s" %
+ (filepath,), file=sys.stderr)
+ else:
+ print(res, file=sys.stderr)
def _readd_sections(self):
del self.sections[self.start_section_index:]
pgen.PackageReportGenerator(self.version_obj))
self.add_section('Threads',
- tgen.ThreadReportGenerator())
+ tgen.ThreadReportGenerator(self.traceback))
self.add_section('Green Threads',
tgen.GreenThreadReportGenerator())
+ self.add_section('Processes',
+ prgen.ProcessReportGenerator())
+
self.add_section('Configuration',
cgen.ConfigReportGenerator())
- Green Threads List
+ - Process List
+
- Configuration Options
:param version_obj: the version object for the current product
+ :param traceback: an (optional) frame object providing the actual
+ traceback for the current thread
"""
- def __init__(self, version_obj):
- super(TextGuruMeditation, self).__init__(version_obj,
+ def __init__(self, version_obj, traceback=None):
+ super(TextGuruMeditation, self).__init__(version_obj, traceback,
'Guru Meditation')
This module provides both the base data model,
as well as several predefined specific data models
to be used in reports.
-"""
\ No newline at end of file
+"""
import collections as col
import copy
+import six
+
class ReportModel(col.MutableMapping):
"""A Report Data Model
model. An appropriate object for a view is callable with
a single parameter: the model to be serialized.
- :param data: a dictionary of data to initially associate with the model
+ If present, the object passed in as data will be transformed
+ into a standard python dict. For mappings, this is fairly
+ straightforward. For sequences, the indices become keys
+ and the items become values.
+
+ :param data: a sequence or mapping of data to associate with the model
:param attached_view: a view object to attach to this model
"""
def __init__(self, data=None, attached_view=None):
self.attached_view = attached_view
- self.data = data or {}
+
+ if data is not None:
+ if isinstance(data, col.Mapping):
+ self.data = dict(data)
+ elif isinstance(data, col.Sequence):
+ # convert a list [a, b, c] to a dict {0: a, 1: b, 2: c}
+ self.data = dict(enumerate(data))
+ else:
+ raise TypeError('Data for the model must be a sequence '
+ 'or mapping.')
+ else:
+ self.data = {}
def __str__(self):
self_cpy = copy.deepcopy(self)
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 __iter__(self):
return self.data.__iter__()
- def set_current_view_type(self, tp):
+ def set_current_view_type(self, tp, visited=None):
"""Set the current view type
This method attempts to set the current view
type for this model and all submodels by calling
- itself recursively on all values (and ignoring the
- ones that are not themselves models)
+ itself recursively on all values, traversing
+ intervening sequences and mappings when possible,
+ and ignoring all other objects.
:param tp: the type of the view ('text', 'json', 'xml', etc)
+ :param visited: a set of object ids for which the corresponding objects
+ have already had their view type set
"""
- for key in self:
- try:
- self[key].set_current_view_type(tp)
- except AttributeError:
- pass
+ if visited is None:
+ visited = set()
+
+ def traverse_obj(obj):
+ oid = id(obj)
+
+ # don't die on recursive structures,
+ # and don't treat strings like sequences
+ if oid in visited or isinstance(obj, six.string_types):
+ return
+
+ visited.add(oid)
+
+ if hasattr(obj, 'set_current_view_type'):
+ obj.set_current_view_type(tp, visited=visited)
+
+ if isinstance(obj, col.Sequence):
+ for item in obj:
+ traverse_obj(item)
+
+ elif isinstance(obj, col.Mapping):
+ for val in six.itervalues(obj):
+ traverse_obj(val)
+
+ traverse_obj(self)
# License for the specific language governing permissions and limitations
# under the License.
-"""Provides Openstack Configuration Model
+"""Provides OpenStack Configuration Model
This module defines a class representing the data
-model for :mod:`oslo.config` configuration options
+model for :mod:`oslo_config` configuration options
"""
-import cinder.openstack.common.report.models.with_default_views as mwdv
-import cinder.openstack.common.report.views.text.generic as generic_text_views
+from cinder.openstack.common.report.models import with_default_views as mwdv
+from cinder.openstack.common.report.views.text import generic as generic_text_views
class ConfigModel(mwdv.ModelWithDefaultViews):
"""A Configuration Options Model
This model holds data about a set of configuration options
- from :mod:`oslo.config`. It supports both the default group
+ from :mod:`oslo_config`. It supports both the default group
of options and named option groups.
:param conf_obj: a configuration object
- :type conf_obj: :class:`oslo.config.cfg.ConfigOpts`
+ :type conf_obj: :class:`oslo_config.cfg.ConfigOpts`
"""
def __init__(self, conf_obj):
def opt_title(optname, co):
return co._opts[optname]['opt'].name
+ def opt_value(opt_obj, value):
+ if opt_obj['opt'].secret:
+ return '***'
+ else:
+ return value
+
self['default'] = dict(
- (opt_title(optname, conf_obj), conf_obj[optname])
+ (opt_title(optname, conf_obj),
+ opt_value(conf_obj._opts[optname], conf_obj[optname]))
for optname in conf_obj._opts
)
for groupname in conf_obj._groups:
group_obj = conf_obj._groups[groupname]
curr_group_opts = dict(
- (opt_title(optname, group_obj), conf_obj[groupname][optname])
- for optname in group_obj._opts
- )
+ (opt_title(optname, group_obj),
+ opt_value(group_obj._opts[optname],
+ conf_obj[groupname][optname]))
+ for optname in group_obj._opts)
groups[group_obj.name] = curr_group_opts
self.update(groups)
--- /dev/null
+# Copyright 2014 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Provides a process model
+
+This module defines a class representing a process,
+potentially with subprocesses.
+"""
+
+import cinder.openstack.common.report.models.with_default_views as mwdv
+import cinder.openstack.common.report.views.text.process as text_views
+
+
+class ProcessModel(mwdv.ModelWithDefaultViews):
+ """A Process Model
+
+ This model holds data about a process,
+ including references to any subprocesses
+
+ :param process: a :class:`psutil.Process` object
+ """
+
+ def __init__(self, process):
+ super(ProcessModel, self).__init__(
+ text_view=text_views.ProcessView())
+
+ self['pid'] = process.pid
+ self['parent_pid'] = process.ppid
+ if hasattr(process, 'uids'):
+ self['uids'] = {'real': process.uids.real,
+ 'effective': process.uids.effective,
+ 'saved': process.uids.saved}
+ else:
+ self['uids'] = {'real': None,
+ 'effective': None,
+ 'saved': None}
+
+ if hasattr(process, 'gids'):
+ self['gids'] = {'real': process.gids.real,
+ 'effective': process.gids.effective,
+ 'saved': process.gids.saved}
+ else:
+ self['gids'] = {'real': None,
+ 'effective': None,
+ 'saved': None}
+
+ self['username'] = process.username
+ self['command'] = process.cmdline
+ self['state'] = process.status
+
+ self['children'] = [ProcessModel(pr) for pr in process.get_children()]
import traceback
-import cinder.openstack.common.report.models.with_default_views as mwdv
-import cinder.openstack.common.report.views.text.threading as text_views
+from cinder.openstack.common.report.models import with_default_views as mwdv
+from cinder.openstack.common.report.views.text import threading as text_views
class StackTraceModel(mwdv.ModelWithDefaultViews):
{'filename': fn, 'line': ln, 'name': nm, 'code': cd}
for fn, ln, nm, cd in traceback.extract_stack(stack_state)
]
-
- if stack_state.f_exc_type is not None:
+ # FIXME(flepied): under Python3 f_exc_type doesn't exist
+ # anymore so we lose information about exceptions
+ if getattr(stack_state, 'f_exc_type', None) is not None:
self['root_exception'] = {
'type': stack_state.f_exc_type,
- 'value': stack_state.f_exc_value
- }
+ 'value': stack_state.f_exc_value}
else:
self['root_exception'] = None
else:
# License for the specific language governing permissions and limitations
# under the License.
-"""Provides Openstack Version Info Model
+"""Provides OpenStack Version Info Model
This module defines a class representing the data
-model for Openstack package and version information
+model for OpenStack package and version information
"""
-import cinder.openstack.common.report.models.with_default_views as mwdv
-import cinder.openstack.common.report.views.text.generic as generic_text_views
+from cinder.openstack.common.report.models import with_default_views as mwdv
+from cinder.openstack.common.report.views.text import generic as generic_text_views
class PackageModel(mwdv.ModelWithDefaultViews):
import copy
-import cinder.openstack.common.report.models.base as base_model
-import cinder.openstack.common.report.views.json.generic as jsonviews
-import cinder.openstack.common.report.views.text.generic as textviews
-import cinder.openstack.common.report.views.xml.generic as xmlviews
+from cinder.openstack.common.report.models import base as base_model
+from cinder.openstack.common.report.views.json import generic as jsonviews
+from cinder.openstack.common.report.views.text import generic as textviews
+from cinder.openstack.common.report.views.xml import generic as xmlviews
class ModelWithDefaultViews(base_model.ReportModel):
when a submodel should have an attached view, but the view
differs depending on the serialization format
- Paramaters are as the superclass, with the exception
- of any parameters ending in '_view': these parameters
+ Parameters are as the superclass, except for any
+ parameters ending in '_view': these parameters
get stored as default views.
The default 'default views' are
text
- :class:`openstack.common.views.text.generic.KeyValueView`
+ :class:`openstack.common.report.views.text.generic.KeyValueView`
xml
- :class:`openstack.common.views.xml.generic.KeyValueView`
+ :class:`openstack.common.report.views.xml.generic.KeyValueView`
json
- :class:`openstack.common.views.json.generic.KeyValueView`
+ :class:`openstack.common.report.views.json.generic.KeyValueView`
.. function:: to_type()
del newargs[k]
super(ModelWithDefaultViews, self).__init__(*args, **newargs)
- def set_current_view_type(self, tp):
+ def set_current_view_type(self, tp, visited=None):
self.attached_view = self.views[tp]
- super(ModelWithDefaultViews, self).set_current_view_type(tp)
+ super(ModelWithDefaultViews, self).set_current_view_type(tp, visited)
def __getattr__(self, attrname):
if attrname[:3] == 'to_':
if self.views[attrname[3:]] is not None:
return lambda: self.views[attrname[3:]](self)
else:
- raise NotImplementedError(_(
- "Model %(module)s.%(name)s does not have a default view "
- "for %(tp)s"), {'module': type(self).__module__,
- 'name': type(self).__name__,
- 'tp': attrname[3:]})
+ raise NotImplementedError((
+ "Model {cn.__module__}.{cn.__name__} does not have" +
+ " a default view for "
+ "{tp}").format(cn=type(self), tp=attrname[3:]))
else:
return super(ModelWithDefaultViews, self).__getattr__(attrname)
"""Provides Report classes
-This module defines various classes representing
-reports and report sections. All reports take the
-form of a report class containing various report sections.
+This module defines various classes representing reports and report sections.
+All reports take the form of a report class containing various report
+sections.
"""
-import cinder.openstack.common.report.views.text.header as header_views
+from cinder.openstack.common.report.views.text import header as header_views
class BasicReport(object):
A Basic Report consists of a collection of :class:`ReportSection`
objects, each of which contains a top-level model and generator.
It collects these sections into a cohesive report which may then
- be serialized by calling :func:`run`
+ be serialized by calling :func:`run`.
"""
def __init__(self):
class ReportSection(object):
"""A Report Section
- A report section contains a generator and a top-level view.
- When something attempts to serialize the section by calling
- str() on it, the section runs the generator and calls the view
- on the resulting model.
+ A report section contains a generator and a top-level view. When something
+ attempts to serialize the section by calling str() on it, the section runs
+ the generator and calls the view on the resulting model.
.. seealso::
:param view: the top-level view for this section
:param generator: the generator for this section
- (any callable object which takes
- no parameters and returns a data model)
+ (any callable object which takes no parameters and returns a data model)
"""
def __init__(self, view, generator):
-# Copyright 2013 Red Hat, Inc.
+# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
Each type contains a submodule called 'generic' containing
several basic, universal views for that type. There is also
a predefined view that utilizes Jinja.
-"""
\ No newline at end of file
+"""
see http://jinja.pocoo.org/ .
"""
+import copy
+
import jinja2
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
This module provides several basic views which serialize
models into JSON.
-"""
\ No newline at end of file
+"""
from oslo_serialization import jsonutils as json
-import cinder.openstack.common.report.utils as utils
+from cinder.openstack.common.report import utils as utils
class BasicKeyValueView(object):
def __call__(self, model):
# this part deals with subviews that were already serialized
cpy = copy.deepcopy(model)
- for key, valstr in model.items():
- if getattr(valstr, '__is_json__', False):
- cpy[key] = json.loads(valstr)
+ for key in model.keys():
+ if getattr(model[key], '__is_json__', False):
+ cpy[key] = json.loads(model[key])
- res = utils.StringWithAttrs(json.dumps(cpy.data))
+ res = utils.StringWithAttrs(json.dumps(cpy.data, sort_keys=True))
res.__is_json__ = True
return res
This module provides several basic views which serialize
models into human-readable text.
-"""
\ No newline at end of file
+"""
if self.before_dict is not None:
res.insert(0, self.before_dict)
- for key in root:
+ for key in sorted(root):
res.extend(serialize(root[key], key, indent + 1))
elif (isinstance(root, col.Sequence) and
not isinstance(root, six.string_types)):
if self.before_list is not None:
res.insert(0, self.before_list)
- for val in root:
+ for val in sorted(root, key=str):
res.extend(serialize(val, None, indent + 1))
else:
str_root = str(root)
self.table_prop_name = table_prop_name
self.column_names = column_names
self.column_values = column_values
- self.column_width = (72 - len(column_names) + 1) / len(column_names)
+ self.column_width = (72 - len(column_names) + 1) // len(column_names)
column_headers = "|".join(
"{ch[" + str(n) + "]: ^" + str(self.column_width) + "}"
def __init__(self, title):
super(TitledView, self).__init__(self.FORMAT_STR.format(title))
-
--- /dev/null
+# Copyright 2014 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Provides process view
+
+This module provides a view for
+visualizing processes in human-readable formm
+"""
+
+import cinder.openstack.common.report.views.jinja_view as jv
+
+
+class ProcessView(jv.JinjaView):
+ """A Process View
+
+ This view displays process models defined by
+ :class:`openstack.common.report.models.process.ProcessModel`
+ """
+
+ VIEW_TEXT = (
+ "Process {{ pid }} (under {{ parent_pid }}) "
+ "[ run by: {{ username }} ({{ uids.real|default('unknown uid') }}),"
+ " state: {{ state }} ]\n"
+ "{% for child in children %}"
+ " {{ child }}"
+ "{% endfor %}"
+ )
in human-readable form.
"""
-import cinder.openstack.common.report.views.jinja_view as jv
+from cinder.openstack.common.report.views import jinja_view as jv
class StackTraceView(jv.JinjaView):
"""A Green Thread View
This view displays a green thread provided by the data
- model :class:`openstack.common.report.models.threading.GreenThreadModel` # noqa
+ model :class:`openstack.common.report.models.threading.GreenThreadModel`
"""
FORMAT_STR = "------{thread_str: ^60}------" + "\n" + "{stack_trace}"
This module provides several basic views which serialize
models into XML.
-"""
\ No newline at end of file
+"""
import six
-import cinder.openstack.common.report.utils as utils
+from cinder.openstack.common.report import utils as utils
class KeyValueView(object):
res = ET.Element(rootkeyname)
if isinstance(rootmodel, col.Mapping):
- for key in rootmodel:
+ for key in sorted(rootmodel):
res.append(serialize(rootmodel[key], key))
elif (isinstance(rootmodel, col.Sequence)
and not isinstance(rootmodel, six.string_types)):
- for val in rootmodel:
+ for val in sorted(rootmodel, key=str):
res.append(serialize(val, 'item'))
elif ET.iselement(rootmodel):
res.append(rootmodel)
return res
- res = utils.StringWithAttrs(ET.tostring(serialize(cpy,
- self.wrapper_name)))
+ str_ = ET.tostring(serialize(cpy,
+ self.wrapper_name),
+ encoding="utf-8").decode("utf-8")
+ res = utils.StringWithAttrs(str_)
res.__is_xml__ = True
return res
module=service
module=versionutils
module=report
+module=report.generators
+module=report.models
+module=report.views
+module=report.views.json
+module=report.views.text
+module=report.views.xml
# The base module to hold the copy of openstack.common
base=cinder
paramiko>=1.13.0
Paste
PasteDeploy>=1.5.0
+psutil>=1.1.1,<2.0.0
pycrypto>=2.6
pyparsing>=2.0.1
python-barbicanclient>=3.0.1