Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: I5f7b28781dfdd89743b9ae7beeda800b19a370fa
This commit is contained in:
parent
5e4ea9af13
commit
ea17875950
@ -1,8 +0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = oslo_reports
|
||||
omit = oslo_reports/tests/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
||||
precision = 2
|
58
.gitignore
vendored
58
.gitignore
vendored
@ -1,58 +0,0 @@
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.eggs
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
cover
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Complexity
|
||||
output/*.html
|
||||
output/*/index.html
|
||||
|
||||
# Sphinx
|
||||
doc/build
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
doc/source/reference/api
|
||||
|
||||
# Editors
|
||||
*~
|
||||
.*.swp
|
||||
|
||||
# reno build
|
||||
releasenotes/build
|
||||
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/oslo.reports.git
|
3
.mailmap
3
.mailmap
@ -1,3 +0,0 @@
|
||||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
@ -1,7 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ ./oslo_reports $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
@ -1,17 +0,0 @@
|
||||
If you would like to contribute to the development of OpenStack, you must
|
||||
follow the steps in this page:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
If you already have a good understanding of how the system works and your
|
||||
OpenStack accounts are set up, you can skip to the development workflow
|
||||
section of this documentation to learn how changes to OpenStack should be
|
||||
submitted for review via the Gerrit tool:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/oslo.reports
|
@ -1,4 +0,0 @@
|
||||
oslo.reports Style Commandments
|
||||
======================================================
|
||||
|
||||
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/
|
176
LICENSE
176
LICENSE
@ -1,176 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
14
README
Normal file
14
README
Normal file
@ -0,0 +1,14 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
60
README.rst
60
README.rst
@ -1,60 +0,0 @@
|
||||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: http://governance.openstack.org/badges/oslo.reports.svg
|
||||
:target: http://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
===================================
|
||||
oslo.reports
|
||||
===================================
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/oslo.reports.svg
|
||||
:target: https://pypi.python.org/pypi/oslo.reports/
|
||||
:alt: Latest Version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/oslo.reports.svg
|
||||
:target: https://pypi.python.org/pypi/oslo.reports/
|
||||
:alt: Downloads
|
||||
|
||||
When things go wrong in (production) deployments of OpenStack collecting debug
|
||||
data is a key first step in the process of triaging & ultimately resolving the
|
||||
problem. Projects like Nova has extensively used logging capabilities which
|
||||
produce a vast amount of data. This does not, however, enable an admin to
|
||||
obtain an accurate view on the current live state of the system. For example,
|
||||
what threads are running, what config parameters are in effect, and more.
|
||||
|
||||
The project oslo.reports hosts a general purpose error report generation
|
||||
framework, known as the "guru meditation report"
|
||||
(cf http://en.wikipedia.org/wiki/Guru_Meditation) to address the issues
|
||||
described above.
|
||||
|
||||
Models: These classes define structured data for a variety of interesting
|
||||
pieces of state. For example, stack traces, threads, config parameters,
|
||||
package version info, etc. They are capable of being serialized to XML / JSON
|
||||
or a plain text representation
|
||||
|
||||
Generators: These classes are used to populate the model classes with the
|
||||
current runtime state of the system
|
||||
|
||||
Views: views serialize models into say JSON, text or xml. There is also
|
||||
a predefined view that utilizes Jinja templating system.
|
||||
|
||||
There will be a number of standard models / generators available for all
|
||||
OpenStack services
|
||||
|
||||
StackTraceModel: a base class for any model which includes a stack trace
|
||||
ThreadModel: a class for information about a thread
|
||||
ExceptionModel: a class for information about a caught exception
|
||||
ConfigModel: a class for information about configuration file settings
|
||||
PackageModel: a class for information about vendor/product/version/package information
|
||||
|
||||
Each OpenStack project will have the ability to register further generator
|
||||
classes to provide custom project specific data.
|
||||
|
||||
* Free software: Apache license
|
||||
* Documentation: https://docs.openstack.org/oslo.reports/latest
|
||||
* Source: https://git.openstack.org/cgit/openstack/oslo.reports
|
||||
* Bugs: https://bugs.launchpad.net/oslo.reports
|
@ -1,89 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
#'sphinx.ext.intersphinx',
|
||||
'openstackdocstheme',
|
||||
'oslo_config.sphinxext',
|
||||
]
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack/oslo.reports'
|
||||
bug_project = 'oslo.reports'
|
||||
bug_tag = ''
|
||||
|
||||
# Must set this variable to include year, month, day, hours, and minutes.
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'oslo.reports'
|
||||
copyright = u'2014, OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
modindex_common_prefix = ['oslo_reports.']
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
# html_static_path = ['static']
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
@ -1,5 +0,0 @@
|
||||
==============
|
||||
Contributing
|
||||
==============
|
||||
|
||||
.. include:: ../../../CONTRIBUTING.rst
|
@ -1,24 +0,0 @@
|
||||
==============
|
||||
oslo.reports
|
||||
==============
|
||||
|
||||
oslo.reports library
|
||||
|
||||
Contents
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
install/index
|
||||
contributor/index
|
||||
user/index
|
||||
reference/index
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
@ -1,7 +0,0 @@
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
At the command line::
|
||||
|
||||
$ pip install oslo.reports
|
@ -1,18 +0,0 @@
|
||||
.. _using:
|
||||
|
||||
=========
|
||||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
opts
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
api/autoindex
|
@ -1,10 +0,0 @@
|
||||
.. _option-definitions:
|
||||
|
||||
=====================
|
||||
Configuration Options
|
||||
=====================
|
||||
|
||||
oslo.reports uses oslo.config to define and manage configuration options
|
||||
to allow the deployer to control where the GMR reports should be generated.
|
||||
|
||||
.. show-options:: oslo.reports
|
@ -1 +0,0 @@
|
||||
.. include:: ../../../ChangeLog
|
@ -1,9 +0,0 @@
|
||||
===================
|
||||
Using oslo.reports
|
||||
===================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
usage
|
||||
history
|
@ -1,755 +0,0 @@
|
||||
========================================================================
|
||||
==== Guru Meditation ====
|
||||
========================================================================
|
||||
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||
|
||||
|
||||
========================================================================
|
||||
==== Package ====
|
||||
========================================================================
|
||||
product = OpenStack Nova
|
||||
vendor = OpenStack Foundation
|
||||
version = 13.0.0
|
||||
========================================================================
|
||||
==== Threads ====
|
||||
========================================================================
|
||||
------ Thread #140417215547200 ------
|
||||
|
||||
/usr/local/lib/python2.7/dist-packages/eventlet/hubs/hub.py:346 in run
|
||||
`self.wait(sleep_time)`
|
||||
|
||||
/usr/local/lib/python2.7/dist-packages/eventlet/hubs/poll.py:82 in wait
|
||||
`sleep(seconds)`
|
||||
|
||||
========================================================================
|
||||
==== Green Threads ====
|
||||
========================================================================
|
||||
------ Green Thread ------
|
||||
|
||||
/usr/local/bin/nova-api:10 in <module>
|
||||
`sys.exit(main())`
|
||||
|
||||
/opt/stack/nova/nova/cmd/api.py:57 in main
|
||||
`launcher.wait()`
|
||||
|
||||
/usr/local/lib/python2.7/dist-packages/oslo_service/service.py:511 in wait
|
||||
`self._respawn_children()`
|
||||
|
||||
/usr/local/lib/python2.7/dist-packages/oslo_service/service.py:495 in _respawn_children
|
||||
`eventlet.greenthread.sleep(self.wait_interval)`
|
||||
|
||||
/usr/local/lib/python2.7/dist-packages/eventlet/greenthread.py:34 in sleep
|
||||
`hub.switch()`
|
||||
|
||||
/usr/local/lib/python2.7/dist-packages/eventlet/hubs/hub.py:294 in switch
|
||||
`return self.greenlet.switch()`
|
||||
|
||||
------ Green Thread ------
|
||||
|
||||
No Traceback!
|
||||
|
||||
========================================================================
|
||||
==== Processes ====
|
||||
========================================================================
|
||||
Process 14756 (under 1) [ run by: stack (1001), state: running ]
|
||||
Process 14770 (under 14756) [ run by: stack (1001), state: sleeping ]
|
||||
Process 14771 (under 14756) [ run by: stack (1001), state: sleeping ]
|
||||
Process 14776 (under 14756) [ run by: stack (1001), state: sleeping ]
|
||||
Process 14777 (under 14756) [ run by: stack (1001), state: sleeping ]
|
||||
Process 14784 (under 14756) [ run by: stack (1001), state: sleeping ]
|
||||
Process 14785 (under 14756) [ run by: stack (1001), state: sleeping ]
|
||||
|
||||
========================================================================
|
||||
==== Configuration ====
|
||||
========================================================================
|
||||
|
||||
api_database:
|
||||
connection = ***
|
||||
connection_debug = 0
|
||||
connection_trace = False
|
||||
idle_timeout = 3600
|
||||
max_overflow = None
|
||||
max_pool_size = None
|
||||
max_retries = 10
|
||||
mysql_sql_mode = TRADITIONAL
|
||||
pool_timeout = None
|
||||
retry_interval = 10
|
||||
slave_connection = ***
|
||||
sqlite_synchronous = True
|
||||
|
||||
cells:
|
||||
bandwidth_update_interval = 600
|
||||
call_timeout = 60
|
||||
capabilities =
|
||||
hypervisor=xenserver;kvm
|
||||
os=linux;windows
|
||||
cell_type = compute
|
||||
enable = False
|
||||
instance_update_sync_database_limit = 100
|
||||
manager = nova.cells.manager.CellsManager
|
||||
mute_child_interval = 300
|
||||
name = nova
|
||||
reserve_percent = 10.0
|
||||
topic = cells
|
||||
|
||||
cinder:
|
||||
cafile = None
|
||||
catalog_info = volumev2:cinderv2:publicURL
|
||||
certfile = None
|
||||
cross_az_attach = True
|
||||
endpoint_template = None
|
||||
http_retries = 3
|
||||
insecure = False
|
||||
keyfile = None
|
||||
os_region_name = RegionOne
|
||||
timeout = None
|
||||
|
||||
conductor:
|
||||
manager = nova.conductor.manager.ConductorManager
|
||||
topic = conductor
|
||||
use_local = False
|
||||
workers = 2
|
||||
|
||||
database:
|
||||
backend = sqlalchemy
|
||||
connection = ***
|
||||
connection_debug = 0
|
||||
connection_trace = False
|
||||
db_inc_retry_interval = True
|
||||
db_max_retries = 20
|
||||
db_max_retry_interval = 10
|
||||
db_retry_interval = 1
|
||||
idle_timeout = 3600
|
||||
max_overflow = None
|
||||
max_pool_size = None
|
||||
max_retries = 10
|
||||
min_pool_size = 1
|
||||
mysql_sql_mode = TRADITIONAL
|
||||
pool_timeout = None
|
||||
retry_interval = 10
|
||||
slave_connection = ***
|
||||
sqlite_db = nova.sqlite
|
||||
sqlite_synchronous = True
|
||||
use_db_reconnect = False
|
||||
use_tpool = False
|
||||
|
||||
default:
|
||||
allow_instance_snapshots = True
|
||||
allow_resize_to_same_host = True
|
||||
allow_same_net_traffic = True
|
||||
api_paste_config = /etc/nova/api-paste.ini
|
||||
api_rate_limit = False
|
||||
auth_strategy = keystone
|
||||
auto_assign_floating_ip = False
|
||||
bandwidth_poll_interval = 600
|
||||
bindir = /usr/local/bin
|
||||
block_device_allocate_retries = 60
|
||||
block_device_allocate_retries_interval = 3
|
||||
boot_script_template = /opt/stack/nova/nova/cloudpipe/bootscript.template
|
||||
ca_file = cacert.pem
|
||||
ca_path = /opt/stack/data/nova/CA
|
||||
cert_manager = nova.cert.manager.CertManager
|
||||
cert_topic = cert
|
||||
client_socket_timeout = 900
|
||||
cnt_vpn_clients = 0
|
||||
compute_available_monitors = None
|
||||
compute_driver = libvirt.LibvirtDriver
|
||||
compute_manager = nova.compute.manager.ComputeManager
|
||||
compute_monitors =
|
||||
compute_resources =
|
||||
vcpu
|
||||
compute_stats_class = nova.compute.stats.Stats
|
||||
compute_topic = compute
|
||||
config-dir = None
|
||||
config-file =
|
||||
/etc/nova/nova.conf
|
||||
config_drive_format = iso9660
|
||||
config_drive_skip_versions = 1.0 2007-01-19 2007-03-01 2007-08-29 2007-10-10 2007-12-15 2008-02-01 2008-09-01
|
||||
console_host = dims-ubuntu
|
||||
console_manager = nova.console.manager.ConsoleProxyManager
|
||||
console_topic = console
|
||||
consoleauth_manager = nova.consoleauth.manager.ConsoleAuthManager
|
||||
consoleauth_topic = consoleauth
|
||||
control_exchange = nova
|
||||
cpu_allocation_ratio = 0.0
|
||||
create_unique_mac_address_attempts = 5
|
||||
crl_file = crl.pem
|
||||
db_driver = nova.db
|
||||
debug = True
|
||||
default_access_ip_network_name = None
|
||||
default_availability_zone = nova
|
||||
default_ephemeral_format = ext4
|
||||
default_flavor = m1.small
|
||||
default_floating_pool = public
|
||||
default_log_levels =
|
||||
amqp=WARN
|
||||
amqplib=WARN
|
||||
boto=WARN
|
||||
glanceclient=WARN
|
||||
iso8601=WARN
|
||||
keystonemiddleware=WARN
|
||||
oslo_messaging=INFO
|
||||
qpid=WARN
|
||||
requests.packages.urllib3.connectionpool=WARN
|
||||
routes.middleware=WARN
|
||||
sqlalchemy=WARN
|
||||
stevedore=WARN
|
||||
suds=INFO
|
||||
urllib3.connectionpool=WARN
|
||||
websocket=WARN
|
||||
default_notification_level = INFO
|
||||
default_publisher_id = None
|
||||
default_schedule_zone = None
|
||||
defer_iptables_apply = False
|
||||
dhcp_domain = novalocal
|
||||
dhcp_lease_time = 86400
|
||||
dhcpbridge = /usr/local/bin/nova-dhcpbridge
|
||||
dhcpbridge_flagfile =
|
||||
/etc/nova/nova.conf
|
||||
dmz_cidr =
|
||||
dmz_mask = 255.255.255.0
|
||||
dmz_net = 10.0.0.0
|
||||
dns_server =
|
||||
dns_update_periodic_interval = -1
|
||||
dnsmasq_config_file =
|
||||
ebtables_exec_attempts = 3
|
||||
ebtables_retry_interval = 1.0
|
||||
ec2_dmz_host = 10.0.0.9
|
||||
ec2_host = 10.0.0.9
|
||||
ec2_listen = 0.0.0.0
|
||||
ec2_listen_port = 8773
|
||||
ec2_path = /
|
||||
ec2_port = 8773
|
||||
ec2_private_dns_show_ip = False
|
||||
ec2_scheme = http
|
||||
ec2_strict_validation = True
|
||||
ec2_timestamp_expiry = 300
|
||||
ec2_workers = 2
|
||||
enable_instance_password = True
|
||||
enable_network_quota = False
|
||||
enable_new_services = True
|
||||
enabled_apis =
|
||||
ec2
|
||||
metadata
|
||||
osapi_compute
|
||||
enabled_ssl_apis =
|
||||
fake_call = False
|
||||
fake_network = False
|
||||
fatal_deprecations = False
|
||||
fatal_exception_format_errors = False
|
||||
firewall_driver = nova.virt.firewall.NoopFirewallDriver
|
||||
fixed_ip_disassociate_timeout = 600
|
||||
fixed_range_v6 = fd00::/48
|
||||
flat_injected = False
|
||||
flat_interface = None
|
||||
flat_network_bridge = None
|
||||
flat_network_dns = 8.8.4.4
|
||||
floating_ip_dns_manager = nova.network.noop_dns_driver.NoopDNSDriver
|
||||
force_config_drive = True
|
||||
force_dhcp_release = True
|
||||
force_raw_images = True
|
||||
force_snat_range =
|
||||
forward_bridge_interface =
|
||||
all
|
||||
fping_path = /usr/sbin/fping
|
||||
gateway = None
|
||||
gateway_v6 = None
|
||||
heal_instance_info_cache_interval = 60
|
||||
host = dims-ubuntu
|
||||
image_cache_manager_interval = 2400
|
||||
image_cache_subdirectory_name = _base
|
||||
image_decryption_dir = /tmp
|
||||
injected_network_template = /opt/stack/nova/nova/virt/interfaces.template
|
||||
instance_build_timeout = 0
|
||||
instance_delete_interval = 300
|
||||
instance_dns_domain =
|
||||
instance_dns_manager = nova.network.noop_dns_driver.NoopDNSDriver
|
||||
instance_format = [instance: %(uuid)s]
|
||||
instance_name_template = instance-%08x
|
||||
instance_usage_audit = False
|
||||
instance_usage_audit_period = month
|
||||
instance_uuid_format = [instance: %(uuid)s]
|
||||
instances_path = /opt/stack/data/nova/instances
|
||||
internal_service_availability_zone = internal
|
||||
iptables_bottom_regex =
|
||||
iptables_drop_action = DROP
|
||||
iptables_top_regex =
|
||||
ipv6_backend = rfc2462
|
||||
key_file = private/cakey.pem
|
||||
keys_path = /opt/stack/data/nova/keys
|
||||
keystone_ec2_insecure = False
|
||||
keystone_ec2_url = http://10.0.0.9:5000/v2.0/ec2tokens
|
||||
l3_lib = nova.network.l3.LinuxNetL3
|
||||
linuxnet_interface_driver =
|
||||
linuxnet_ovs_integration_bridge = br-int
|
||||
live_migration_retry_count = 30
|
||||
lockout_attempts = 5
|
||||
lockout_minutes = 15
|
||||
lockout_window = 15
|
||||
log-config-append = None
|
||||
log-date-format = %Y-%m-%d %H:%M:%S
|
||||
log-dir = None
|
||||
log-file = None
|
||||
log-format = None
|
||||
log_options = True
|
||||
logging_context_format_string = %(asctime)s.%(msecs)03d %(color)s%(levelname)s %(name)s [%(request_id)s %(user_name)s %(project_name)s%(color)s] %(instance)s%(color)s%(message)s
|
||||
logging_debug_format_suffix = from (pid=%(process)d) %(funcName)s %(pathname)s:%(lineno)d
|
||||
logging_default_format_string = %(asctime)s.%(msecs)03d %(color)s%(levelname)s %(name)s [-%(color)s] %(instance)s%(color)s%(message)s
|
||||
logging_exception_prefix = %(color)s%(asctime)s.%(msecs)03d TRACE %(name)s %(instance)s
|
||||
max_age = 0
|
||||
max_concurrent_builds = 10
|
||||
max_concurrent_live_migrations = 1
|
||||
max_header_line = 16384
|
||||
max_local_block_devices = 3
|
||||
maximum_instance_delete_attempts = 5
|
||||
memcached_servers = None
|
||||
metadata_cache_expiration = 15
|
||||
metadata_host = 10.0.0.9
|
||||
metadata_listen = 0.0.0.0
|
||||
metadata_listen_port = 8775
|
||||
metadata_manager = nova.api.manager.MetadataManager
|
||||
metadata_port = 8775
|
||||
metadata_workers = 2
|
||||
migrate_max_retries = -1
|
||||
mkisofs_cmd = genisoimage
|
||||
monkey_patch = False
|
||||
monkey_patch_modules =
|
||||
nova.api.ec2.cloud:nova.notifications.notify_decorator
|
||||
nova.compute.api:nova.notifications.notify_decorator
|
||||
multi_host = False
|
||||
multi_instance_display_name_template = %(name)s-%(count)d
|
||||
my_block_storage_ip = 10.0.0.9
|
||||
my_ip = 10.0.0.9
|
||||
network_allocate_retries = 0
|
||||
network_api_class = nova.network.neutronv2.api.API
|
||||
network_device_mtu = None
|
||||
network_driver = nova.network.linux_net
|
||||
network_manager = nova.network.manager.VlanManager
|
||||
network_size = 256
|
||||
network_topic = network
|
||||
networks_path = /opt/stack/data/nova/networks
|
||||
neutron_default_tenant_id = default
|
||||
non_inheritable_image_properties =
|
||||
bittorrent
|
||||
cache_in_nova
|
||||
notification_driver =
|
||||
notification_topics =
|
||||
notifications
|
||||
notify_api_faults = False
|
||||
notify_on_state_change = None
|
||||
null_kernel = nokernel
|
||||
num_networks = 1
|
||||
osapi_compute_ext_list =
|
||||
osapi_compute_extension =
|
||||
nova.api.openstack.compute.legacy_v2.contrib.standard_extensions
|
||||
osapi_compute_link_prefix = None
|
||||
osapi_compute_listen = 0.0.0.0
|
||||
osapi_compute_listen_port = 8774
|
||||
osapi_compute_unique_server_name_scope =
|
||||
osapi_compute_workers = 2
|
||||
osapi_glance_link_prefix = None
|
||||
osapi_hide_server_address_states =
|
||||
building
|
||||
osapi_max_limit = 1000
|
||||
ovs_vsctl_timeout = 120
|
||||
password_length = 12
|
||||
pci_alias =
|
||||
pci_passthrough_whitelist =
|
||||
periodic_enable = True
|
||||
periodic_fuzzy_delay = 60
|
||||
policy_default_rule = default
|
||||
policy_dirs =
|
||||
policy.d
|
||||
policy_file = policy.json
|
||||
preallocate_images = none
|
||||
project_cert_subject = /C=US/ST=California/O=OpenStack/OU=NovaDev/CN=project-ca-%.16s-%s
|
||||
public_interface = eth0
|
||||
publish_errors = False
|
||||
pybasedir = /opt/stack/nova
|
||||
quota_cores = 20
|
||||
quota_driver = nova.quota.DbQuotaDriver
|
||||
quota_fixed_ips = -1
|
||||
quota_floating_ips = 10
|
||||
quota_injected_file_content_bytes = 10240
|
||||
quota_injected_file_path_length = 255
|
||||
quota_injected_files = 5
|
||||
quota_instances = 10
|
||||
quota_key_pairs = 100
|
||||
quota_metadata_items = 128
|
||||
quota_networks = 3
|
||||
quota_ram = 51200
|
||||
quota_security_group_rules = 20
|
||||
quota_security_groups = 10
|
||||
quota_server_group_members = 10
|
||||
quota_server_groups = 10
|
||||
ram_allocation_ratio = 0.0
|
||||
reboot_timeout = 0
|
||||
reclaim_instance_interval = 0
|
||||
region_list =
|
||||
remove_unused_base_images = True
|
||||
remove_unused_original_minimum_age_seconds = 86400
|
||||
report_interval = 10
|
||||
rescue_timeout = 0
|
||||
reservation_expire = 86400
|
||||
reserved_host_disk_mb = 0
|
||||
reserved_host_memory_mb = 512
|
||||
resize_confirm_window = 0
|
||||
resize_fs_using_block_device = False
|
||||
resume_guests_state_on_host_boot = False
|
||||
rootwrap_config = /etc/nova/rootwrap.conf
|
||||
routing_source_ip = 10.0.0.9
|
||||
rpc_backend = rabbit
|
||||
rpc_response_timeout = 60
|
||||
run_external_periodic_tasks = True
|
||||
running_deleted_instance_action = reap
|
||||
running_deleted_instance_poll_interval = 1800
|
||||
running_deleted_instance_timeout = 0
|
||||
s3_access_key = notchecked
|
||||
s3_affix_tenant = False
|
||||
s3_host = 10.0.0.9
|
||||
s3_port = 3333
|
||||
s3_secret_key = notchecked
|
||||
s3_use_ssl = False
|
||||
scheduler_available_filters =
|
||||
nova.scheduler.filters.all_filters
|
||||
scheduler_default_filters =
|
||||
AvailabilityZoneFilter
|
||||
ComputeCapabilitiesFilter
|
||||
ComputeFilter
|
||||
DiskFilter
|
||||
ImagePropertiesFilter
|
||||
RamFilter
|
||||
RetryFilter
|
||||
ServerGroupAffinityFilter
|
||||
ServerGroupAntiAffinityFilter
|
||||
scheduler_instance_sync_interval = 120
|
||||
scheduler_manager = nova.scheduler.manager.SchedulerManager
|
||||
scheduler_max_attempts = 3
|
||||
scheduler_topic = scheduler
|
||||
scheduler_tracks_instance_changes = True
|
||||
scheduler_weight_classes =
|
||||
nova.scheduler.weights.all_weighers
|
||||
secure_proxy_ssl_header = None
|
||||
security_group_api = neutron
|
||||
send_arp_for_ha = False
|
||||
send_arp_for_ha_count = 3
|
||||
service_down_time = 60
|
||||
servicegroup_driver = db
|
||||
share_dhcp_address = False
|
||||
shelved_offload_time = 0
|
||||
shelved_poll_interval = 3600
|
||||
shutdown_timeout = 60
|
||||
snapshot_name_template = snapshot-%s
|
||||
ssl_ca_file = None
|
||||
ssl_cert_file = None
|
||||
ssl_key_file = None
|
||||
state_path = /opt/stack/data/nova
|
||||
sync_power_state_interval = 600
|
||||
syslog-log-facility = LOG_USER
|
||||
tcp_keepidle = 600
|
||||
teardown_unused_network_gateway = False
|
||||
tempdir = None
|
||||
transport_url = None
|
||||
until_refresh = 0
|
||||
update_dns_entries = False
|
||||
update_resources_interval = 0
|
||||
use-syslog = False
|
||||
use-syslog-rfc-format = True
|
||||
use_cow_images = True
|
||||
use_forwarded_for = False
|
||||
use_ipv6 = False
|
||||
use_network_dns_servers = False
|
||||
use_neutron_default_nets = False
|
||||
use_project_ca = False
|
||||
use_rootwrap_daemon = False
|
||||
use_single_default_gateway = False
|
||||
use_stderr = True
|
||||
user_cert_subject = /C=US/ST=California/O=OpenStack/OU=NovaDev/CN=%.16s-%.16s-%s
|
||||
vcpu_pin_set = None
|
||||
vendordata_driver = nova.api.metadata.vendordata_json.JsonFileVendorData
|
||||
verbose = True
|
||||
vif_plugging_is_fatal = True
|
||||
vif_plugging_timeout = 300
|
||||
virt_mkfs =
|
||||
vlan_interface = None
|
||||
vlan_start = 100
|
||||
volume_api_class = nova.volume.cinder.API
|
||||
volume_usage_poll_interval = 0
|
||||
vpn_flavor = m1.tiny
|
||||
vpn_image_id = 0
|
||||
vpn_ip = 10.0.0.9
|
||||
vpn_key_suffix = -vpn
|
||||
vpn_start = 1000
|
||||
wsgi_default_pool_size = 1000
|
||||
wsgi_keep_alive = True
|
||||
wsgi_log_format = %(client_ip)s "%(request_line)s" status: %(status_code)s len: %(body_length)s time: %(wall_seconds).7f
|
||||
|
||||
ephemeral_storage_encryption:
|
||||
cipher = aes-xts-plain64
|
||||
enabled = False
|
||||
key_size = 512
|
||||
|
||||
glance:
|
||||
allowed_direct_url_schemes =
|
||||
api_insecure = False
|
||||
api_servers =
|
||||
http://10.0.0.9:9292
|
||||
host = 10.0.0.9
|
||||
num_retries = 0
|
||||
port = 9292
|
||||
protocol = http
|
||||
|
||||
guestfs:
|
||||
debug = False
|
||||
|
||||
image_file_url:
|
||||
filesystems =
|
||||
|
||||
ironic:
|
||||
admin_auth_token = ***
|
||||
admin_password = ***
|
||||
admin_tenant_name = None
|
||||
admin_url = None
|
||||
admin_username = None
|
||||
api_endpoint = None
|
||||
api_max_retries = 60
|
||||
api_retry_interval = 2
|
||||
api_version = 1
|
||||
client_log_level = None
|
||||
|
||||
keymgr:
|
||||
api_class = nova.keymgr.conf_key_mgr.ConfKeyManager
|
||||
|
||||
keystone_authtoken:
|
||||
admin_password = ***
|
||||
admin_tenant_name = admin
|
||||
admin_token = ***
|
||||
admin_user = None
|
||||
auth-url = http://10.0.0.9:35357
|
||||
auth_admin_prefix =
|
||||
auth_host = 127.0.0.1
|
||||
auth_plugin = password
|
||||
auth_port = 35357
|
||||
auth_protocol = https
|
||||
auth_section = None
|
||||
auth_uri = http://10.0.0.9:5000
|
||||
auth_version = None
|
||||
cache = None
|
||||
cafile = /opt/stack/data/ca-bundle.pem
|
||||
certfile = None
|
||||
check_revocations_for_cached = False
|
||||
delay_auth_decision = False
|
||||
domain-id = None
|
||||
domain-name = None
|
||||
enforce_token_bind = permissive
|
||||
hash_algorithms =
|
||||
md5
|
||||
http_connect_timeout = None
|
||||
http_request_max_retries = 3
|
||||
identity_uri = None
|
||||
include_service_catalog = True
|
||||
insecure = False
|
||||
keyfile = None
|
||||
memcache_pool_conn_get_timeout = 10
|
||||
memcache_pool_dead_retry = 300
|
||||
memcache_pool_maxsize = 10
|
||||
memcache_pool_socket_timeout = 3
|
||||
memcache_pool_unused_timeout = 60
|
||||
memcache_secret_key = ***
|
||||
memcache_security_strategy = None
|
||||
memcache_use_advanced_pool = False
|
||||
memcached_servers = None
|
||||
password = password
|
||||
project-domain-id = default
|
||||
project-domain-name = None
|
||||
project-id = None
|
||||
project-name = service
|
||||
region_name = None
|
||||
revocation_cache_time = 10
|
||||
signing_dir = /var/cache/nova
|
||||
tenant-id = None
|
||||
tenant-name = None
|
||||
token_cache_time = 300
|
||||
trust-id = None
|
||||
user-domain-id = default
|
||||
user-domain-name = None
|
||||
user-id = None
|
||||
user-name = nova
|
||||
|
||||
libvirt:
|
||||
block_migration_flag = VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_LIVE, VIR_MIGRATE_TUNNELLED, VIR_MIGRATE_NON_SHARED_INC
|
||||
checksum_base_images = False
|
||||
checksum_interval_seconds = 3600
|
||||
connection_uri =
|
||||
cpu_mode = none
|
||||
cpu_model = None
|
||||
disk_cachemodes =
|
||||
disk_prefix = None
|
||||
gid_maps =
|
||||
hw_disk_discard = None
|
||||
hw_machine_type = None
|
||||
image_info_filename_pattern = /opt/stack/data/nova/instances/_base/%(image)s.info
|
||||
images_rbd_ceph_conf =
|
||||
images_rbd_pool = rbd
|
||||
images_type = default
|
||||
images_volume_group = None
|
||||
inject_key = False
|
||||
inject_partition = -2
|
||||
inject_password = False
|
||||
iscsi_iface = None
|
||||
iscsi_use_multipath = False
|
||||
live_migration_bandwidth = 0
|
||||
live_migration_completion_timeout = 800
|
||||
live_migration_downtime = 500
|
||||
live_migration_downtime_delay = 75
|
||||
live_migration_downtime_steps = 10
|
||||
live_migration_flag = VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_LIVE, VIR_MIGRATE_TUNNELLED
|
||||
live_migration_progress_timeout = 150
|
||||
live_migration_uri = qemu+ssh://stack@%s/system
|
||||
mem_stats_period_seconds = 10
|
||||
num_iscsi_scan_tries = 5
|
||||
qemu_allowed_storage_drivers =
|
||||
rbd_secret_uuid = None
|
||||
rbd_user = None
|
||||
remote_filesystem_transport = ssh
|
||||
remove_unused_kernels = True
|
||||
remove_unused_resized_minimum_age_seconds = 3600
|
||||
rescue_image_id = None
|
||||
rescue_kernel_id = None
|
||||
rescue_ramdisk_id = None
|
||||
rng_dev_path = None
|
||||
snapshot_compression = False
|
||||
snapshot_image_format = None
|
||||
snapshots_directory = /opt/stack/data/nova/instances/snapshots
|
||||
sparse_logical_volumes = False
|
||||
sysinfo_serial = auto
|
||||
uid_maps =
|
||||
use_usb_tablet = False
|
||||
use_virtio_for_bridges = True
|
||||
virt_type = kvm
|
||||
volume_clear = zero
|
||||
volume_clear_size = 0
|
||||
wait_soft_reboot_seconds = 120
|
||||
xen_hvmloader_path = /usr/lib/xen/boot/hvmloader
|
||||
|
||||
mks:
|
||||
enabled = False
|
||||
mksproxy_base_url = http://127.0.0.1:6090/
|
||||
|
||||
neutron:
|
||||
admin_auth_url = http://10.0.0.9:35357/v2.0
|
||||
admin_password = ***
|
||||
admin_tenant_id = None
|
||||
admin_tenant_name = service
|
||||
admin_user_id = None
|
||||
admin_username = neutron
|
||||
auth_plugin = None
|
||||
auth_section = None
|
||||
auth_strategy = keystone
|
||||
cafile = None
|
||||
certfile = None
|
||||
extension_sync_interval = 600
|
||||
insecure = False
|
||||
keyfile = None
|
||||
metadata_proxy_shared_secret = ***
|
||||
ovs_bridge = br-int
|
||||
region_name = RegionOne
|
||||
service_metadata_proxy = True
|
||||
timeout = None
|
||||
url = http://10.0.0.9:9696
|
||||
|
||||
osapi_v21:
|
||||
enabled = True
|
||||
extensions_blacklist =
|
||||
extensions_whitelist =
|
||||
|
||||
oslo_concurrency:
|
||||
disable_process_locking = False
|
||||
lock_path = /opt/stack/data/nova
|
||||
|
||||
oslo_messaging_rabbit:
|
||||
amqp_auto_delete = False
|
||||
amqp_durable_queues = False
|
||||
fake_rabbit = False
|
||||
heartbeat_rate = 2
|
||||
heartbeat_timeout_threshold = 60
|
||||
kombu_reconnect_delay = 1.0
|
||||
kombu_reconnect_timeout = 60
|
||||
kombu_ssl_ca_certs =
|
||||
kombu_ssl_certfile =
|
||||
kombu_ssl_keyfile =
|
||||
kombu_ssl_version =
|
||||
rabbit_ha_queues = False
|
||||
rabbit_host = localhost
|
||||
rabbit_hosts =
|
||||
10.0.0.9
|
||||
rabbit_login_method = AMQPLAIN
|
||||
rabbit_max_retries = 0
|
||||
rabbit_password = ***
|
||||
rabbit_port = 5672
|
||||
rabbit_retry_backoff = 2
|
||||
rabbit_retry_interval = 1
|
||||
rabbit_use_ssl = False
|
||||
rabbit_userid = stackrabbit
|
||||
rabbit_virtual_host = /
|
||||
rpc_conn_pool_size = 30
|
||||
send_single_reply = False
|
||||
|
||||
oslo_middleware:
|
||||
max_request_body_size = 114688
|
||||
|
||||
oslo_versionedobjects:
|
||||
fatal_exception_format_errors = False
|
||||
|
||||
rdp:
|
||||
enabled = False
|
||||
html5_proxy_base_url = http://127.0.0.1:6083/
|
||||
|
||||
remote_debug:
|
||||
host = None
|
||||
port = None
|
||||
|
||||
serial_console:
|
||||
base_url = ws://127.0.0.1:6083/
|
||||
enabled = False
|
||||
listen = 127.0.0.1
|
||||
port_range = 10000:20000
|
||||
proxyclient_address = 127.0.0.1
|
||||
|
||||
spice:
|
||||
agent_enabled = True
|
||||
enabled = False
|
||||
html5proxy_base_url = http://10.0.0.9:6082/spice_auto.html
|
||||
keymap = en-us
|
||||
server_listen = 127.0.0.1
|
||||
server_proxyclient_address = 127.0.0.1
|
||||
|
||||
ssl:
|
||||
ca_file = None
|
||||
cert_file = None
|
||||
key_file = None
|
||||
|
||||
upgrade_levels:
|
||||
baseapi = None
|
||||
cells = None
|
||||
cert = None
|
||||
compute = None
|
||||
conductor = None
|
||||
console = None
|
||||
consoleauth = None
|
||||
network = None
|
||||
scheduler = None
|
||||
|
||||
vnc:
|
||||
enabled = False
|
||||
keymap = en-us
|
||||
novncproxy_base_url = http://10.0.0.9:6080/vnc_auto.html
|
||||
vncserver_listen = 127.0.0.1
|
||||
vncserver_proxyclient_address = 127.0.0.1
|
||||
xvpvncproxy_base_url = http://10.0.0.9:6081/console
|
||||
|
||||
workarounds:
|
||||
destroy_after_evacuate = True
|
||||
disable_libvirt_livesnapshot = True
|
||||
disable_rootwrap = False
|
||||
handle_virt_lifecycle_events = True
|
@ -1,35 +0,0 @@
|
||||
=======
|
||||
Usage
|
||||
=======
|
||||
|
||||
Every long running service process should have a call to install a
|
||||
signal handler which will trigger the guru meditation framework upon
|
||||
receipt of SIGUSR1/SIGUSR2. This will result in the process dumping a
|
||||
complete report of its current state to stderr.
|
||||
|
||||
For RPC listeners, it may also be desirable to install some kind of hook in
|
||||
the RPC request dispatcher that will save a guru meditation report whenever
|
||||
the processing of a request results in an uncaught exception. It could save
|
||||
these reports to a well known directory
|
||||
(/var/log/openstack/<project>/<service>/) for later analysis by the sysadmin
|
||||
or automated bug analysis tools.
|
||||
|
||||
To use oslo.reports in a project, you need to add the following call to
|
||||
:py:func:`~oslo_reports.TextGuruMeditation.setup_autorun` somewhere really
|
||||
early in the startup sequence of the process::
|
||||
|
||||
from oslo_reports import guru_meditation_report as gmr
|
||||
|
||||
gmr.TextGuruMeditation.setup_autorun(version='13.0.0')
|
||||
|
||||
Note that the version parameter is the version of the component itself.
|
||||
|
||||
To trigger the report to be generated::
|
||||
|
||||
kill -SIGUSR2 <process_id>
|
||||
|
||||
|
||||
Here is a sample report:
|
||||
|
||||
.. include:: report.txt
|
||||
:literal:
|
@ -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:`oslo_reports.report.BasicReport` )
|
||||
is composed of one or more report sections
|
||||
( :class:`oslo_reports.report.BasicSection` ),
|
||||
which contain generators which generate data models
|
||||
( :class:`oslo_reports.models.base.ReportModels` ),
|
||||
which are then serialized by views.
|
||||
"""
|
@ -1,25 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""oslo.i18n integration module.
|
||||
|
||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html .
|
||||
|
||||
"""
|
||||
|
||||
import oslo_i18n
|
||||
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain='oslo_reports')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
@ -1,27 +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 six
|
||||
|
||||
|
||||
class StringWithAttrs(six.text_type):
|
||||
"""A String that can have arbitrary attributes"""
|
||||
|
||||
pass
|
@ -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:`oslo_reports.models.base.ReportModel` ).
|
||||
A generator is any object which is callable with no parameters
|
||||
and returns a data model.
|
||||
"""
|
@ -1,44 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides OpenStack config generators
|
||||
|
||||
This module defines a class for configuration
|
||||
generators for generating the model in
|
||||
:mod:`oslo_reports.models.conf`.
|
||||
"""
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from oslo_reports.models import conf as cm
|
||||
|
||||
|
||||
class ConfigReportGenerator(object):
|
||||
"""A Configuration Data Generator
|
||||
|
||||
This generator returns
|
||||
:class:`oslo_reports.models.conf.ConfigModel`,
|
||||
by default using the configuration options stored
|
||||
in :attr:`oslo_config.cfg.CONF`, which is where
|
||||
OpenStack stores everything.
|
||||
|
||||
:param cnf: the configuration option object
|
||||
:type cnf: :class:`oslo_config.cfg.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, cnf=cfg.CONF):
|
||||
self.conf_obj = cnf
|
||||
|
||||
def __call__(self):
|
||||
return cm.ConfigModel(self.conf_obj)
|
@ -1,38 +0,0 @@
|
||||
# Copyright 2014 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides process-data generators
|
||||
|
||||
This modules defines a class for generating
|
||||
process data by way of the psutil package.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import psutil
|
||||
|
||||
from oslo_reports.models import process as pm
|
||||
|
||||
|
||||
class ProcessReportGenerator(object):
|
||||
"""A Process Data Generator
|
||||
|
||||
This generator returns a
|
||||
:class:`oslo_reports.models.process.ProcessModel`
|
||||
based on the current process (which will also include
|
||||
all subprocesses, recursively) using the :class:`psutil.Process` class`.
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
return pm.ProcessModel(psutil.Process(os.getpid()))
|
@ -1,104 +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:`oslo_reports.models.threading`.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import gc
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from oslo_reports.models import threading as tm
|
||||
from oslo_reports.models import with_default_views as mwdv
|
||||
from oslo_reports.views.text import generic as text_views
|
||||
|
||||
|
||||
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)]
|
||||
|
||||
|
||||
class ThreadReportGenerator(object):
|
||||
"""A Thread Data Generator
|
||||
|
||||
This generator returns a collection of
|
||||
:class:`oslo_reports.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:`oslo_reports.models.threading.GreenThreadModel`
|
||||
objects by introspecting the current python garbage collection
|
||||
state, and sifting through for :class:`greenlet.greenlet` objects.
|
||||
|
||||
.. seealso::
|
||||
|
||||
Function :func:`_find_objects`
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
import greenlet
|
||||
|
||||
threadModels = [
|
||||
tm.GreenThreadModel(gr.gr_frame)
|
||||
for gr in _find_objects(greenlet.greenlet)
|
||||
]
|
||||
|
||||
return mwdv.ModelWithDefaultViews(threadModels,
|
||||
text_view=text_views.MultiView())
|
@ -1,60 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides OpenStack version generators
|
||||
|
||||
This module defines a class for OpenStack
|
||||
version and package information
|
||||
generators for generating the model in
|
||||
:mod:`oslo_reports.models.version`.
|
||||
"""
|
||||
|
||||
from oslo_reports.models import version as vm
|
||||
|
||||
|
||||
class PackageReportGenerator(object):
|
||||
"""A Package Information Data Generator
|
||||
|
||||
This generator returns
|
||||
:class:`oslo_reports.models.version.PackageModel`,
|
||||
extracting data from the given version object, which should follow
|
||||
the general format defined in Nova's version information (i.e. it
|
||||
should contain the methods vendor_string, product_string, and
|
||||
version_string_with_package).
|
||||
|
||||
:param version_object: the version information object
|
||||
"""
|
||||
|
||||
def __init__(self, version_obj):
|
||||
self.version_obj = version_obj
|
||||
|
||||
def __call__(self):
|
||||
if hasattr(self.version_obj, "vendor_string"):
|
||||
vendor_string = self.version_obj.vendor_string()
|
||||
else:
|
||||
vendor_string = None
|
||||
|
||||
if hasattr(self.version_obj, "product_string"):
|
||||
product_string = self.version_obj.product_string()
|
||||
else:
|
||||
product_string = None
|
||||
|
||||
if hasattr(self.version_obj, "version_string_with_package"):
|
||||
version_string_with_package = self.version_obj.\
|
||||
version_string_with_package()
|
||||
else:
|
||||
version_string_with_package = None
|
||||
|
||||
return vm.PackageModel(vendor_string, product_string,
|
||||
version_string_with_package)
|
@ -1,297 +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
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as oslo_logging
|
||||
from oslo_reports import opts as gmr_opts
|
||||
from oslo_reports import guru_meditation_report as gmr
|
||||
|
||||
CONF = cfg.CONF
|
||||
# maybe import some options here...
|
||||
|
||||
def main():
|
||||
oslo_logging.register_options(CONF)
|
||||
gmr_opts.set_defaults(CONF)
|
||||
|
||||
CONF(sys.argv[1:], default_config_files=['myapp.conf'])
|
||||
oslo_logging.setup(CONF, 'myapp')
|
||||
|
||||
gmr.TextGuruMeditation.register_section('Some Special Section',
|
||||
special_section_generator)
|
||||
gmr.TextGuruMeditation.setup_autorun(version_object, conf=CONF)
|
||||
|
||||
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 -USR2 $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 logging
|
||||
import os
|
||||
import signal
|
||||
import stat
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from oslo_reports.generators import conf as cgen
|
||||
from oslo_reports.generators import process as prgen
|
||||
from oslo_reports.generators import threading as tgen
|
||||
from oslo_reports.generators import version as pgen
|
||||
from oslo_reports import report
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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 or use file modification events.
|
||||
|
||||
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, conf=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. It can also use file modification events instead of
|
||||
signals.
|
||||
|
||||
: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
|
||||
:param conf: Configuration object, managed by the caller.
|
||||
"""
|
||||
|
||||
if log_dir is None and conf is not None:
|
||||
log_dir = conf.oslo_reports.log_dir
|
||||
|
||||
if signum:
|
||||
cls._setup_signal(signum, version, service_name, log_dir)
|
||||
return
|
||||
|
||||
if conf and conf.oslo_reports.file_event_handler:
|
||||
cls._setup_file_watcher(
|
||||
conf.oslo_reports.file_event_handler,
|
||||
conf.oslo_reports.file_event_handler_interval,
|
||||
version, service_name, log_dir)
|
||||
else:
|
||||
if hasattr(signal, 'SIGUSR1'):
|
||||
# TODO(dims) We need to remove this in the "O" release cycle
|
||||
LOG.warning("Guru meditation now registers SIGUSR1 and "
|
||||
"SIGUSR2 by default for backward "
|
||||
"compatibility. SIGUSR1 will no longer be "
|
||||
"registered in a future release, so please "
|
||||
"use SIGUSR2 to generate reports.")
|
||||
cls._setup_signal(signal.SIGUSR1,
|
||||
version, service_name, log_dir)
|
||||
if hasattr(signal, 'SIGUSR2'):
|
||||
cls._setup_signal(signal.SIGUSR2,
|
||||
version, service_name, log_dir)
|
||||
|
||||
@classmethod
|
||||
def _setup_file_watcher(cls, filepath, interval, version, service_name,
|
||||
log_dir):
|
||||
|
||||
st = os.stat(filepath)
|
||||
if not bool(st.st_mode & stat.S_IRGRP):
|
||||
LOG.error("Guru Meditation Report does not have read "
|
||||
"permissions to '%s' file.", filepath)
|
||||
|
||||
def _handler():
|
||||
mtime = time.time()
|
||||
while True:
|
||||
try:
|
||||
stat = os.stat(filepath)
|
||||
if stat.st_mtime > mtime:
|
||||
cls.handle_signal(version, service_name, log_dir, None)
|
||||
mtime = stat.st_mtime
|
||||
except OSError:
|
||||
msg = ("Guru Meditation Report cannot read " +
|
||||
"'{0}' file".format(filepath))
|
||||
raise IOError(msg)
|
||||
finally:
|
||||
time.sleep(interval)
|
||||
|
||||
th = threading.Thread(target=_handler)
|
||||
th.daemon = True
|
||||
th.start()
|
||||
|
||||
@classmethod
|
||||
def _setup_signal(cls, signum, version, service_name, log_dir):
|
||||
signal.signal(signum,
|
||||
lambda sn, f: cls.handle_signal(
|
||||
version, service_name, log_dir, f))
|
||||
|
||||
@classmethod
|
||||
def handle_signal(cls, version, service_name, log_dir, frame):
|
||||
"""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 frame: the frame object provided to the signal handler
|
||||
"""
|
||||
|
||||
try:
|
||||
res = cls(version, frame).run()
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
print("Unable to run Guru Meditation Report!",
|
||||
file=sys.stderr)
|
||||
else:
|
||||
if log_dir:
|
||||
service_name = service_name or os.path.basename(
|
||||
inspect.stack()[-1][1])
|
||||
filename = "%s_gurumeditation_%s" % (
|
||||
service_name, timeutils.utcnow().strftime(
|
||||
cls.timestamp_fmt))
|
||||
filepath = os.path.join(log_dir, filename)
|
||||
try:
|
||||
with open(filepath, "w") as dumpfile:
|
||||
dumpfile.write(res)
|
||||
except Exception:
|
||||
print("Unable to dump Guru Meditation Report to file %s" %
|
||||
(filepath,), file=sys.stderr)
|
||||
else:
|
||||
print(res, file=sys.stderr)
|
||||
|
||||
def _readd_sections(self):
|
||||
del self.sections[self.start_section_index:]
|
||||
|
||||
self.add_section('Package',
|
||||
pgen.PackageReportGenerator(self.version_obj))
|
||||
|
||||
self.add_section('Threads',
|
||||
tgen.ThreadReportGenerator(self.traceback))
|
||||
|
||||
self.add_section('Green Threads',
|
||||
tgen.GreenThreadReportGenerator())
|
||||
|
||||
self.add_section('Processes',
|
||||
prgen.ProcessReportGenerator())
|
||||
|
||||
self.add_section('Configuration',
|
||||
cgen.ConfigReportGenerator())
|
||||
|
||||
try:
|
||||
for section_title, generator in self.persistent_sections:
|
||||
self.add_section(section_title, generator)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
self._readd_sections()
|
||||
return super(GuruMeditation, self).run()
|
||||
|
||||
|
||||
# GuruMeditation must come first to get the correct MRO
|
||||
class TextGuruMeditation(GuruMeditation, report.TextReport):
|
||||
"""A Text Guru Meditation Report
|
||||
|
||||
This report is the basic human-readable Guru Meditation Report
|
||||
|
||||
It contains the following sections by default
|
||||
(in addition to any registered persistent sections):
|
||||
|
||||
- Package Information
|
||||
|
||||
- Threads List
|
||||
|
||||
- Green Threads List
|
||||
|
||||
- Process List
|
||||
|
||||
- Configuration Options
|
||||
|
||||
:param version_obj: the version object for the current product
|
||||
:param traceback: an (optional) frame object providing the actual
|
||||
traceback for the current thread
|
||||
"""
|
||||
|
||||
def __init__(self, version_obj, traceback=None):
|
||||
super(TextGuruMeditation, self).__init__(version_obj, traceback,
|
||||
'Guru Meditation')
|
@ -1,18 +0,0 @@
|
||||
# Andi Chandler <andi@gowling.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: oslo.reports 1.9.1.dev1\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-06-10 16:23+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-06-09 11:13+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
"Language: en-GB\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "Path to a log directory where to create a file"
|
||||
msgstr "Path to a log directory where to create a file"
|
@ -1,20 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides data models
|
||||
|
||||
This module provides both the base data model,
|
||||
as well as several predefined specific data models
|
||||
to be used in reports.
|
||||
"""
|
@ -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] = six.text_type(self_cpy[key])
|
||||
|
||||
if self.attached_view is not None:
|
||||
return self.attached_view(self_cpy)
|
||||
else:
|
||||
raise Exception("Cannot stringify model: no attached view")
|
||||
|
||||
def __repr__(self):
|
||||
if self.attached_view is not None:
|
||||
return ("<Model {cl.__module__}.{cl.__name__} {dt}"
|
||||
" with view {vw.__module__}."
|
||||
"{vw.__name__}>").format(cl=type(self),
|
||||
dt=self.data,
|
||||
vw=type(self.attached_view))
|
||||
else:
|
||||
return ("<Model {cl.__module__}.{cl.__name__} {dt}"
|
||||
" with no view>").format(cl=type(self),
|
||||
dt=self.data)
|
||||
|
||||
def __getitem__(self, attrname):
|
||||
return self.data[attrname]
|
||||
|
||||
def __setitem__(self, attrname, attrval):
|
||||
self.data[attrname] = attrval
|
||||
|
||||
def __delitem__(self, attrname):
|
||||
del self.data[attrname]
|
||||
|
||||
def __contains__(self, key):
|
||||
return self.data.__contains__(key)
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
# Needed for deepcopy in Python3. That will avoid an infinite loop
|
||||
# in __getattr__ .
|
||||
if 'data' not in self.__dict__:
|
||||
self.data = {}
|
||||
|
||||
try:
|
||||
return self.data[attrname]
|
||||
except KeyError:
|
||||
# we don't have that key in data, and the
|
||||
# model class doesn't have that attribute
|
||||
raise AttributeError(
|
||||
"'{cl}' object has no attribute '{an}'".format(
|
||||
cl=type(self).__name__, an=attrname
|
||||
)
|
||||
)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
def __iter__(self):
|
||||
return self.data.__iter__()
|
||||
|
||||
def set_current_view_type(self, tp, visited=None):
|
||||
"""Set the current view type
|
||||
|
||||
This method attempts to set the current view
|
||||
type for this model and all submodels by calling
|
||||
itself recursively on all values, traversing
|
||||
intervening sequences and mappings when possible,
|
||||
and ignoring all other objects.
|
||||
|
||||
:param tp: the type of the view ('text', 'json', 'xml', etc)
|
||||
:param visited: a set of object ids for which the corresponding objects
|
||||
have already had their view type set
|
||||
"""
|
||||
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
def traverse_obj(obj):
|
||||
oid = id(obj)
|
||||
|
||||
# don't die on recursive structures,
|
||||
# and don't treat strings like sequences
|
||||
if oid in visited or isinstance(obj, six.string_types):
|
||||
return
|
||||
|
||||
visited.add(oid)
|
||||
|
||||
if hasattr(obj, 'set_current_view_type'):
|
||||
obj.set_current_view_type(tp, visited=visited)
|
||||
|
||||
if isinstance(obj, col.Sequence):
|
||||
for item in obj:
|
||||
traverse_obj(item)
|
||||
|
||||
elif isinstance(obj, col.Mapping):
|
||||
for val in six.itervalues(obj):
|
||||
traverse_obj(val)
|
||||
|
||||
traverse_obj(self)
|
@ -1,66 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides OpenStack Configuration Model
|
||||
|
||||
This module defines a class representing the data
|
||||
model for :mod:`oslo_config` configuration options
|
||||
"""
|
||||
|
||||
from oslo_reports.models import with_default_views as mwdv
|
||||
from oslo_reports.views.text import generic as generic_text_views
|
||||
|
||||
|
||||
class ConfigModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Configuration Options Model
|
||||
|
||||
This model holds data about a set of configuration options
|
||||
from :mod:`oslo_config`. It supports both the default group
|
||||
of options and named option groups.
|
||||
|
||||
:param conf_obj: a configuration object
|
||||
:type conf_obj: :class:`oslo_config.cfg.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, conf_obj):
|
||||
kv_view = generic_text_views.KeyValueView(dict_sep=": ",
|
||||
before_dict='')
|
||||
super(ConfigModel, self).__init__(text_view=kv_view)
|
||||
|
||||
def opt_title(optname, co):
|
||||
return co._opts[optname]['opt'].name
|
||||
|
||||
def opt_value(opt_obj, value):
|
||||
if opt_obj['opt'].secret:
|
||||
return '***'
|
||||
else:
|
||||
return value
|
||||
|
||||
self['default'] = dict(
|
||||
(opt_title(optname, conf_obj),
|
||||
opt_value(conf_obj._opts[optname], conf_obj[optname]))
|
||||
for optname in conf_obj._opts
|
||||
)
|
||||
|
||||
groups = {}
|
||||
for groupname in conf_obj._groups:
|
||||
group_obj = conf_obj._groups[groupname]
|
||||
curr_group_opts = dict(
|
||||
(opt_title(optname, group_obj),
|
||||
opt_value(group_obj._opts[optname],
|
||||
conf_obj[groupname][optname]))
|
||||
for optname in group_obj._opts)
|
||||
groups[group_obj.name] = curr_group_opts
|
||||
|
||||
self.update(groups)
|
@ -1,73 +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 psutil
|
||||
|
||||
import oslo_reports.models.with_default_views as mwdv
|
||||
import oslo_reports.views.text.process as text_views
|
||||
|
||||
PS1 = psutil.version_info[0] == 1
|
||||
|
||||
|
||||
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 PS1 else process.ppid())
|
||||
if hasattr(process, 'uids'):
|
||||
self['uids'] = {
|
||||
'real': (process.uids.real if PS1 else process.uids().real),
|
||||
'effective': (process.uids.effective if PS1
|
||||
else process.uids().effective),
|
||||
'saved': (process.uids.saved if PS1 else process.uids().saved)
|
||||
}
|
||||
else:
|
||||
self['uids'] = {'real': None,
|
||||
'effective': None,
|
||||
'saved': None}
|
||||
|
||||
if hasattr(process, 'gids'):
|
||||
self['gids'] = {
|
||||
'real': (process.gids.real if PS1 else process.gids().real),
|
||||
'effective': (process.gids.effective if PS1
|
||||
else process.gids().effective),
|
||||
'saved': (process.gids.saved if PS1 else process.gids().saved)
|
||||
}
|
||||
else:
|
||||
self['gids'] = {'real': None,
|
||||
'effective': None,
|
||||
'saved': None}
|
||||
|
||||
self['username'] = process.username if PS1 else process.username()
|
||||
self['command'] = process.cmdline if PS1 else process.cmdline()
|
||||
self['state'] = process.status if PS1 else process.status()
|
||||
|
||||
children = process.get_children() if PS1 else process.children()
|
||||
self['children'] = [ProcessModel(pr) for pr in children]
|
@ -1,100 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides threading and stack-trace models
|
||||
|
||||
This module defines classes representing thread, green
|
||||
thread, and stack trace data models
|
||||
"""
|
||||
|
||||
import traceback
|
||||
|
||||
from oslo_reports.models import with_default_views as mwdv
|
||||
from oslo_reports.views.text import threading as text_views
|
||||
|
||||
|
||||
class StackTraceModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Stack Trace Model
|
||||
|
||||
This model holds data from a python stack trace,
|
||||
commonly extracted from running thread information
|
||||
|
||||
:param stack_state: the python stack_state object
|
||||
"""
|
||||
|
||||
def __init__(self, stack_state):
|
||||
super(StackTraceModel, self).__init__(
|
||||
text_view=text_views.StackTraceView())
|
||||
|
||||
if (stack_state is not None):
|
||||
self['lines'] = [
|
||||
{'filename': fn, 'line': ln, 'name': nm, 'code': cd}
|
||||
for fn, ln, nm, cd in traceback.extract_stack(stack_state)
|
||||
]
|
||||
# FIXME(flepied): under Python3 f_exc_type doesn't exist
|
||||
# anymore so we lose information about exceptions
|
||||
if getattr(stack_state, 'f_exc_type', None) is not None:
|
||||
self['root_exception'] = {
|
||||
'type': stack_state.f_exc_type,
|
||||
'value': stack_state.f_exc_value}
|
||||
else:
|
||||
self['root_exception'] = None
|
||||
else:
|
||||
self['lines'] = []
|
||||
self['root_exception'] = None
|
||||
|
||||
|
||||
class ThreadModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Thread Model
|
||||
|
||||
This model holds data for information about an
|
||||
individual thread. It holds both a thread id,
|
||||
as well as a stack trace for the thread
|
||||
|
||||
.. seealso::
|
||||
|
||||
Class :class:`StackTraceModel`
|
||||
|
||||
:param int thread_id: the id of the thread
|
||||
:param stack: the python stack state for the current thread
|
||||
"""
|
||||
|
||||
# threadId, stack in sys._current_frams().items()
|
||||
def __init__(self, thread_id, stack):
|
||||
super(ThreadModel, self).__init__(text_view=text_views.ThreadView())
|
||||
|
||||
self['thread_id'] = thread_id
|
||||
self['stack_trace'] = StackTraceModel(stack)
|
||||
|
||||
|
||||
class GreenThreadModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Green Thread Model
|
||||
|
||||
This model holds data for information about an
|
||||
individual thread. Unlike the thread model,
|
||||
it holds just a stack trace, since green threads
|
||||
do not have thread ids.
|
||||
|
||||
.. seealso::
|
||||
|
||||
Class :class:`StackTraceModel`
|
||||
|
||||
:param stack: the python stack state for the green thread
|
||||
"""
|
||||
|
||||
# gr in greenpool.coroutines_running --> gr.gr_frame
|
||||
def __init__(self, stack):
|
||||
super(GreenThreadModel, self).__init__(
|
||||
{'stack_trace': StackTraceModel(stack)},
|
||||
text_view=text_views.GreenThreadView())
|
@ -1,44 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides OpenStack Version Info Model
|
||||
|
||||
This module defines a class representing the data
|
||||
model for OpenStack package and version information
|
||||
"""
|
||||
|
||||
from oslo_reports.models import with_default_views as mwdv
|
||||
from oslo_reports.views.text import generic as generic_text_views
|
||||
|
||||
|
||||
class PackageModel(mwdv.ModelWithDefaultViews):
|
||||
"""A Package Information Model
|
||||
|
||||
This model holds information about the current
|
||||
package. It contains vendor, product, and version
|
||||
information.
|
||||
|
||||
:param str vendor: the product vendor
|
||||
:param str product: the product name
|
||||
:param str version: the product version
|
||||
"""
|
||||
|
||||
def __init__(self, vendor, product, version):
|
||||
super(PackageModel, self).__init__(
|
||||
text_view=generic_text_views.KeyValueView()
|
||||
)
|
||||
|
||||
self['vendor'] = vendor
|
||||
self['product'] = product
|
||||
self['version'] = version
|
@ -1,81 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
from oslo_reports.models import base as base_model
|
||||
from oslo_reports.views.json import generic as jsonviews
|
||||
from oslo_reports.views.text import generic as textviews
|
||||
from oslo_reports.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:`oslo_reports.views.text.generic.KeyValueView`
|
||||
xml
|
||||
:class:`oslo_reports.views.xml.generic.KeyValueView`
|
||||
json
|
||||
:class:`oslo_reports.views.json.generic.KeyValueView`
|
||||
|
||||
.. function:: to_type()
|
||||
|
||||
('type' is one of the 'default views' defined for this model)
|
||||
Serializes this model using the default view for 'type'
|
||||
|
||||
:rtype: str
|
||||
:returns: this model serialized as 'type'
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.views = {
|
||||
'text': textviews.KeyValueView(),
|
||||
'json': jsonviews.KeyValueView(),
|
||||
'xml': xmlviews.KeyValueView()
|
||||
}
|
||||
|
||||
newargs = copy.copy(kwargs)
|
||||
for k in kwargs:
|
||||
if k.endswith('_view'):
|
||||
self.views[k[:-5]] = kwargs[k]
|
||||
del newargs[k]
|
||||
super(ModelWithDefaultViews, self).__init__(*args, **newargs)
|
||||
|
||||
def set_current_view_type(self, tp, visited=None):
|
||||
self.attached_view = self.views[tp]
|
||||
super(ModelWithDefaultViews, self).set_current_view_type(tp, visited)
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
if attrname[:3] == 'to_':
|
||||
if self.views[attrname[3:]] is not None:
|
||||
return lambda: self.views[attrname[3:]](self)
|
||||
else:
|
||||
raise NotImplementedError((
|
||||
"Model {cn.__module__}.{cn.__name__} does not have" +
|
||||
" a default view for "
|
||||
"{tp}").format(cn=type(self), tp=attrname[3:]))
|
||||
else:
|
||||
return super(ModelWithDefaultViews, self).__getattr__(attrname)
|
@ -1,71 +0,0 @@
|
||||
# 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.
|
||||
|
||||
__all__ = [
|
||||
'list_opts',
|
||||
'set_defaults',
|
||||
]
|
||||
|
||||
import copy
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from oslo_reports._i18n import _
|
||||
|
||||
|
||||
_option_group = 'oslo_reports'
|
||||
|
||||
_options = [
|
||||
cfg.StrOpt('log_dir',
|
||||
help=_('Path to a log directory where to create a file')),
|
||||
cfg.StrOpt('file_event_handler',
|
||||
help=_('The path to a file to watch for changes to trigger '
|
||||
'the reports, instead of signals. Setting this option '
|
||||
'disables the signal trigger for the reports. If '
|
||||
'application is running as a WSGI application it is '
|
||||
'recommended to use this instead of signals.')),
|
||||
cfg.IntOpt('file_event_handler_interval',
|
||||
default=1,
|
||||
help=_('How many seconds to wait between polls when '
|
||||
'file_event_handler is set'))
|
||||
]
|
||||
|
||||
|
||||
def list_opts():
|
||||
"""Return a list of oslo.config options available in the library.
|
||||
|
||||
The returned list includes all oslo.config options which may be registered
|
||||
at runtime by the library.
|
||||
Each element of the list is a tuple. The first element is the name of the
|
||||
group under which the list of elements in the second element will be
|
||||
registered. A group name of None corresponds to the [DEFAULT] group in
|
||||
config files.
|
||||
This function is also discoverable via the 'oslo_messaging' entry point
|
||||
under the 'oslo.config.opts' namespace.
|
||||
The purpose of this is to allow tools like the Oslo sample config file
|
||||
generator to discover the options exposed to users by this library.
|
||||
|
||||
:returns: a list of (group_name, opts) tuples
|
||||
"""
|
||||
|
||||
return [(_option_group, copy.deepcopy(_options))]
|
||||
|
||||
|
||||
def set_defaults(conf):
|
||||
"""Set defaults for configuration variables.
|
||||
|
||||
Overrides default options values.
|
||||
|
||||
:param conf: Configuration object, managed by the caller.
|
||||
:type conf: oslo.config.cfg.ConfigOpts
|
||||
"""
|
||||
conf.register_opts(_options, group=_option_group)
|
@ -1,189 +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.
|
||||
"""
|
||||
|
||||
import six
|
||||
|
||||
from oslo_reports.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:`oslo_reports.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(six.text_type(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() or unicode() 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:`oslo_reports.models.with_default_view.ModelWithDefaultView` # noqa
|
||||
(the entire class)
|
||||
|
||||
Class :class:`oslo_reports.models.base.ReportModel`
|
||||
:func:`oslo_reports.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:`oslo_reports.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:`oslo_reports.views.text.header.TitledView`
|
||||
|
||||
:param str heading: the title for the section
|
||||
:param generator: the method or class which generates the model
|
||||
:param index: the index at which to insert the section
|
||||
(or None to append)
|
||||
:type index: int or None
|
||||
"""
|
||||
|
||||
super(TextReport, self).add_section(header_views.TitledView(heading),
|
||||
generator,
|
||||
index)
|
@ -1,144 +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
|
||||
import six
|
||||
|
||||
from oslo_reports.models import base as base_model
|
||||
from oslo_reports import report
|
||||
|
||||
|
||||
class BasicView(object):
|
||||
def __call__(self, model):
|
||||
res = ""
|
||||
for k in sorted(model.keys()):
|
||||
res += six.text_type(k) + ": " + six.text_type(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: ' + six.text_type(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]})
|
||||
self.assertRaisesRegex(Exception,
|
||||
'Cannot stringify model: no attached view',
|
||||
six.text_type, model)
|
||||
|
||||
def test_str_returns_string_with_attached_view(self):
|
||||
model = base_model.ReportModel(data={'a': 1, 'b': 2},
|
||||
attached_view=BasicView())
|
||||
|
||||
self.assertEqual(six.text_type(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;', six.text_type(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;', six.text_type(model))
|
||||
|
||||
model['d'] = 16
|
||||
|
||||
self.assertEqual('a: 2;b: 4;c: 8;d: 16;', six.text_type(model))
|
||||
self.assertEqual({'a': 2, 'b': 4, 'c': 8}, mp.data)
|
@ -1,249 +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
|
||||
import threading
|
||||
|
||||
# needed to get greenthreads
|
||||
import fixtures
|
||||
import greenlet
|
||||
import mock
|
||||
from oslotest import base
|
||||
import six
|
||||
|
||||
import oslo_config
|
||||
from oslo_config import fixture
|
||||
from oslo_reports import guru_meditation_report as gmr
|
||||
from oslo_reports.models import with_default_views as mwdv
|
||||
from oslo_reports import opts
|
||||
|
||||
|
||||
CONF = oslo_config.cfg.CONF
|
||||
opts.set_defaults(CONF)
|
||||
|
||||
|
||||
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 GmrConfigFixture(fixture.Config):
|
||||
def setUp(self):
|
||||
super(GmrConfigFixture, self).setUp()
|
||||
|
||||
self.conf.set_override(
|
||||
'file_event_handler',
|
||||
'/tmp/file',
|
||||
group='oslo_reports')
|
||||
self.conf.set_override(
|
||||
'file_event_handler_interval',
|
||||
10,
|
||||
group='oslo_reports')
|
||||
self.conf.set_override(
|
||||
'log_dir',
|
||||
'/var/fake_log',
|
||||
group='oslo_reports')
|
||||
|
||||
|
||||
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
|
||||
|
||||
self.CONF = self.useFixture(GmrConfigFixture(CONF)).conf
|
||||
|
||||
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.SIGUSR2)
|
||||
self.assertIn('Guru Meditation', sys.stderr.getvalue())
|
||||
|
||||
@mock.patch.object(gmr.TextGuruMeditation, '_setup_file_watcher')
|
||||
def test_register_autorun_without_signals(self, mock_setup_fh):
|
||||
version = FakeVersionObj()
|
||||
gmr.TextGuruMeditation.setup_autorun(version, conf=self.CONF)
|
||||
mock_setup_fh.assert_called_once_with(
|
||||
'/tmp/file', 10, version, None, '/var/fake_log')
|
||||
|
||||
@mock.patch('os.stat')
|
||||
@mock.patch('time.sleep')
|
||||
@mock.patch.object(threading.Thread, 'start')
|
||||
def test_setup_file_watcher(self, mock_thread, mock_sleep, mock_stat):
|
||||
version = FakeVersionObj()
|
||||
mock_stat.return_value.st_mtime = 3
|
||||
|
||||
gmr.TextGuruMeditation._setup_file_watcher(
|
||||
self.CONF.oslo_reports.file_event_handler,
|
||||
self.CONF.oslo_reports.file_event_handler_interval,
|
||||
version, None, self.CONF.oslo_reports.log_dir)
|
||||
|
||||
mock_stat.assert_called_once_with('/tmp/file')
|
||||
self.assertEqual(1, mock_thread.called)
|
||||
|
||||
@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.SIGUSR2)
|
||||
with open(os.path.join(
|
||||
log_dir, "fake-service_gurumeditation_20140101120000")) as df:
|
||||
self.assertIn('Guru Meditation', df.read())
|
||||
|
||||
@mock.patch.object(gmr.TextGuruMeditation, 'run')
|
||||
def test_fail_prints_traceback(self, run_mock):
|
||||
class RunFail(Exception):
|
||||
pass
|
||||
|
||||
run_mock.side_effect = RunFail()
|
||||
gmr.TextGuruMeditation.setup_autorun(FakeVersionObj())
|
||||
self.old_stderr = sys.stderr
|
||||
sys.stderr = six.StringIO()
|
||||
|
||||
os.kill(os.getpid(), signal.SIGUSR2)
|
||||
self.assertIn('RunFail', sys.stderr.getvalue())
|
||||
|
||||
def tearDown(self):
|
||||
super(TestGuruMeditationReport, self).tearDown()
|
||||
if self.old_stderr is not None:
|
||||
sys.stderr = self.old_stderr
|
@ -1,141 +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
|
||||
import six
|
||||
|
||||
from oslo_reports.generators import conf as os_cgen
|
||||
from oslo_reports.generators import threading as os_tgen
|
||||
from oslo_reports.generators import version as os_pgen
|
||||
from oslo_reports.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(six.text_type(model))
|
||||
|
||||
def test_thread_generator_tb(self):
|
||||
class FakeModel(object):
|
||||
def __init__(self, thread_id, tb):
|
||||
self.traceback = tb
|
||||
|
||||
with mock.patch('oslo_reports.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(six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(model))
|
@ -1,426 +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
|
||||
import six
|
||||
|
||||
from oslo_reports.models import base as base_model
|
||||
from oslo_reports.models import with_default_views as mwdv
|
||||
from oslo_reports import report
|
||||
from oslo_reports.views import jinja_view as jv
|
||||
from oslo_reports.views.json import generic as json_generic
|
||||
from oslo_reports.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', six.text_type(model))
|
||||
|
||||
model.set_current_view_type('json')
|
||||
self.assertEqual('{"int": 1, "string": "value"}', six.text_type(model))
|
||||
|
||||
model.set_current_view_type('xml')
|
||||
|
||||
self.assertEqual('<model><int>1</int><string>value</string></model>',
|
||||
six.text_type(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: six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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"}',
|
||||
six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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',
|
||||
six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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, six.text_type(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',
|
||||
six.text_type(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',
|
||||
six.text_type(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',
|
||||
six.text_type(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)
|
@ -1,22 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides predefined views
|
||||
|
||||
This module provides a collection of predefined views
|
||||
for use in reports. It is separated by type (xml, json, or text).
|
||||
Each type contains a submodule called 'generic' containing
|
||||
several basic, universal views for that type. There is also
|
||||
a predefined view that utilizes Jinja.
|
||||
"""
|
@ -1,137 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides Jinja Views
|
||||
|
||||
This module provides views that utilize the Jinja templating
|
||||
system for serialization. For more information on Jinja, please
|
||||
see http://jinja.pocoo.org/ .
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
import jinja2
|
||||
|
||||
|
||||
class JinjaView(object):
|
||||
"""A Jinja View
|
||||
|
||||
This view renders the given model using the provided Jinja
|
||||
template. The template can be given in various ways.
|
||||
If the `VIEw_TEXT` property is defined, that is used as template.
|
||||
Othewise, if a `path` parameter is passed to the constructor, that
|
||||
is used to load a file containing the template. If the `path`
|
||||
parameter is None, the `text` parameter is used as the template.
|
||||
|
||||
The leading newline character and trailing newline character are stripped
|
||||
from the template (provided they exist). Baseline indentation is
|
||||
also stripped from each line. The baseline indentation is determined by
|
||||
checking the indentation of the first line, after stripping off the leading
|
||||
newline (if any).
|
||||
|
||||
:param str path: the path to the Jinja template
|
||||
:param str text: the text of the Jinja template
|
||||
"""
|
||||
|
||||
def __init__(self, path=None, text=None):
|
||||
try:
|
||||
self._text = self.VIEW_TEXT
|
||||
except AttributeError:
|
||||
if path is not None:
|
||||
with open(path, 'r') as f:
|
||||
self._text = f.read()
|
||||
elif text is not None:
|
||||
self._text = text
|
||||
else:
|
||||
self._text = ""
|
||||
|
||||
if self._text[0] == "\n":
|
||||
self._text = self._text[1:]
|
||||
|
||||
newtext = self._text.lstrip()
|
||||
amt = len(self._text) - len(newtext)
|
||||
if (amt > 0):
|
||||
base_indent = self._text[0:amt]
|
||||
lines = self._text.splitlines()
|
||||
newlines = []
|
||||
for line in lines:
|
||||
if line.startswith(base_indent):
|
||||
newlines.append(line[amt:])
|
||||
else:
|
||||
newlines.append(line)
|
||||
self._text = "\n".join(newlines)
|
||||
|
||||
if self._text[-1] == "\n":
|
||||
self._text = self._text[:-1]
|
||||
|
||||
self._regentemplate = True
|
||||
self._templatecache = None
|
||||
|
||||
def __call__(self, model):
|
||||
return self.template.render(**model)
|
||||
|
||||
def __deepcopy__(self, memodict):
|
||||
res = object.__new__(JinjaView)
|
||||
res._text = copy.deepcopy(self._text, memodict)
|
||||
|
||||
# regenerate the template on a deepcopy
|
||||
res._regentemplate = True
|
||||
res._templatecache = None
|
||||
|
||||
return res
|
||||
|
||||
@property
|
||||
def template(self):
|
||||
"""Get the Compiled Template
|
||||
|
||||
Gets the compiled template, using a cached copy if possible
|
||||
(stored in attr:`_templatecache`) or otherwise recompiling
|
||||
the template if the compiled template is not present or is
|
||||
invalid (due to attr:`_regentemplate` being set to True).
|
||||
|
||||
:returns: the compiled Jinja template
|
||||
:rtype: :class:`jinja2.Template`
|
||||
"""
|
||||
|
||||
if self._templatecache is None or self._regentemplate:
|
||||
self._templatecache = jinja2.Template(self._text)
|
||||
self._regentemplate = False
|
||||
|
||||
return self._templatecache
|
||||
|
||||
def _gettext(self):
|
||||
"""Get the Template Text
|
||||
|
||||
Gets the text of the current template
|
||||
|
||||
:returns: the text of the Jinja template
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
return self._text
|
||||
|
||||
def _settext(self, textval):
|
||||
"""Set the Template Text
|
||||
|
||||
Sets the text of the current template, marking it
|
||||
for recompilation next time the compiled template
|
||||
is retrieved via attr:`template` .
|
||||
|
||||
:param str textval: the new text of the Jinja template
|
||||
"""
|
||||
|
||||
self._text = textval
|
||||
self.regentemplate = True
|
||||
|
||||
text = property(_gettext, _settext)
|
@ -1,19 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides basic JSON views
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into JSON.
|
||||
"""
|
@ -1,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:`oslo_reports._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 oslo_reports 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, sort_keys=True))
|
||||
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
|
@ -1,19 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides basic text views
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into human-readable text.
|
||||
"""
|
@ -1,203 +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 = sorted([six.text_type(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 sorted(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 = six.text_type(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[{n}]: ^{width}}}".format(n=n, width=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[{n}]: ^{width}}}".format(n=n, width=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 = [six.text_type(raw_row[prop_name])
|
||||
for prop_name in self.column_values]
|
||||
# double format is in case we have roundoff error
|
||||
res += '{0: <72}\n'.format(self.row_fmt_str.format(cv=row))
|
||||
|
||||
return res
|
@ -1,53 +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
|
||||
"""
|
||||
|
||||
import six
|
||||
|
||||
|
||||
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 six.text_type(self.header) + "\n" + six.text_type(model)
|
||||
|
||||
|
||||
class TitledView(HeaderView):
|
||||
"""A Text View With a Title
|
||||
|
||||
This view simply serializes the model, and places
|
||||
a preformatted header containing the given title
|
||||
text on top. The title text can be up to 64 characters
|
||||
long.
|
||||
|
||||
:param str title: the title of the view
|
||||
"""
|
||||
|
||||
FORMAT_STR = ('=' * 72) + "\n===={0: ^64}====\n" + ('=' * 72)
|
||||
|
||||
def __init__(self, title):
|
||||
super(TitledView, self).__init__(self.FORMAT_STR.format(title))
|
@ -1,38 +0,0 @@
|
||||
# Copyright 2014 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides process view
|
||||
|
||||
This module provides a view for
|
||||
visualizing processes in human-readable form
|
||||
"""
|
||||
|
||||
import oslo_reports.views.jinja_view as jv
|
||||
|
||||
|
||||
class ProcessView(jv.JinjaView):
|
||||
"""A Process View
|
||||
|
||||
This view displays process models defined by
|
||||
:class:`oslo_reports.models.process.ProcessModel`
|
||||
"""
|
||||
|
||||
VIEW_TEXT = (
|
||||
"Process {{ pid }} (under {{ parent_pid }}) "
|
||||
"[ run by: {{ username }} ({{ uids.real|default('unknown uid') }}),"
|
||||
" state: {{ state }} ]\n"
|
||||
"{% for child in children %}"
|
||||
" {{ child }}"
|
||||
"{% endfor %}"
|
||||
)
|
@ -1,80 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides thread and stack-trace views
|
||||
|
||||
This module provides a collection of views for
|
||||
visualizing threads, green threads, and stack traces
|
||||
in human-readable form.
|
||||
"""
|
||||
|
||||
from oslo_reports.views import jinja_view as jv
|
||||
|
||||
|
||||
class StackTraceView(jv.JinjaView):
|
||||
"""A Stack Trace View
|
||||
|
||||
This view displays stack trace models defined by
|
||||
:class:`oslo_reports.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:`oslo_reports.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:`oslo_reports.models.threading.ThreadModel` # noqa
|
||||
"""
|
||||
|
||||
FORMAT_STR = "------{thread_str: ^60}------" + "\n" + "{stack_trace}"
|
||||
|
||||
def __call__(self, model):
|
||||
return self.FORMAT_STR.format(
|
||||
thread_str=" Thread #{0} ".format(model.thread_id),
|
||||
stack_trace=model.stack_trace
|
||||
)
|
@ -1,19 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides basic XML views
|
||||
|
||||
This module provides several basic views which serialize
|
||||
models into XML.
|
||||
"""
|
@ -1,87 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Provides generic XML views
|
||||
|
||||
This modules defines several basic views for serializing
|
||||
data to XML. Submodels that have already been serialized
|
||||
as XML may have their string values marked with `__is_xml__
|
||||
= True` using :class:`oslo_reports._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 oslo_reports 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 = six.text_type(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
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- Introduce reno for deployer release notes.
|
@ -1,281 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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.
|
||||
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'openstackdocstheme',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack/oslo.reports'
|
||||
bug_project = 'oslo.reports'
|
||||
bug_tag = ''
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'oslo.reports Release Notes'
|
||||
copyright = u'2016, oslo.reports Developers'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
import pkg_resources
|
||||
release = pkg_resources.get_distribution('oslo.reports').version
|
||||
# The short X.Y version.
|
||||
version = release
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
# keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
# html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
# html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'oslo.reportsReleaseNotesDoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'oslo.reportsReleaseNotes.tex',
|
||||
u'oslo.reports Release Notes Documentation',
|
||||
u'oslo.reports Developers', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'oslo.reportsReleaseNotes',
|
||||
u'oslo.reports Release Notes Documentation',
|
||||
[u'oslo.reports Developers'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'oslo.reportsReleaseNotes',
|
||||
u'oslo.reports Release Notes Documentation',
|
||||
u'oslo.reports Developers', 'oslo.reportsReleaseNotes',
|
||||
'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
# -- Options for Internationalization output ------------------------------
|
||||
locale_dirs = ['locale/']
|
@ -1,9 +0,0 @@
|
||||
============================
|
||||
oslo.reports Release Notes
|
||||
============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
unreleased
|
||||
ocata
|
@ -1,6 +0,0 @@
|
||||
===================================
|
||||
Ocata Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: origin/stable/ocata
|
@ -1,5 +0,0 @@
|
||||
==========================
|
||||
Unreleased Release Notes
|
||||
==========================
|
||||
|
||||
.. release-notes::
|
@ -1,11 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
Jinja2!=2.9.0,!=2.9.1,!=2.9.2,!=2.9.3,!=2.9.4,>=2.8 # BSD License (3 clause)
|
||||
oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
|
||||
psutil>=3.2.2 # BSD
|
||||
six>=1.9.0 # MIT
|
||||
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
|
||||
oslo.utils>=3.20.0 # Apache-2.0
|
61
setup.cfg
61
setup.cfg
@ -1,61 +0,0 @@
|
||||
[metadata]
|
||||
name = oslo.reports
|
||||
summary = oslo.reports library
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = https://docs.openstack.org/oslo.reports/latest
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.5
|
||||
|
||||
[files]
|
||||
packages =
|
||||
oslo_reports
|
||||
|
||||
[pbr]
|
||||
autodoc_index_modules = true
|
||||
api_doc_dir = reference/api
|
||||
autodoc_exclude_modules =
|
||||
oslo_reports._i18n
|
||||
oslo_reports._utils
|
||||
oslo_reports.tests.*
|
||||
|
||||
[entry_points]
|
||||
oslo.config.opts =
|
||||
oslo.reports = oslo_reports.opts:list_opts
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
warning-is-error = 1
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[compile_catalog]
|
||||
directory = oslo_reports/locale
|
||||
domain = oslo_reports
|
||||
|
||||
[update_catalog]
|
||||
domain = oslo_reports
|
||||
output_dir = oslo_reports/locale
|
||||
input_file = oslo_reports/locale/oslo_reports.pot
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = oslo_reports/locale/oslo_reports.pot
|
||||
|
||||
[wheel]
|
||||
universal = true
|
29
setup.py
29
setup.py
@ -1,29 +0,0 @@
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
@ -1,18 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
|
||||
# These are needed for docs generation
|
||||
openstackdocstheme>=1.11.0 # Apache-2.0
|
||||
sphinx>=1.6.2 # BSD
|
||||
|
||||
# for testing optional parts
|
||||
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
|
||||
eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT
|
||||
greenlet>=0.3.2 # MIT
|
||||
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
reno!=2.3.1,>=1.8.0 # Apache-2.0
|
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Client constraint file contains this client version pin that is in conflict
|
||||
# with installing the client from source. We should remove the version pin in
|
||||
# the constraints file before applying it for from-source installation.
|
||||
|
||||
CONSTRAINTS_FILE="$1"
|
||||
shift 1
|
||||
|
||||
set -e
|
||||
|
||||
# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
|
||||
# published to logs.openstack.org for easy debugging.
|
||||
localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
|
||||
|
||||
if [[ "$CONSTRAINTS_FILE" != http* ]]; then
|
||||
CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
|
||||
fi
|
||||
# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
|
||||
curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
|
||||
|
||||
pip install -c"$localfile" openstack-requirements
|
||||
|
||||
# This is the main purpose of the script: Allow local installation of
|
||||
# the current repo. It is listed in constraints file and thus any
|
||||
# install will be constrained and we need to unconstrain it.
|
||||
edit-constraints "$localfile" -- "$CLIENT_NAME"
|
||||
|
||||
pip install -c"$localfile" -U "$@"
|
||||
exit $?
|
45
tox.ini
45
tox.ini
@ -1,45 +0,0 @@
|
||||
[tox]
|
||||
minversion = 2.0
|
||||
envlist = py35,py27,pypy,pep8
|
||||
|
||||
[testenv]
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
BRANCH_NAME=master
|
||||
CLIENT_NAME=oslo.reports
|
||||
install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||
commands = python setup.py testr --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py test --coverage --coverage-package-name=oslo_reports --testr-args='{posargs}'
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
show-source = True
|
||||
ignore = E123,E125
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
||||
|
||||
[hacking]
|
||||
import_exceptions =
|
||||
|
||||
[testenv:pip-missing-reqs]
|
||||
# do not install test-requirements as that will pollute the virtualenv for
|
||||
# determining missing packages
|
||||
# this also means that pip-missing-reqs must be installed separately, outside
|
||||
# of the requirements.txt files
|
||||
deps = pip_missing_reqs
|
||||
commands = pip-missing-reqs -d --ignore-module=oslo_reports* --ignore-module=pkg_resources --ignore-file=oslo_reports/test.py --ignore-file=oslo_reports/tests/* oslo_reports
|
||||
|
||||
[testenv:releasenotes]
|
||||
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
Loading…
Reference in New Issue
Block a user