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:
Tony Breeds 2017-09-12 16:07:02 -06:00
parent 5e4ea9af13
commit ea17875950
69 changed files with 14 additions and 4958 deletions

View File

@ -1,8 +0,0 @@
[run]
branch = True
source = oslo_reports
omit = oslo_reports/tests/*
[report]
ignore_errors = True
precision = 2

58
.gitignore vendored
View File

@ -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

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/oslo.reports.git

View File

@ -1,3 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>

View File

@ -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

View File

@ -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

View File

@ -1,4 +0,0 @@
oslo.reports Style Commandments
======================================================
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/

176
LICENSE
View File

@ -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
View 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.

View File

@ -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

View File

@ -1,2 +0,0 @@
[python: **.py]

View File

@ -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}

View File

@ -1,5 +0,0 @@
==============
Contributing
==============
.. include:: ../../../CONTRIBUTING.rst

View File

@ -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`

View File

@ -1,7 +0,0 @@
============
Installation
============
At the command line::
$ pip install oslo.reports

View File

@ -1,18 +0,0 @@
.. _using:
=========
Reference
=========
.. toctree::
:maxdepth: 2
opts
API
===
.. toctree::
:maxdepth: 1
api/autoindex

View File

@ -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

View File

@ -1 +0,0 @@
.. include:: ../../../ChangeLog

View File

@ -1,9 +0,0 @@
===================
Using oslo.reports
===================
.. toctree::
:maxdepth: 2
usage
history

View File

@ -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

View File

@ -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:

View File

@ -1,25 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides a way to generate serializable reports
This package/module provides mechanisms for defining reports
which may then be serialized into various data types. Each
report ( :class:`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.
"""

View File

@ -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

View File

@ -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

View File

@ -1,21 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides Data Model Generators
This module defines classes for generating data models
( :class:`oslo_reports.models.base.ReportModel` ).
A generator is any object which is callable with no parameters
and returns a data model.
"""

View File

@ -1,44 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides OpenStack config generators
This module defines a class for configuration
generators for generating the model in
:mod:`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)

View File

@ -1,38 +0,0 @@
# Copyright 2014 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides process-data generators
This modules defines a class for generating
process data by way of the psutil package.
"""
import os
import psutil
from 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()))

View File

@ -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())

View File

@ -1,60 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides OpenStack version generators
This module defines a class for OpenStack
version and package information
generators for generating the model in
:mod:`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)

View File

@ -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')

View File

@ -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"

View File

@ -1,20 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides data models
This module provides both the base data model,
as well as several predefined specific data models
to be used in reports.
"""

View File

@ -1,162 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides the base report model
This module defines a class representing the basic report
data model from which all data models should inherit (or
at least implement similar functionality). Data models
store unserialized data generated by generators during
the report serialization process.
"""
import collections as col
import copy
import six
class ReportModel(col.MutableMapping):
"""A Report Data Model
A report data model contains data generated by some
generator method or class. Data may be read or written
using dictionary-style access, and may be read (but not
written) using object-member-style access. Additionally,
a data model may have an associated view. This view is
used to serialize the model when str() is called on the
model. An appropriate object for a view is callable with
a single parameter: the model to be serialized.
If present, the object passed in as data will be transformed
into a standard python dict. For mappings, this is fairly
straightforward. For sequences, the indices become keys
and the items become values.
:param data: a sequence or mapping of data to associate with the model
:param attached_view: a view object to attach to this model
"""
def __init__(self, data=None, attached_view=None):
self.attached_view = attached_view
if data is not None:
if isinstance(data, col.Mapping):
self.data = dict(data)
elif isinstance(data, col.Sequence):
# convert a list [a, b, c] to a dict {0: a, 1: b, 2: c}
self.data = dict(enumerate(data))
else:
raise TypeError('Data for the model must be a sequence '
'or mapping.')
else:
self.data = {}
def __str__(self):
self_cpy = copy.deepcopy(self)
for key in self_cpy:
if getattr(self_cpy[key], 'attached_view', None) is not None:
self_cpy[key] = 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)

View File

@ -1,66 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides OpenStack Configuration Model
This module defines a class representing the data
model for :mod:`oslo_config` configuration options
"""
from 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)

View File

@ -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]

View File

@ -1,100 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides threading and stack-trace models
This module defines classes representing thread, green
thread, and stack trace data models
"""
import traceback
from 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())

View File

@ -1,44 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides OpenStack Version Info Model
This module defines a class representing the data
model for OpenStack package and version information
"""
from 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

View File

@ -1,81 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from 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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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.
"""

View File

@ -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)

View File

@ -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.
"""

View File

@ -1,66 +0,0 @@
# Copyright 2013 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides 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

View File

@ -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.
"""

View File

@ -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

View File

@ -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))

View File

@ -1,38 +0,0 @@
# Copyright 2014 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Provides process 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 %}"
)

View File

@ -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
)

View File

@ -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.
"""

View File

@ -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

View File

@ -1,3 +0,0 @@
---
other:
- Introduce reno for deployer release notes.

View File

@ -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/']

View File

@ -1,9 +0,0 @@
============================
oslo.reports Release Notes
============================
.. toctree::
:maxdepth: 1
unreleased
ocata

View File

@ -1,6 +0,0 @@
===================================
Ocata Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/ocata

View File

@ -1,5 +0,0 @@
==========================
Unreleased Release Notes
==========================
.. release-notes::

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
View File

@ -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