45b1c02d11
Currently the setup_autorun() method expects the user-defined signal, SIGUSR1, to generate Guru Meditation Report upon killing a Nova Compute process, while supplying the said signal. However, testing in a DevStack environment, manually attempting to kill a Nova Compute process with SIGUSR1 [kill -s USR1 `pgrep nova-compute`] does not result in the process being terminated, and no error report is generated. It turns out[*] that SIGUSR1 is used by Apache 'mod_wsgi'. Using, the other user-defined signal, SIGUSR2 resolves this issue (i.e. 'nova-compute' process is terminated, and the Guru Meditation Report is generated successfully). So, use the signal USR2 instead of USR1. Also, update the corresponding tests. DocImpact: With this patch merged, to generate a successful Guru Meditation Report, supply the signal USR2 while killing a Nova Compute process [`kill -s USR2 `pgrep nova-compute`]. [*] https://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIRestrictSignal Change-Id: I9d3b6079ba2cca41fe4723723a6f80b2c3c0b9c0
239 lines
8.0 KiB
Python
239 lines
8.0 KiB
Python
# 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 os
|
|
import signal
|
|
import sys
|
|
|
|
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
|
|
|
|
|
|
class GuruMeditation(object):
|
|
"""A Guru Meditation Report Mixin/Base Class
|
|
|
|
This class is a base class for Guru Meditation Reports.
|
|
It provides facilities for registering sections and
|
|
setting up functionality to auto-run the report on
|
|
a certain signal.
|
|
|
|
This class should always be used in conjunction with
|
|
a Report class via multiple inheritance. It should
|
|
always come first in the class list to ensure the
|
|
MRO is correct.
|
|
"""
|
|
|
|
timestamp_fmt = "%Y%m%d%H%M%S"
|
|
|
|
def __init__(self, version_obj, sig_handler_tb=None, *args, **kwargs):
|
|
self.version_obj = version_obj
|
|
self.traceback = sig_handler_tb
|
|
|
|
super(GuruMeditation, self).__init__(*args, **kwargs)
|
|
self.start_section_index = len(self.sections)
|
|
|
|
@classmethod
|
|
def register_section(cls, section_title, generator):
|
|
"""Register a New Section
|
|
|
|
This method registers a persistent section for the current
|
|
class.
|
|
|
|
:param str section_title: the title of the section
|
|
:param generator: the generator for the section
|
|
"""
|
|
|
|
try:
|
|
cls.persistent_sections.append([section_title, generator])
|
|
except AttributeError:
|
|
cls.persistent_sections = [[section_title, generator]]
|
|
|
|
@classmethod
|
|
def setup_autorun(cls, version, service_name=None,
|
|
log_dir=None, signum=None, 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.
|
|
|
|
: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 not signum and hasattr(signal, 'SIGUSR2'):
|
|
# SIGUSR2 is not supported on all platforms
|
|
signum = signal.SIGUSR2
|
|
|
|
if signum:
|
|
if log_dir is None and conf is not None:
|
|
log_dir = conf.oslo_reports.log_dir
|
|
signal.signal(signum,
|
|
lambda sn, tb: cls.handle_signal(
|
|
version, service_name, log_dir, tb))
|
|
|
|
@classmethod
|
|
def handle_signal(cls, version, service_name, log_dir, traceback):
|
|
"""The Signal Handler
|
|
|
|
This method (indirectly) handles receiving a registered signal and
|
|
dumping the Guru Meditation Report to stderr or a file in a given dir.
|
|
If service name and log dir are not None, the report will be dumped to
|
|
a file named $service_name_gurumeditation_$current_time in the log_dir
|
|
directory.
|
|
This method is designed to be curried into a proper signal handler by
|
|
currying out the version
|
|
parameter.
|
|
|
|
:param version: the version object for the current product
|
|
:param service_name: this program name used to construct logfile name
|
|
:param logdir: path to a log directory where to create a file
|
|
:param traceback: the traceback provided to the signal handler
|
|
"""
|
|
|
|
try:
|
|
res = cls(version, traceback).run()
|
|
except Exception:
|
|
print("Unable to run Guru Meditation Report!",
|
|
file=sys.stderr)
|
|
else:
|
|
if log_dir:
|
|
service_name = service_name or os.path.basename(
|
|
inspect.stack()[-1][1])
|
|
filename = "%s_gurumeditation_%s" % (
|
|
service_name, timeutils.utcnow().strftime(
|
|
cls.timestamp_fmt))
|
|
filepath = os.path.join(log_dir, filename)
|
|
try:
|
|
with open(filepath, "w") as dumpfile:
|
|
dumpfile.write(res)
|
|
except Exception:
|
|
print("Unable to dump Guru Meditation Report to file %s" %
|
|
(filepath,), file=sys.stderr)
|
|
else:
|
|
print(res, file=sys.stderr)
|
|
|
|
def _readd_sections(self):
|
|
del self.sections[self.start_section_index:]
|
|
|
|
self.add_section('Package',
|
|
pgen.PackageReportGenerator(self.version_obj))
|
|
|
|
self.add_section('Threads',
|
|
tgen.ThreadReportGenerator(self.traceback))
|
|
|
|
self.add_section('Green Threads',
|
|
tgen.GreenThreadReportGenerator())
|
|
|
|
self.add_section('Processes',
|
|
prgen.ProcessReportGenerator())
|
|
|
|
self.add_section('Configuration',
|
|
cgen.ConfigReportGenerator())
|
|
|
|
try:
|
|
for section_title, generator in self.persistent_sections:
|
|
self.add_section(section_title, generator)
|
|
except AttributeError:
|
|
pass
|
|
|
|
def run(self):
|
|
self._readd_sections()
|
|
return super(GuruMeditation, self).run()
|
|
|
|
|
|
# GuruMeditation must come first to get the correct MRO
|
|
class TextGuruMeditation(GuruMeditation, report.TextReport):
|
|
"""A Text Guru Meditation Report
|
|
|
|
This report is the basic human-readable Guru Meditation Report
|
|
|
|
It contains the following sections by default
|
|
(in addition to any registered persistent sections):
|
|
|
|
- Package Information
|
|
|
|
- Threads List
|
|
|
|
- Green Threads List
|
|
|
|
- Process List
|
|
|
|
- Configuration Options
|
|
|
|
:param version_obj: the version object for the current product
|
|
:param traceback: an (optional) frame object providing the actual
|
|
traceback for the current thread
|
|
"""
|
|
|
|
def __init__(self, version_obj, traceback=None):
|
|
super(TextGuruMeditation, self).__init__(version_obj, traceback,
|
|
'Guru Meditation')
|