diff options
author | Davanum Srinivas <davanum@gmail.com> | 2015-06-28 20:58:46 -0400 |
---|---|---|
committer | Davanum Srinivas (dims) <davanum@gmail.com> | 2015-08-15 01:02:48 +0000 |
commit | 0cc2d63d78b66c9ddfccb31a050ee9498278b0f4 (patch) | |
tree | 6a40b04374b33dd237071206ccf3f61a1604dc9e | |
parent | f7934f4d01472d9c856b7f6f8e4482bf43241d46 (diff) | |
download | oslo-incubator-0cc2d63d78b66c9ddfccb31a050ee9498278b0f4.tar.gz |
Remove openstack/common/report as oslo.reports is graduating
implements blueprint graduate-oslo-reports
Change-Id: I6228c9ec258dd894595535f0e24e9f7ffd72d43c
33 files changed, 0 insertions, 2910 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index cd17dadb..64b973b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -89,11 +89,6 @@ M: S: Orphan F: memorycache.py -== reports == - -M: -S: Graduating -F: report/ == scheduler == diff --git a/openstack/common/report/__init__.py b/openstack/common/report/__init__.py deleted file mode 100644 index 35390ecd..00000000 --- a/openstack/common/report/__init__.py +++ /dev/null @@ -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. -""" diff --git a/openstack/common/report/generators/__init__.py b/openstack/common/report/generators/__init__.py deleted file mode 100644 index 68473f22..00000000 --- a/openstack/common/report/generators/__init__.py +++ /dev/null @@ -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. -""" diff --git a/openstack/common/report/generators/conf.py b/openstack/common/report/generators/conf.py deleted file mode 100644 index 619e4898..00000000 --- a/openstack/common/report/generators/conf.py +++ /dev/null @@ -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 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) diff --git a/openstack/common/report/generators/process.py b/openstack/common/report/generators/process.py deleted file mode 100644 index 9c2151fc..00000000 --- a/openstack/common/report/generators/process.py +++ /dev/null @@ -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 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())) diff --git a/openstack/common/report/generators/threading.py b/openstack/common/report/generators/threading.py deleted file mode 100644 index b3038cf2..00000000 --- a/openstack/common/report/generators/threading.py +++ /dev/null @@ -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 openstack.common.report.models import threading as tm -from openstack.common.report.models import with_default_views as mwdv -from openstack.common.report import utils as rutils -from 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()) diff --git a/openstack/common/report/generators/version.py b/openstack/common/report/generators/version.py deleted file mode 100644 index 5979f883..00000000 --- a/openstack/common/report/generators/version.py +++ /dev/null @@ -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 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) diff --git a/openstack/common/report/guru_meditation_report.py b/openstack/common/report/guru_meditation_report.py deleted file mode 100644 index bff9234f..00000000 --- a/openstack/common/report/guru_meditation_report.py +++ /dev/null @@ -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 openstack.common.report.generators import conf as cgen -from openstack.common.report.generators import process as prgen -from openstack.common.report.generators import threading as tgen -from openstack.common.report.generators import version as pgen -from 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') diff --git a/openstack/common/report/models/__init__.py b/openstack/common/report/models/__init__.py deleted file mode 100644 index 7bfed3d8..00000000 --- a/openstack/common/report/models/__init__.py +++ /dev/null @@ -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. -""" diff --git a/openstack/common/report/models/base.py b/openstack/common/report/models/base.py deleted file mode 100644 index d840c5b5..00000000 --- a/openstack/common/report/models/base.py +++ /dev/null @@ -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) diff --git a/openstack/common/report/models/conf.py b/openstack/common/report/models/conf.py deleted file mode 100644 index 23429373..00000000 --- a/openstack/common/report/models/conf.py +++ /dev/null @@ -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 openstack.common.report.models import with_default_views as mwdv -from 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) diff --git a/openstack/common/report/models/process.py b/openstack/common/report/models/process.py deleted file mode 100644 index d953b942..00000000 --- a/openstack/common/report/models/process.py +++ /dev/null @@ -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 openstack.common.report.models.with_default_views as mwdv -import 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()] diff --git a/openstack/common/report/models/threading.py b/openstack/common/report/models/threading.py deleted file mode 100644 index a3ffc861..00000000 --- a/openstack/common/report/models/threading.py +++ /dev/null @@ -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 openstack.common.report.models import with_default_views as mwdv -from 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()) diff --git a/openstack/common/report/models/version.py b/openstack/common/report/models/version.py deleted file mode 100644 index 89e73d42..00000000 --- a/openstack/common/report/models/version.py +++ /dev/null @@ -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 openstack.common.report.models import with_default_views as mwdv -from 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 diff --git a/openstack/common/report/models/with_default_views.py b/openstack/common/report/models/with_default_views.py deleted file mode 100644 index 058fbb21..00000000 --- a/openstack/common/report/models/with_default_views.py +++ /dev/null @@ -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 openstack.common.report.models import base as base_model -from openstack.common.report.views.json import generic as jsonviews -from openstack.common.report.views.text import generic as textviews -from 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) diff --git a/openstack/common/report/report.py b/openstack/common/report/report.py deleted file mode 100644 index 1eb937b5..00000000 --- a/openstack/common/report/report.py +++ /dev/null @@ -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 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) diff --git a/openstack/common/report/utils.py b/openstack/common/report/utils.py deleted file mode 100644 index fb71e36a..00000000 --- a/openstack/common/report/utils.py +++ /dev/null @@ -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)] diff --git a/openstack/common/report/views/__init__.py b/openstack/common/report/views/__init__.py deleted file mode 100644 index 612959b2..00000000 --- a/openstack/common/report/views/__init__.py +++ /dev/null @@ -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. -""" diff --git a/openstack/common/report/views/jinja_view.py b/openstack/common/report/views/jinja_view.py deleted file mode 100644 index 5f57dc34..00000000 --- a/openstack/common/report/views/jinja_view.py +++ /dev/null @@ -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) diff --git a/openstack/common/report/views/json/__init__.py b/openstack/common/report/views/json/__init__.py deleted file mode 100644 index 47bd33b6..00000000 --- a/openstack/common/report/views/json/__init__.py +++ /dev/null @@ -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. -""" diff --git a/openstack/common/report/views/json/generic.py b/openstack/common/report/views/json/generic.py deleted file mode 100644 index a8814ea7..00000000 --- a/openstack/common/report/views/json/generic.py +++ /dev/null @@ -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 generic JSON views - -This modules defines several basic views for serializing -data to JSON. Submodels that have already been serialized -as JSON may have their string values marked with `__is_json__ -= True` using :class:`openstack.common.report.utils.StringWithAttrs` -(each of the classes within this module does this automatically, -and non-naive serializers check for this attribute and handle -such strings specially) -""" - -import copy - -from oslo_serialization import jsonutils as json - -from 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 model.keys(): - 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 diff --git a/openstack/common/report/views/text/__init__.py b/openstack/common/report/views/text/__init__.py deleted file mode 100644 index c0974844..00000000 --- a/openstack/common/report/views/text/__init__.py +++ /dev/null @@ -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. -""" diff --git a/openstack/common/report/views/text/generic.py b/openstack/common/report/views/text/generic.py deleted file mode 100644 index 3b30a070..00000000 --- a/openstack/common/report/views/text/generic.py +++ /dev/null @@ -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 diff --git a/openstack/common/report/views/text/header.py b/openstack/common/report/views/text/header.py deleted file mode 100644 index 58d06c0d..00000000 --- a/openstack/common/report/views/text/header.py +++ /dev/null @@ -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)) diff --git a/openstack/common/report/views/text/process.py b/openstack/common/report/views/text/process.py deleted file mode 100644 index edd2be7c..00000000 --- a/openstack/common/report/views/text/process.py +++ /dev/null @@ -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 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 %}" - ) diff --git a/openstack/common/report/views/text/threading.py b/openstack/common/report/views/text/threading.py deleted file mode 100644 index f8b208d2..00000000 --- a/openstack/common/report/views/text/threading.py +++ /dev/null @@ -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 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 - ) diff --git a/openstack/common/report/views/xml/__init__.py b/openstack/common/report/views/xml/__init__.py deleted file mode 100644 index a40fec98..00000000 --- a/openstack/common/report/views/xml/__init__.py +++ /dev/null @@ -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. -""" diff --git a/openstack/common/report/views/xml/generic.py b/openstack/common/report/views/xml/generic.py deleted file mode 100644 index 13b6a0ab..00000000 --- a/openstack/common/report/views/xml/generic.py +++ /dev/null @@ -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 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 diff --git a/tests/unit/reports/__init__.py b/tests/unit/reports/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/tests/unit/reports/__init__.py +++ /dev/null diff --git a/tests/unit/reports/test_base_report.py b/tests/unit/reports/test_base_report.py deleted file mode 100644 index db655fce..00000000 --- a/tests/unit/reports/test_base_report.py +++ /dev/null @@ -1,150 +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 collections as col -import re - -from oslotest import base - -from openstack.common.report.models import base as base_model -from openstack.common.report import report - - -class BasicView(object): - def __call__(self, model): - res = "" - for k in sorted(model.keys()): - res += str(k) + ": " + str(model[k]) + ";" - return res - - -def basic_generator(): - return base_model.ReportModel(data={'string': 'value', 'int': 1}) - - -class TestBasicReport(base.BaseTestCase): - def setUp(self): - super(TestBasicReport, self).setUp() - - self.report = report.BasicReport() - - def test_add_section(self): - self.report.add_section(BasicView(), basic_generator) - self.assertEqual(len(self.report.sections), 1) - - def test_append_section(self): - self.report.add_section(BasicView(), lambda: {'a': 1}) - self.report.add_section(BasicView(), basic_generator) - - self.assertEqual(len(self.report.sections), 2) - self.assertEqual(self.report.sections[1].generator, basic_generator) - - def test_insert_section(self): - self.report.add_section(BasicView(), lambda: {'a': 1}) - self.report.add_section(BasicView(), basic_generator, 0) - - self.assertEqual(len(self.report.sections), 2) - self.assertEqual(self.report.sections[0].generator, basic_generator) - - def test_basic_render(self): - self.report.add_section(BasicView(), basic_generator) - self.assertEqual(self.report.run(), "int: 1;string: value;") - - -class TestBaseModel(base.BaseTestCase): - def test_submodel_attached_view(self): - class TmpView(object): - def __call__(self, model): - return '{len: ' + str(len(model.c)) + '}' - - def generate_model_with_submodel(): - base_m = basic_generator() - tv = TmpView() - base_m['submodel'] = base_model.ReportModel(data={'c': [1, 2, 3]}, - attached_view=tv) - return base_m - - self.assertEqual(BasicView()(generate_model_with_submodel()), - 'int: 1;string: value;submodel: {len: 3};') - - def test_str_throws_error_with_no_attached_view(self): - model = base_model.ReportModel(data={'c': [1, 2, 3]}) - - # ugly code for python 2.6 compat, since python 2.6 - # does not have assertRaisesRegexp - try: - str(model) - except Exception as e: - err_str = 'Cannot stringify model: no attached view' - self.assertEqual(str(e), err_str) - else: - self.assertTrue(False) - - def test_str_returns_string_with_attached_view(self): - model = base_model.ReportModel(data={'a': 1, 'b': 2}, - attached_view=BasicView()) - - self.assertEqual(str(model), 'a: 1;b: 2;') - - def test_model_repr(self): - model1 = base_model.ReportModel(data={'a': 1, 'b': 2}, - attached_view=BasicView()) - - model2 = base_model.ReportModel(data={'a': 1, 'b': 2}) - - base_re = r"<Model [^ ]+\.[^ ]+ \{.+\} with " - with_view_re = base_re + r"view [^ ]+\.[^ ]+>" - without_view_re = base_re + r"no view>" - - self.assertTrue(re.match(with_view_re, repr(model1))) - self.assertTrue(re.match(without_view_re, repr(model2))) - - def test_getattr(self): - model = base_model.ReportModel(data={'a': 1}) - - self.assertEqual(model.a, 1) - - self.assertRaises(AttributeError, getattr, model, 'b') - - def test_data_as_sequence_is_handled_properly(self): - model = base_model.ReportModel(data=['a', 'b']) - model.attached_view = BasicView() - - # if we don't handle lists properly, we'll get a TypeError here - self.assertEqual('0: a;1: b;', str(model)) - - def test_immutable_mappings_produce_mutable_models(self): - class SomeImmutableMapping(col.Mapping): - def __init__(self): - self.data = {'a': 2, 'b': 4, 'c': 8} - - def __getitem__(self, key): - return self.data[key] - - def __len__(self): - return len(self.data) - - def __iter__(self): - return iter(self.data) - - mp = SomeImmutableMapping() - model = base_model.ReportModel(data=mp) - model.attached_view = BasicView() - - self.assertEqual('a: 2;b: 4;c: 8;', str(model)) - - model['d'] = 16 - - self.assertEqual('a: 2;b: 4;c: 8;d: 16;', str(model)) - self.assertEqual({'a': 2, 'b': 4, 'c': 8}, mp.data) diff --git a/tests/unit/reports/test_guru_meditation_report.py b/tests/unit/reports/test_guru_meditation_report.py deleted file mode 100644 index 68e8ea57..00000000 --- a/tests/unit/reports/test_guru_meditation_report.py +++ /dev/null @@ -1,186 +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. - -from __future__ import print_function - -import datetime -import os -import re -import signal -import sys - -# needed to get greenthreads -import fixtures -import greenlet -import mock -from oslotest import base -import six - -from openstack.common.report import guru_meditation_report as gmr -from openstack.common.report.models import with_default_views as mwdv - - -class FakeVersionObj(object): - def vendor_string(self): - return 'Cheese Shoppe' - - def product_string(self): - return 'Sharp Cheddar' - - def version_string_with_package(self): - return '1.0.0' - - -def skip_body_lines(start_line, report_lines): - curr_line = start_line - while (len(report_lines[curr_line]) == 0 - or report_lines[curr_line][0] != '='): - curr_line += 1 - - return curr_line - - -class TestGuruMeditationReport(base.BaseTestCase): - def setUp(self): - super(TestGuruMeditationReport, self).setUp() - - self.curr_g = greenlet.getcurrent() - - self.report = gmr.TextGuruMeditation(FakeVersionObj()) - - self.old_stderr = None - - def test_basic_report(self): - report_lines = self.report.run().split('\n') - - target_str_header = ['========================================================================', # noqa - '==== Guru Meditation ====', # noqa - '========================================================================', # noqa - '||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||', # noqa - '', - '', - '========================================================================', # noqa - '==== Package ====', # noqa - '========================================================================', # noqa - 'product = Sharp Cheddar', - 'vendor = Cheese Shoppe', - 'version = 1.0.0', - '========================================================================', # noqa - '==== Threads ====', # noqa - '========================================================================'] # noqa - - # first the header and version info... - self.assertEqual(target_str_header, - report_lines[0:len(target_str_header)]) - - # followed by at least one thread... - # NOTE(zqfan): add an optional '-' because sys._current_frames() - # may return a negative thread id on 32 bit operating system. - self.assertTrue(re.match(r'------(\s+)Thread #-?\d+\1\s?------', - report_lines[len(target_str_header)])) - self.assertEqual('', report_lines[len(target_str_header) + 1]) - - # followed by more thread stuff stuff... - curr_line = skip_body_lines(len(target_str_header) + 2, report_lines) - - # followed by at least one green thread - target_str_gt = ['========================================================================', # noqa - '==== Green Threads ====', # noqa - '========================================================================', # noqa - '------ Green Thread ------', # noqa - ''] - end_bound = curr_line + len(target_str_gt) - self.assertEqual(target_str_gt, - report_lines[curr_line:end_bound]) - - # followed by some more green thread stuff - curr_line = skip_body_lines(curr_line + len(target_str_gt), - report_lines) - - # followed by the processes header - target_str_p_head = ['========================================================================', # noqa - '==== Processes ====', # noqa - '========================================================================'] # noqa - end_bound = curr_line + len(target_str_p_head) - self.assertEqual(target_str_p_head, - report_lines[curr_line:end_bound]) - - curr_line += len(target_str_p_head) - - # followed by at least one process - self.assertTrue(re.match("Process \d+ \(under \d+\)", - report_lines[curr_line])) - - # followed by some more process stuff - curr_line = skip_body_lines(curr_line + 1, report_lines) - - # followed finally by the configuration - target_str_config = ['========================================================================', # noqa - '==== Configuration ====', # noqa - '========================================================================', # noqa - ''] - end_bound = curr_line + len(target_str_config) - self.assertEqual(target_str_config, - report_lines[curr_line:end_bound]) - - def test_reg_persistent_section(self): - def fake_gen(): - fake_data = {'cheddar': ['sharp', 'mild'], - 'swiss': ['with holes', 'with lots of holes'], - 'american': ['orange', 'yellow']} - - return mwdv.ModelWithDefaultViews(data=fake_data) - - gmr.TextGuruMeditation.register_section('Cheese Types', fake_gen) - - report_lines = self.report.run() - target_lst = ['========================================================================', # noqa - '==== Cheese Types ====', # noqa - '========================================================================', # noqa - 'american = ', - ' orange', - ' yellow', - 'cheddar = ', - ' mild', - ' sharp', - 'swiss = ', - ' with holes', - ' with lots of holes'] - target_str = '\n'.join(target_lst) - self.assertIn(target_str, report_lines) - - def test_register_autorun(self): - gmr.TextGuruMeditation.setup_autorun(FakeVersionObj()) - self.old_stderr = sys.stderr - sys.stderr = six.StringIO() - - os.kill(os.getpid(), signal.SIGUSR1) - self.assertIn('Guru Meditation', sys.stderr.getvalue()) - - @mock.patch('oslo_utils.timeutils.utcnow', - return_value=datetime.datetime(2014, 1, 1, 12, 0, 0)) - def test_register_autorun_log_dir(self, mock_strtime): - log_dir = self.useFixture(fixtures.TempDir()).path - gmr.TextGuruMeditation.setup_autorun( - FakeVersionObj(), "fake-service", log_dir) - - os.kill(os.getpid(), signal.SIGUSR1) - with open(os.path.join( - log_dir, "fake-service_gurumeditation_20140101120000")) as df: - self.assertIn('Guru Meditation', df.read()) - - def tearDown(self): - super(TestGuruMeditationReport, self).tearDown() - if self.old_stderr is not None: - sys.stderr = self.old_stderr diff --git a/tests/unit/reports/test_openstack_generators.py b/tests/unit/reports/test_openstack_generators.py deleted file mode 100644 index 7b98c2e1..00000000 --- a/tests/unit/reports/test_openstack_generators.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import threading - -import greenlet -import mock -from oslo_config import cfg -from oslotest import base - -from openstack.common.report.generators import conf as os_cgen -from openstack.common.report.generators import threading as os_tgen -from openstack.common.report.generators import version as os_pgen -from openstack.common.report.models import threading as os_tmod - - -class TestOpenstackGenerators(base.BaseTestCase): - def test_thread_generator(self): - model = os_tgen.ThreadReportGenerator()() - # self.assertGreaterEqual(len(model.keys()), 1) - self.assertTrue(len(model.keys()) >= 1) - was_ok = False - for val in model.values(): - self.assertIsInstance(val, os_tmod.ThreadModel) - self.assertIsNotNone(val.stack_trace) - if val.thread_id == threading.current_thread().ident: - was_ok = True - break - - self.assertTrue(was_ok) - - model.set_current_view_type('text') - self.assertIsNotNone(str(model)) - - def test_thread_generator_tb(self): - class FakeModel(object): - def __init__(self, thread_id, tb): - self.traceback = tb - - with mock.patch('openstack.common.report.models' - '.threading.ThreadModel', FakeModel): - model = os_tgen.ThreadReportGenerator("fake traceback")() - curr_thread = model.get(threading.current_thread().ident, None) - self.assertIsNotNone(curr_thread, None) - self.assertEqual("fake traceback", curr_thread.traceback) - - def test_green_thread_generator(self): - curr_g = greenlet.getcurrent() - - model = os_tgen.GreenThreadReportGenerator()() - - # self.assertGreaterEqual(len(model.keys()), 1) - self.assertTrue(len(model.keys()) >= 1) - - was_ok = False - for tm in model.values(): - if tm.stack_trace == os_tmod.StackTraceModel(curr_g.gr_frame): - was_ok = True - break - self.assertTrue(was_ok) - - model.set_current_view_type('text') - self.assertIsNotNone(str(model)) - - def test_config_model(self): - conf = cfg.ConfigOpts() - conf.register_opt(cfg.StrOpt('crackers', default='triscuit')) - conf.register_opt(cfg.StrOpt('secrets', secret=True, - default='should not show')) - conf.register_group(cfg.OptGroup('cheese', title='Cheese Info')) - conf.register_opt(cfg.IntOpt('sharpness', default=1), - group='cheese') - conf.register_opt(cfg.StrOpt('name', default='cheddar'), - group='cheese') - conf.register_opt(cfg.BoolOpt('from_cow', default=True), - group='cheese') - conf.register_opt(cfg.StrOpt('group_secrets', secret=True, - default='should not show'), - group='cheese') - - model = os_cgen.ConfigReportGenerator(conf)() - model.set_current_view_type('text') - - target_str = ('\ncheese: \n' - ' from_cow = True\n' - ' group_secrets = ***\n' - ' name = cheddar\n' - ' sharpness = 1\n' - '\n' - 'default: \n' - ' crackers = triscuit\n' - ' secrets = ***') - self.assertEqual(target_str, str(model)) - - def test_package_report_generator(self): - class VersionObj(object): - def vendor_string(self): - return 'Cheese Shoppe' - - def product_string(self): - return 'Sharp Cheddar' - - def version_string_with_package(self): - return '1.0.0' - - model = os_pgen.PackageReportGenerator(VersionObj())() - model.set_current_view_type('text') - - target_str = ('product = Sharp Cheddar\n' - 'vendor = Cheese Shoppe\n' - 'version = 1.0.0') - self.assertEqual(target_str, str(model)) - - def test_package_report_generator_without_vendor_string(self): - class VersionObj(object): - def product_string(self): - return 'Sharp Cheddar' - - def version_string_with_package(self): - return '1.0.0' - - model = os_pgen.PackageReportGenerator(VersionObj())() - model.set_current_view_type('text') - - target_str = ('product = Sharp Cheddar\n' - 'vendor = None\n' - 'version = 1.0.0') - self.assertEqual(target_str, str(model)) diff --git a/tests/unit/reports/test_views.py b/tests/unit/reports/test_views.py deleted file mode 100644 index fdc4fe35..00000000 --- a/tests/unit/reports/test_views.py +++ /dev/null @@ -1,420 +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 - -import mock -from oslotest import base - -from openstack.common.report.models import base as base_model -from openstack.common.report.models import with_default_views as mwdv -from openstack.common.report import report -from openstack.common.report.views import jinja_view as jv -from openstack.common.report.views.json import generic as json_generic -from openstack.common.report.views.text import generic as text_generic - - -def mwdv_generator(): - return mwdv.ModelWithDefaultViews(data={'string': 'value', 'int': 1}) - - -class TestModelReportType(base.BaseTestCase): - def test_model_with_default_views(self): - model = mwdv_generator() - - model.set_current_view_type('text') - self.assertEqual('int = 1\nstring = value', str(model)) - - model.set_current_view_type('json') - self.assertEqual('{"int": 1, "string": "value"}', str(model)) - - model.set_current_view_type('xml') - - self.assertEqual('<model><int>1</int><string>value</string></model>', - str(model)) - - def test_recursive_type_propagation_with_nested_models(self): - model = mwdv_generator() - model['submodel'] = mwdv_generator() - - model.set_current_view_type('json') - - self.assertEqual(model.submodel.views['json'], - model.submodel.attached_view) - - def test_recursive_type_propagation_with_nested_dicts(self): - nested_model = mwdv.ModelWithDefaultViews(json_view='abc') - data = {'a': 1, 'b': {'c': nested_model}} - top_model = base_model.ReportModel(data=data) - - top_model.set_current_view_type('json') - self.assertEqual(nested_model.attached_view, - nested_model.views['json']) - - def test_recursive_type_propagation_with_nested_lists(self): - nested_model = mwdv_generator() - data = {'a': 1, 'b': [nested_model]} - top_model = base_model.ReportModel(data=data) - - top_model.set_current_view_type('json') - self.assertEqual(nested_model.attached_view, - nested_model.views['json']) - - def test_recursive_type_propogation_on_recursive_structures(self): - nested_model = mwdv_generator() - data = {'a': 1, 'b': [nested_model]} - nested_model['c'] = data - top_model = base_model.ReportModel(data=data) - - top_model.set_current_view_type('json') - self.assertEqual(nested_model.attached_view, - nested_model.views['json']) - del nested_model['c'] - - def test_report_of_type(self): - rep = report.ReportOfType('json') - rep.add_section(lambda x: str(x), mwdv_generator) - - self.assertEqual('{"int": 1, "string": "value"}', rep.run()) - - # NOTE: this also tests views.text.header - def test_text_report(self): - rep = report.TextReport('Test Report') - rep.add_section('An Important Section', mwdv_generator) - rep.add_section('Another Important Section', mwdv_generator) - - target_str = ('========================================================================\n' # noqa - '==== Test Report ====\n' # noqa - '========================================================================\n' # noqa - '||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n' # noqa - '\n' # noqa - '\n' # noqa - '========================================================================\n' # noqa - '==== An Important Section ====\n' # noqa - '========================================================================\n' # noqa - 'int = 1\n' # noqa - 'string = value\n' # noqa - '========================================================================\n' # noqa - '==== Another Important Section ====\n' # noqa - '========================================================================\n' # noqa - 'int = 1\n' # noqa - 'string = value') # noqa - self.assertEqual(target_str, rep.run()) - - def test_to_type(self): - model = mwdv_generator() - - self.assertEqual('<model><int>1</int><string>value</string></model>', - model.to_xml()) - - -class TestGenericXMLView(base.BaseTestCase): - def setUp(self): - super(TestGenericXMLView, self).setUp() - - self.model = mwdv_generator() - self.model.set_current_view_type('xml') - - def test_dict_serialization(self): - self.model['dt'] = {'a': 1, 'b': 2} - - target_str = ('<model>' - '<dt><a>1</a><b>2</b></dt>' - '<int>1</int>' - '<string>value</string></model>') - self.assertEqual(target_str, str(self.model)) - - def test_list_serialization(self): - self.model['lt'] = ['a', 'b'] - - target_str = ('<model>' - '<int>1</int>' - '<lt><item>a</item><item>b</item></lt>' - '<string>value</string></model>') - self.assertEqual(target_str, str(self.model)) - - def test_list_in_dict_serialization(self): - self.model['dt'] = {'a': 1, 'b': [2, 3]} - - target_str = ('<model>' - '<dt><a>1</a>' - '<b><item>2</item><item>3</item></b></dt>' - '<int>1</int>' - '<string>value</string></model>') - self.assertEqual(target_str, str(self.model)) - - def test_dict_in_list_serialization(self): - self.model['lt'] = [1, {'b': 2, 'c': 3}] - - target_str = ('<model>' - '<int>1</int>' - '<lt><item>1</item>' - '<item><b>2</b><c>3</c></item></lt>' - '<string>value</string></model>') - self.assertEqual(target_str, str(self.model)) - - def test_submodel_serialization(self): - sm = mwdv_generator() - sm.set_current_view_type('xml') - - self.model['submodel'] = sm - - target_str = ('<model>' - '<int>1</int>' - '<string>value</string>' - '<submodel>' - '<model><int>1</int><string>value</string></model>' - '</submodel>' - '</model>') - self.assertEqual(target_str, str(self.model)) - - def test_wrapper_name(self): - self.model.attached_view.wrapper_name = 'cheese' - - target_str = ('<cheese>' - '<int>1</int>' - '<string>value</string>' - '</cheese>') - self.assertEqual(target_str, str(self.model)) - - -class TestGenericJSONViews(base.BaseTestCase): - def setUp(self): - super(TestGenericJSONViews, self).setUp() - - self.model = mwdv_generator() - self.model.set_current_view_type('json') - - def test_basic_kv_view(self): - attached_view = json_generic.BasicKeyValueView() - self.model = base_model.ReportModel(data={'string': 'value', 'int': 1}, - attached_view=attached_view) - - self.assertEqual('{"int": 1, "string": "value"}', str(self.model)) - - def test_dict_serialization(self): - self.model['dt'] = {'a': 1, 'b': 2} - - target_str = ('{' - '"dt": {"a": 1, "b": 2}, ' - '"int": 1, ' - '"string": "value"' - '}') - self.assertEqual(target_str, str(self.model)) - - def test_list_serialization(self): - self.model['lt'] = ['a', 'b'] - - target_str = ('{' - '"int": 1, ' - '"lt": ["a", "b"], ' - '"string": "value"' - '}') - self.assertEqual(target_str, str(self.model)) - - def test_list_in_dict_serialization(self): - self.model['dt'] = {'a': 1, 'b': [2, 3]} - - target_str = ('{' - '"dt": {"a": 1, "b": [2, 3]}, ' - '"int": 1, ' - '"string": "value"' - '}') - self.assertEqual(target_str, str(self.model)) - - def test_dict_in_list_serialization(self): - self.model['lt'] = [1, {'b': 2, 'c': 3}] - - target_str = ('{' - '"int": 1, ' - '"lt": [1, {"b": 2, "c": 3}], ' - '"string": "value"' - '}') - self.assertEqual(target_str, str(self.model)) - - def test_submodel_serialization(self): - sm = mwdv_generator() - sm.set_current_view_type('json') - - self.model['submodel'] = sm - - target_str = ('{' - '"int": 1, ' - '"string": "value", ' - '"submodel": {"int": 1, "string": "value"}' - '}') - self.assertEqual(target_str, str(self.model)) - - -class TestGenericTextViews(base.BaseTestCase): - def setUp(self): - super(TestGenericTextViews, self).setUp() - - self.model = mwdv_generator() - self.model.set_current_view_type('text') - - def test_multi_view(self): - attached_view = text_generic.MultiView() - self.model = base_model.ReportModel(data={}, - attached_view=attached_view) - - self.model['1'] = mwdv_generator() - self.model['2'] = mwdv_generator() - self.model['2']['int'] = 2 - self.model.set_current_view_type('text') - - target_str = ('int = 1\n' - 'string = value\n' - 'int = 2\n' - 'string = value') - self.assertEqual(target_str, str(self.model)) - - def test_basic_kv_view(self): - attached_view = text_generic.BasicKeyValueView() - self.model = base_model.ReportModel(data={'string': 'value', 'int': 1}, - attached_view=attached_view) - - self.assertEqual('int = 1\nstring = value\n', str(self.model)) - - def test_table_view(self): - column_names = ['Column A', 'Column B'] - column_values = ['a', 'b'] - attached_view = text_generic.TableView(column_names, column_values, - 'table') - self.model = base_model.ReportModel(data={}, - attached_view=attached_view) - - self.model['table'] = [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}] - - target_str = (' Column A | Column B \n' # noqa - '------------------------------------------------------------------------\n' # noqa - ' 1 | 2 \n' # noqa - ' 3 | 4 \n') # noqa - - self.assertEqual(target_str, str(self.model)) - - def test_dict_serialization(self): - self.model['dt'] = {'a': 1, 'b': 2} - - target_str = ('dt = \n' - ' a = 1\n' - ' b = 2\n' - 'int = 1\n' - 'string = value') - self.assertEqual(target_str, str(self.model)) - - def test_list_serialization(self): - self.model['lt'] = ['a', 'b'] - - target_str = ('int = 1\n' - 'lt = \n' - ' a\n' - ' b\n' - 'string = value') - self.assertEqual(target_str, str(self.model)) - - def test_list_in_dict_serialization(self): - self.model['dt'] = {'a': 1, 'b': [2, 3]} - - target_str = ('dt = \n' - ' a = 1\n' - ' b = \n' - ' 2\n' - ' 3\n' - 'int = 1\n' - 'string = value') - - self.assertEqual(target_str, str(self.model)) - - def test_dict_in_list_serialization(self): - self.model['lt'] = [1, {'b': 2, 'c': 3}] - - target_str = ('int = 1\n' - 'lt = \n' - ' 1\n' - ' [dict]\n' - ' b = 2\n' - ' c = 3\n' - 'string = value') - self.assertEqual(target_str, str(self.model)) - - def test_submodel_serialization(self): - sm = mwdv_generator() - sm.set_current_view_type('text') - - self.model['submodel'] = sm - - target_str = ('int = 1\n' - 'string = value\n' - 'submodel = \n' - ' int = 1\n' - ' string = value') - self.assertEqual(target_str, str(self.model)) - - def test_custom_indent_string(self): - view = text_generic.KeyValueView(indent_str='~~') - - self.model['lt'] = ['a', 'b'] - self.model.attached_view = view - - target_str = ('int = 1\n' - 'lt = \n' - '~~a\n' - '~~b\n' - 'string = value') - self.assertEqual(target_str, str(self.model)) - - -def get_open_mocks(rv): - file_mock = mock.MagicMock(name='file_obj') - file_mock.read.return_value = rv - open_mock = mock.MagicMock(name='open') - open_mock().__enter__.return_value = file_mock - return (open_mock, file_mock) - - -class TestJinjaView(base.BaseTestCase): - - TEMPL_STR = "int is {{ int }}, string is {{ string }}" - MM_OPEN, MM_FILE = get_open_mocks(TEMPL_STR) - - def setUp(self): - super(TestJinjaView, self).setUp() - self.model = base_model.ReportModel(data={'int': 1, 'string': 'value'}) - - @mock.mock_open(MM_OPEN) - def test_load_from_file(self): - self.model.attached_view = jv.JinjaView(path='a/b/c/d.jinja.txt') - - self.assertEqual('int is 1, string is value', str(self.model)) - self.MM_FILE.assert_called_with_once('a/b/c/d.jinja.txt') - - def test_direct_pass(self): - self.model.attached_view = jv.JinjaView(text=self.TEMPL_STR) - - self.assertEqual('int is 1, string is value', str(self.model)) - - def test_load_from_class(self): - class TmpJinjaView(jv.JinjaView): - VIEW_TEXT = TestJinjaView.TEMPL_STR - - self.model.attached_view = TmpJinjaView() - - self.assertEqual('int is 1, string is value', str(self.model)) - - def test_is_deepcopiable(self): - view_orig = jv.JinjaView(text=self.TEMPL_STR) - view_cpy = copy.deepcopy(view_orig) - - self.assertIsNot(view_orig, view_cpy) |