Add ruff
Change-Id: I71f003998461723241edd7fc16f8a7970ebdc0d0 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
+7
-11
@@ -12,18 +12,14 @@ repos:
|
||||
- id: debug-statements
|
||||
- id: check-yaml
|
||||
files: .*\.(yaml|yml)$
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.5
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: ['--fix', '--unsafe-fixes']
|
||||
- id: ruff-format
|
||||
- repo: https://opendev.org/openstack/hacking
|
||||
rev: 7.0.0
|
||||
rev: 8.0.0
|
||||
hooks:
|
||||
- id: hacking
|
||||
additional_dependencies: []
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: 1.8.6
|
||||
hooks:
|
||||
- id: bandit
|
||||
args: ['-c', 'pyproject.toml']
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.20.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py3-only]
|
||||
|
||||
+10
-9
@@ -31,9 +31,7 @@ import sys
|
||||
# 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.extend([
|
||||
os.path.abspath('../..'),
|
||||
])
|
||||
sys.path.extend([os.path.abspath('../..')])
|
||||
|
||||
|
||||
# -- General configuration ---------------------
|
||||
@@ -98,7 +96,7 @@ html_theme = 'openstackdocs'
|
||||
html_static_path = []
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
htmlhelp_basename = f'{project}doc'
|
||||
|
||||
# -- Options for LaTeX output --------------
|
||||
|
||||
@@ -108,10 +106,13 @@ latex_elements = {}
|
||||
# (source start file, target name, title, author,
|
||||
# documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
'%s Documentation' % project,
|
||||
'OpenStack Foundation', 'manual'),
|
||||
(
|
||||
'index',
|
||||
f'{project}.tex',
|
||||
f'{project} Documentation',
|
||||
'OpenStack Foundation',
|
||||
'manual',
|
||||
),
|
||||
]
|
||||
|
||||
# -- Options for Texinfo output -----------
|
||||
@@ -127,7 +128,7 @@ texinfo_documents = [
|
||||
'OSprofiler Team',
|
||||
'OSprofiler',
|
||||
'One line description of project.',
|
||||
'Miscellaneous'
|
||||
'Miscellaneous',
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@ def split(text, strip=True):
|
||||
if isinstance(text, (tuple, list)):
|
||||
return text
|
||||
if not isinstance(text, str):
|
||||
raise TypeError(
|
||||
"Unknown how to split '{}': {}".format(text, type(text)))
|
||||
raise TypeError(f"Unknown how to split '{text}': {type(text)}")
|
||||
if strip:
|
||||
return [t.strip() for t in text.split(",") if t.strip()]
|
||||
else:
|
||||
@@ -104,13 +103,14 @@ def signed_unpack(data, hmac_data, hmac_keys):
|
||||
for hmac_key in hmac_keys:
|
||||
try:
|
||||
user_hmac_data = generate_hmac(data, hmac_key)
|
||||
except Exception: # nosec
|
||||
except Exception: # noqa: S110
|
||||
pass
|
||||
else:
|
||||
if hmac.compare_digest(hmac_data, user_hmac_data):
|
||||
try:
|
||||
contents = json.loads(
|
||||
binary_decode(base64.urlsafe_b64decode(data)))
|
||||
binary_decode(base64.urlsafe_b64decode(data))
|
||||
)
|
||||
contents["hmac_key"] = hmac_key
|
||||
return contents
|
||||
except Exception:
|
||||
@@ -124,7 +124,7 @@ def itersubclasses(cls, _seen=None):
|
||||
_seen = _seen or set()
|
||||
try:
|
||||
subs = cls.__subclasses__()
|
||||
except TypeError: # fails only when cls is type
|
||||
except TypeError: # fails only when cls is type
|
||||
subs = cls.__subclasses__(cls)
|
||||
for sub in subs:
|
||||
if sub not in _seen:
|
||||
@@ -146,7 +146,7 @@ def import_modules_from_package(package):
|
||||
if filename.startswith("__") or not filename.endswith(".py"):
|
||||
continue
|
||||
new_package = ".".join(root.split(os.sep)).split("....")[1]
|
||||
module_name = "{}.{}".format(new_package, filename[:-3])
|
||||
module_name = f"{new_package}.{filename[:-3]}"
|
||||
__import__(module_name)
|
||||
|
||||
|
||||
|
||||
@@ -37,9 +37,11 @@ def arg(*args, **kwargs):
|
||||
... def entity_create(args):
|
||||
... pass
|
||||
"""
|
||||
|
||||
def _decorator(func):
|
||||
add_arg(func, *args, **kwargs)
|
||||
return func
|
||||
|
||||
return _decorator
|
||||
|
||||
|
||||
|
||||
+100
-46
@@ -33,26 +33,56 @@ class TraceCommands(BaseCommand):
|
||||
group_name = "trace"
|
||||
|
||||
@cliutils.arg("trace", help="File with trace or trace id")
|
||||
@cliutils.arg("--connection-string", dest="conn_str",
|
||||
default=(cliutils.env("OSPROFILER_CONNECTION_STRING")),
|
||||
help="Storage driver's connection string. Defaults to "
|
||||
"env[OSPROFILER_CONNECTION_STRING] if set")
|
||||
@cliutils.arg("--transport-url", dest="transport_url",
|
||||
help="Oslo.messaging transport URL (for messaging:// driver "
|
||||
"only), e.g. rabbit://user:password@host:5672/")
|
||||
@cliutils.arg("--idle-timeout", dest="idle_timeout", type=int, default=1,
|
||||
help="How long to wait for the trace to finish, in seconds "
|
||||
"(for messaging:// driver only)")
|
||||
@cliutils.arg("--json", dest="use_json", action="store_true",
|
||||
help="show trace in JSON")
|
||||
@cliutils.arg("--html", dest="use_html", action="store_true",
|
||||
help="show trace in HTML")
|
||||
@cliutils.arg("--local-libs", dest="local_libs", action="store_true",
|
||||
help="use local static files of html in /libs/")
|
||||
@cliutils.arg("--dot", dest="use_dot", action="store_true",
|
||||
help="show trace in DOT language")
|
||||
@cliutils.arg("--render-dot", dest="render_dot_filename",
|
||||
help="filename for rendering the dot graph in pdf format")
|
||||
@cliutils.arg(
|
||||
"--connection-string",
|
||||
dest="conn_str",
|
||||
default=(cliutils.env("OSPROFILER_CONNECTION_STRING")),
|
||||
help="Storage driver's connection string. Defaults to "
|
||||
"env[OSPROFILER_CONNECTION_STRING] if set",
|
||||
)
|
||||
@cliutils.arg(
|
||||
"--transport-url",
|
||||
dest="transport_url",
|
||||
help="Oslo.messaging transport URL (for messaging:// driver "
|
||||
"only), e.g. rabbit://user:password@host:5672/",
|
||||
)
|
||||
@cliutils.arg(
|
||||
"--idle-timeout",
|
||||
dest="idle_timeout",
|
||||
type=int,
|
||||
default=1,
|
||||
help="How long to wait for the trace to finish, in seconds "
|
||||
"(for messaging:// driver only)",
|
||||
)
|
||||
@cliutils.arg(
|
||||
"--json",
|
||||
dest="use_json",
|
||||
action="store_true",
|
||||
help="show trace in JSON",
|
||||
)
|
||||
@cliutils.arg(
|
||||
"--html",
|
||||
dest="use_html",
|
||||
action="store_true",
|
||||
help="show trace in HTML",
|
||||
)
|
||||
@cliutils.arg(
|
||||
"--local-libs",
|
||||
dest="local_libs",
|
||||
action="store_true",
|
||||
help="use local static files of html in /libs/",
|
||||
)
|
||||
@cliutils.arg(
|
||||
"--dot",
|
||||
dest="use_dot",
|
||||
action="store_true",
|
||||
help="show trace in DOT language",
|
||||
)
|
||||
@cliutils.arg(
|
||||
"--render-dot",
|
||||
dest="render_dot_filename",
|
||||
help="filename for rendering the dot graph in pdf format",
|
||||
)
|
||||
@cliutils.arg("--out", dest="file_name", help="save output in file")
|
||||
def show(self, args):
|
||||
"""Display trace results in HTML, JSON or DOT format."""
|
||||
@@ -61,7 +91,8 @@ class TraceCommands(BaseCommand):
|
||||
raise exc.CommandError(
|
||||
"You must provide connection string via"
|
||||
" either --connection-string or "
|
||||
"via env[OSPROFILER_CONNECTION_STRING]")
|
||||
"via env[OSPROFILER_CONNECTION_STRING]"
|
||||
)
|
||||
|
||||
trace = None
|
||||
|
||||
@@ -76,8 +107,10 @@ class TraceCommands(BaseCommand):
|
||||
trace = engine.get_report(args.trace)
|
||||
|
||||
if not trace or not trace.get("children"):
|
||||
msg = ("Trace with UUID %s not found. Please check the HMAC key "
|
||||
"used in the command." % args.trace)
|
||||
msg = (
|
||||
f"Trace with UUID {args.trace} not found. Please check the "
|
||||
f"HMAC key used in the command."
|
||||
)
|
||||
raise exc.CommandError(msg)
|
||||
|
||||
# Since datetime.datetime is not JSON serializable by default,
|
||||
@@ -89,16 +122,25 @@ class TraceCommands(BaseCommand):
|
||||
return obj
|
||||
|
||||
if args.use_json:
|
||||
output = json.dumps(trace, default=datetime_json_serialize,
|
||||
separators=(",", ": "),
|
||||
indent=2)
|
||||
output = json.dumps(
|
||||
trace,
|
||||
default=datetime_json_serialize,
|
||||
separators=(",", ": "),
|
||||
indent=2,
|
||||
)
|
||||
elif args.use_html:
|
||||
with open(os.path.join(os.path.dirname(__file__),
|
||||
"template.html")) as html_template:
|
||||
with open(
|
||||
os.path.join(os.path.dirname(__file__), "template.html")
|
||||
) as html_template:
|
||||
output = html_template.read().replace(
|
||||
"$DATA", json.dumps(trace, indent=4,
|
||||
separators=(",", ": "),
|
||||
default=datetime_json_serialize))
|
||||
"$DATA",
|
||||
json.dumps(
|
||||
trace,
|
||||
indent=4,
|
||||
separators=(",", ": "),
|
||||
default=datetime_json_serialize,
|
||||
),
|
||||
)
|
||||
if args.local_libs:
|
||||
output = output.replace("$LOCAL", "true")
|
||||
else:
|
||||
@@ -109,8 +151,10 @@ class TraceCommands(BaseCommand):
|
||||
if args.render_dot_filename:
|
||||
dot_graph.render(args.render_dot_filename, cleanup=True)
|
||||
else:
|
||||
raise exc.CommandError("You should choose one of the following "
|
||||
"output formats: json, html or dot.")
|
||||
raise exc.CommandError(
|
||||
"You should choose one of the following "
|
||||
"output formats: json, html or dot."
|
||||
)
|
||||
|
||||
if args.file_name:
|
||||
with open(args.file_name, "w+") as output_file:
|
||||
@@ -123,7 +167,8 @@ class TraceCommands(BaseCommand):
|
||||
import graphviz
|
||||
except ImportError:
|
||||
raise exc.CommandError(
|
||||
"graphviz library is required to use this option.")
|
||||
"graphviz library is required to use this option."
|
||||
)
|
||||
|
||||
dot = graphviz.Digraph(format="pdf")
|
||||
next_id = [0]
|
||||
@@ -132,14 +177,15 @@ class TraceCommands(BaseCommand):
|
||||
time_taken = info["finished"] - info["started"]
|
||||
service = info["service"] + ":" if "service" in info else ""
|
||||
name = info["name"]
|
||||
label = "%s%s - %d ms" % (service, name, time_taken)
|
||||
label = f"{service}{name} - {time_taken} ms"
|
||||
|
||||
if name == "wsgi":
|
||||
req = info["meta.raw_payload.wsgi-start"]["info"]["request"]
|
||||
label = "{}\\n{} {}..".format(label, req["method"],
|
||||
req["path"][:30])
|
||||
label = "{}\\n{} {}..".format(
|
||||
label, req["method"], req["path"][:30]
|
||||
)
|
||||
elif name == "rpc" or name == "driver":
|
||||
raw = info["meta.raw_payload.%s-start" % name]
|
||||
raw = info[f"meta.raw_payload.{name}-start"]
|
||||
fn_name = raw["info"]["function"]["name"]
|
||||
label = "{}\\n{}".format(label, fn_name.split(".")[-1])
|
||||
|
||||
@@ -158,20 +204,28 @@ class TraceCommands(BaseCommand):
|
||||
_create_sub_graph(trace)
|
||||
return dot
|
||||
|
||||
@cliutils.arg("--connection-string", dest="conn_str",
|
||||
default=cliutils.env("OSPROFILER_CONNECTION_STRING"),
|
||||
help="Storage driver's connection string. Defaults to "
|
||||
"env[OSPROFILER_CONNECTION_STRING] if set")
|
||||
@cliutils.arg("--error-trace", dest="error_trace",
|
||||
type=bool, default=False,
|
||||
help="List all traces that contain error.")
|
||||
@cliutils.arg(
|
||||
"--connection-string",
|
||||
dest="conn_str",
|
||||
default=cliutils.env("OSPROFILER_CONNECTION_STRING"),
|
||||
help="Storage driver's connection string. Defaults to "
|
||||
"env[OSPROFILER_CONNECTION_STRING] if set",
|
||||
)
|
||||
@cliutils.arg(
|
||||
"--error-trace",
|
||||
dest="error_trace",
|
||||
type=bool,
|
||||
default=False,
|
||||
help="List all traces that contain error.",
|
||||
)
|
||||
def list(self, args):
|
||||
"""List all traces"""
|
||||
if not args.conn_str:
|
||||
raise exc.CommandError(
|
||||
"You must provide connection string via"
|
||||
" either --connection-string or "
|
||||
"via env[OSPROFILER_CONNECTION_STRING]")
|
||||
"via env[OSPROFILER_CONNECTION_STRING]"
|
||||
)
|
||||
try:
|
||||
engine = base.get_driver(args.conn_str, **args.__dict__)
|
||||
except Exception as e:
|
||||
|
||||
+17
-13
@@ -31,7 +31,6 @@ from osprofiler import opts
|
||||
|
||||
|
||||
class OSProfilerShell:
|
||||
|
||||
def __init__(self, argv):
|
||||
args = self._get_base_parser().parse_args(argv)
|
||||
opts.set_defaults(cfg.CONF)
|
||||
@@ -40,14 +39,12 @@ class OSProfilerShell:
|
||||
|
||||
def _get_base_parser(self):
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="osprofiler",
|
||||
description=__doc__.strip(),
|
||||
add_help=True
|
||||
prog="osprofiler", description=__doc__.strip(), add_help=True
|
||||
)
|
||||
|
||||
parser.add_argument("-v", "--version",
|
||||
action="version",
|
||||
version=osprofiler.__version__)
|
||||
parser.add_argument(
|
||||
"-v", "--version", action="version", version=osprofiler.__version__
|
||||
)
|
||||
|
||||
self._append_subcommands(parser)
|
||||
|
||||
@@ -60,22 +57,29 @@ class OSProfilerShell:
|
||||
subcommand_parser = group_parser.add_subparsers()
|
||||
|
||||
for name, callback in inspect.getmembers(
|
||||
group_cls(), predicate=inspect.ismethod):
|
||||
group_cls(), predicate=inspect.ismethod
|
||||
):
|
||||
command = name.replace("_", "-")
|
||||
desc = callback.__doc__ or ""
|
||||
help_message = desc.strip().split("\n")[0]
|
||||
arguments = getattr(callback, "arguments", [])
|
||||
|
||||
command_parser = subcommand_parser.add_parser(
|
||||
command, help=help_message, description=desc)
|
||||
for (args, kwargs) in arguments:
|
||||
command, help=help_message, description=desc
|
||||
)
|
||||
for args, kwargs in arguments:
|
||||
command_parser.add_argument(*args, **kwargs)
|
||||
command_parser.set_defaults(func=callback)
|
||||
|
||||
def _no_project_and_domain_set(self, args):
|
||||
if not (args.os_project_id or (args.os_project_name
|
||||
and (args.os_user_domain_name or args.os_user_domain_id))
|
||||
or (args.os_tenant_id or args.os_tenant_name)):
|
||||
if not (
|
||||
args.os_project_id
|
||||
or (
|
||||
args.os_project_name
|
||||
and (args.os_user_domain_name or args.os_user_domain_id)
|
||||
)
|
||||
or (args.os_tenant_id or args.os_tenant_name)
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from osprofiler.drivers import base # noqa
|
||||
from osprofiler.drivers import elasticsearch_driver # noqa
|
||||
from osprofiler.drivers import jaeger # noqa
|
||||
from osprofiler.drivers import otlp # noqa
|
||||
from osprofiler.drivers import otlp # noqa
|
||||
from osprofiler.drivers import loginsight # noqa
|
||||
from osprofiler.drivers import messaging # noqa
|
||||
from osprofiler.drivers import mongodb # noqa
|
||||
|
||||
+70
-48
@@ -31,22 +31,30 @@ def get_driver(connection_string, *args, **kwargs):
|
||||
connection_string += "://"
|
||||
|
||||
parsed_connection = urlparse.urlparse(connection_string)
|
||||
LOG.debug("String %s looks like a connection string, trying it.",
|
||||
connection_string)
|
||||
LOG.debug(
|
||||
"String %s looks like a connection string, trying it.",
|
||||
connection_string,
|
||||
)
|
||||
|
||||
backend = parsed_connection.scheme
|
||||
# NOTE(toabctl): To be able to use the connection_string for as sqlalchemy
|
||||
# connection string, transform the backend to the correct driver
|
||||
# See https://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
|
||||
if backend in ["mysql", "mysql+pymysql", "mysql+mysqldb",
|
||||
"postgresql", "postgresql+psycopg2"]:
|
||||
if backend in [
|
||||
"mysql",
|
||||
"mysql+pymysql",
|
||||
"mysql+mysqldb",
|
||||
"postgresql",
|
||||
"postgresql+psycopg2",
|
||||
]:
|
||||
backend = "sqlalchemy"
|
||||
for driver in _utils.itersubclasses(Driver):
|
||||
if backend == driver.get_name():
|
||||
return driver(connection_string, *args, **kwargs)
|
||||
|
||||
raise ValueError("Driver not found for connection string: "
|
||||
"%s" % connection_string)
|
||||
raise ValueError(
|
||||
f"Driver not found for connection string: {connection_string}"
|
||||
)
|
||||
|
||||
|
||||
class Driver:
|
||||
@@ -60,8 +68,9 @@ class Driver:
|
||||
|
||||
default_trace_fields = {"base_id", "timestamp"}
|
||||
|
||||
def __init__(self, connection_str, project=None, service=None, host=None,
|
||||
**kwargs):
|
||||
def __init__(
|
||||
self, connection_str, project=None, service=None, host=None, **kwargs
|
||||
):
|
||||
self.connection_str = connection_str
|
||||
self.project = project
|
||||
self.service = service
|
||||
@@ -94,18 +103,20 @@ class Driver:
|
||||
With parent_id and trace_id it's quite simple to build
|
||||
tree of trace elements, which simplify analyze of trace.
|
||||
"""
|
||||
raise NotImplementedError("{}: This method is either not supported "
|
||||
"or has to be overridden".format(
|
||||
self.get_name()))
|
||||
raise NotImplementedError(
|
||||
f"{self.get_name()}: This method is either not supported "
|
||||
"or has to be overridden"
|
||||
)
|
||||
|
||||
def get_report(self, base_id):
|
||||
"""Forms and returns report composed from the stored notifications.
|
||||
|
||||
:param base_id: Base id of trace elements.
|
||||
"""
|
||||
raise NotImplementedError("{}: This method is either not supported "
|
||||
"or has to be overridden".format(
|
||||
self.get_name()))
|
||||
raise NotImplementedError(
|
||||
f"{self.get_name()}: This method is either not supported "
|
||||
"or has to be overridden"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
@@ -120,9 +131,10 @@ class Driver:
|
||||
:returns: List of traces, where each trace is a dictionary containing
|
||||
at least `base_id` and `timestamp`.
|
||||
"""
|
||||
raise NotImplementedError("{}: This method is either not supported "
|
||||
"or has to be overridden".format(
|
||||
self.get_name()))
|
||||
raise NotImplementedError(
|
||||
f"{self.get_name()}: This method is either not supported "
|
||||
"or has to be overridden"
|
||||
)
|
||||
|
||||
def list_error_traces(self):
|
||||
"""Query all error traces from the storage.
|
||||
@@ -130,23 +142,24 @@ class Driver:
|
||||
:return List of traces, where each trace is a dictionary containing
|
||||
`base_id` and `timestamp`.
|
||||
"""
|
||||
raise NotImplementedError("{}: This method is either not supported "
|
||||
"or has to be overridden".format(
|
||||
self.get_name()))
|
||||
raise NotImplementedError(
|
||||
f"{self.get_name()}: This method is either not supported "
|
||||
"or has to be overridden"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _build_tree(nodes):
|
||||
"""Builds the tree (forest) data structure based on the list of nodes.
|
||||
|
||||
Tree building works in O(n*log(n)).
|
||||
Tree building works in O(n*log(n)).
|
||||
|
||||
:param nodes: dict of nodes, where each node is a dictionary with fields
|
||||
"parent_id", "trace_id", "info"
|
||||
:returns: list of top level ("root") nodes in form of dictionaries,
|
||||
each containing the "info" and "children" fields, where
|
||||
"children" is the list of child nodes ("children" will be
|
||||
empty for leafs)
|
||||
"""
|
||||
:param nodes: dict of nodes, where each node is a dictionary with
|
||||
fields "parent_id", "trace_id", "info"
|
||||
:returns: list of top level ("root") nodes in form of dictionaries,
|
||||
each containing the "info" and "children" fields, where
|
||||
"children" is the list of child nodes ("children" will be
|
||||
empty for leafs)
|
||||
"""
|
||||
|
||||
tree = []
|
||||
|
||||
@@ -162,12 +175,22 @@ class Driver:
|
||||
|
||||
for trace_id in nodes:
|
||||
nodes[trace_id]["children"].sort(
|
||||
key=lambda x: x["info"]["started"])
|
||||
key=lambda x: x["info"]["started"]
|
||||
)
|
||||
|
||||
return sorted(tree, key=lambda x: x["info"]["started"])
|
||||
|
||||
def _append_results(self, trace_id, parent_id, name, project, service,
|
||||
host, timestamp, raw_payload=None):
|
||||
def _append_results(
|
||||
self,
|
||||
trace_id,
|
||||
parent_id,
|
||||
name,
|
||||
project,
|
||||
service,
|
||||
host,
|
||||
timestamp,
|
||||
raw_payload=None,
|
||||
):
|
||||
"""Appends the notification to the dictionary of notifications.
|
||||
|
||||
:param trace_id: UUID of current trace point
|
||||
@@ -181,8 +204,9 @@ class Driver:
|
||||
:param raw_payload: raw notification without any filtering, with all
|
||||
fields included
|
||||
"""
|
||||
timestamp = datetime.datetime.strptime(timestamp,
|
||||
"%Y-%m-%dT%H:%M:%S.%f")
|
||||
timestamp = datetime.datetime.strptime(
|
||||
timestamp, "%Y-%m-%dT%H:%M:%S.%f"
|
||||
)
|
||||
if trace_id not in self.result:
|
||||
self.result[trace_id] = {
|
||||
"info": {
|
||||
@@ -195,8 +219,7 @@ class Driver:
|
||||
"parent_id": parent_id,
|
||||
}
|
||||
|
||||
self.result[trace_id]["info"]["meta.raw_payload.%s"
|
||||
% name] = raw_payload
|
||||
self.result[trace_id]["info"][f"meta.raw_payload.{name}"] = raw_payload
|
||||
|
||||
if name.endswith("stop"):
|
||||
self.result[trace_id]["info"]["finished"] = timestamp
|
||||
@@ -224,8 +247,9 @@ class Driver:
|
||||
def msec(dt):
|
||||
# NOTE(boris-42): Unfortunately this is the simplest way that works
|
||||
# in py26 and py27
|
||||
microsec = (dt.microseconds + (dt.seconds + dt.days * 24 * 3600)
|
||||
* 1e6)
|
||||
microsec = (
|
||||
dt.microseconds + (dt.seconds + dt.days * 24 * 3600) * 1e6
|
||||
)
|
||||
return int(microsec / 1000.0)
|
||||
|
||||
stats = {}
|
||||
@@ -241,18 +265,14 @@ class Driver:
|
||||
|
||||
op_type = r["info"]["name"]
|
||||
op_started = msec(r["info"]["started"] - self.started_at)
|
||||
op_finished = msec(r["info"]["finished"]
|
||||
- self.started_at)
|
||||
op_finished = msec(r["info"]["finished"] - self.started_at)
|
||||
duration = op_finished - op_started
|
||||
|
||||
r["info"]["started"] = op_started
|
||||
r["info"]["finished"] = op_finished
|
||||
|
||||
if op_type not in stats:
|
||||
stats[op_type] = {
|
||||
"count": 1,
|
||||
"duration": duration
|
||||
}
|
||||
stats[op_type] = {"count": 1, "duration": duration}
|
||||
else:
|
||||
stats[op_type]["count"] += 1
|
||||
stats[op_type]["duration"] += duration
|
||||
@@ -261,13 +281,15 @@ class Driver:
|
||||
"info": {
|
||||
"name": "total",
|
||||
"started": 0,
|
||||
"finished": msec(
|
||||
self.finished_at - self.started_at
|
||||
) if self.started_at else None,
|
||||
"finished": msec(self.finished_at - self.started_at)
|
||||
if self.started_at
|
||||
else None,
|
||||
"last_trace_started": msec(
|
||||
self.last_started_at - self.started_at
|
||||
) if self.started_at else None
|
||||
)
|
||||
if self.started_at
|
||||
else None,
|
||||
},
|
||||
"children": self._build_tree(self.result),
|
||||
"stats": stats
|
||||
"stats": stats,
|
||||
}
|
||||
|
||||
@@ -22,27 +22,38 @@ from osprofiler import exc
|
||||
|
||||
|
||||
class ElasticsearchDriver(base.Driver):
|
||||
def __init__(self, connection_str, index_name="osprofiler-notifications",
|
||||
project=None, service=None, host=None, conf=cfg.CONF,
|
||||
**kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
connection_str,
|
||||
index_name="osprofiler-notifications",
|
||||
project=None,
|
||||
service=None,
|
||||
host=None,
|
||||
conf=cfg.CONF,
|
||||
**kwargs,
|
||||
):
|
||||
"""Elasticsearch driver for OSProfiler."""
|
||||
|
||||
super().__init__(connection_str,
|
||||
project=project,
|
||||
service=service,
|
||||
host=host,
|
||||
conf=conf,
|
||||
**kwargs)
|
||||
super().__init__(
|
||||
connection_str,
|
||||
project=project,
|
||||
service=service,
|
||||
host=host,
|
||||
conf=conf,
|
||||
**kwargs,
|
||||
)
|
||||
try:
|
||||
from elasticsearch import Elasticsearch
|
||||
except ImportError:
|
||||
raise exc.CommandError(
|
||||
"To use OSProfiler with ElasticSearch driver, "
|
||||
"please install `elasticsearch` library. "
|
||||
"To install with pip:\n `pip install elasticsearch`.")
|
||||
"To install with pip:\n `pip install elasticsearch`."
|
||||
)
|
||||
|
||||
client_url = parser.urlunparse(parser.urlparse(self.connection_str)
|
||||
._replace(scheme="http"))
|
||||
client_url = parser.urlunparse(
|
||||
parser.urlparse(self.connection_str)._replace(scheme="http")
|
||||
)
|
||||
self.conf = conf
|
||||
self.client = Elasticsearch(client_url)
|
||||
self.index_name = index_name
|
||||
@@ -69,11 +80,16 @@ class ElasticsearchDriver(base.Driver):
|
||||
info = info.copy()
|
||||
info["project"] = self.project
|
||||
info["service"] = self.service
|
||||
self.client.index(index=self.index_name,
|
||||
doc_type=self.conf.profiler.es_doc_type, body=info)
|
||||
self.client.index(
|
||||
index=self.index_name,
|
||||
doc_type=self.conf.profiler.es_doc_type,
|
||||
body=info,
|
||||
)
|
||||
|
||||
if (self.filter_error_trace
|
||||
and info.get("info", {}).get("etype") is not None):
|
||||
if (
|
||||
self.filter_error_trace
|
||||
and info.get("info", {}).get("etype") is not None
|
||||
):
|
||||
self.notify_error_trace(info)
|
||||
|
||||
def notify_error_trace(self, info):
|
||||
@@ -81,7 +97,7 @@ class ElasticsearchDriver(base.Driver):
|
||||
self.client.index(
|
||||
index=self.index_name_error,
|
||||
doc_type=self.conf.profiler.es_doc_type,
|
||||
body={"base_id": info["base_id"], "timestamp": info["timestamp"]}
|
||||
body={"base_id": info["base_id"], "timestamp": info["timestamp"]},
|
||||
)
|
||||
|
||||
def _hits(self, response):
|
||||
@@ -96,9 +112,9 @@ class ElasticsearchDriver(base.Driver):
|
||||
while scroll_size > 0:
|
||||
for hit in response["hits"]["hits"]:
|
||||
result.append(hit["_source"])
|
||||
response = self.client.scroll(scroll_id=scroll_id,
|
||||
scroll=self.conf.profiler.
|
||||
es_scroll_time)
|
||||
response = self.client.scroll(
|
||||
scroll_id=scroll_id, scroll=self.conf.profiler.es_scroll_time
|
||||
)
|
||||
scroll_id = response["_scroll_id"]
|
||||
scroll_size = len(response["hits"]["hits"])
|
||||
|
||||
@@ -115,12 +131,17 @@ class ElasticsearchDriver(base.Driver):
|
||||
query = {"match_all": {}}
|
||||
fields = set(fields or self.default_trace_fields)
|
||||
|
||||
response = self.client.search(index=self.index_name,
|
||||
doc_type=self.conf.profiler.es_doc_type,
|
||||
size=self.conf.profiler.es_scroll_size,
|
||||
scroll=self.conf.profiler.es_scroll_time,
|
||||
body={"_source": fields, "query": query,
|
||||
"sort": [{"timestamp": "asc"}]})
|
||||
response = self.client.search(
|
||||
index=self.index_name,
|
||||
doc_type=self.conf.profiler.es_doc_type,
|
||||
size=self.conf.profiler.es_scroll_size,
|
||||
scroll=self.conf.profiler.es_scroll_time,
|
||||
body={
|
||||
"_source": fields,
|
||||
"query": query,
|
||||
"sort": [{"timestamp": "asc"}],
|
||||
},
|
||||
)
|
||||
|
||||
return self._hits(response)
|
||||
|
||||
@@ -134,8 +155,8 @@ class ElasticsearchDriver(base.Driver):
|
||||
body={
|
||||
"_source": self.default_trace_fields,
|
||||
"query": {"match_all": {}},
|
||||
"sort": [{"timestamp": "asc"}]
|
||||
}
|
||||
"sort": [{"timestamp": "asc"}],
|
||||
},
|
||||
)
|
||||
|
||||
return self._hits(response)
|
||||
@@ -145,12 +166,13 @@ class ElasticsearchDriver(base.Driver):
|
||||
|
||||
:param base_id: Base id of trace elements.
|
||||
"""
|
||||
response = self.client.search(index=self.index_name,
|
||||
doc_type=self.conf.profiler.es_doc_type,
|
||||
size=self.conf.profiler.es_scroll_size,
|
||||
scroll=self.conf.profiler.es_scroll_time,
|
||||
body={"query": {
|
||||
"match": {"base_id": base_id}}})
|
||||
response = self.client.search(
|
||||
index=self.index_name,
|
||||
doc_type=self.conf.profiler.es_doc_type,
|
||||
size=self.conf.profiler.es_scroll_size,
|
||||
scroll=self.conf.profiler.es_scroll_time,
|
||||
body={"query": {"match": {"base_id": base_id}}},
|
||||
)
|
||||
|
||||
for n in self._hits(response):
|
||||
trace_id = n["trace_id"]
|
||||
@@ -161,7 +183,8 @@ class ElasticsearchDriver(base.Driver):
|
||||
host = n["info"]["host"]
|
||||
timestamp = n["timestamp"]
|
||||
|
||||
self._append_results(trace_id, parent_id, name, project, service,
|
||||
host, timestamp, n)
|
||||
self._append_results(
|
||||
trace_id, parent_id, name, project, service, host, timestamp, n
|
||||
)
|
||||
|
||||
return self._parse_results()
|
||||
|
||||
@@ -19,8 +19,15 @@ from osprofiler import exc
|
||||
|
||||
# TODO(tkajinam): Remove this and the deprecated options after G-release
|
||||
class Jaeger(base.Driver):
|
||||
def __init__(self, connection_str, project=None, service=None, host=None,
|
||||
conf=None, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
connection_str,
|
||||
project=None,
|
||||
service=None,
|
||||
host=None,
|
||||
conf=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""Jaeger driver for OSProfiler."""
|
||||
|
||||
raise exc.CommandError('Jaeger driver is no longer supported')
|
||||
|
||||
@@ -46,23 +46,25 @@ class LogInsightDriver(base.Driver):
|
||||
to Log Insight server at 10.1.2.3 using username "osprofiler" and password
|
||||
"p@ssword" is: loginsight://osprofiler:p%40ssword@10.1.2.3
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, connection_str, project=None, service=None, host=None,
|
||||
**kwargs):
|
||||
super().__init__(connection_str,
|
||||
project=project,
|
||||
service=service,
|
||||
host=host)
|
||||
self, connection_str, project=None, service=None, host=None, **kwargs
|
||||
):
|
||||
super().__init__(
|
||||
connection_str, project=project, service=service, host=host
|
||||
)
|
||||
|
||||
parsed_connection = urlparse.urlparse(connection_str)
|
||||
try:
|
||||
creds, host = parsed_connection.netloc.split("@")
|
||||
username, password = creds.split(":")
|
||||
except ValueError:
|
||||
raise ValueError("Connection string format is: loginsight://"
|
||||
"<username>:<password>@<loginsight-host>. If the "
|
||||
"username or password contains the character '@' "
|
||||
"or ':', it must be escaped using URL encoding.")
|
||||
raise ValueError(
|
||||
"Connection string format is: loginsight://"
|
||||
"<username>:<password>@<loginsight-host>. If the "
|
||||
"username or password contains the character '@' "
|
||||
"or ':', it must be escaped using URL encoding."
|
||||
)
|
||||
|
||||
username = urlparse.unquote(username)
|
||||
password = urlparse.unquote(password)
|
||||
@@ -86,12 +88,14 @@ class LogInsightDriver(base.Driver):
|
||||
def _create_field(name, content):
|
||||
return {"name": name, "content": content}
|
||||
|
||||
event["fields"] = [_create_field("base_id", trace["base_id"]),
|
||||
_create_field("trace_id", trace["trace_id"]),
|
||||
_create_field("project", trace["project"]),
|
||||
_create_field("service", trace["service"]),
|
||||
_create_field("name", trace["name"]),
|
||||
_create_field("trace", json.dumps(trace))]
|
||||
event["fields"] = [
|
||||
_create_field("base_id", trace["base_id"]),
|
||||
_create_field("trace_id", trace["trace_id"]),
|
||||
_create_field("project", trace["project"]),
|
||||
_create_field("service", trace["service"]),
|
||||
_create_field("name", trace["name"]),
|
||||
_create_field("trace", json.dumps(trace)),
|
||||
]
|
||||
|
||||
self._client.send_event(event)
|
||||
|
||||
@@ -119,8 +123,15 @@ class LogInsightDriver(base.Driver):
|
||||
timestamp = trace["timestamp"]
|
||||
|
||||
self._append_results(
|
||||
trace_id, parent_id, name, project, service, host,
|
||||
timestamp, trace)
|
||||
trace_id,
|
||||
parent_id,
|
||||
name,
|
||||
project,
|
||||
service,
|
||||
host,
|
||||
timestamp,
|
||||
trace,
|
||||
)
|
||||
break
|
||||
|
||||
return self._parse_results()
|
||||
@@ -134,11 +145,18 @@ class LogInsightClient:
|
||||
# API paths
|
||||
SESSIONS_PATH = "api/v1/sessions"
|
||||
CURRENT_SESSIONS_PATH = "api/v1/sessions/current"
|
||||
EVENTS_INGEST_PATH = "api/v1/events/ingest/%s" % LI_OSPROFILER_AGENT_ID
|
||||
EVENTS_INGEST_PATH = f"api/v1/events/ingest/{LI_OSPROFILER_AGENT_ID}"
|
||||
QUERY_EVENTS_BASE_PATH = "api/v1/events"
|
||||
|
||||
def __init__(self, host, username, password, api_port=9000,
|
||||
api_ssl_port=9543, query_timeout=60000):
|
||||
def __init__(
|
||||
self,
|
||||
host,
|
||||
username,
|
||||
password,
|
||||
api_port=9000,
|
||||
api_ssl_port=9543,
|
||||
query_timeout=60000,
|
||||
):
|
||||
self._host = host
|
||||
self._username = username
|
||||
self._password = password
|
||||
@@ -149,11 +167,13 @@ class LogInsightClient:
|
||||
self._session_id = None
|
||||
|
||||
def _build_base_url(self, scheme):
|
||||
proto_str = "%s://" % scheme
|
||||
host_str = ("[%s]" % self._host if netaddr.valid_ipv6(self._host)
|
||||
else self._host)
|
||||
port_str = ":%d" % (self._api_ssl_port if scheme == "https"
|
||||
else self._api_port)
|
||||
proto_str = f"{scheme}://"
|
||||
host_str = (
|
||||
f"[{self._host}]" if netaddr.valid_ipv6(self._host) else self._host
|
||||
)
|
||||
port_str = ":%s" % (
|
||||
self._api_ssl_port if scheme == "https" else self._api_port
|
||||
)
|
||||
return proto_str + host_str + port_str
|
||||
|
||||
def _check_response(self, resp):
|
||||
@@ -173,8 +193,9 @@ class LogInsightClient:
|
||||
raise exc.LogInsightAPIError(msg)
|
||||
|
||||
def _send_request(
|
||||
self, method, scheme, path, headers=None, body=None, params=None):
|
||||
url = "{}/{}".format(self._build_base_url(scheme), path)
|
||||
self, method, scheme, path, headers=None, body=None, params=None
|
||||
):
|
||||
url = f"{self._build_base_url(scheme)}/{path}"
|
||||
|
||||
headers = headers or {}
|
||||
headers["content-type"] = "application/json"
|
||||
@@ -182,7 +203,8 @@ class LogInsightClient:
|
||||
params = params or {}
|
||||
|
||||
req = requests.Request(
|
||||
method, url, headers=headers, data=json.dumps(body), params=params)
|
||||
method, url, headers=headers, data=json.dumps(body), params=params
|
||||
)
|
||||
req = req.prepare()
|
||||
resp = self._session.send(req, verify=False)
|
||||
|
||||
@@ -198,16 +220,20 @@ class LogInsightClient:
|
||||
|
||||
def _is_current_session_active(self):
|
||||
try:
|
||||
self._send_request("get",
|
||||
"https",
|
||||
self.CURRENT_SESSIONS_PATH,
|
||||
headers=self._get_auth_header())
|
||||
LOG.debug("Current session %s is active.",
|
||||
self._trunc_session_id())
|
||||
self._send_request(
|
||||
"get",
|
||||
"https",
|
||||
self.CURRENT_SESSIONS_PATH,
|
||||
headers=self._get_auth_header(),
|
||||
)
|
||||
LOG.debug(
|
||||
"Current session %s is active.", self._trunc_session_id()
|
||||
)
|
||||
return True
|
||||
except (exc.LogInsightLoginTimeout, exc.LogInsightAPIError):
|
||||
LOG.debug("Current session %s is not active.",
|
||||
self._trunc_session_id())
|
||||
LOG.debug(
|
||||
"Current session %s is not active.", self._trunc_session_id()
|
||||
)
|
||||
return False
|
||||
|
||||
@synchronized("li_login_lock")
|
||||
@@ -218,40 +244,43 @@ class LogInsightClient:
|
||||
return
|
||||
|
||||
LOG.info("Logging into Log Insight server: %s.", self._host)
|
||||
resp = self._send_request("post",
|
||||
"https",
|
||||
self.SESSIONS_PATH,
|
||||
body={"username": self._username,
|
||||
"password": self._password})
|
||||
resp = self._send_request(
|
||||
"post",
|
||||
"https",
|
||||
self.SESSIONS_PATH,
|
||||
body={"username": self._username, "password": self._password},
|
||||
)
|
||||
|
||||
self._session_id = resp["sessionId"]
|
||||
LOG.debug("Established session %s.", self._trunc_session_id())
|
||||
|
||||
def send_event(self, event):
|
||||
events = {"events": [event]}
|
||||
self._send_request("post",
|
||||
"http",
|
||||
self.EVENTS_INGEST_PATH,
|
||||
body=events)
|
||||
self._send_request(
|
||||
"post", "http", self.EVENTS_INGEST_PATH, body=events
|
||||
)
|
||||
|
||||
def query_events(self, params):
|
||||
# Assumes that the keys and values in the params are strings and
|
||||
# the operator is "CONTAINS".
|
||||
constraints = []
|
||||
for field, value in params.items():
|
||||
constraints.append("{}/CONTAINS+{}".format(field, value))
|
||||
constraints.append(f"{field}/CONTAINS+{value}")
|
||||
constraints.append("timestamp/GT+0")
|
||||
|
||||
path = "{}/{}".format(self.QUERY_EVENTS_BASE_PATH,
|
||||
"/".join(constraints))
|
||||
path = "{}/{}".format(
|
||||
self.QUERY_EVENTS_BASE_PATH, "/".join(constraints)
|
||||
)
|
||||
|
||||
def _query_events():
|
||||
return self._send_request("get",
|
||||
"https",
|
||||
path,
|
||||
headers=self._get_auth_header(),
|
||||
params={"limit": 20000,
|
||||
"timeout": self._query_timeout})
|
||||
return self._send_request(
|
||||
"get",
|
||||
"https",
|
||||
path,
|
||||
headers=self._get_auth_header(),
|
||||
params={"limit": 20000, "timeout": self._query_timeout},
|
||||
)
|
||||
|
||||
try:
|
||||
resp = _query_events()
|
||||
except exc.LogInsightLoginTimeout:
|
||||
|
||||
@@ -23,9 +23,18 @@ from osprofiler.drivers import base
|
||||
|
||||
|
||||
class Messaging(base.Driver):
|
||||
def __init__(self, connection_str, project=None, service=None, host=None,
|
||||
context=None, conf=None, transport_url=None,
|
||||
idle_timeout=1, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
connection_str,
|
||||
project=None,
|
||||
service=None,
|
||||
host=None,
|
||||
context=None,
|
||||
conf=None,
|
||||
transport_url=None,
|
||||
idle_timeout=1,
|
||||
**kwargs,
|
||||
):
|
||||
"""Driver that uses messaging as transport for notifications
|
||||
|
||||
:param connection_str: OSProfiler driver connection string,
|
||||
@@ -46,19 +55,22 @@ class Messaging(base.Driver):
|
||||
|
||||
self.oslo_messaging = importutils.try_import("oslo_messaging")
|
||||
if not self.oslo_messaging:
|
||||
raise ValueError("Oslo.messaging library is required for "
|
||||
"messaging driver")
|
||||
raise ValueError(
|
||||
"Oslo.messaging library is required for messaging driver"
|
||||
)
|
||||
|
||||
super().__init__(connection_str, project=project,
|
||||
service=service, host=host)
|
||||
super().__init__(
|
||||
connection_str, project=project, service=service, host=host
|
||||
)
|
||||
|
||||
self.context = context
|
||||
|
||||
if not conf:
|
||||
oslo_config = importutils.try_import("oslo_config")
|
||||
if not oslo_config:
|
||||
raise ValueError("Oslo.config library is required for "
|
||||
"messaging driver")
|
||||
raise ValueError(
|
||||
"Oslo.config library is required for messaging driver"
|
||||
)
|
||||
conf = oslo_config.cfg.CONF
|
||||
|
||||
transport_kwargs = {}
|
||||
@@ -66,10 +78,15 @@ class Messaging(base.Driver):
|
||||
transport_kwargs["url"] = transport_url
|
||||
|
||||
self.transport = self.oslo_messaging.get_notification_transport(
|
||||
conf, **transport_kwargs)
|
||||
conf, **transport_kwargs
|
||||
)
|
||||
self.client = self.oslo_messaging.Notifier(
|
||||
self.transport, publisher_id=self.host, driver="messaging",
|
||||
topics=["profiler"], retry=0)
|
||||
self.transport,
|
||||
publisher_id=self.host,
|
||||
driver="messaging",
|
||||
topics=["profiler"],
|
||||
retry=0,
|
||||
)
|
||||
|
||||
self.idle_timeout = idle_timeout
|
||||
|
||||
@@ -95,16 +112,19 @@ class Messaging(base.Driver):
|
||||
|
||||
info["project"] = self.project
|
||||
info["service"] = self.service
|
||||
self.client.info(context or self.context,
|
||||
"profiler.%s" % info["service"],
|
||||
info)
|
||||
self.client.info(
|
||||
context or self.context,
|
||||
"profiler.{}".format(info["service"]),
|
||||
info,
|
||||
)
|
||||
|
||||
def get_report(self, base_id):
|
||||
notification_endpoint = NotifyEndpoint(self.oslo_messaging, base_id)
|
||||
endpoints = [notification_endpoint]
|
||||
targets = [self.oslo_messaging.Target(topic="profiler")]
|
||||
server = self.oslo_messaging.notify.get_notification_listener(
|
||||
self.transport, targets, endpoints, executor="threading")
|
||||
self.transport, targets, endpoints, executor="threading"
|
||||
)
|
||||
|
||||
state = dict(running=False)
|
||||
sfn = functools.partial(signal_handler, state=state)
|
||||
@@ -119,8 +139,10 @@ class Messaging(base.Driver):
|
||||
# failed to start the server
|
||||
raise
|
||||
except SignalExit:
|
||||
print("Execution interrupted while trying to connect to "
|
||||
"messaging server. No data was collected.")
|
||||
print(
|
||||
"Execution interrupted while trying to connect to "
|
||||
"messaging server. No data was collected."
|
||||
)
|
||||
return {}
|
||||
|
||||
# connected to server, now read the data
|
||||
@@ -148,9 +170,12 @@ class Messaging(base.Driver):
|
||||
events = notification_endpoint.get_messages()
|
||||
|
||||
if not events:
|
||||
print("No events are collected for Trace UUID %s. Please note "
|
||||
"that osprofiler has read ALL events from profiler topic, "
|
||||
"but has not found any for specified Trace UUID." % base_id)
|
||||
print(
|
||||
f"No events are collected for Trace UUID {base_id}. "
|
||||
f"Please note that osprofiler has read ALL events from "
|
||||
f"profiler topic, but has not found any for specified Trace "
|
||||
f"UUID."
|
||||
)
|
||||
|
||||
for n in events:
|
||||
trace_id = n["trace_id"]
|
||||
@@ -161,19 +186,20 @@ class Messaging(base.Driver):
|
||||
host = n["info"]["host"]
|
||||
timestamp = n["timestamp"]
|
||||
|
||||
self._append_results(trace_id, parent_id, name, project, service,
|
||||
host, timestamp, n)
|
||||
self._append_results(
|
||||
trace_id, parent_id, name, project, service, host, timestamp, n
|
||||
)
|
||||
|
||||
return self._parse_results()
|
||||
|
||||
|
||||
class NotifyEndpoint:
|
||||
|
||||
def __init__(self, oslo_messaging, base_id):
|
||||
self.received_messages = []
|
||||
self.last_read_time = time.time()
|
||||
self.filter_rule = oslo_messaging.NotificationFilter(
|
||||
payload={"base_id": base_id})
|
||||
payload={"base_id": base_id}
|
||||
)
|
||||
|
||||
def info(self, ctxt, publisher_id, event_type, payload, metadata):
|
||||
self.received_messages.append(payload)
|
||||
|
||||
@@ -18,19 +18,32 @@ from osprofiler import exc
|
||||
|
||||
|
||||
class MongoDB(base.Driver):
|
||||
def __init__(self, connection_str, db_name="osprofiler", project=None,
|
||||
service=None, host=None, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
connection_str,
|
||||
db_name="osprofiler",
|
||||
project=None,
|
||||
service=None,
|
||||
host=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""MongoDB driver for OSProfiler."""
|
||||
|
||||
super().__init__(connection_str, project=project,
|
||||
service=service, host=host, **kwargs)
|
||||
super().__init__(
|
||||
connection_str,
|
||||
project=project,
|
||||
service=service,
|
||||
host=host,
|
||||
**kwargs,
|
||||
)
|
||||
try:
|
||||
from pymongo import MongoClient
|
||||
except ImportError:
|
||||
raise exc.CommandError(
|
||||
"To use OSProfiler with MongoDB driver, "
|
||||
"please install `pymongo` library. "
|
||||
"To install with pip:\n `pip install pymongo`.")
|
||||
"To install with pip:\n `pip install pymongo`."
|
||||
)
|
||||
|
||||
client = MongoClient(self.connection_str, connect=False)
|
||||
self.db = client[db_name]
|
||||
@@ -57,8 +70,10 @@ class MongoDB(base.Driver):
|
||||
data["service"] = self.service
|
||||
self.db.profiler.insert_one(data)
|
||||
|
||||
if (self.filter_error_trace
|
||||
and data.get("info", {}).get("etype") is not None):
|
||||
if (
|
||||
self.filter_error_trace
|
||||
and data.get("info", {}).get("etype") is not None
|
||||
):
|
||||
self.notify_error_trace(data)
|
||||
|
||||
def notify_error_trace(self, data):
|
||||
@@ -66,7 +81,7 @@ class MongoDB(base.Driver):
|
||||
self.db.profiler_error.update(
|
||||
{"base_id": data["base_id"]},
|
||||
{"base_id": data["base_id"], "timestamp": data["timestamp"]},
|
||||
upsert=True
|
||||
upsert=True,
|
||||
)
|
||||
|
||||
def list_traces(self, fields=None):
|
||||
@@ -81,8 +96,12 @@ class MongoDB(base.Driver):
|
||||
ids = self.db.profiler.find({}).distinct("base_id")
|
||||
out_format = {"base_id": 1, "timestamp": 1, "_id": 0}
|
||||
out_format.update({i: 1 for i in fields})
|
||||
return [self.db.profiler.find(
|
||||
{"base_id": i}, out_format).sort("timestamp")[0] for i in ids]
|
||||
return [
|
||||
self.db.profiler.find({"base_id": i}, out_format).sort(
|
||||
"timestamp"
|
||||
)[0]
|
||||
for i in ids
|
||||
]
|
||||
|
||||
def list_error_traces(self):
|
||||
"""Returns all traces that have error/exception."""
|
||||
@@ -103,7 +122,8 @@ class MongoDB(base.Driver):
|
||||
host = n["info"]["host"]
|
||||
timestamp = n["timestamp"]
|
||||
|
||||
self._append_results(trace_id, parent_id, name, project, service,
|
||||
host, timestamp, n)
|
||||
self._append_results(
|
||||
trace_id, parent_id, name, project, service, host, timestamp, n
|
||||
)
|
||||
|
||||
return self._parse_results()
|
||||
|
||||
+56
-30
@@ -24,17 +24,31 @@ from osprofiler import exc
|
||||
|
||||
|
||||
class OTLP(base.Driver):
|
||||
def __init__(self, connection_str, project=None, service=None, host=None,
|
||||
conf=cfg.CONF, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
connection_str,
|
||||
project=None,
|
||||
service=None,
|
||||
host=None,
|
||||
conf=cfg.CONF,
|
||||
**kwargs,
|
||||
):
|
||||
"""OTLP driver using OTLP exporters."""
|
||||
|
||||
super().__init__(connection_str, project=project,
|
||||
service=service, host=host,
|
||||
conf=conf, **kwargs)
|
||||
super().__init__(
|
||||
connection_str,
|
||||
project=project,
|
||||
service=service,
|
||||
host=host,
|
||||
conf=conf,
|
||||
**kwargs,
|
||||
)
|
||||
try:
|
||||
from opentelemetry import trace as trace_api
|
||||
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter # noqa
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
|
||||
OTLPSpanExporter,
|
||||
) # noqa
|
||||
from opentelemetry.sdk.resources import Resource
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
@@ -46,33 +60,31 @@ class OTLP(base.Driver):
|
||||
"please install `opentelemetry-sdk` and "
|
||||
"opentelemetry-exporter-otlp libraries. "
|
||||
"To install with pip:\n `pip install opentelemetry-sdk "
|
||||
"opentelemetry-exporter-otlp`.")
|
||||
"opentelemetry-exporter-otlp`."
|
||||
)
|
||||
|
||||
service_name = self._get_service_name(conf, project, service)
|
||||
resource = Resource(attributes={
|
||||
"service.name": service_name
|
||||
})
|
||||
resource = Resource(attributes={"service.name": service_name})
|
||||
|
||||
parsed_url = parser.urlparse(connection_str)
|
||||
# TODO("sahid"): We also want to handle https scheme?
|
||||
parsed_url = parsed_url._replace(scheme="http")
|
||||
|
||||
self.trace_api.set_tracer_provider(
|
||||
TracerProvider(resource=resource))
|
||||
self.trace_api.set_tracer_provider(TracerProvider(resource=resource))
|
||||
self.tracer = self.trace_api.get_tracer(__name__)
|
||||
|
||||
exporter = OTLPSpanExporter("{}/v1/traces".format(
|
||||
parsed_url.geturl()))
|
||||
exporter = OTLPSpanExporter(f"{parsed_url.geturl()}/v1/traces")
|
||||
self.trace_api.get_tracer_provider().add_span_processor(
|
||||
BatchSpanProcessor(exporter))
|
||||
BatchSpanProcessor(exporter)
|
||||
)
|
||||
|
||||
self.spans = collections.deque()
|
||||
|
||||
def _get_service_name(self, conf, project, service):
|
||||
prefix = conf.profiler_otlp.service_name_prefix
|
||||
if prefix:
|
||||
return "{}-{}-{}".format(prefix, project, service)
|
||||
return "{}-{}".format(project, service)
|
||||
return f"{prefix}-{project}-{service}"
|
||||
return f"{project}-{service}"
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
@@ -81,7 +93,7 @@ class OTLP(base.Driver):
|
||||
def _kind(self, name):
|
||||
if "wsgi" in name:
|
||||
return self.trace_api.SpanKind.SERVER
|
||||
elif ("db" in name or "http" in name or "api" in name):
|
||||
elif "db" in name or "http" in name or "api" in name:
|
||||
return self.trace_api.SpanKind.CLIENT
|
||||
return self.trace_api.SpanKind.INTERNAL
|
||||
|
||||
@@ -89,13 +101,16 @@ class OTLP(base.Driver):
|
||||
info = payload["info"]
|
||||
if info.get("request"):
|
||||
return "WSGI_{}_{}".format(
|
||||
info["request"]["method"], info["request"]["path"])
|
||||
info["request"]["method"], info["request"]["path"]
|
||||
)
|
||||
elif info.get("db"):
|
||||
return "SQL_{}".format(
|
||||
info["db"]["statement"].split(' ', 1)[0].upper())
|
||||
info["db"]["statement"].split(' ', 1)[0].upper()
|
||||
)
|
||||
elif info.get("requests"):
|
||||
return "REQUESTS_{}_{}".format(
|
||||
info["requests"]["method"], info["requests"]["hostname"])
|
||||
info["requests"]["method"], info["requests"]["hostname"]
|
||||
)
|
||||
return payload["name"].rstrip("-start")
|
||||
|
||||
def notify(self, payload):
|
||||
@@ -105,24 +120,29 @@ class OTLP(base.Driver):
|
||||
span_id=utils.shorten_id(payload["parent_id"]),
|
||||
is_remote=False,
|
||||
trace_flags=self.trace_api.TraceFlags(
|
||||
self.trace_api.TraceFlags.SAMPLED))
|
||||
self.trace_api.TraceFlags.SAMPLED
|
||||
),
|
||||
)
|
||||
|
||||
ctx = self.trace_api.set_span_in_context(
|
||||
self.trace_api.NonRecordingSpan(parent))
|
||||
self.trace_api.NonRecordingSpan(parent)
|
||||
)
|
||||
|
||||
# OTLP Tracing span
|
||||
span = self.tracer.start_span(
|
||||
name=self._name(payload),
|
||||
kind=self._kind(payload['name']),
|
||||
attributes=self.create_span_tags(payload),
|
||||
context=ctx)
|
||||
context=ctx,
|
||||
)
|
||||
|
||||
span._context = self.trace_api.SpanContext(
|
||||
trace_id=span.context.trace_id,
|
||||
span_id=utils.shorten_id(payload["trace_id"]),
|
||||
is_remote=span.context.is_remote,
|
||||
trace_flags=span.context.trace_flags,
|
||||
trace_state=span.context.trace_state)
|
||||
trace_state=span.context.trace_state,
|
||||
)
|
||||
|
||||
self.spans.append(span)
|
||||
else:
|
||||
@@ -132,17 +152,23 @@ class OTLP(base.Driver):
|
||||
for call in ("db", "function"):
|
||||
if payload.get("info", {}).get(call, {}).get("result"):
|
||||
span.set_attribute(
|
||||
"result", payload["info"][call]["result"])
|
||||
"result", payload["info"][call]["result"]
|
||||
)
|
||||
# Store result of requests
|
||||
if payload.get("info", {}).get("requests"):
|
||||
span.set_attribute(
|
||||
"status_code", payload["info"]["requests"]["status_code"])
|
||||
"status_code", payload["info"]["requests"]["status_code"]
|
||||
)
|
||||
# Span error tag and log
|
||||
if payload["info"].get("etype"):
|
||||
span.set_attribute("error", True)
|
||||
span.add_event("log", {
|
||||
"error.kind": payload["info"]["etype"],
|
||||
"message": payload["info"]["message"]})
|
||||
span.add_event(
|
||||
"log",
|
||||
{
|
||||
"error.kind": payload["info"]["etype"],
|
||||
"message": payload["info"]["message"],
|
||||
},
|
||||
)
|
||||
span.end()
|
||||
|
||||
def get_report(self, base_id):
|
||||
|
||||
@@ -25,24 +25,41 @@ from osprofiler import exc
|
||||
|
||||
|
||||
class Redis(base.Driver):
|
||||
@removals.removed_kwarg("db", message="'db' parameter is deprecated "
|
||||
"and will be removed in future. "
|
||||
"Please specify 'db' in "
|
||||
"'connection_string' instead.")
|
||||
def __init__(self, connection_str, db=0, project=None,
|
||||
service=None, host=None, conf=cfg.CONF, **kwargs):
|
||||
@removals.removed_kwarg(
|
||||
"db",
|
||||
message="'db' parameter is deprecated "
|
||||
"and will be removed in future. "
|
||||
"Please specify 'db' in "
|
||||
"'connection_string' instead.",
|
||||
)
|
||||
def __init__(
|
||||
self,
|
||||
connection_str,
|
||||
db=0,
|
||||
project=None,
|
||||
service=None,
|
||||
host=None,
|
||||
conf=cfg.CONF,
|
||||
**kwargs,
|
||||
):
|
||||
"""Redis driver for OSProfiler."""
|
||||
|
||||
super().__init__(connection_str, project=project,
|
||||
service=service, host=host,
|
||||
conf=conf, **kwargs)
|
||||
super().__init__(
|
||||
connection_str,
|
||||
project=project,
|
||||
service=service,
|
||||
host=host,
|
||||
conf=conf,
|
||||
**kwargs,
|
||||
)
|
||||
try:
|
||||
from redis import Redis as _Redis
|
||||
except ImportError:
|
||||
raise exc.CommandError(
|
||||
"To use OSProfiler with Redis driver, "
|
||||
"please install `redis` library. "
|
||||
"To install with pip:\n `pip install redis`.")
|
||||
"To install with pip:\n `pip install redis`."
|
||||
)
|
||||
|
||||
# only connection over network is supported with schema
|
||||
# redis://[:password]@host[:port][/db]
|
||||
@@ -74,17 +91,18 @@ class Redis(base.Driver):
|
||||
key = self.namespace_opt + data["base_id"]
|
||||
self.db.lpush(key, jsonutils.dumps(data))
|
||||
|
||||
if (self.filter_error_trace
|
||||
and data.get("info", {}).get("etype") is not None):
|
||||
if (
|
||||
self.filter_error_trace
|
||||
and data.get("info", {}).get("etype") is not None
|
||||
):
|
||||
self.notify_error_trace(data)
|
||||
|
||||
def notify_error_trace(self, data):
|
||||
"""Store base_id and timestamp of error trace to a separate key."""
|
||||
key = self.namespace_error + data["base_id"]
|
||||
value = jsonutils.dumps({
|
||||
"base_id": data["base_id"],
|
||||
"timestamp": data["timestamp"]
|
||||
})
|
||||
value = jsonutils.dumps(
|
||||
{"base_id": data["base_id"], "timestamp": data["timestamp"]}
|
||||
)
|
||||
self.db.set(key, value)
|
||||
|
||||
def list_traces(self, fields=None):
|
||||
@@ -105,8 +123,13 @@ class Redis(base.Driver):
|
||||
for i in ids:
|
||||
# for each trace query the first event to have a timestamp
|
||||
first_event = jsonutils.loads(self.db.lindex(i, 1))
|
||||
result.append({key: value for key, value in first_event.items()
|
||||
if key in fields})
|
||||
result.append(
|
||||
{
|
||||
key: value
|
||||
for key, value in first_event.items()
|
||||
if key in fields
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
def _list_traces_legacy(self, fields):
|
||||
@@ -121,8 +144,13 @@ class Redis(base.Driver):
|
||||
for trace in traces:
|
||||
if trace["base_id"] not in seen_ids:
|
||||
seen_ids.add(trace["base_id"])
|
||||
result.append({key: value for key, value in trace.items()
|
||||
if key in fields})
|
||||
result.append(
|
||||
{
|
||||
key: value
|
||||
for key, value in trace.items()
|
||||
if key in fields
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
def list_error_traces(self):
|
||||
@@ -144,9 +172,11 @@ class Redis(base.Driver):
|
||||
|
||||
:param base_id: Base id of trace elements.
|
||||
"""
|
||||
|
||||
def iterate_events():
|
||||
for key in self.db.scan_iter(
|
||||
match=self.namespace + base_id + "*"): # legacy
|
||||
match=self.namespace + base_id + "*"
|
||||
): # legacy
|
||||
yield self.db.get(key)
|
||||
|
||||
yield from self.db.lrange(self.namespace_opt + base_id, 0, -1)
|
||||
@@ -161,40 +191,62 @@ class Redis(base.Driver):
|
||||
host = n["info"]["host"]
|
||||
timestamp = n["timestamp"]
|
||||
|
||||
self._append_results(trace_id, parent_id, name, project, service,
|
||||
host, timestamp, n)
|
||||
self._append_results(
|
||||
trace_id, parent_id, name, project, service, host, timestamp, n
|
||||
)
|
||||
|
||||
return self._parse_results()
|
||||
|
||||
|
||||
class RedisSentinel(Redis, base.Driver):
|
||||
@removals.removed_kwarg("db", message="'db' parameter is deprecated "
|
||||
"and will be removed in future. "
|
||||
"Please specify 'db' in "
|
||||
"'connection_string' instead.")
|
||||
def __init__(self, connection_str, db=0, project=None,
|
||||
service=None, host=None, conf=cfg.CONF, **kwargs):
|
||||
@removals.removed_kwarg(
|
||||
"db",
|
||||
message="'db' parameter is deprecated "
|
||||
"and will be removed in future. "
|
||||
"Please specify 'db' in "
|
||||
"'connection_string' instead.",
|
||||
)
|
||||
def __init__(
|
||||
self,
|
||||
connection_str,
|
||||
db=0,
|
||||
project=None,
|
||||
service=None,
|
||||
host=None,
|
||||
conf=cfg.CONF,
|
||||
**kwargs,
|
||||
):
|
||||
"""Redis driver for OSProfiler."""
|
||||
|
||||
super().__init__(connection_str, project=project,
|
||||
service=service, host=host,
|
||||
conf=conf, **kwargs)
|
||||
super().__init__(
|
||||
connection_str,
|
||||
project=project,
|
||||
service=service,
|
||||
host=host,
|
||||
conf=conf,
|
||||
**kwargs,
|
||||
)
|
||||
try:
|
||||
from redis.sentinel import Sentinel
|
||||
except ImportError:
|
||||
raise exc.CommandError(
|
||||
"To use this command, you should install "
|
||||
"'redis' manually. Use command:\n "
|
||||
"'pip install redis'.")
|
||||
"'pip install redis'."
|
||||
)
|
||||
|
||||
self.conf = conf
|
||||
socket_timeout = self.conf.profiler.socket_timeout
|
||||
parsed_url = parser.urlparse(self.connection_str)
|
||||
sentinel = Sentinel([(parsed_url.hostname, int(parsed_url.port))],
|
||||
password=parsed_url.password,
|
||||
socket_timeout=socket_timeout)
|
||||
self.db = sentinel.master_for(self.conf.profiler.sentinel_service_name,
|
||||
socket_timeout=socket_timeout)
|
||||
sentinel = Sentinel(
|
||||
[(parsed_url.hostname, int(parsed_url.port))],
|
||||
password=parsed_url.password,
|
||||
socket_timeout=socket_timeout,
|
||||
)
|
||||
self.db = sentinel.master_for(
|
||||
self.conf.profiler.sentinel_service_name,
|
||||
socket_timeout=socket_timeout,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
|
||||
@@ -24,10 +24,12 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SQLAlchemyDriver(base.Driver):
|
||||
def __init__(self, connection_str, project=None, service=None, host=None,
|
||||
**kwargs):
|
||||
super().__init__(connection_str, project=project,
|
||||
service=service, host=host)
|
||||
def __init__(
|
||||
self, connection_str, project=None, service=None, host=None, **kwargs
|
||||
):
|
||||
super().__init__(
|
||||
connection_str, project=project, service=service, host=host
|
||||
)
|
||||
|
||||
try:
|
||||
from sqlalchemy import create_engine
|
||||
@@ -38,7 +40,8 @@ class SQLAlchemyDriver(base.Driver):
|
||||
else:
|
||||
self._metadata = MetaData()
|
||||
self._data_table = Table(
|
||||
"data", self._metadata,
|
||||
"data",
|
||||
self._metadata,
|
||||
Column("id", Integer, primary_key=True),
|
||||
# timestamp - date/time of the trace point
|
||||
Column("timestamp", String(26), index=True),
|
||||
@@ -54,7 +57,7 @@ class SQLAlchemyDriver(base.Driver):
|
||||
Column("service", String(255), index=True),
|
||||
# name - trace point name
|
||||
Column("name", String(255), index=True),
|
||||
Column("data", JSON)
|
||||
Column("data", JSON),
|
||||
)
|
||||
|
||||
# we don't want to kill any service that does use osprofiler
|
||||
@@ -66,8 +69,10 @@ class SQLAlchemyDriver(base.Driver):
|
||||
# startup when using the sqlalchemy driver...
|
||||
self._metadata.create_all(self._engine, checkfirst=True)
|
||||
except Exception:
|
||||
LOG.exception("Failed to create engine/connection and setup "
|
||||
"intial database tables")
|
||||
LOG.exception(
|
||||
"Failed to create engine/connection and setup "
|
||||
"intial database tables"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
@@ -95,19 +100,23 @@ class SQLAlchemyDriver(base.Driver):
|
||||
service=service,
|
||||
host=host,
|
||||
name=name,
|
||||
data=jsonutils.dumps(data)
|
||||
data=jsonutils.dumps(data),
|
||||
)
|
||||
self._conn.execute(ins)
|
||||
except Exception:
|
||||
LOG.exception("Can not store osprofiler tracepoint {} "
|
||||
"(base_id {})".format(trace_id, base_id))
|
||||
LOG.exception(
|
||||
"Can not store osprofiler tracepoint %s (base id %s)",
|
||||
trace_id,
|
||||
base_id,
|
||||
)
|
||||
|
||||
def list_traces(self, fields=None):
|
||||
try:
|
||||
from sqlalchemy.sql import select
|
||||
except ImportError:
|
||||
raise exc.CommandError(
|
||||
"To use this command, you should install 'SQLAlchemy'")
|
||||
"To use this command, you should install 'SQLAlchemy'"
|
||||
)
|
||||
stmt = select([self._data_table])
|
||||
seen_ids = set()
|
||||
result = []
|
||||
@@ -115,8 +124,13 @@ class SQLAlchemyDriver(base.Driver):
|
||||
for trace in traces:
|
||||
if trace["base_id"] not in seen_ids:
|
||||
seen_ids.add(trace["base_id"])
|
||||
result.append({key: value for key, value in trace.items()
|
||||
if key in fields})
|
||||
result.append(
|
||||
{
|
||||
key: value
|
||||
for key, value in trace.items()
|
||||
if key in fields
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
def get_report(self, base_id):
|
||||
@@ -124,9 +138,11 @@ class SQLAlchemyDriver(base.Driver):
|
||||
from sqlalchemy.sql import select
|
||||
except ImportError:
|
||||
raise exc.CommandError(
|
||||
"To use this command, you should install 'SQLAlchemy'")
|
||||
"To use this command, you should install 'SQLAlchemy'"
|
||||
)
|
||||
stmt = select([self._data_table]).where(
|
||||
self._data_table.c.base_id == base_id)
|
||||
self._data_table.c.base_id == base_id
|
||||
)
|
||||
results = self._conn.execute(stmt).fetchall()
|
||||
for n in results:
|
||||
timestamp = n["timestamp"]
|
||||
@@ -137,6 +153,14 @@ class SQLAlchemyDriver(base.Driver):
|
||||
service = n["service"]
|
||||
host = n["host"]
|
||||
data = jsonutils.loads(n["data"])
|
||||
self._append_results(trace_id, parent_id, name, project, service,
|
||||
host, timestamp, data)
|
||||
self._append_results(
|
||||
trace_id,
|
||||
parent_id,
|
||||
name,
|
||||
project,
|
||||
service,
|
||||
host,
|
||||
timestamp,
|
||||
data,
|
||||
)
|
||||
return self._parse_results()
|
||||
|
||||
@@ -30,11 +30,10 @@ import tokenize
|
||||
|
||||
from hacking import core
|
||||
|
||||
re_no_construct_dict = re.compile(
|
||||
r"\sdict\(\)")
|
||||
re_no_construct_list = re.compile(
|
||||
r"\slist\(\)")
|
||||
re_str_format = re.compile(r"""
|
||||
re_no_construct_dict = re.compile(r"\sdict\(\)")
|
||||
re_no_construct_list = re.compile(r"\slist\(\)")
|
||||
re_str_format = re.compile(
|
||||
r"""
|
||||
% # start of specifier
|
||||
\(([^)]+)\) # mapping key, in group 1
|
||||
[#0 +\-]? # optional conversion flag
|
||||
@@ -42,9 +41,10 @@ re_str_format = re.compile(r"""
|
||||
(?:\.\d*)? # optional precision
|
||||
[hLl]? # optional length modifier
|
||||
[A-z%] # conversion modifier
|
||||
""", re.X)
|
||||
re_raises = re.compile(
|
||||
r"\s:raise[^s] *.*$|\s:raises *:.*$|\s:raises *[^:]+$")
|
||||
""",
|
||||
re.X,
|
||||
)
|
||||
re_raises = re.compile(r"\s:raise[^s] *.*$|\s:raises *:.*$|\s:raises *[^:]+$")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
@@ -68,7 +68,7 @@ def _parse_assert_mock_str(line):
|
||||
|
||||
if point != -1:
|
||||
end_pos = line[point:].find("(") + point
|
||||
return point, line[point + 1: end_pos], line[: point]
|
||||
return point, line[point + 1 : end_pos], line[:point]
|
||||
else:
|
||||
return None, None, None
|
||||
|
||||
@@ -83,8 +83,12 @@ def check_assert_methods_from_mock(logical_line, filename):
|
||||
N303 - related to nonexistent "assert_called_once"
|
||||
"""
|
||||
|
||||
correct_names = ["assert_any_call", "assert_called_once_with",
|
||||
"assert_called_with", "assert_has_calls"]
|
||||
correct_names = [
|
||||
"assert_any_call",
|
||||
"assert_called_once_with",
|
||||
"assert_called_with",
|
||||
"assert_has_calls",
|
||||
]
|
||||
ignored_files = ["./tests/unit/test_hacking.py"]
|
||||
|
||||
if filename.startswith("./tests") and filename not in ignored_files:
|
||||
@@ -93,31 +97,42 @@ def check_assert_methods_from_mock(logical_line, filename):
|
||||
if pos:
|
||||
if method_name not in correct_names:
|
||||
error_number = "N301"
|
||||
msg = ("%(error_number)s:'%(method)s' is not present in `mock`"
|
||||
" library. %(custom_msg)s For more details, visit "
|
||||
"http://www.voidspace.org.uk/python/mock/ .")
|
||||
msg = (
|
||||
"%(error_number)s:'%(method)s' is not present in `mock`"
|
||||
" library. %(custom_msg)s For more details, visit "
|
||||
"http://www.voidspace.org.uk/python/mock/ ."
|
||||
)
|
||||
|
||||
if method_name == "assert_called":
|
||||
error_number = "N302"
|
||||
custom_msg = ("Maybe, you should try to use "
|
||||
"'assertTrue(%s.called)' instead." %
|
||||
obj_name)
|
||||
custom_msg = (
|
||||
"Maybe, you should try to use "
|
||||
f"'assertTrue({obj_name}.called)' instead."
|
||||
)
|
||||
elif method_name == "assert_called_once":
|
||||
# For more details, see a bug in Rally:
|
||||
# https://bugs.launchpad.net/rally/+bug/1305991
|
||||
error_number = "N303"
|
||||
custom_msg = ("Maybe, you should try to use "
|
||||
"'assertEqual(1, %s.call_count)' "
|
||||
"or '%s.assert_called_once_with()'"
|
||||
" instead." % (obj_name, obj_name))
|
||||
custom_msg = (
|
||||
"Maybe, you should try to use "
|
||||
f"'assertEqual(1, {obj_name}.call_count)' "
|
||||
f"or '{obj_name}.assert_called_once_with()'"
|
||||
" instead."
|
||||
)
|
||||
else:
|
||||
custom_msg = ("Correct 'assert_*' methods: '%s'."
|
||||
% "', '".join(correct_names))
|
||||
custom_msg = "Correct 'assert_*' methods: '{}'.".format(
|
||||
"', '".join(correct_names)
|
||||
)
|
||||
|
||||
yield (pos, msg % {
|
||||
"error_number": error_number,
|
||||
"method": method_name,
|
||||
"custom_msg": custom_msg})
|
||||
yield (
|
||||
pos,
|
||||
msg
|
||||
% {
|
||||
"error_number": error_number,
|
||||
"method": method_name,
|
||||
"custom_msg": custom_msg,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@skip_ignored_lines
|
||||
@@ -132,12 +147,10 @@ def check_quotes(logical_line, filename):
|
||||
in_multiline_string = False
|
||||
single_quotas_are_used = False
|
||||
|
||||
check_tripple = (
|
||||
lambda line, i, char: (
|
||||
i + 2 < len(line)
|
||||
and (char == line[i] == line[i + 1] == line[i + 2])
|
||||
def check_tripple(line, i, char):
|
||||
return i + 2 < len(line) and (
|
||||
char == line[i] == line[i + 1] == line[i + 2]
|
||||
)
|
||||
)
|
||||
|
||||
i = 0
|
||||
while i < len(logical_line):
|
||||
@@ -199,9 +212,11 @@ def check_dict_formatting_in_string(logical_line, tokens):
|
||||
# NOTE(stpierre): Can't use @skip_ignored_lines here because it's
|
||||
# a stupid decorator that only works on functions that take
|
||||
# (logical_line, filename) as arguments.
|
||||
if (not logical_line
|
||||
if (
|
||||
not logical_line
|
||||
or logical_line.startswith("#")
|
||||
or logical_line.endswith("# noqa")):
|
||||
or logical_line.endswith("# noqa")
|
||||
):
|
||||
return
|
||||
|
||||
current_string = ""
|
||||
@@ -241,9 +256,11 @@ def check_dict_formatting_in_string(logical_line, tokens):
|
||||
for match in re_str_format.finditer(current_string):
|
||||
format_keys.add(match.group(1))
|
||||
if len(format_keys) == 1:
|
||||
yield (0,
|
||||
"N352 Do not use mapping key string formatting "
|
||||
"with a single key")
|
||||
yield (
|
||||
0,
|
||||
"N352 Do not use mapping key string formatting "
|
||||
"with a single key",
|
||||
)
|
||||
if text != ")":
|
||||
# NOTE(stpierre): You can have a parenthesized string
|
||||
# followed by %, so a closing paren doesn't obviate
|
||||
@@ -267,8 +284,11 @@ def check_using_unicode(logical_line, filename):
|
||||
"""
|
||||
|
||||
if re.search(r"\bunicode\(", logical_line):
|
||||
yield (0, "N353 'unicode' function is absent in python3. Please "
|
||||
"use 'str' instead.")
|
||||
yield (
|
||||
0,
|
||||
"N353 'unicode' function is absent in python3. Please "
|
||||
"use 'str' instead.",
|
||||
)
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
@@ -278,9 +298,14 @@ def check_raises(physical_line, filename):
|
||||
N354
|
||||
"""
|
||||
|
||||
ignored_files = ["./tests/unit/test_hacking.py",
|
||||
"./tests/hacking/checks.py"]
|
||||
ignored_files = [
|
||||
"./tests/unit/test_hacking.py",
|
||||
"./tests/hacking/checks.py",
|
||||
]
|
||||
if filename not in ignored_files:
|
||||
if re_raises.search(physical_line):
|
||||
return (0, "N354 ':Please use ':raises Exception: conditions' "
|
||||
"in docstrings.")
|
||||
return (
|
||||
0,
|
||||
"N354 ':Please use ':raises Exception: conditions' "
|
||||
"in docstrings.",
|
||||
)
|
||||
|
||||
@@ -37,7 +37,8 @@ def init_from_conf(conf, context, project, service, host, **kwargs):
|
||||
service=service,
|
||||
host=host,
|
||||
conf=conf,
|
||||
**kwargs)
|
||||
**kwargs,
|
||||
)
|
||||
notifier.set(_notifier)
|
||||
web.enable(conf.profiler.hmac_keys)
|
||||
if conf.profiler.trace_requests:
|
||||
|
||||
+12
-7
@@ -46,9 +46,9 @@ def get():
|
||||
def set(notifier):
|
||||
"""Service that are going to use profiler should set callable notifier.
|
||||
|
||||
Callable notifier is instance of callable object, that accept exactly
|
||||
one argument "info". "info" - is dictionary of values that contains
|
||||
profiling information.
|
||||
Callable notifier is instance of callable object, that accept exactly
|
||||
one argument "info". "info" - is dictionary of values that contains
|
||||
profiling information.
|
||||
"""
|
||||
global __notifier
|
||||
__notifier = notifier
|
||||
@@ -68,11 +68,16 @@ def create(connection_string, *args, **kwargs):
|
||||
try:
|
||||
driver = base.get_driver(connection_string, *args, **kwargs)
|
||||
__notifier_cache[connection_string] = driver.notify
|
||||
LOG.info("osprofiler is enabled with connection string: %s",
|
||||
connection_string)
|
||||
LOG.info(
|
||||
"osprofiler is enabled with connection string: %s",
|
||||
connection_string,
|
||||
)
|
||||
except Exception:
|
||||
LOG.exception("Could not initialize driver for connection string "
|
||||
"%s, osprofiler is disabled", connection_string)
|
||||
LOG.exception(
|
||||
"Could not initialize driver for connection string "
|
||||
"%s, osprofiler is disabled",
|
||||
connection_string,
|
||||
)
|
||||
__notifier_cache[connection_string] = _noop_notifier
|
||||
|
||||
return __notifier_cache[connection_string]
|
||||
|
||||
+84
-49
@@ -29,7 +29,8 @@ _profiler_opt_group = cfg.OptGroup(
|
||||
OSprofiler library allows to trace requests going through various OpenStack
|
||||
services and create the accumulated report of what time was spent on each
|
||||
request processing step.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_enabled_opt = cfg.BoolOpt(
|
||||
"enabled",
|
||||
@@ -46,7 +47,8 @@ Possible values:
|
||||
* False: Disables the feature. The profiling cannot be started via this project
|
||||
operations. If the profiling is triggered by another project, this project
|
||||
part will be empty.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_trace_sqlalchemy_opt = cfg.BoolOpt(
|
||||
"trace_sqlalchemy",
|
||||
@@ -62,7 +64,8 @@ Possible values:
|
||||
trace and can the be analyzed by how much time was spent for that.
|
||||
* False: Disables SQL requests profiling. The spent time is only shown on a
|
||||
higher level of operations. Single SQL queries cannot be analyzed this way.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_trace_requests_opt = cfg.BoolOpt(
|
||||
"trace_requests",
|
||||
@@ -78,7 +81,8 @@ Possible values:
|
||||
|
||||
* True: Enables requests profiling.
|
||||
* False: Disables requests profiling.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_hmac_keys_opt = cfg.StrOpt(
|
||||
"hmac_keys",
|
||||
@@ -97,7 +101,8 @@ profiling. Also, to generate correct profiling information across all services
|
||||
at least one key needs to be consistent between OpenStack projects. This
|
||||
ensures it can be used from client side to generate the trace, containing
|
||||
information from all possible resources.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_connection_string_opt = cfg.StrOpt(
|
||||
"connection_string",
|
||||
@@ -116,14 +121,16 @@ Examples of possible values:
|
||||
spans.
|
||||
* ``otlp://127.0.0.1:4318`` - use OpenTelementry as driver for sending spans
|
||||
to jaeger.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_es_doc_type_opt = cfg.StrOpt(
|
||||
"es_doc_type",
|
||||
default="notification",
|
||||
help="""
|
||||
Document type for notification indexing in elasticsearch.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_es_scroll_time_opt = cfg.StrOpt(
|
||||
"es_scroll_time",
|
||||
@@ -132,7 +139,8 @@ _es_scroll_time_opt = cfg.StrOpt(
|
||||
This parameter is a time value parameter (for example: es_scroll_time=2m),
|
||||
indicating for how long the nodes that participate in the search will maintain
|
||||
relevant resources in order to continue and support it.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_es_scroll_size_opt = cfg.IntOpt(
|
||||
"es_scroll_size",
|
||||
@@ -140,7 +148,8 @@ _es_scroll_size_opt = cfg.IntOpt(
|
||||
help="""
|
||||
Elasticsearch splits large requests in batches. This parameter defines
|
||||
maximum size of each batch (for example: es_scroll_size=10000).
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_socket_timeout_opt = cfg.FloatOpt(
|
||||
"socket_timeout",
|
||||
@@ -148,7 +157,8 @@ _socket_timeout_opt = cfg.FloatOpt(
|
||||
help="""
|
||||
Redissentinel provides a timeout option on the connections.
|
||||
This parameter defines that timeout (for example: socket_timeout=0.1).
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_sentinel_service_name_opt = cfg.StrOpt(
|
||||
"sentinel_service_name",
|
||||
@@ -157,7 +167,8 @@ _sentinel_service_name_opt = cfg.StrOpt(
|
||||
Redissentinel uses a service name to identify a master redis service.
|
||||
This parameter defines the name (for example:
|
||||
``sentinal_service_name=mymaster``).
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_filter_error_trace = cfg.BoolOpt(
|
||||
"filter_error_trace",
|
||||
@@ -171,7 +182,8 @@ Possible values:
|
||||
|
||||
* True: Enable filter traces that contain error/exception.
|
||||
* False: Disable the filter.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_PROFILER_OPTS = [
|
||||
_enabled_opt,
|
||||
@@ -184,14 +196,14 @@ _PROFILER_OPTS = [
|
||||
_es_scroll_size_opt,
|
||||
_socket_timeout_opt,
|
||||
_sentinel_service_name_opt,
|
||||
_filter_error_trace
|
||||
_filter_error_trace,
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(_PROFILER_OPTS, group=_profiler_opt_group)
|
||||
|
||||
_jaegerprofiler_opt_group = cfg.OptGroup(
|
||||
"profiler_jaeger",
|
||||
title="Jaeger's profiler driver related options")
|
||||
"profiler_jaeger", title="Jaeger's profiler driver related options"
|
||||
)
|
||||
|
||||
_service_name_prefix = cfg.StrOpt(
|
||||
"service_name_prefix",
|
||||
@@ -199,7 +211,8 @@ _service_name_prefix = cfg.StrOpt(
|
||||
deprecated_reason="Jager driver is no longer supported",
|
||||
help="""
|
||||
Set service name prefix to Jaeger service name.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_process_tags = cfg.DictOpt(
|
||||
"process_tags",
|
||||
@@ -208,24 +221,23 @@ _process_tags = cfg.DictOpt(
|
||||
deprecated_reason="Jager driver is no longer supported",
|
||||
help="""
|
||||
Set process tracer tags.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_JAEGER_OPTS = [
|
||||
_service_name_prefix,
|
||||
_process_tags
|
||||
]
|
||||
_JAEGER_OPTS = [_service_name_prefix, _process_tags]
|
||||
|
||||
cfg.CONF.register_opts(_JAEGER_OPTS, group=_jaegerprofiler_opt_group)
|
||||
|
||||
_otlp_profiler_opt_group = cfg.OptGroup(
|
||||
"profiler_otlp",
|
||||
title="OTLP's profiler driver related options")
|
||||
"profiler_otlp", title="OTLP's profiler driver related options"
|
||||
)
|
||||
|
||||
_otlp_service_name_prefix = cfg.StrOpt(
|
||||
"service_name_prefix",
|
||||
help="""
|
||||
Set service name prefix to OTLP exporters.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
_OTLP_OPTS = [
|
||||
_otlp_service_name_prefix,
|
||||
@@ -234,45 +246,66 @@ _OTLP_OPTS = [
|
||||
cfg.CONF.register_opts(_OTLP_OPTS, group=_otlp_profiler_opt_group)
|
||||
|
||||
|
||||
def set_defaults(conf, enabled=None, trace_sqlalchemy=None, hmac_keys=None,
|
||||
connection_string=None, es_doc_type=None,
|
||||
es_scroll_time=None, es_scroll_size=None,
|
||||
socket_timeout=None, sentinel_service_name=None):
|
||||
def set_defaults(
|
||||
conf,
|
||||
enabled=None,
|
||||
trace_sqlalchemy=None,
|
||||
hmac_keys=None,
|
||||
connection_string=None,
|
||||
es_doc_type=None,
|
||||
es_scroll_time=None,
|
||||
es_scroll_size=None,
|
||||
socket_timeout=None,
|
||||
sentinel_service_name=None,
|
||||
):
|
||||
conf.register_opts(_PROFILER_OPTS, group=_profiler_opt_group)
|
||||
|
||||
if enabled is not None:
|
||||
conf.set_default("enabled", enabled,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default("enabled", enabled, group=_profiler_opt_group.name)
|
||||
if trace_sqlalchemy is not None:
|
||||
conf.set_default("trace_sqlalchemy", trace_sqlalchemy,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default(
|
||||
"trace_sqlalchemy",
|
||||
trace_sqlalchemy,
|
||||
group=_profiler_opt_group.name,
|
||||
)
|
||||
if hmac_keys is not None:
|
||||
conf.set_default("hmac_keys", hmac_keys,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default(
|
||||
"hmac_keys", hmac_keys, group=_profiler_opt_group.name
|
||||
)
|
||||
|
||||
if connection_string is not None:
|
||||
conf.set_default("connection_string", connection_string,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default(
|
||||
"connection_string",
|
||||
connection_string,
|
||||
group=_profiler_opt_group.name,
|
||||
)
|
||||
|
||||
if es_doc_type is not None:
|
||||
conf.set_default("es_doc_type", es_doc_type,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default(
|
||||
"es_doc_type", es_doc_type, group=_profiler_opt_group.name
|
||||
)
|
||||
|
||||
if es_scroll_time is not None:
|
||||
conf.set_default("es_scroll_time", es_scroll_time,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default(
|
||||
"es_scroll_time", es_scroll_time, group=_profiler_opt_group.name
|
||||
)
|
||||
|
||||
if es_scroll_size is not None:
|
||||
conf.set_default("es_scroll_size", es_scroll_size,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default(
|
||||
"es_scroll_size", es_scroll_size, group=_profiler_opt_group.name
|
||||
)
|
||||
|
||||
if socket_timeout is not None:
|
||||
conf.set_default("socket_timeout", socket_timeout,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default(
|
||||
"socket_timeout", socket_timeout, group=_profiler_opt_group.name
|
||||
)
|
||||
|
||||
if sentinel_service_name is not None:
|
||||
conf.set_default("sentinel_service_name", sentinel_service_name,
|
||||
group=_profiler_opt_group.name)
|
||||
conf.set_default(
|
||||
"sentinel_service_name",
|
||||
sentinel_service_name,
|
||||
group=_profiler_opt_group.name,
|
||||
)
|
||||
|
||||
|
||||
def is_trace_enabled(conf=None):
|
||||
@@ -302,6 +335,8 @@ def disable_web_trace(conf=None):
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [(_profiler_opt_group.name, _PROFILER_OPTS),
|
||||
(_jaegerprofiler_opt_group, _JAEGER_OPTS),
|
||||
(_otlp_profiler_opt_group, _OTLP_OPTS)]
|
||||
return [
|
||||
(_profiler_opt_group.name, _PROFILER_OPTS),
|
||||
(_jaegerprofiler_opt_group, _JAEGER_OPTS),
|
||||
(_otlp_profiler_opt_group, _OTLP_OPTS),
|
||||
]
|
||||
|
||||
+47
-28
@@ -39,10 +39,11 @@ def _ensure_no_multiple_traced(traceable_attrs):
|
||||
for attr_name, attr in traceable_attrs:
|
||||
traced_times = getattr(attr, "__traced__", 0)
|
||||
if traced_times:
|
||||
raise ValueError("Can not apply new trace on top of"
|
||||
" previously traced attribute '%s' since"
|
||||
" it has been traced %s times previously"
|
||||
% (attr_name, traced_times))
|
||||
raise ValueError(
|
||||
"Can not apply new trace on top of"
|
||||
f" previously traced attribute '{attr_name}' since"
|
||||
f" it has been traced {traced_times} times previously"
|
||||
)
|
||||
|
||||
|
||||
def init(hmac_key, base_id=None, parent_id=None):
|
||||
@@ -57,8 +58,9 @@ def init(hmac_key, base_id=None, parent_id=None):
|
||||
:returns: Profiler instance
|
||||
"""
|
||||
if get() is None:
|
||||
__local_ctx.profiler = _Profiler(hmac_key, base_id=base_id,
|
||||
parent_id=parent_id)
|
||||
__local_ctx.profiler = _Profiler(
|
||||
hmac_key, base_id=base_id, parent_id=parent_id
|
||||
)
|
||||
return __local_ctx.profiler
|
||||
|
||||
|
||||
@@ -89,8 +91,13 @@ def stop(info=None):
|
||||
profiler.stop(info=info)
|
||||
|
||||
|
||||
def trace(name, info=None, hide_args=False, hide_result=True,
|
||||
allow_multiple_trace=True):
|
||||
def trace(
|
||||
name,
|
||||
info=None,
|
||||
hide_args=False,
|
||||
hide_result=True,
|
||||
allow_multiple_trace=True,
|
||||
):
|
||||
"""Trace decorator for functions.
|
||||
|
||||
Very useful if you would like to add trace point on existing function:
|
||||
@@ -122,8 +129,9 @@ def trace(name, info=None, hide_args=False, hide_result=True,
|
||||
def decorator(f):
|
||||
trace_times = getattr(f, "__traced__", 0)
|
||||
if not allow_multiple_trace and trace_times:
|
||||
raise ValueError("Function '%s' has already"
|
||||
" been traced %s times" % (f, trace_times))
|
||||
raise ValueError(
|
||||
f"Function '{f}' has already been traced {trace_times} times"
|
||||
)
|
||||
|
||||
try:
|
||||
f.__traced__ = trace_times + 1
|
||||
@@ -160,7 +168,7 @@ def trace(name, info=None, hide_args=False, hide_result=True,
|
||||
except Exception as ex:
|
||||
stop_info = {
|
||||
"etype": reflection.get_class_name(ex),
|
||||
"message": str(ex)
|
||||
"message": str(ex),
|
||||
}
|
||||
raise
|
||||
else:
|
||||
@@ -175,9 +183,16 @@ def trace(name, info=None, hide_args=False, hide_result=True,
|
||||
return decorator
|
||||
|
||||
|
||||
def trace_cls(name, info=None, hide_args=False, hide_result=True,
|
||||
trace_private=False, allow_multiple_trace=True,
|
||||
trace_class_methods=False, trace_static_methods=False):
|
||||
def trace_cls(
|
||||
name,
|
||||
info=None,
|
||||
hide_args=False,
|
||||
hide_result=True,
|
||||
trace_private=False,
|
||||
allow_multiple_trace=True,
|
||||
trace_class_methods=False,
|
||||
trace_static_methods=False,
|
||||
):
|
||||
"""Trace decorator for instances of class .
|
||||
|
||||
Very useful if you would like to add trace point on existing method:
|
||||
@@ -254,8 +269,9 @@ def trace_cls(name, info=None, hide_args=False, hide_result=True,
|
||||
# halfway trace this class).
|
||||
_ensure_no_multiple_traced(traceable_attrs)
|
||||
for i, (attr_name, attr) in enumerate(traceable_attrs):
|
||||
wrapped_method = trace(name, info=info, hide_args=hide_args,
|
||||
hide_result=hide_result)(attr)
|
||||
wrapped_method = trace(
|
||||
name, info=info, hide_args=hide_args, hide_result=hide_result
|
||||
)(attr)
|
||||
wrapper = traceable_wrappers[i]
|
||||
if wrapper is not None:
|
||||
wrapped_method = wrapper(wrapped_method)
|
||||
@@ -288,6 +304,7 @@ class TracedMeta(type):
|
||||
mandatory key included - "name", that will define name of action to be
|
||||
traced - E.g. wsgi, rpc, db, etc...
|
||||
"""
|
||||
|
||||
def __init__(cls, cls_name, bases, attrs):
|
||||
super().__init__(cls_name, bases, attrs)
|
||||
|
||||
@@ -295,14 +312,17 @@ class TracedMeta(type):
|
||||
trace_private = trace_args.pop("trace_private", False)
|
||||
allow_multiple_trace = trace_args.pop("allow_multiple_trace", True)
|
||||
if "name" not in trace_args:
|
||||
raise TypeError("Please specify __trace_args__ class level "
|
||||
"dictionary attribute with mandatory 'name' key - "
|
||||
"e.g. __trace_args__ = {'name': 'rpc'}")
|
||||
raise TypeError(
|
||||
"Please specify __trace_args__ class level "
|
||||
"dictionary attribute with mandatory 'name' key - "
|
||||
"e.g. __trace_args__ = {'name': 'rpc'}"
|
||||
)
|
||||
|
||||
traceable_attrs = []
|
||||
for attr_name, attr_value in attrs.items():
|
||||
if not (inspect.ismethod(attr_value)
|
||||
or inspect.isfunction(attr_value)):
|
||||
if not (
|
||||
inspect.ismethod(attr_value) or inspect.isfunction(attr_value)
|
||||
):
|
||||
continue
|
||||
if attr_name.startswith("__"):
|
||||
continue
|
||||
@@ -314,12 +334,12 @@ class TracedMeta(type):
|
||||
# halfway trace this class).
|
||||
_ensure_no_multiple_traced(traceable_attrs)
|
||||
for attr_name, attr_value in traceable_attrs:
|
||||
setattr(cls, attr_name, trace(**trace_args)(getattr(cls,
|
||||
attr_name)))
|
||||
setattr(
|
||||
cls, attr_name, trace(**trace_args)(getattr(cls, attr_name))
|
||||
)
|
||||
|
||||
|
||||
class Trace:
|
||||
|
||||
def __init__(self, name, info=None):
|
||||
"""With statement way to use profiler start()/stop().
|
||||
|
||||
@@ -346,13 +366,12 @@ class Trace:
|
||||
if etype:
|
||||
info = {
|
||||
"etype": reflection.get_class_name(etype),
|
||||
"message": value.args[0] if value.args else None
|
||||
"message": value.args[0] if value.args else None,
|
||||
}
|
||||
stop(info=info)
|
||||
|
||||
|
||||
class _Profiler:
|
||||
|
||||
def __init__(self, hmac_key, base_id=None, parent_id=None):
|
||||
self.hmac_key = hmac_key
|
||||
if not base_id:
|
||||
@@ -403,7 +422,7 @@ class _Profiler:
|
||||
info["host"] = self._host
|
||||
self._name.append(name)
|
||||
self._trace_stack.append(str(uuidutils.generate_uuid()))
|
||||
self._notify("%s-start" % name, info)
|
||||
self._notify(f"{name}-start", info)
|
||||
|
||||
def stop(self, info=None):
|
||||
"""Finish latest event.
|
||||
@@ -414,7 +433,7 @@ class _Profiler:
|
||||
"""
|
||||
info = info or {}
|
||||
info["host"] = self._host
|
||||
self._notify("%s-stop" % self._name.pop(), info)
|
||||
self._notify(f"{self._name.pop()}-stop", info)
|
||||
self._trace_stack.pop()
|
||||
|
||||
def _notify(self, name, info):
|
||||
|
||||
+20
-13
@@ -31,6 +31,7 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
|
||||
def send(self, request, *args, **kwargs):
|
||||
parsed_url = parser.urlparse(request.url)
|
||||
|
||||
@@ -41,23 +42,27 @@ else:
|
||||
elif not port and parsed_url.scheme == "https":
|
||||
port = 443
|
||||
|
||||
profiler.start(parsed_url.scheme, info={"requests": {
|
||||
"method": request.method,
|
||||
"query": parsed_url.query,
|
||||
"path": parsed_url.path,
|
||||
"hostname": parsed_url.hostname,
|
||||
"port": port,
|
||||
"scheme": parsed_url.scheme}})
|
||||
profiler.start(
|
||||
parsed_url.scheme,
|
||||
info={
|
||||
"requests": {
|
||||
"method": request.method,
|
||||
"query": parsed_url.query,
|
||||
"path": parsed_url.path,
|
||||
"hostname": parsed_url.hostname,
|
||||
"port": port,
|
||||
"scheme": parsed_url.scheme,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Profiling headers are overrident to take in account this new
|
||||
# context/span.
|
||||
request.headers.update(
|
||||
web.get_trace_id_headers())
|
||||
request.headers.update(web.get_trace_id_headers())
|
||||
|
||||
response = _FUNC(self, request, *args, **kwargs)
|
||||
|
||||
profiler.stop(info={"requests": {
|
||||
"status_code": response.status_code}})
|
||||
profiler.stop(info={"requests": {"status_code": response.status_code}})
|
||||
|
||||
return response
|
||||
|
||||
@@ -69,5 +74,7 @@ def enable():
|
||||
HTTPAdapter.send = send
|
||||
LOG.debug("profiling requests enabled")
|
||||
else:
|
||||
LOG.warning("unable to activate profiling for requests, "
|
||||
"please ensure that python requests is installed.")
|
||||
LOG.warning(
|
||||
"unable to activate profiling for requests, "
|
||||
"please ensure that python requests is installed."
|
||||
)
|
||||
|
||||
+15
-18
@@ -42,11 +42,13 @@ def add_tracing(sqlalchemy, engine, name, hide_result=True):
|
||||
"""Add tracing to all sqlalchemy calls."""
|
||||
|
||||
if not _DISABLED:
|
||||
sqlalchemy.event.listen(engine, "before_cursor_execute",
|
||||
_before_cursor_execute(name))
|
||||
sqlalchemy.event.listen(
|
||||
engine, "after_cursor_execute",
|
||||
_after_cursor_execute(hide_result=hide_result)
|
||||
engine, "before_cursor_execute", _before_cursor_execute(name)
|
||||
)
|
||||
sqlalchemy.event.listen(
|
||||
engine,
|
||||
"after_cursor_execute",
|
||||
_after_cursor_execute(hide_result=hide_result),
|
||||
)
|
||||
sqlalchemy.event.listen(engine, "handle_error", handle_error)
|
||||
|
||||
@@ -64,10 +66,7 @@ def _before_cursor_execute(name):
|
||||
"""Add listener that will send trace info before query is executed."""
|
||||
|
||||
def handler(conn, cursor, statement, params, context, executemany):
|
||||
info = {"db": {
|
||||
"statement": statement,
|
||||
"params": params}
|
||||
}
|
||||
info = {"db": {"statement": statement, "params": params}}
|
||||
profiler.start(name, info=info)
|
||||
|
||||
return handler
|
||||
@@ -84,11 +83,7 @@ def _after_cursor_execute(hide_result=True):
|
||||
def handler(conn, cursor, statement, params, context, executemany):
|
||||
if not hide_result:
|
||||
# Add SQL result to trace info in *-stop phase
|
||||
info = {
|
||||
"db": {
|
||||
"result": str(cursor._rows)
|
||||
}
|
||||
}
|
||||
info = {"db": {"result": str(cursor._rows)}}
|
||||
profiler.stop(info=info)
|
||||
else:
|
||||
profiler.stop()
|
||||
@@ -99,7 +94,8 @@ def _after_cursor_execute(hide_result=True):
|
||||
def handle_error(exception_context):
|
||||
"""Handle SQLAlchemy errors"""
|
||||
exception_class_name = reflection.get_class_name(
|
||||
exception_context.original_exception)
|
||||
exception_context.original_exception
|
||||
)
|
||||
original_exception = str(exception_context.original_exception)
|
||||
chained_exception = str(exception_context.chained_exception)
|
||||
|
||||
@@ -108,9 +104,10 @@ def handle_error(exception_context):
|
||||
"message": original_exception,
|
||||
"db": {
|
||||
"original_exception": original_exception,
|
||||
"chained_exception": chained_exception
|
||||
}
|
||||
"chained_exception": chained_exception,
|
||||
},
|
||||
}
|
||||
profiler.stop(info=info)
|
||||
LOG.debug("OSProfiler has handled SQLAlchemy error: %s",
|
||||
original_exception)
|
||||
LOG.debug(
|
||||
"OSProfiler has handled SQLAlchemy error: %s", original_exception
|
||||
)
|
||||
|
||||
@@ -39,17 +39,15 @@ class Foo:
|
||||
|
||||
|
||||
class DriverTestCase(test.FunctionalTestCase):
|
||||
|
||||
SERVICE = "service"
|
||||
PROJECT = "project"
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
CONF(["--config-file", os.path.dirname(__file__) + "/config.cfg"])
|
||||
opts.set_defaults(CONF,
|
||||
enabled=True,
|
||||
trace_sqlalchemy=False,
|
||||
hmac_keys="SECRET_KEY")
|
||||
opts.set_defaults(
|
||||
CONF, enabled=True, trace_sqlalchemy=False, hmac_keys="SECRET_KEY"
|
||||
)
|
||||
|
||||
def _assert_dict(self, info, **kwargs):
|
||||
for key in kwargs:
|
||||
@@ -58,28 +56,33 @@ class DriverTestCase(test.FunctionalTestCase):
|
||||
def _assert_child_dict(self, child, base_id, parent_id, name, fn_name):
|
||||
self.assertEqual(parent_id, child["parent_id"])
|
||||
|
||||
exp_info = {"name": "rpc",
|
||||
"service": self.SERVICE,
|
||||
"project": self.PROJECT}
|
||||
exp_info = {
|
||||
"name": "rpc",
|
||||
"service": self.SERVICE,
|
||||
"project": self.PROJECT,
|
||||
}
|
||||
self._assert_dict(child["info"], **exp_info)
|
||||
|
||||
raw_start = child["info"]["meta.raw_payload.%s-start" % name]
|
||||
raw_start = child["info"][f"meta.raw_payload.{name}-start"]
|
||||
self.assertEqual(fn_name, raw_start["info"]["function"]["name"])
|
||||
exp_raw = {"name": "%s-start" % name,
|
||||
"service": self.SERVICE,
|
||||
"trace_id": child["trace_id"],
|
||||
"project": self.PROJECT,
|
||||
"base_id": base_id}
|
||||
exp_raw = {
|
||||
"name": f"{name}-start",
|
||||
"service": self.SERVICE,
|
||||
"trace_id": child["trace_id"],
|
||||
"project": self.PROJECT,
|
||||
"base_id": base_id,
|
||||
}
|
||||
self._assert_dict(raw_start, **exp_raw)
|
||||
|
||||
raw_stop = child["info"]["meta.raw_payload.%s-stop" % name]
|
||||
exp_raw["name"] = "%s-stop" % name
|
||||
raw_stop = child["info"][f"meta.raw_payload.{name}-stop"]
|
||||
exp_raw["name"] = f"{name}-stop"
|
||||
self._assert_dict(raw_stop, **exp_raw)
|
||||
|
||||
def test_get_report(self):
|
||||
# initialize profiler notifier (the same way as in services)
|
||||
initializer.init_from_conf(
|
||||
CONF, {}, self.PROJECT, self.SERVICE, "host")
|
||||
CONF, {}, self.PROJECT, self.SERVICE, "host"
|
||||
)
|
||||
profiler.init("SECRET_KEY")
|
||||
|
||||
# grab base_id
|
||||
@@ -90,11 +93,13 @@ class DriverTestCase(test.FunctionalTestCase):
|
||||
foo.bar(1)
|
||||
|
||||
# instantiate report engine (the same way as in osprofiler CLI)
|
||||
engine = base.get_driver(CONF.profiler.connection_string,
|
||||
project=self.PROJECT,
|
||||
service=self.SERVICE,
|
||||
host="host",
|
||||
conf=CONF)
|
||||
engine = base.get_driver(
|
||||
CONF.profiler.connection_string,
|
||||
project=self.PROJECT,
|
||||
service=self.SERVICE,
|
||||
host="host",
|
||||
conf=CONF,
|
||||
)
|
||||
|
||||
# generate the report
|
||||
report = engine.get_report(base_id)
|
||||
@@ -107,30 +112,41 @@ class DriverTestCase(test.FunctionalTestCase):
|
||||
|
||||
cbar = report["children"][0]
|
||||
self._assert_child_dict(
|
||||
cbar, base_id, base_id, "rpc",
|
||||
"osprofiler.tests.functional.test_driver.Foo.bar")
|
||||
cbar,
|
||||
base_id,
|
||||
base_id,
|
||||
"rpc",
|
||||
"osprofiler.tests.functional.test_driver.Foo.bar",
|
||||
)
|
||||
|
||||
self.assertEqual(1, len(cbar["children"]))
|
||||
cbaz = cbar["children"][0]
|
||||
self._assert_child_dict(
|
||||
cbaz, base_id, cbar["trace_id"], "rpc",
|
||||
"osprofiler.tests.functional.test_driver.Foo.baz")
|
||||
cbaz,
|
||||
base_id,
|
||||
cbar["trace_id"],
|
||||
"rpc",
|
||||
"osprofiler.tests.functional.test_driver.Foo.baz",
|
||||
)
|
||||
|
||||
|
||||
class RedisDriverTestCase(DriverTestCase):
|
||||
def setUp(self):
|
||||
super(DriverTestCase, self).setUp()
|
||||
CONF([])
|
||||
opts.set_defaults(CONF,
|
||||
connection_string="redis://localhost:6379",
|
||||
enabled=True,
|
||||
trace_sqlalchemy=False,
|
||||
hmac_keys="SECRET_KEY")
|
||||
opts.set_defaults(
|
||||
CONF,
|
||||
connection_string="redis://localhost:6379",
|
||||
enabled=True,
|
||||
trace_sqlalchemy=False,
|
||||
hmac_keys="SECRET_KEY",
|
||||
)
|
||||
|
||||
def test_list_traces(self):
|
||||
# initialize profiler notifier (the same way as in services)
|
||||
initializer.init_from_conf(
|
||||
CONF, {}, self.PROJECT, self.SERVICE, "host")
|
||||
CONF, {}, self.PROJECT, self.SERVICE, "host"
|
||||
)
|
||||
profiler.init("SECRET_KEY")
|
||||
|
||||
# grab base_id
|
||||
@@ -141,11 +157,13 @@ class RedisDriverTestCase(DriverTestCase):
|
||||
foo.bar(1)
|
||||
|
||||
# instantiate report engine (the same way as in osprofiler CLI)
|
||||
engine = base.get_driver(CONF.profiler.connection_string,
|
||||
project=self.PROJECT,
|
||||
service=self.SERVICE,
|
||||
host="host",
|
||||
conf=CONF)
|
||||
engine = base.get_driver(
|
||||
CONF.profiler.connection_string,
|
||||
project=self.PROJECT,
|
||||
service=self.SERVICE,
|
||||
host="host",
|
||||
conf=CONF,
|
||||
)
|
||||
|
||||
# generate the report
|
||||
traces = engine.list_traces()
|
||||
|
||||
@@ -21,6 +21,7 @@ from testtools import testcase
|
||||
|
||||
class TestCase(testcase.TestCase):
|
||||
"""Test case base class for all osprofiler unit tests."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ from osprofiler.tests import test
|
||||
|
||||
@ddt.ddt
|
||||
class ShellTestCase(test.TestCase):
|
||||
|
||||
TRACE_ID = "c598094d-bbee-40b6-b317-d76003b679d3"
|
||||
|
||||
def setUp(self):
|
||||
@@ -40,8 +39,8 @@ class ShellTestCase(test.TestCase):
|
||||
os.environ = self.old_environment
|
||||
|
||||
def _trace_show_cmd(self, format_=None):
|
||||
cmd = "trace show --connection-string redis:// %s" % self.TRACE_ID
|
||||
return cmd if format_ is None else "{} --{}".format(cmd, format_)
|
||||
cmd = f"trace show --connection-string redis:// {self.TRACE_ID}"
|
||||
return cmd if format_ is None else f"{cmd} --{format_}"
|
||||
|
||||
@mock.patch("sys.stdout", io.StringIO())
|
||||
@mock.patch("osprofiler.cmd.shell.OSProfilerShell")
|
||||
@@ -61,41 +60,45 @@ class ShellTestCase(test.TestCase):
|
||||
else:
|
||||
raise ValueError(
|
||||
"Expected: `osprofiler.exc.CommandError` is raised with "
|
||||
"message: '%s'." % expected_message)
|
||||
f"message: '{expected_message}'."
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.drivers.redis_driver.Redis.get_report")
|
||||
def test_trace_show_no_selected_format(self, mock_get):
|
||||
mock_get.return_value = self._create_mock_notifications()
|
||||
msg = ("You should choose one of the following output formats: "
|
||||
"json, html or dot.")
|
||||
msg = (
|
||||
"You should choose one of the following output formats: "
|
||||
"json, html or dot."
|
||||
)
|
||||
self._test_with_command_error(self._trace_show_cmd(), msg)
|
||||
|
||||
@mock.patch("osprofiler.drivers.redis_driver.Redis.get_report")
|
||||
@ddt.data(None, {"info": {"started": 0, "finished": 1, "name": "total"},
|
||||
"children": []})
|
||||
@ddt.data(
|
||||
None,
|
||||
{
|
||||
"info": {"started": 0, "finished": 1, "name": "total"},
|
||||
"children": [],
|
||||
},
|
||||
)
|
||||
def test_trace_show_trace_id_not_found(self, notifications, mock_get):
|
||||
mock_get.return_value = notifications
|
||||
|
||||
msg = ("Trace with UUID %s not found. Please check the HMAC key "
|
||||
"used in the command." % self.TRACE_ID)
|
||||
msg = (
|
||||
f"Trace with UUID {self.TRACE_ID} not found. Please check the "
|
||||
f"HMAC key used in the command."
|
||||
)
|
||||
|
||||
self._test_with_command_error(self._trace_show_cmd(), msg)
|
||||
|
||||
def _create_mock_notifications(self):
|
||||
notifications = {
|
||||
"info": {
|
||||
"started": 0,
|
||||
"finished": 1,
|
||||
"name": "total"
|
||||
},
|
||||
"children": [{
|
||||
"info": {
|
||||
"started": 0,
|
||||
"finished": 1,
|
||||
"name": "total"
|
||||
},
|
||||
"children": []
|
||||
}]
|
||||
"info": {"started": 0, "finished": 1, "name": "total"},
|
||||
"children": [
|
||||
{
|
||||
"info": {"started": 0, "finished": 1, "name": "total"},
|
||||
"children": [],
|
||||
}
|
||||
],
|
||||
}
|
||||
return notifications
|
||||
|
||||
@@ -106,9 +109,16 @@ class ShellTestCase(test.TestCase):
|
||||
mock_get.return_value = notifications
|
||||
|
||||
self.run_command(self._trace_show_cmd(format_="json"))
|
||||
self.assertEqual("%s\n" % json.dumps(notifications, indent=2,
|
||||
separators=(",", ": "),),
|
||||
sys.stdout.getvalue())
|
||||
self.assertEqual(
|
||||
"{}\n".format(
|
||||
json.dumps(
|
||||
notifications,
|
||||
indent=2,
|
||||
separators=(",", ": "),
|
||||
)
|
||||
),
|
||||
sys.stdout.getvalue(),
|
||||
)
|
||||
|
||||
@mock.patch("sys.stdout", io.StringIO())
|
||||
@mock.patch("osprofiler.drivers.redis_driver.Redis.get_report")
|
||||
@@ -124,20 +134,27 @@ class ShellTestCase(test.TestCase):
|
||||
"It is a period of civil war. Rebel"
|
||||
"spaceships, striking from a hidden"
|
||||
"base, have won their first victory"
|
||||
"against the evil Galactic Empire.")
|
||||
"against the evil Galactic Empire."
|
||||
)
|
||||
|
||||
with mock.patch("osprofiler.cmd.commands.open",
|
||||
mock.mock_open(read_data=html_template), create=True):
|
||||
with mock.patch(
|
||||
"osprofiler.cmd.commands.open",
|
||||
mock.mock_open(read_data=html_template),
|
||||
create=True,
|
||||
):
|
||||
self.run_command(self._trace_show_cmd(format_="html"))
|
||||
self.assertEqual("A long time ago in a galaxy far, far away..."
|
||||
" some_data = %s"
|
||||
"It is a period of civil war. Rebel"
|
||||
"spaceships, striking from a hidden"
|
||||
"base, have won their first victory"
|
||||
"against the evil Galactic Empire."
|
||||
"\n" % json.dumps(notifications, indent=4,
|
||||
separators=(",", ": ")),
|
||||
sys.stdout.getvalue())
|
||||
self.assertEqual(
|
||||
"A long time ago in a galaxy far, far away..."
|
||||
" some_data = {}"
|
||||
"It is a period of civil war. Rebel"
|
||||
"spaceships, striking from a hidden"
|
||||
"base, have won their first victory"
|
||||
"against the evil Galactic Empire."
|
||||
"\n".format(
|
||||
json.dumps(notifications, indent=4, separators=(",", ": "))
|
||||
),
|
||||
sys.stdout.getvalue(),
|
||||
)
|
||||
|
||||
@mock.patch("sys.stdout", io.StringIO())
|
||||
@mock.patch("osprofiler.drivers.redis_driver.Redis.get_report")
|
||||
@@ -145,11 +162,14 @@ class ShellTestCase(test.TestCase):
|
||||
notifications = self._create_mock_notifications()
|
||||
mock_get.return_value = notifications
|
||||
|
||||
with mock.patch("osprofiler.cmd.commands.open",
|
||||
mock.mock_open(), create=True) as mock_open:
|
||||
self.run_command("%s --out='/file'" %
|
||||
self._trace_show_cmd(format_="json"))
|
||||
with mock.patch(
|
||||
"osprofiler.cmd.commands.open", mock.mock_open(), create=True
|
||||
) as mock_open:
|
||||
self.run_command(
|
||||
"{} --out='/file'".format(self._trace_show_cmd(format_="json"))
|
||||
)
|
||||
|
||||
output = mock_open.return_value.__enter__.return_value
|
||||
output.write.assert_called_once_with(
|
||||
json.dumps(notifications, indent=2, separators=(",", ": ")))
|
||||
json.dumps(notifications, indent=2, separators=(",", ": "))
|
||||
)
|
||||
|
||||
@@ -20,11 +20,15 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class TitlesTestCase(test.TestCase):
|
||||
|
||||
specs_path = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
os.pardir, os.pardir, os.pardir, os.pardir,
|
||||
"doc", "specs")
|
||||
os.pardir,
|
||||
os.pardir,
|
||||
os.pardir,
|
||||
os.pardir,
|
||||
"doc",
|
||||
"specs",
|
||||
)
|
||||
|
||||
def _get_title(self, section_tree):
|
||||
section = {"subtitles": []}
|
||||
@@ -51,21 +55,27 @@ class TitlesTestCase(test.TestCase):
|
||||
|
||||
msgs = []
|
||||
if len(missing_sections) > 0:
|
||||
msgs.append("Missing sections: %s" % missing_sections)
|
||||
msgs.append(f"Missing sections: {missing_sections}")
|
||||
if len(extra_sections) > 0:
|
||||
msgs.append("Extra sections: %s" % extra_sections)
|
||||
msgs.append(f"Extra sections: {extra_sections}")
|
||||
|
||||
for section in expect.keys():
|
||||
missing_subsections = [x for x in expect[section]
|
||||
if x not in actual.get(section, {})]
|
||||
missing_subsections = [
|
||||
x for x in expect[section] if x not in actual.get(section, {})
|
||||
]
|
||||
# extra subsections are allowed
|
||||
if len(missing_subsections) > 0:
|
||||
msgs.append("Section '%s' is missing subsections: %s"
|
||||
% (section, missing_subsections))
|
||||
msgs.append(
|
||||
f"Section '{section}' is missing subsections: "
|
||||
f"{missing_subsections}"
|
||||
)
|
||||
|
||||
if len(msgs) > 0:
|
||||
self.fail("While checking '%s':\n %s"
|
||||
% (filename, "\n ".join(msgs)))
|
||||
self.fail(
|
||||
"While checking '{}':\n {}".format(
|
||||
filename, "\n ".join(msgs)
|
||||
)
|
||||
)
|
||||
|
||||
def _check_lines_wrapping(self, tpl, raw):
|
||||
for i, line in enumerate(raw.split("\n")):
|
||||
@@ -73,22 +83,28 @@ class TitlesTestCase(test.TestCase):
|
||||
continue
|
||||
self.assertTrue(
|
||||
len(line) < 80,
|
||||
msg="%s:%d: Line limited to a maximum of 79 characters." %
|
||||
(tpl, i + 1))
|
||||
msg=(
|
||||
f"{tpl}:{i + 1}: Line limited to a maximum of 79 "
|
||||
f"characters."
|
||||
),
|
||||
)
|
||||
|
||||
def _check_no_cr(self, tpl, raw):
|
||||
matches = re.findall("\r", raw)
|
||||
self.assertEqual(
|
||||
len(matches), 0,
|
||||
"Found %s literal carriage returns in file %s" %
|
||||
(len(matches), tpl))
|
||||
len(matches),
|
||||
0,
|
||||
f"Found {len(matches)} literal carriage returns in file {tpl}",
|
||||
)
|
||||
|
||||
def _check_trailing_spaces(self, tpl, raw):
|
||||
for i, line in enumerate(raw.split("\n")):
|
||||
trailing_spaces = re.findall(" +$", line)
|
||||
self.assertEqual(
|
||||
len(trailing_spaces), 0,
|
||||
"Found trailing spaces on line {} of {}".format(i + 1, tpl))
|
||||
len(trailing_spaces),
|
||||
0,
|
||||
f"Found trailing spaces on line {i + 1} of {tpl}",
|
||||
)
|
||||
|
||||
def test_template(self):
|
||||
with open(os.path.join(self.specs_path, "template.rst")) as f:
|
||||
@@ -98,17 +114,19 @@ class TitlesTestCase(test.TestCase):
|
||||
template_titles = self._get_titles(spec)
|
||||
|
||||
for d in ["implemented", "in-progress"]:
|
||||
spec_dir = "{}/{}".format(self.specs_path, d)
|
||||
spec_dir = f"{self.specs_path}/{d}"
|
||||
|
||||
self.assertTrue(os.path.isdir(spec_dir),
|
||||
"%s is not a directory" % spec_dir)
|
||||
self.assertTrue(
|
||||
os.path.isdir(spec_dir), f"{spec_dir} is not a directory"
|
||||
)
|
||||
for filename in glob.glob(spec_dir + "/*"):
|
||||
if filename.endswith("README.rst"):
|
||||
continue
|
||||
|
||||
self.assertTrue(
|
||||
filename.endswith(".rst"),
|
||||
"spec's file must have .rst ext. Found: %s" % filename)
|
||||
f"spec's file must have .rst ext. Found: {filename}",
|
||||
)
|
||||
with open(filename) as f:
|
||||
data = f.read()
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class NotifierBaseTestCase(test.TestCase):
|
||||
|
||||
def test_factory(self):
|
||||
|
||||
class A(base.Driver):
|
||||
@@ -34,7 +33,6 @@ class NotifierBaseTestCase(test.TestCase):
|
||||
def test_factory_with_args(self):
|
||||
|
||||
class B(base.Driver):
|
||||
|
||||
def __init__(self, c_str, a, b=10):
|
||||
self.a = a
|
||||
self.b = b
|
||||
@@ -49,9 +47,11 @@ class NotifierBaseTestCase(test.TestCase):
|
||||
self.assertEqual(22, base.get_driver("b://", 5, b=7).notify(10))
|
||||
|
||||
def test_driver_not_found(self):
|
||||
self.assertRaises(ValueError, base.get_driver,
|
||||
"Driver not found for connection string: "
|
||||
"nonexisting://")
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
base.get_driver,
|
||||
"Driver not found for connection string: nonexisting://",
|
||||
)
|
||||
|
||||
def test_build_empty_tree(self):
|
||||
class C(base.Driver):
|
||||
@@ -73,12 +73,21 @@ class NotifierBaseTestCase(test.TestCase):
|
||||
"21": {"parent_id": "2", "trace_id": "21", "info": {"started": 6}},
|
||||
"22": {"parent_id": "2", "trace_id": "22", "info": {"started": 7}},
|
||||
"11": {"parent_id": "1", "trace_id": "11", "info": {"started": 1}},
|
||||
"113": {"parent_id": "11", "trace_id": "113",
|
||||
"info": {"started": 3}},
|
||||
"112": {"parent_id": "11", "trace_id": "112",
|
||||
"info": {"started": 2}},
|
||||
"114": {"parent_id": "11", "trace_id": "114",
|
||||
"info": {"started": 5}}
|
||||
"113": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "113",
|
||||
"info": {"started": 3},
|
||||
},
|
||||
"112": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "112",
|
||||
"info": {"started": 2},
|
||||
},
|
||||
"114": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "114",
|
||||
"info": {"started": 5},
|
||||
},
|
||||
}
|
||||
|
||||
expected_output = [
|
||||
@@ -92,28 +101,49 @@ class NotifierBaseTestCase(test.TestCase):
|
||||
"trace_id": "11",
|
||||
"info": {"started": 1},
|
||||
"children": [
|
||||
{"parent_id": "11", "trace_id": "112",
|
||||
"info": {"started": 2}, "children": []},
|
||||
{"parent_id": "11", "trace_id": "113",
|
||||
"info": {"started": 3}, "children": []},
|
||||
{"parent_id": "11", "trace_id": "114",
|
||||
"info": {"started": 5}, "children": []}
|
||||
]
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "112",
|
||||
"info": {"started": 2},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "113",
|
||||
"info": {"started": 3},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "114",
|
||||
"info": {"started": 5},
|
||||
"children": [],
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
"parent_id": "0",
|
||||
"trace_id": "2",
|
||||
"info": {"started": 1},
|
||||
"children": [
|
||||
{"parent_id": "2", "trace_id": "21",
|
||||
"info": {"started": 6}, "children": []},
|
||||
{"parent_id": "2", "trace_id": "22",
|
||||
"info": {"started": 7}, "children": []}
|
||||
]
|
||||
}
|
||||
{
|
||||
"parent_id": "2",
|
||||
"trace_id": "21",
|
||||
"info": {"started": 6},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "2",
|
||||
"trace_id": "22",
|
||||
"info": {"started": 7},
|
||||
"children": [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
expected_output, base.get_driver("d://")._build_tree(test_input))
|
||||
expected_output, base.get_driver("d://")._build_tree(test_input)
|
||||
)
|
||||
|
||||
@@ -20,11 +20,11 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class ElasticsearchTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.elasticsearch = ElasticsearchDriver(
|
||||
"elasticsearch://localhost:9001/")
|
||||
"elasticsearch://localhost:9001/"
|
||||
)
|
||||
self.elasticsearch.project = "project"
|
||||
self.elasticsearch.service = "service"
|
||||
|
||||
@@ -35,23 +35,20 @@ class ElasticsearchTestCase(test.TestCase):
|
||||
service = "service"
|
||||
host = "host"
|
||||
|
||||
info = {
|
||||
"a": 10,
|
||||
"project": project,
|
||||
"service": service,
|
||||
"host": host
|
||||
}
|
||||
info = {"a": 10, "project": project, "service": service, "host": host}
|
||||
self.elasticsearch.notify(info)
|
||||
|
||||
self.elasticsearch.client\
|
||||
.index.assert_called_once_with(index="osprofiler-notifications",
|
||||
doc_type="notification",
|
||||
body=info)
|
||||
self.elasticsearch.client.index.assert_called_once_with(
|
||||
index="osprofiler-notifications",
|
||||
doc_type="notification",
|
||||
body=info,
|
||||
)
|
||||
|
||||
def test_get_empty_report(self):
|
||||
self.elasticsearch.client = mock.MagicMock()
|
||||
self.elasticsearch.client.search = mock\
|
||||
.MagicMock(return_value={"_scroll_id": "1", "hits": {"hits": []}})
|
||||
self.elasticsearch.client.search = mock.MagicMock(
|
||||
return_value={"_scroll_id": "1", "hits": {"hits": []}}
|
||||
)
|
||||
self.elasticsearch.client.reset_mock()
|
||||
|
||||
get_report = self.elasticsearch.get_report
|
||||
@@ -59,14 +56,13 @@ class ElasticsearchTestCase(test.TestCase):
|
||||
|
||||
get_report(base_id)
|
||||
|
||||
self.elasticsearch.client\
|
||||
.search.assert_called_once_with(index="osprofiler-notifications",
|
||||
doc_type="notification",
|
||||
size=10000,
|
||||
scroll="2m",
|
||||
body={"query": {
|
||||
"match": {"base_id": base_id}}
|
||||
})
|
||||
self.elasticsearch.client.search.assert_called_once_with(
|
||||
index="osprofiler-notifications",
|
||||
doc_type="notification",
|
||||
size=10000,
|
||||
scroll="2m",
|
||||
body={"query": {"match": {"base_id": base_id}}},
|
||||
)
|
||||
|
||||
def test_get_non_empty_report(self):
|
||||
base_id = "1"
|
||||
@@ -82,34 +78,37 @@ class ElasticsearchTestCase(test.TestCase):
|
||||
"service": "service",
|
||||
"parent_id": "0",
|
||||
"name": "test",
|
||||
"info": {
|
||||
"host": "host"
|
||||
},
|
||||
"trace_id": "1"
|
||||
"info": {"host": "host"},
|
||||
"trace_id": "1",
|
||||
}
|
||||
}
|
||||
]}}
|
||||
]
|
||||
},
|
||||
}
|
||||
elasticsearch_second_response = {
|
||||
"_scroll_id": base_id,
|
||||
"hits": {"hits": []}}
|
||||
"hits": {"hits": []},
|
||||
}
|
||||
self.elasticsearch.client = mock.MagicMock()
|
||||
self.elasticsearch.client.search = \
|
||||
mock.MagicMock(return_value=elasticsearch_first_response)
|
||||
self.elasticsearch.client.scroll = \
|
||||
mock.MagicMock(return_value=elasticsearch_second_response)
|
||||
self.elasticsearch.client.search = mock.MagicMock(
|
||||
return_value=elasticsearch_first_response
|
||||
)
|
||||
self.elasticsearch.client.scroll = mock.MagicMock(
|
||||
return_value=elasticsearch_second_response
|
||||
)
|
||||
|
||||
self.elasticsearch.client.reset_mock()
|
||||
|
||||
self.elasticsearch.get_report(base_id)
|
||||
|
||||
self.elasticsearch.client\
|
||||
.search.assert_called_once_with(index="osprofiler-notifications",
|
||||
doc_type="notification",
|
||||
size=10000,
|
||||
scroll="2m",
|
||||
body={"query": {
|
||||
"match": {"base_id": base_id}}
|
||||
})
|
||||
self.elasticsearch.client.search.assert_called_once_with(
|
||||
index="osprofiler-notifications",
|
||||
doc_type="notification",
|
||||
size=10000,
|
||||
scroll="2m",
|
||||
body={"query": {"match": {"base_id": base_id}}},
|
||||
)
|
||||
|
||||
self.elasticsearch.client\
|
||||
.scroll.assert_called_once_with(scroll_id=base_id, scroll="2m")
|
||||
self.elasticsearch.client.scroll.assert_called_once_with(
|
||||
scroll_id=base_id, scroll="2m"
|
||||
)
|
||||
|
||||
@@ -25,7 +25,6 @@ from osprofiler.tests import test
|
||||
|
||||
@ddt.ddt
|
||||
class LogInsightDriverTestCase(test.TestCase):
|
||||
|
||||
BASE_ID = "8d28af1e-acc0-498c-9890-6908e33eff5f"
|
||||
|
||||
def setUp(self):
|
||||
@@ -34,13 +33,15 @@ class LogInsightDriverTestCase(test.TestCase):
|
||||
self._project = "cinder"
|
||||
self._service = "osapi_volume"
|
||||
self._host = "ubuntu"
|
||||
with mock.patch.object(loginsight, "LogInsightClient",
|
||||
return_value=self._client):
|
||||
with mock.patch.object(
|
||||
loginsight, "LogInsightClient", return_value=self._client
|
||||
):
|
||||
self._driver = loginsight.LogInsightDriver(
|
||||
"loginsight://username:password@host",
|
||||
project=self._project,
|
||||
service=self._service,
|
||||
host=self._host)
|
||||
host=self._host,
|
||||
)
|
||||
|
||||
@mock.patch.object(loginsight, "LogInsightClient")
|
||||
def test_init(self, client_class):
|
||||
@@ -51,9 +52,11 @@ class LogInsightDriverTestCase(test.TestCase):
|
||||
client_class.assert_called_once_with("host", "username", "password")
|
||||
client.login.assert_called_once_with()
|
||||
|
||||
@ddt.data("loginsight://username@host",
|
||||
"loginsight://username:p@ssword@host",
|
||||
"loginsight://us:rname:password@host")
|
||||
@ddt.data(
|
||||
"loginsight://username@host",
|
||||
"loginsight://username:p@ssword@host",
|
||||
"loginsight://us:rname:password@host",
|
||||
)
|
||||
def test_init_with_invalid_connection_string(self, conn_str):
|
||||
self.assertRaises(ValueError, loginsight.LogInsightDriver, conn_str)
|
||||
|
||||
@@ -69,18 +72,22 @@ class LogInsightDriverTestCase(test.TestCase):
|
||||
def test_get_name(self):
|
||||
self.assertEqual("loginsight", self._driver.get_name())
|
||||
|
||||
def _create_trace(self,
|
||||
name,
|
||||
timestamp,
|
||||
parent_id="8d28af1e-acc0-498c-9890-6908e33eff5f",
|
||||
base_id=BASE_ID,
|
||||
trace_id="e465db5c-9672-45a1-b90b-da918f30aef6"):
|
||||
return {"parent_id": parent_id,
|
||||
"name": name,
|
||||
"base_id": base_id,
|
||||
"trace_id": trace_id,
|
||||
"timestamp": timestamp,
|
||||
"info": {"host": self._host}}
|
||||
def _create_trace(
|
||||
self,
|
||||
name,
|
||||
timestamp,
|
||||
parent_id="8d28af1e-acc0-498c-9890-6908e33eff5f",
|
||||
base_id=BASE_ID,
|
||||
trace_id="e465db5c-9672-45a1-b90b-da918f30aef6",
|
||||
):
|
||||
return {
|
||||
"parent_id": parent_id,
|
||||
"name": name,
|
||||
"base_id": base_id,
|
||||
"trace_id": trace_id,
|
||||
"timestamp": timestamp,
|
||||
"info": {"host": self._host},
|
||||
}
|
||||
|
||||
def _create_start_trace(self):
|
||||
return self._create_trace("wsgi-start", "2016-10-04t11:50:21.902303")
|
||||
@@ -98,20 +105,17 @@ class LogInsightDriverTestCase(test.TestCase):
|
||||
|
||||
trace["project"] = self._project
|
||||
trace["service"] = self._service
|
||||
exp_event = {"text": "OSProfiler trace",
|
||||
"fields": [{"name": "base_id",
|
||||
"content": trace["base_id"]},
|
||||
{"name": "trace_id",
|
||||
"content": trace["trace_id"]},
|
||||
{"name": "project",
|
||||
"content": trace["project"]},
|
||||
{"name": "service",
|
||||
"content": trace["service"]},
|
||||
{"name": "name",
|
||||
"content": trace["name"]},
|
||||
{"name": "trace",
|
||||
"content": json_str}]
|
||||
}
|
||||
exp_event = {
|
||||
"text": "OSProfiler trace",
|
||||
"fields": [
|
||||
{"name": "base_id", "content": trace["base_id"]},
|
||||
{"name": "trace_id", "content": trace["trace_id"]},
|
||||
{"name": "project", "content": trace["project"]},
|
||||
{"name": "service", "content": trace["service"]},
|
||||
{"name": "name", "content": trace["name"]},
|
||||
{"name": "trace", "content": json_str},
|
||||
],
|
||||
}
|
||||
self._client.send_event.assert_called_once_with(exp_event)
|
||||
|
||||
@mock.patch.object(loginsight.LogInsightDriver, "_append_results")
|
||||
@@ -125,61 +129,83 @@ class LogInsightDriverTestCase(test.TestCase):
|
||||
stop_trace["project"] = self._project
|
||||
stop_trace["service"] = self._service
|
||||
|
||||
resp = {"events": [{"text": "OSProfiler trace",
|
||||
"fields": [{"name": "trace",
|
||||
"content": json.dumps(start_trace)
|
||||
}
|
||||
]
|
||||
},
|
||||
{"text": "OSProfiler trace",
|
||||
"fields": [{"name": "trace",
|
||||
"content": json.dumps(stop_trace)
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
resp = {
|
||||
"events": [
|
||||
{
|
||||
"text": "OSProfiler trace",
|
||||
"fields": [
|
||||
{"name": "trace", "content": json.dumps(start_trace)}
|
||||
],
|
||||
},
|
||||
{
|
||||
"text": "OSProfiler trace",
|
||||
"fields": [
|
||||
{"name": "trace", "content": json.dumps(stop_trace)}
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
self._client.query_events = mock.Mock(return_value=resp)
|
||||
|
||||
self._driver.get_report(self.BASE_ID)
|
||||
self._client.query_events.assert_called_once_with({"base_id":
|
||||
self.BASE_ID})
|
||||
self._client.query_events.assert_called_once_with(
|
||||
{"base_id": self.BASE_ID}
|
||||
)
|
||||
append_results.assert_has_calls(
|
||||
[mock.call(start_trace["trace_id"], start_trace["parent_id"],
|
||||
start_trace["name"], start_trace["project"],
|
||||
start_trace["service"], start_trace["info"]["host"],
|
||||
start_trace["timestamp"], start_trace),
|
||||
mock.call(stop_trace["trace_id"], stop_trace["parent_id"],
|
||||
stop_trace["name"], stop_trace["project"],
|
||||
stop_trace["service"], stop_trace["info"]["host"],
|
||||
stop_trace["timestamp"], stop_trace)
|
||||
])
|
||||
[
|
||||
mock.call(
|
||||
start_trace["trace_id"],
|
||||
start_trace["parent_id"],
|
||||
start_trace["name"],
|
||||
start_trace["project"],
|
||||
start_trace["service"],
|
||||
start_trace["info"]["host"],
|
||||
start_trace["timestamp"],
|
||||
start_trace,
|
||||
),
|
||||
mock.call(
|
||||
stop_trace["trace_id"],
|
||||
stop_trace["parent_id"],
|
||||
stop_trace["name"],
|
||||
stop_trace["project"],
|
||||
stop_trace["service"],
|
||||
stop_trace["info"]["host"],
|
||||
stop_trace["timestamp"],
|
||||
stop_trace,
|
||||
),
|
||||
]
|
||||
)
|
||||
parse_results.assert_called_once_with()
|
||||
|
||||
|
||||
class LogInsightClientTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._host = "localhost"
|
||||
self._username = "username"
|
||||
self._password = "password" # nosec
|
||||
self._password = "password" # noqa: S105
|
||||
self._client = loginsight.LogInsightClient(
|
||||
self._host, self._username, self._password)
|
||||
self._host, self._username, self._password
|
||||
)
|
||||
self._client._session_id = "4ff800d1-3175-4b49-9209-39714ea56416"
|
||||
|
||||
def test_check_response_login_timeout(self):
|
||||
resp = mock.Mock(status_code=440)
|
||||
self.assertRaises(
|
||||
exc.LogInsightLoginTimeout, self._client._check_response, resp)
|
||||
exc.LogInsightLoginTimeout, self._client._check_response, resp
|
||||
)
|
||||
|
||||
def test_check_response_api_error(self):
|
||||
resp = mock.Mock(status_code=401, ok=False)
|
||||
resp.text = json.dumps(
|
||||
{"errorMessage": "Invalid username or password.",
|
||||
"errorCode": "FIELD_ERROR"})
|
||||
{
|
||||
"errorMessage": "Invalid username or password.",
|
||||
"errorCode": "FIELD_ERROR",
|
||||
}
|
||||
)
|
||||
e = self.assertRaises(
|
||||
exc.LogInsightAPIError, self._client._check_response, resp)
|
||||
exc.LogInsightAPIError, self._client._check_response, resp
|
||||
)
|
||||
self.assertEqual("Invalid username or password.", str(e))
|
||||
|
||||
@mock.patch("requests.Request")
|
||||
@@ -204,16 +230,24 @@ class LogInsightClientTestCase(test.TestCase):
|
||||
body = mock.sentinel.body
|
||||
params = mock.sentinel.params
|
||||
ret = self._client._send_request(
|
||||
"get", "https", "api/v1/events", header, body, params)
|
||||
"get", "https", "api/v1/events", header, body, params
|
||||
)
|
||||
|
||||
self.assertEqual(resp_json, ret)
|
||||
exp_headers = {"X-LI-Session-Id": "foo",
|
||||
"content-type": "application/json"}
|
||||
exp_headers = {
|
||||
"X-LI-Session-Id": "foo",
|
||||
"content-type": "application/json",
|
||||
}
|
||||
request_class.assert_called_once_with(
|
||||
"get", "https://localhost:9543/api/v1/events", headers=exp_headers,
|
||||
data=data, params=mock.sentinel.params)
|
||||
self._client._session.send.assert_called_once_with(prep_req,
|
||||
verify=False)
|
||||
"get",
|
||||
"https://localhost:9543/api/v1/events",
|
||||
headers=exp_headers,
|
||||
data=data,
|
||||
params=mock.sentinel.params,
|
||||
)
|
||||
self._client._session.send.assert_called_once_with(
|
||||
prep_req, verify=False
|
||||
)
|
||||
check_resp.assert_called_once_with(resp)
|
||||
|
||||
@mock.patch.object(loginsight.LogInsightClient, "_send_request")
|
||||
@@ -221,29 +255,41 @@ class LogInsightClientTestCase(test.TestCase):
|
||||
self.assertTrue(self._client._is_current_session_active())
|
||||
exp_header = {"X-LI-Session-Id": self._client._session_id}
|
||||
send_request.assert_called_once_with(
|
||||
"get", "https", "api/v1/sessions/current", headers=exp_header)
|
||||
"get", "https", "api/v1/sessions/current", headers=exp_header
|
||||
)
|
||||
|
||||
@mock.patch.object(loginsight.LogInsightClient, "_send_request")
|
||||
def test_is_current_session_active_with_expired_session(self,
|
||||
send_request):
|
||||
def test_is_current_session_active_with_expired_session(
|
||||
self, send_request
|
||||
):
|
||||
send_request.side_effect = exc.LogInsightLoginTimeout
|
||||
|
||||
self.assertFalse(self._client._is_current_session_active())
|
||||
send_request.assert_called_once_with(
|
||||
"get", "https", "api/v1/sessions/current",
|
||||
headers={"X-LI-Session-Id": self._client._session_id})
|
||||
"get",
|
||||
"https",
|
||||
"api/v1/sessions/current",
|
||||
headers={"X-LI-Session-Id": self._client._session_id},
|
||||
)
|
||||
|
||||
@mock.patch.object(loginsight.LogInsightClient,
|
||||
"_is_current_session_active", return_value=True)
|
||||
@mock.patch.object(
|
||||
loginsight.LogInsightClient,
|
||||
"_is_current_session_active",
|
||||
return_value=True,
|
||||
)
|
||||
@mock.patch.object(loginsight.LogInsightClient, "_send_request")
|
||||
def test_login_with_current_session_active(self, send_request,
|
||||
is_current_session_active):
|
||||
def test_login_with_current_session_active(
|
||||
self, send_request, is_current_session_active
|
||||
):
|
||||
self._client.login()
|
||||
is_current_session_active.assert_called_once_with()
|
||||
send_request.assert_not_called()
|
||||
|
||||
@mock.patch.object(loginsight.LogInsightClient,
|
||||
"_is_current_session_active", return_value=False)
|
||||
@mock.patch.object(
|
||||
loginsight.LogInsightClient,
|
||||
"_is_current_session_active",
|
||||
return_value=False,
|
||||
)
|
||||
@mock.patch.object(loginsight.LogInsightClient, "_send_request")
|
||||
def test_login(self, send_request, is_current_session_active):
|
||||
new_session_id = "569a80aa-be5c-49e5-82c1-bb62392d2667"
|
||||
@@ -254,7 +300,8 @@ class LogInsightClientTestCase(test.TestCase):
|
||||
is_current_session_active.assert_called_once_with()
|
||||
exp_body = {"username": self._username, "password": self._password}
|
||||
send_request.assert_called_once_with(
|
||||
"post", "https", "api/v1/sessions", body=exp_body)
|
||||
"post", "https", "api/v1/sessions", body=exp_body
|
||||
)
|
||||
self.assertEqual(new_session_id, self._client._session_id)
|
||||
|
||||
@mock.patch.object(loginsight.LogInsightClient, "_send_request")
|
||||
@@ -263,10 +310,12 @@ class LogInsightClientTestCase(test.TestCase):
|
||||
self._client.send_event(event)
|
||||
|
||||
exp_body = {"events": [event]}
|
||||
exp_path = ("api/v1/events/ingest/%s" %
|
||||
self._client.LI_OSPROFILER_AGENT_ID)
|
||||
exp_path = (
|
||||
f"api/v1/events/ingest/{self._client.LI_OSPROFILER_AGENT_ID}"
|
||||
)
|
||||
send_request.assert_called_once_with(
|
||||
"post", "http", exp_path, body=exp_body)
|
||||
"post", "http", exp_path, body=exp_body
|
||||
)
|
||||
|
||||
@mock.patch.object(loginsight.LogInsightClient, "_send_request")
|
||||
def test_query_events(self, send_request):
|
||||
@@ -277,8 +326,12 @@ class LogInsightClientTestCase(test.TestCase):
|
||||
exp_header = {"X-LI-Session-Id": self._client._session_id}
|
||||
exp_params = {"limit": 20000, "timeout": self._client._query_timeout}
|
||||
send_request.assert_called_once_with(
|
||||
"get", "https", "api/v1/events/foo/CONTAINS+bar/timestamp/GT+0",
|
||||
headers=exp_header, params=exp_params)
|
||||
"get",
|
||||
"https",
|
||||
"api/v1/events/foo/CONTAINS+bar/timestamp/GT+0",
|
||||
headers=exp_header,
|
||||
params=exp_params,
|
||||
)
|
||||
|
||||
@mock.patch.object(loginsight.LogInsightClient, "_send_request")
|
||||
@mock.patch.object(loginsight.LogInsightClient, "login")
|
||||
@@ -291,6 +344,10 @@ class LogInsightClientTestCase(test.TestCase):
|
||||
exp_header = {"X-LI-Session-Id": self._client._session_id}
|
||||
exp_params = {"limit": 20000, "timeout": self._client._query_timeout}
|
||||
exp_send_request_call = mock.call(
|
||||
"get", "https", "api/v1/events/foo/CONTAINS+bar/timestamp/GT+0",
|
||||
headers=exp_header, params=exp_params)
|
||||
"get",
|
||||
"https",
|
||||
"api/v1/events/foo/CONTAINS+bar/timestamp/GT+0",
|
||||
headers=exp_header,
|
||||
params=exp_params,
|
||||
)
|
||||
send_request.assert_has_calls([exp_send_request_call] * 2)
|
||||
|
||||
@@ -20,15 +20,19 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class MessagingTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("oslo_utils.importutils.try_import")
|
||||
def test_init_no_oslo_messaging(self, try_import_mock):
|
||||
try_import_mock.return_value = None
|
||||
|
||||
self.assertRaises(
|
||||
ValueError, base.get_driver,
|
||||
"messaging://", project="project", service="service",
|
||||
host="host", context={})
|
||||
ValueError,
|
||||
base.get_driver,
|
||||
"messaging://",
|
||||
project="project",
|
||||
service="service",
|
||||
host="host",
|
||||
context={},
|
||||
)
|
||||
|
||||
@mock.patch("oslo_utils.importutils.try_import")
|
||||
def test_init_and_notify(self, try_import_mock):
|
||||
@@ -48,25 +52,30 @@ class MessagingTestCase(test.TestCase):
|
||||
oslo_messaging_mock.get_notification_transport.return_value = transport
|
||||
|
||||
notify_func = base.get_driver(
|
||||
"messaging://", project=project, service=service,
|
||||
context=context, host=host).notify
|
||||
"messaging://",
|
||||
project=project,
|
||||
service=service,
|
||||
context=context,
|
||||
host=host,
|
||||
).notify
|
||||
|
||||
oslo_messaging_mock.Notifier.assert_called_once_with(
|
||||
transport, publisher_id=host, driver="messaging",
|
||||
topics=["profiler"], retry=0)
|
||||
transport,
|
||||
publisher_id=host,
|
||||
driver="messaging",
|
||||
topics=["profiler"],
|
||||
retry=0,
|
||||
)
|
||||
|
||||
info = {
|
||||
"a": 10,
|
||||
"project": project,
|
||||
"service": service,
|
||||
"host": host
|
||||
}
|
||||
info = {"a": 10, "project": project, "service": service, "host": host}
|
||||
notify_func(info)
|
||||
|
||||
notifier_mock.info.assert_called_once_with(
|
||||
context, "profiler.service", info)
|
||||
context, "profiler.service", info
|
||||
)
|
||||
|
||||
notifier_mock.reset_mock()
|
||||
notify_func(info, context="my_context")
|
||||
notifier_mock.info.assert_called_once_with(
|
||||
"my_context", "profiler.service", info)
|
||||
"my_context", "profiler.service", info
|
||||
)
|
||||
|
||||
@@ -34,12 +34,21 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
"21": {"parent_id": "2", "trace_id": "21", "info": {"started": 6}},
|
||||
"22": {"parent_id": "2", "trace_id": "22", "info": {"started": 7}},
|
||||
"11": {"parent_id": "1", "trace_id": "11", "info": {"started": 1}},
|
||||
"113": {"parent_id": "11", "trace_id": "113",
|
||||
"info": {"started": 3}},
|
||||
"112": {"parent_id": "11", "trace_id": "112",
|
||||
"info": {"started": 2}},
|
||||
"114": {"parent_id": "11", "trace_id": "114",
|
||||
"info": {"started": 5}}
|
||||
"113": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "113",
|
||||
"info": {"started": 3},
|
||||
},
|
||||
"112": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "112",
|
||||
"info": {"started": 2},
|
||||
},
|
||||
"114": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "114",
|
||||
"info": {"started": 5},
|
||||
},
|
||||
}
|
||||
|
||||
expected_output = [
|
||||
@@ -53,27 +62,47 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
"trace_id": "11",
|
||||
"info": {"started": 1},
|
||||
"children": [
|
||||
{"parent_id": "11", "trace_id": "112",
|
||||
"info": {"started": 2}, "children": []},
|
||||
{"parent_id": "11", "trace_id": "113",
|
||||
"info": {"started": 3}, "children": []},
|
||||
{"parent_id": "11", "trace_id": "114",
|
||||
"info": {"started": 5}, "children": []}
|
||||
]
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "112",
|
||||
"info": {"started": 2},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "113",
|
||||
"info": {"started": 3},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "114",
|
||||
"info": {"started": 5},
|
||||
"children": [],
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
"parent_id": "0",
|
||||
"trace_id": "2",
|
||||
"info": {"started": 1},
|
||||
"children": [
|
||||
{"parent_id": "2", "trace_id": "21",
|
||||
"info": {"started": 6}, "children": []},
|
||||
{"parent_id": "2", "trace_id": "22",
|
||||
"info": {"started": 7}, "children": []}
|
||||
]
|
||||
}
|
||||
{
|
||||
"parent_id": "2",
|
||||
"trace_id": "21",
|
||||
"info": {"started": 6},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "2",
|
||||
"trace_id": "22",
|
||||
"info": {"started": 7},
|
||||
"children": [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
result = self.mongodb._build_tree(test_input)
|
||||
@@ -88,7 +117,7 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
"name": "total",
|
||||
"started": 0,
|
||||
"finished": None,
|
||||
"last_trace_started": None
|
||||
"last_trace_started": None,
|
||||
},
|
||||
"children": [],
|
||||
"stats": {},
|
||||
@@ -108,9 +137,9 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
"path": "/v2/a322b5049d224a90bf8786c644409400/volumes",
|
||||
"scheme": "http",
|
||||
"method": "POST",
|
||||
"query": ""
|
||||
"query": "",
|
||||
},
|
||||
"service": None
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-start",
|
||||
"service": "main",
|
||||
@@ -118,35 +147,24 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"project": "keystone",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
|
||||
{
|
||||
"info": {
|
||||
"project": None,
|
||||
"host": "ubuntu",
|
||||
"service": None
|
||||
},
|
||||
"info": {"project": None, "host": "ubuntu", "service": None},
|
||||
"name": "wsgi-stop",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.380405",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf",
|
||||
"project": "keystone",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
|
||||
{
|
||||
"info": {
|
||||
"project": None,
|
||||
"host": "ubuntu",
|
||||
"db": {
|
||||
"params": {
|
||||
|
||||
},
|
||||
"statement": "SELECT 1"
|
||||
},
|
||||
"service": None
|
||||
"db": {"params": {}, "statement": "SELECT 1"},
|
||||
"service": None,
|
||||
},
|
||||
"name": "db-start",
|
||||
"service": "main",
|
||||
@@ -154,24 +172,18 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a",
|
||||
"project": "keystone",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
|
||||
{
|
||||
"info": {
|
||||
"project": None,
|
||||
"host": "ubuntu",
|
||||
"service": None
|
||||
},
|
||||
"info": {"project": None, "host": "ubuntu", "service": None},
|
||||
"name": "db-stop",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.415486",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a",
|
||||
"project": "keystone",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
|
||||
{
|
||||
"info": {
|
||||
"project": None,
|
||||
@@ -180,9 +192,9 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
"path": "/v2/a322b5049d224a90bf8786c644409400/volumes",
|
||||
"scheme": "http",
|
||||
"method": "GET",
|
||||
"query": ""
|
||||
"query": "",
|
||||
},
|
||||
"service": None
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-start",
|
||||
"service": "main",
|
||||
@@ -190,124 +202,167 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b",
|
||||
"project": "keystone",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
}]
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
]
|
||||
|
||||
expected = {"children": [{"children": [{
|
||||
"children": [],
|
||||
"info": {"finished": 76,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.db-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"db": {"params": {},
|
||||
"statement": "SELECT 1"},
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None},
|
||||
"name": "db-start",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.395365",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a"},
|
||||
"meta.raw_payload.db-stop": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None},
|
||||
"name": "db-stop",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.415486",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a"},
|
||||
"name": "db",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 56,
|
||||
"exception": "None"},
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a"}],
|
||||
|
||||
"info": {"finished": 0,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"host": "ubuntu",
|
||||
"project": None,
|
||||
"request": {"method": "POST",
|
||||
"path": "/v2/a322b5049d224a90bf8"
|
||||
"786c644409400/volumes",
|
||||
"query": "",
|
||||
"scheme": "http"},
|
||||
"service": None},
|
||||
"name": "wsgi-start",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.338776",
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a"},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 0},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a"},
|
||||
|
||||
{"children": [],
|
||||
"info": {"finished": 41,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-stop": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None},
|
||||
"name": "wsgi-stop",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.380405",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf"},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 41,
|
||||
"exception": "None"},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf"},
|
||||
|
||||
{"children": [],
|
||||
"info": {"finished": 88,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"host": "ubuntu",
|
||||
"project": None,
|
||||
"request": {"method": "GET",
|
||||
"path": "/v2/a322b5049d224a90bf"
|
||||
"8786c644409400/volumes",
|
||||
"query": "",
|
||||
"scheme": "http"},
|
||||
"service": None},
|
||||
"name": "wsgi-start",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.427444",
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b"},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 88},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b"}],
|
||||
expected = {
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"info": {
|
||||
"finished": 76,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.db-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"info": {
|
||||
"db": {
|
||||
"params": {},
|
||||
"statement": "SELECT 1",
|
||||
},
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None,
|
||||
},
|
||||
"name": "db-start",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.395365",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a", # noqa: E501
|
||||
},
|
||||
"meta.raw_payload.db-stop": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"info": {
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None,
|
||||
},
|
||||
"name": "db-stop",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.415486",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a", # noqa: E501
|
||||
},
|
||||
"name": "db",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 56,
|
||||
"exception": "None",
|
||||
},
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a", # noqa: E501
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a",
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"finished": 0,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/v2/a322b5049d224a90bf8"
|
||||
"786c644409400/volumes",
|
||||
"query": "",
|
||||
"scheme": "http",
|
||||
},
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-start",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.338776",
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 0,
|
||||
},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"info": {
|
||||
"finished": 41,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-stop": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-stop",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.380405",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf",
|
||||
},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 41,
|
||||
"exception": "None",
|
||||
},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf",
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"info": {
|
||||
"finished": 88,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/v2/a322b5049d224a90bf"
|
||||
"8786c644409400/volumes",
|
||||
"query": "",
|
||||
"scheme": "http",
|
||||
},
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-start",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.427444",
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b",
|
||||
},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 88,
|
||||
},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b",
|
||||
},
|
||||
],
|
||||
"info": {
|
||||
"finished": 88,
|
||||
"name": "total",
|
||||
"started": 0,
|
||||
"last_trace_started": 88},
|
||||
"stats": {"db": {"count": 1, "duration": 20},
|
||||
"wsgi": {"count": 3, "duration": 0}}}
|
||||
"last_trace_started": 88,
|
||||
},
|
||||
"stats": {
|
||||
"db": {"count": 1, "duration": 20},
|
||||
"wsgi": {"count": 3, "duration": 0},
|
||||
},
|
||||
}
|
||||
|
||||
self.mongodb.db.profiler.find.return_value = results
|
||||
|
||||
@@ -316,6 +371,5 @@ class MongoDBParserTestCase(test.TestCase):
|
||||
result = self.mongodb.get_report(base_id)
|
||||
|
||||
expected_filter = [{"base_id": base_id}, {"_id": 0}]
|
||||
self.mongodb.db.profiler.find.assert_called_once_with(
|
||||
*expected_filter)
|
||||
self.mongodb.db.profiler.find.assert_called_once_with(*expected_filter)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@@ -22,7 +22,6 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class OTLPTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
@@ -34,9 +33,7 @@ class OTLPTestCase(test.TestCase):
|
||||
"trace_id": "1c089ea8-28fe-4f3d-8c00-f6daa2bc32f1",
|
||||
"parent_id": "e2715537-3d1c-4f0c-b3af-87355dc5fc5b",
|
||||
"timestamp": "2018-05-03T04:31:51.781381",
|
||||
"info": {
|
||||
"host": "test"
|
||||
}
|
||||
"info": {"host": "test"},
|
||||
}
|
||||
|
||||
self.payload_stop = {
|
||||
@@ -45,18 +42,15 @@ class OTLPTestCase(test.TestCase):
|
||||
"trace_id": "1c089ea8-28fe-4f3d-8c00-f6daa2bc32f1",
|
||||
"parent_id": "e2715537-3d1c-4f0c-b3af-87355dc5fc5b",
|
||||
"timestamp": "2018-05-03T04:31:51.781381",
|
||||
"info": {
|
||||
"host": "test",
|
||||
"function": {
|
||||
"result": 1
|
||||
}
|
||||
}
|
||||
"info": {"host": "test", "function": {"result": 1}},
|
||||
}
|
||||
|
||||
self.driver = otlp.OTLP(
|
||||
"otlp://127.0.0.1:6831",
|
||||
project="nova", service="api",
|
||||
conf=cfg.CONF)
|
||||
project="nova",
|
||||
service="api",
|
||||
conf=cfg.CONF,
|
||||
)
|
||||
|
||||
def test_notify_start(self):
|
||||
self.driver.notify(self.payload_start)
|
||||
@@ -70,12 +64,7 @@ class OTLPTestCase(test.TestCase):
|
||||
mock_end.assert_called_once()
|
||||
|
||||
def test_notify_stop_with_db_result(self):
|
||||
self.payload_stop["info"] = {
|
||||
"host": "test",
|
||||
"db": {
|
||||
"result": "()"
|
||||
}
|
||||
}
|
||||
self.payload_stop["info"] = {"host": "test", "db": {"result": "()"}}
|
||||
mock_end = mock.MagicMock()
|
||||
self.driver.notify(self.payload_start)
|
||||
self.driver.spans[0].end = mock_end
|
||||
@@ -90,7 +79,7 @@ class OTLPTestCase(test.TestCase):
|
||||
"db": {
|
||||
"original_exception": "test_exception",
|
||||
"chained_exception": "test_chained_exception",
|
||||
}
|
||||
},
|
||||
}
|
||||
mock_end = mock.MagicMock()
|
||||
self.driver.notify(self.payload_start)
|
||||
@@ -99,10 +88,7 @@ class OTLPTestCase(test.TestCase):
|
||||
mock_end.assert_called_once()
|
||||
|
||||
def test_notify_stop_without_function_result(self):
|
||||
self.payload_stop["info"] = {
|
||||
"host": "test",
|
||||
"function": {}
|
||||
}
|
||||
self.payload_stop["info"] = {"host": "test", "function": {}}
|
||||
mock_end = mock.MagicMock()
|
||||
self.driver.notify(self.payload_start)
|
||||
self.driver.spans[0].end = mock_end
|
||||
@@ -114,7 +100,7 @@ class OTLPTestCase(test.TestCase):
|
||||
"host": "test",
|
||||
"requests": {
|
||||
"status_code": 200,
|
||||
}
|
||||
},
|
||||
}
|
||||
mock_end = mock.MagicMock()
|
||||
self.driver.notify(self.payload_start)
|
||||
@@ -123,14 +109,16 @@ class OTLPTestCase(test.TestCase):
|
||||
mock_end.assert_called_once()
|
||||
|
||||
def test_service_name_default(self):
|
||||
self.assertEqual("pr1-svc1", self.driver._get_service_name(
|
||||
cfg.CONF, "pr1", "svc1"))
|
||||
self.assertEqual(
|
||||
"pr1-svc1", self.driver._get_service_name(cfg.CONF, "pr1", "svc1")
|
||||
)
|
||||
|
||||
def test_service_name_prefix(self):
|
||||
cfg.CONF.set_default(
|
||||
"service_name_prefix", "prx1", "profiler_otlp")
|
||||
self.assertEqual("prx1-pr1-svc1", self.driver._get_service_name(
|
||||
cfg.CONF, "pr1", "svc1"))
|
||||
cfg.CONF.set_default("service_name_prefix", "prx1", "profiler_otlp")
|
||||
self.assertEqual(
|
||||
"prx1-pr1-svc1",
|
||||
self.driver._get_service_name(cfg.CONF, "pr1", "svc1"),
|
||||
)
|
||||
|
||||
def test_process_tags(self):
|
||||
# Need to be implemented.
|
||||
|
||||
@@ -37,12 +37,21 @@ class RedisParserTestCase(test.TestCase):
|
||||
"21": {"parent_id": "2", "trace_id": "21", "info": {"started": 6}},
|
||||
"22": {"parent_id": "2", "trace_id": "22", "info": {"started": 7}},
|
||||
"11": {"parent_id": "1", "trace_id": "11", "info": {"started": 1}},
|
||||
"113": {"parent_id": "11", "trace_id": "113",
|
||||
"info": {"started": 3}},
|
||||
"112": {"parent_id": "11", "trace_id": "112",
|
||||
"info": {"started": 2}},
|
||||
"114": {"parent_id": "11", "trace_id": "114",
|
||||
"info": {"started": 5}}
|
||||
"113": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "113",
|
||||
"info": {"started": 3},
|
||||
},
|
||||
"112": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "112",
|
||||
"info": {"started": 2},
|
||||
},
|
||||
"114": {
|
||||
"parent_id": "11",
|
||||
"trace_id": "114",
|
||||
"info": {"started": 5},
|
||||
},
|
||||
}
|
||||
|
||||
expected_output = [
|
||||
@@ -56,27 +65,47 @@ class RedisParserTestCase(test.TestCase):
|
||||
"trace_id": "11",
|
||||
"info": {"started": 1},
|
||||
"children": [
|
||||
{"parent_id": "11", "trace_id": "112",
|
||||
"info": {"started": 2}, "children": []},
|
||||
{"parent_id": "11", "trace_id": "113",
|
||||
"info": {"started": 3}, "children": []},
|
||||
{"parent_id": "11", "trace_id": "114",
|
||||
"info": {"started": 5}, "children": []}
|
||||
]
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "112",
|
||||
"info": {"started": 2},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "113",
|
||||
"info": {"started": 3},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "11",
|
||||
"trace_id": "114",
|
||||
"info": {"started": 5},
|
||||
"children": [],
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
"parent_id": "0",
|
||||
"trace_id": "2",
|
||||
"info": {"started": 1},
|
||||
"children": [
|
||||
{"parent_id": "2", "trace_id": "21",
|
||||
"info": {"started": 6}, "children": []},
|
||||
{"parent_id": "2", "trace_id": "22",
|
||||
"info": {"started": 7}, "children": []}
|
||||
]
|
||||
}
|
||||
{
|
||||
"parent_id": "2",
|
||||
"trace_id": "21",
|
||||
"info": {"started": 6},
|
||||
"children": [],
|
||||
},
|
||||
{
|
||||
"parent_id": "2",
|
||||
"trace_id": "22",
|
||||
"info": {"started": 7},
|
||||
"children": [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
result = self.redisdb._build_tree(test_input)
|
||||
@@ -91,7 +120,7 @@ class RedisParserTestCase(test.TestCase):
|
||||
"name": "total",
|
||||
"started": 0,
|
||||
"finished": None,
|
||||
"last_trace_started": None
|
||||
"last_trace_started": None,
|
||||
},
|
||||
"children": [],
|
||||
"stats": {},
|
||||
@@ -111,9 +140,9 @@ class RedisParserTestCase(test.TestCase):
|
||||
"path": "/v2/a322b5049d224a90bf8786c644409400/volumes",
|
||||
"scheme": "http",
|
||||
"method": "POST",
|
||||
"query": ""
|
||||
"query": "",
|
||||
},
|
||||
"service": None
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-start",
|
||||
"service": "main",
|
||||
@@ -121,35 +150,24 @@ class RedisParserTestCase(test.TestCase):
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"project": "keystone",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
|
||||
{
|
||||
"info": {
|
||||
"project": None,
|
||||
"host": "ubuntu",
|
||||
"service": None
|
||||
},
|
||||
"info": {"project": None, "host": "ubuntu", "service": None},
|
||||
"name": "wsgi-stop",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.380405",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf",
|
||||
"project": "keystone",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
|
||||
{
|
||||
"info": {
|
||||
"project": None,
|
||||
"host": "ubuntu",
|
||||
"db": {
|
||||
"params": {
|
||||
|
||||
},
|
||||
"statement": "SELECT 1"
|
||||
},
|
||||
"service": None
|
||||
"db": {"params": {}, "statement": "SELECT 1"},
|
||||
"service": None,
|
||||
},
|
||||
"name": "db-start",
|
||||
"service": "main",
|
||||
@@ -157,24 +175,18 @@ class RedisParserTestCase(test.TestCase):
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a",
|
||||
"project": "keystone",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
|
||||
{
|
||||
"info": {
|
||||
"project": None,
|
||||
"host": "ubuntu",
|
||||
"service": None
|
||||
},
|
||||
"info": {"project": None, "host": "ubuntu", "service": None},
|
||||
"name": "db-stop",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.415486",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a",
|
||||
"project": "keystone",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
|
||||
{
|
||||
"info": {
|
||||
"project": None,
|
||||
@@ -183,9 +195,9 @@ class RedisParserTestCase(test.TestCase):
|
||||
"path": "/v2/a322b5049d224a90bf8786c644409400/volumes",
|
||||
"scheme": "http",
|
||||
"method": "GET",
|
||||
"query": ""
|
||||
"query": "",
|
||||
},
|
||||
"service": None
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-start",
|
||||
"service": "main",
|
||||
@@ -193,127 +205,175 @@ class RedisParserTestCase(test.TestCase):
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b",
|
||||
"project": "keystone",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4"
|
||||
}]
|
||||
results = {result["base_id"] + "_" + result["trace_id"]
|
||||
+ "_" + result["timestamp"]: result
|
||||
for result in result_elements}
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
},
|
||||
]
|
||||
results = {
|
||||
result["base_id"]
|
||||
+ "_"
|
||||
+ result["trace_id"]
|
||||
+ "_"
|
||||
+ result["timestamp"]: result
|
||||
for result in result_elements
|
||||
}
|
||||
|
||||
expected = {"children": [{"children": [{
|
||||
"children": [],
|
||||
"info": {"finished": 76,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.db-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"db": {"params": {},
|
||||
"statement": "SELECT 1"},
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None},
|
||||
"name": "db-start",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.395365",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a"},
|
||||
"meta.raw_payload.db-stop": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None},
|
||||
"name": "db-stop",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.415486",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a"},
|
||||
"name": "db",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 56,
|
||||
"exception": "None"},
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a"}],
|
||||
|
||||
"info": {"finished": 0,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"host": "ubuntu",
|
||||
"project": None,
|
||||
"request": {"method": "POST",
|
||||
"path": "/v2/a322b5049d224a90bf8"
|
||||
"786c644409400/volumes",
|
||||
"query": "",
|
||||
"scheme": "http"},
|
||||
"service": None},
|
||||
"name": "wsgi-start",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.338776",
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a"},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 0},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a"},
|
||||
|
||||
{"children": [],
|
||||
"info": {"finished": 41,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-stop": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None},
|
||||
"name": "wsgi-stop",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.380405",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf"},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 41,
|
||||
"exception": "None"},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf"},
|
||||
|
||||
{"children": [],
|
||||
"info": {"finished": 88,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {"host": "ubuntu",
|
||||
"project": None,
|
||||
"request": {"method": "GET",
|
||||
"path": "/v2/a322b5049d224a90bf"
|
||||
"8786c644409400/volumes",
|
||||
"query": "",
|
||||
"scheme": "http"},
|
||||
"service": None},
|
||||
"name": "wsgi-start",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.427444",
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b"},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 88},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b"}],
|
||||
expected = {
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"info": {
|
||||
"finished": 76,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.db-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"info": {
|
||||
"db": {
|
||||
"params": {},
|
||||
"statement": "SELECT 1",
|
||||
},
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None,
|
||||
},
|
||||
"name": "db-start",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.395365",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a", # noqa: E501
|
||||
},
|
||||
"meta.raw_payload.db-stop": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"info": {
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None,
|
||||
},
|
||||
"name": "db-stop",
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.415486",
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a", # noqa: E501
|
||||
},
|
||||
"name": "db",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 56,
|
||||
"exception": "None",
|
||||
},
|
||||
"parent_id": "06320327-2c2c-45ae-923a-515de890276a", # noqa: E501
|
||||
"trace_id": "1baf1d24-9ca9-4f4c-bd3f-01b7e0c0735a",
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"finished": 0,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/v2/a322b5049d224a90bf8"
|
||||
"786c644409400/volumes",
|
||||
"query": "",
|
||||
"scheme": "http",
|
||||
},
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-start",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.338776",
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 0,
|
||||
},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "06320327-2c2c-45ae-923a-515de890276a",
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"info": {
|
||||
"finished": 41,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-stop": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-stop",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.380405",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf",
|
||||
},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 41,
|
||||
"exception": "None",
|
||||
},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "839ca3f1-afcb-45be-a4a1-679124c552bf",
|
||||
},
|
||||
{
|
||||
"children": [],
|
||||
"info": {
|
||||
"finished": 88,
|
||||
"host": "ubuntu",
|
||||
"meta.raw_payload.wsgi-start": {
|
||||
"base_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"info": {
|
||||
"host": "ubuntu",
|
||||
"project": None,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/v2/a322b5049d224a90bf"
|
||||
"8786c644409400/volumes",
|
||||
"query": "",
|
||||
"scheme": "http",
|
||||
},
|
||||
"service": None,
|
||||
},
|
||||
"name": "wsgi-start",
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4", # noqa: E501
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"timestamp": "2015-12-23T14:02:22.427444",
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b",
|
||||
},
|
||||
"name": "wsgi",
|
||||
"project": "keystone",
|
||||
"service": "main",
|
||||
"started": 88,
|
||||
},
|
||||
"parent_id": "7253ca8c-33b3-4f84-b4f1-f5a4311ddfa4",
|
||||
"trace_id": "016c97fd-87f3-40b2-9b55-e431156b694b",
|
||||
},
|
||||
],
|
||||
"info": {
|
||||
"finished": 88,
|
||||
"name": "total",
|
||||
"started": 0,
|
||||
"last_trace_started": 88},
|
||||
"stats": {"db": {"count": 1, "duration": 20},
|
||||
"wsgi": {"count": 3, "duration": 0}}}
|
||||
"last_trace_started": 88,
|
||||
},
|
||||
"stats": {
|
||||
"db": {"count": 1, "duration": 20},
|
||||
"wsgi": {"count": 3, "duration": 0},
|
||||
},
|
||||
}
|
||||
|
||||
self.redisdb.db.scan_iter.return_value = list(results.keys())
|
||||
|
||||
@@ -328,5 +388,6 @@ class RedisParserTestCase(test.TestCase):
|
||||
|
||||
expected_filter = self.redisdb.namespace + "10*"
|
||||
self.redisdb.db.scan_iter.assert_called_once_with(
|
||||
match=expected_filter)
|
||||
match=expected_filter
|
||||
)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@@ -18,12 +18,12 @@ from osprofiler import initializer
|
||||
|
||||
|
||||
class InitializerTestCase(testtools.TestCase):
|
||||
|
||||
@mock.patch("osprofiler.notifier.set")
|
||||
@mock.patch("osprofiler.notifier.create")
|
||||
@mock.patch("osprofiler.web.enable")
|
||||
def test_initializer(self, web_enable_mock, notifier_create_mock,
|
||||
notifier_set_mock):
|
||||
def test_initializer(
|
||||
self, web_enable_mock, notifier_create_mock, notifier_set_mock
|
||||
):
|
||||
conf = mock.Mock()
|
||||
conf.profiler.connection_string = "driver://"
|
||||
conf.profiler.hmac_keys = "hmac_keys"
|
||||
@@ -38,7 +38,12 @@ class InitializerTestCase(testtools.TestCase):
|
||||
initializer.init_from_conf(conf, context, project, service, host)
|
||||
|
||||
notifier_create_mock.assert_called_once_with(
|
||||
"driver://", context=context, project=project, service=service,
|
||||
host=host, conf=conf)
|
||||
"driver://",
|
||||
context=context,
|
||||
project=project,
|
||||
service=service,
|
||||
host=host,
|
||||
conf=conf,
|
||||
)
|
||||
notifier_set_mock.assert_called_once_with(notifier_mock)
|
||||
web_enable_mock.assert_called_once_with("hmac_keys")
|
||||
|
||||
@@ -20,7 +20,6 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class NotifierTestCase(test.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
notifier.set(notifier._noop_notifier) # restore defaults
|
||||
notifier.clear_notifier_cache()
|
||||
|
||||
@@ -30,19 +30,22 @@ class ConfigTestCase(test.TestCase):
|
||||
opts.set_defaults(self.conf_fixture.conf)
|
||||
self.assertFalse(self.conf_fixture.conf.profiler.enabled)
|
||||
self.assertFalse(self.conf_fixture.conf.profiler.trace_sqlalchemy)
|
||||
self.assertEqual("SECRET_KEY",
|
||||
self.conf_fixture.conf.profiler.hmac_keys)
|
||||
self.assertEqual(
|
||||
"SECRET_KEY", self.conf_fixture.conf.profiler.hmac_keys
|
||||
)
|
||||
self.assertFalse(opts.is_trace_enabled(self.conf_fixture.conf))
|
||||
self.assertFalse(opts.is_db_trace_enabled(self.conf_fixture.conf))
|
||||
|
||||
def test_options_defaults_override(self):
|
||||
opts.set_defaults(self.conf_fixture.conf, enabled=True,
|
||||
trace_sqlalchemy=True,
|
||||
hmac_keys="MY_KEY")
|
||||
opts.set_defaults(
|
||||
self.conf_fixture.conf,
|
||||
enabled=True,
|
||||
trace_sqlalchemy=True,
|
||||
hmac_keys="MY_KEY",
|
||||
)
|
||||
self.assertTrue(self.conf_fixture.conf.profiler.enabled)
|
||||
self.assertTrue(self.conf_fixture.conf.profiler.trace_sqlalchemy)
|
||||
self.assertEqual("MY_KEY",
|
||||
self.conf_fixture.conf.profiler.hmac_keys)
|
||||
self.assertEqual("MY_KEY", self.conf_fixture.conf.profiler.hmac_keys)
|
||||
self.assertTrue(opts.is_trace_enabled(self.conf_fixture.conf))
|
||||
self.assertTrue(opts.is_db_trace_enabled(self.conf_fixture.conf))
|
||||
|
||||
@@ -58,8 +61,9 @@ class ConfigTestCase(test.TestCase):
|
||||
@mock.patch("osprofiler.web.enable")
|
||||
@mock.patch("osprofiler.web.disable")
|
||||
def test_web_trace_enabled(self, mock_disable, mock_enable):
|
||||
opts.set_defaults(self.conf_fixture.conf, enabled=True,
|
||||
hmac_keys="MY_KEY")
|
||||
opts.set_defaults(
|
||||
self.conf_fixture.conf, enabled=True, hmac_keys="MY_KEY"
|
||||
)
|
||||
opts.enable_web_trace(self.conf_fixture.conf)
|
||||
opts.disable_web_trace(self.conf_fixture.conf)
|
||||
mock_enable.assert_called_once_with("MY_KEY")
|
||||
|
||||
@@ -25,7 +25,6 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class ProfilerGlobMethodsTestCase(test.TestCase):
|
||||
|
||||
def test_get_profiler_not_inited(self):
|
||||
profiler.clean()
|
||||
self.assertIsNone(profiler.get())
|
||||
@@ -60,7 +59,6 @@ class ProfilerGlobMethodsTestCase(test.TestCase):
|
||||
|
||||
|
||||
class ProfilerTestCase(test.TestCase):
|
||||
|
||||
def test_profiler_get_shorten_id(self):
|
||||
uuid_id = "4e3e0ec6-2938-40b1-8504-09eb1d4b0dee"
|
||||
prof = profiler._Profiler("secret", base_id="1", parent_id="2")
|
||||
@@ -103,8 +101,9 @@ class ProfilerTestCase(test.TestCase):
|
||||
@mock.patch("osprofiler.profiler.timeutils")
|
||||
@mock.patch("osprofiler.profiler.uuidutils.generate_uuid")
|
||||
@mock.patch("osprofiler.profiler.notifier.notify")
|
||||
def test_profiler_start(self, mock_notify, mock_generate_uuid,
|
||||
mock_timeutils):
|
||||
def test_profiler_start(
|
||||
self, mock_notify, mock_generate_uuid, mock_timeutils
|
||||
):
|
||||
mock_generate_uuid.return_value = "44"
|
||||
now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
||||
mock_timeutils.utcnow.return_value = now
|
||||
@@ -156,7 +155,6 @@ class ProfilerTestCase(test.TestCase):
|
||||
|
||||
|
||||
class WithTraceTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@mock.patch("osprofiler.profiler.start")
|
||||
def test_with_trace(self, mock_start, mock_stop):
|
||||
@@ -180,10 +178,9 @@ class WithTraceTestCase(test.TestCase):
|
||||
|
||||
self.assertRaises(ValueError, foo)
|
||||
mock_start.assert_called_once_with("foo", info=None)
|
||||
mock_stop.assert_called_once_with(info={
|
||||
"etype": "ValueError",
|
||||
"message": "bar"
|
||||
})
|
||||
mock_stop.assert_called_once_with(
|
||||
info={"etype": "ValueError", "message": "bar"}
|
||||
)
|
||||
|
||||
|
||||
@profiler.trace("function", info={"info": "some_info"})
|
||||
@@ -207,7 +204,6 @@ def trace_with_result_func(a, i=10):
|
||||
|
||||
|
||||
class TraceDecoratorTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@mock.patch("osprofiler.profiler.start")
|
||||
def test_duplicate_trace_disallow(self, mock_start, mock_stop):
|
||||
@@ -219,7 +215,8 @@ class TraceDecoratorTestCase(test.TestCase):
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
profiler.trace("test-again", allow_multiple_trace=False),
|
||||
trace_me)
|
||||
trace_me,
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@mock.patch("osprofiler.profiler.start")
|
||||
@@ -230,8 +227,8 @@ class TraceDecoratorTestCase(test.TestCase):
|
||||
"function": {
|
||||
"name": "osprofiler.tests.unit.test_profiler.traced_func",
|
||||
"args": str((1,)),
|
||||
"kwargs": str({})
|
||||
}
|
||||
"kwargs": str({}),
|
||||
},
|
||||
}
|
||||
mock_start.assert_called_once_with("function", info=expected_info)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
@@ -243,7 +240,7 @@ class TraceDecoratorTestCase(test.TestCase):
|
||||
expected_info = {
|
||||
"function": {
|
||||
"name": "osprofiler.tests.unit.test_profiler"
|
||||
".trace_hide_args_func"
|
||||
".trace_hide_args_func"
|
||||
}
|
||||
}
|
||||
mock_start.assert_called_once_with("hide_args", info=expected_info)
|
||||
@@ -270,23 +267,18 @@ class TraceDecoratorTestCase(test.TestCase):
|
||||
start_info = {
|
||||
"function": {
|
||||
"name": "osprofiler.tests.unit.test_profiler"
|
||||
".trace_with_result_func",
|
||||
".trace_with_result_func",
|
||||
"args": str((1,)),
|
||||
"kwargs": str({"i": 2})
|
||||
"kwargs": str({"i": 2}),
|
||||
}
|
||||
}
|
||||
|
||||
stop_info = {
|
||||
"function": {
|
||||
"result": str((1, 2))
|
||||
}
|
||||
}
|
||||
stop_info = {"function": {"result": str((1, 2))}}
|
||||
mock_start.assert_called_once_with("hide_result", info=start_info)
|
||||
mock_stop.assert_called_once_with(info=stop_info)
|
||||
|
||||
|
||||
class FakeTracedCls:
|
||||
|
||||
def method1(self, a, b, c=10):
|
||||
return a + b + c
|
||||
|
||||
@@ -345,8 +337,9 @@ class FakeTraceClassMethodSkip(FakeTraceClassMethodBase):
|
||||
def py3_info(info):
|
||||
# NOTE(boris-42): py33 I hate you.
|
||||
info_py3 = copy.deepcopy(info)
|
||||
new_name = re.sub("FakeTrace[^.]*", "FakeTracedCls",
|
||||
info_py3["function"]["name"])
|
||||
new_name = re.sub(
|
||||
"FakeTrace[^.]*", "FakeTracedCls", info_py3["function"]["name"]
|
||||
)
|
||||
info_py3["function"]["name"] = new_name
|
||||
return info_py3
|
||||
|
||||
@@ -357,7 +350,6 @@ def possible_mock_calls(name, info):
|
||||
|
||||
|
||||
class TraceClsDecoratorTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@mock.patch("osprofiler.profiler.start")
|
||||
def test_args(self, mock_start, mock_stop):
|
||||
@@ -366,15 +358,19 @@ class TraceClsDecoratorTestCase(test.TestCase):
|
||||
expected_info = {
|
||||
"a": 10,
|
||||
"function": {
|
||||
"name": ("osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceClassWithInfo.method1"),
|
||||
"name": (
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceClassWithInfo.method1"
|
||||
),
|
||||
"args": str((fake_cls, 5, 15)),
|
||||
"kwargs": str({})
|
||||
}
|
||||
"kwargs": str({}),
|
||||
},
|
||||
}
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@@ -385,15 +381,19 @@ class TraceClsDecoratorTestCase(test.TestCase):
|
||||
expected_info = {
|
||||
"a": 10,
|
||||
"function": {
|
||||
"name": ("osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceClassWithInfo.method3"),
|
||||
"name": (
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceClassWithInfo.method3"
|
||||
),
|
||||
"args": str((fake_cls,)),
|
||||
"kwargs": str({"g": 5, "h": 10})
|
||||
}
|
||||
"kwargs": str({"g": 5, "h": 10}),
|
||||
},
|
||||
}
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@@ -412,14 +412,18 @@ class TraceClsDecoratorTestCase(test.TestCase):
|
||||
expected_info = {
|
||||
"b": 20,
|
||||
"function": {
|
||||
"name": ("osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceClassHideArgs.method1"),
|
||||
}
|
||||
"name": (
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceClassHideArgs.method1"
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("a", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("a", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@@ -430,23 +434,28 @@ class TraceClsDecoratorTestCase(test.TestCase):
|
||||
|
||||
expected_info = {
|
||||
"function": {
|
||||
"name": ("osprofiler.tests.unit.test_profiler"
|
||||
".FakeTracePrivate._method"),
|
||||
"name": (
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
".FakeTracePrivate._method"
|
||||
),
|
||||
"args": str((fake_cls, 5)),
|
||||
"kwargs": str({})
|
||||
"kwargs": str({}),
|
||||
}
|
||||
}
|
||||
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@mock.patch("osprofiler.profiler.start")
|
||||
@test.testcase.skip(
|
||||
"Static method tracing was disabled due the bug. This test should be "
|
||||
"skipped until we find the way to address it.")
|
||||
"skipped until we find the way to address it."
|
||||
)
|
||||
def test_static(self, mock_start, mock_stop):
|
||||
fake_cls = FakeTraceStaticMethod()
|
||||
|
||||
@@ -459,18 +468,19 @@ class TraceClsDecoratorTestCase(test.TestCase):
|
||||
# expect to see method4 because method is
|
||||
# static and doesn't have reference to class
|
||||
# - and FakeTraceStatic.method4 in PY3
|
||||
"name":
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
"osprofiler.tests.unit.test_profiler.FakeTraceStatic"
|
||||
".method4",
|
||||
"name": "osprofiler.tests.unit.test_profiler"
|
||||
"osprofiler.tests.unit.test_profiler.FakeTraceStatic"
|
||||
".method4",
|
||||
"args": str((25,)),
|
||||
"kwargs": str({})
|
||||
"kwargs": str({}),
|
||||
}
|
||||
}
|
||||
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@@ -489,8 +499,7 @@ class TraceClsDecoratorTestCase(test.TestCase):
|
||||
|
||||
|
||||
class FakeTraceWithMetaclassBase(metaclass=profiler.TracedMeta):
|
||||
__trace_args__ = {"name": "rpc",
|
||||
"info": {"a": 10}}
|
||||
__trace_args__ = {"name": "rpc", "info": {"a": 10}}
|
||||
|
||||
def method1(self, a, b, c=10):
|
||||
return a + b + c
|
||||
@@ -511,29 +520,27 @@ class FakeTraceDummy(FakeTraceWithMetaclassBase):
|
||||
|
||||
|
||||
class FakeTraceWithMetaclassHideArgs(FakeTraceWithMetaclassBase):
|
||||
__trace_args__ = {"name": "a",
|
||||
"info": {"b": 20},
|
||||
"hide_args": True}
|
||||
__trace_args__ = {"name": "a", "info": {"b": 20}, "hide_args": True}
|
||||
|
||||
def method5(self, k, l):
|
||||
return k + l
|
||||
|
||||
|
||||
class FakeTraceWithMetaclassPrivate(FakeTraceWithMetaclassBase):
|
||||
__trace_args__ = {"name": "rpc",
|
||||
"trace_private": True}
|
||||
__trace_args__ = {"name": "rpc", "trace_private": True}
|
||||
|
||||
def _new_private_method(self, m):
|
||||
return 2 * m
|
||||
|
||||
|
||||
class TraceWithMetaclassTestCase(test.TestCase):
|
||||
|
||||
def test_no_name_exception(self):
|
||||
def define_class_with_no_name():
|
||||
class FakeTraceWithMetaclassNoName(FakeTracedCls,
|
||||
metaclass=profiler.TracedMeta):
|
||||
class FakeTraceWithMetaclassNoName(
|
||||
FakeTracedCls, metaclass=profiler.TracedMeta
|
||||
):
|
||||
pass
|
||||
|
||||
self.assertRaises(TypeError, define_class_with_no_name, 1)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@@ -544,15 +551,19 @@ class TraceWithMetaclassTestCase(test.TestCase):
|
||||
expected_info = {
|
||||
"a": 10,
|
||||
"function": {
|
||||
"name": ("osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceWithMetaclassBase.method1"),
|
||||
"name": (
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceWithMetaclassBase.method1"
|
||||
),
|
||||
"args": str((fake_cls, 5, 15)),
|
||||
"kwargs": str({})
|
||||
}
|
||||
"kwargs": str({}),
|
||||
},
|
||||
}
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@@ -563,15 +574,19 @@ class TraceWithMetaclassTestCase(test.TestCase):
|
||||
expected_info = {
|
||||
"a": 10,
|
||||
"function": {
|
||||
"name": ("osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceWithMetaclassBase.method3"),
|
||||
"name": (
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceWithMetaclassBase.method3"
|
||||
),
|
||||
"args": str((fake_cls,)),
|
||||
"kwargs": str({"g": 5, "h": 10})
|
||||
}
|
||||
"kwargs": str({"g": 5, "h": 10}),
|
||||
},
|
||||
}
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@@ -590,14 +605,18 @@ class TraceWithMetaclassTestCase(test.TestCase):
|
||||
expected_info = {
|
||||
"b": 20,
|
||||
"function": {
|
||||
"name": ("osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceWithMetaclassHideArgs.method5")
|
||||
}
|
||||
"name": (
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceWithMetaclassHideArgs.method5"
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("a", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("a", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@mock.patch("osprofiler.profiler.stop")
|
||||
@@ -608,14 +627,18 @@ class TraceWithMetaclassTestCase(test.TestCase):
|
||||
|
||||
expected_info = {
|
||||
"function": {
|
||||
"name": ("osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceWithMetaclassPrivate._new_private_method"),
|
||||
"name": (
|
||||
"osprofiler.tests.unit.test_profiler"
|
||||
".FakeTraceWithMetaclassPrivate._new_private_method"
|
||||
),
|
||||
"args": str((fake_cls, 5)),
|
||||
"kwargs": str({})
|
||||
"kwargs": str({}),
|
||||
}
|
||||
}
|
||||
|
||||
self.assertEqual(1, len(mock_start.call_args_list))
|
||||
self.assertIn(mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info))
|
||||
self.assertIn(
|
||||
mock_start.call_args_list[0],
|
||||
possible_mock_calls("rpc", expected_info),
|
||||
)
|
||||
mock_stop.assert_called_once_with(info=None)
|
||||
|
||||
@@ -22,7 +22,6 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class SqlalchemyTracingTestCase(test.TestCase):
|
||||
|
||||
@mock.patch("osprofiler.sqlalchemy.profiler")
|
||||
def test_before_execute(self, mock_profiler):
|
||||
handler = sqlalchemy._before_cursor_execute("sql")
|
||||
@@ -43,11 +42,7 @@ class SqlalchemyTracingTestCase(test.TestCase):
|
||||
cursor = mock.MagicMock()
|
||||
cursor._rows = (1,)
|
||||
handler(1, cursor, 2, 3, 4, 5)
|
||||
info = {
|
||||
"db": {
|
||||
"result": str(cursor._rows)
|
||||
}
|
||||
}
|
||||
info = {"db": {"result": str(cursor._rows)}}
|
||||
mock_profiler.stop.assert_called_once_with(info=info)
|
||||
|
||||
@mock.patch("osprofiler.sqlalchemy.profiler")
|
||||
@@ -66,15 +61,16 @@ class SqlalchemyTracingTestCase(test.TestCase):
|
||||
"db": {
|
||||
"original_exception": str(original_exception),
|
||||
"chained_exception": str(chained_exception),
|
||||
}
|
||||
},
|
||||
}
|
||||
mock_profiler.stop.assert_called_once_with(info=expected_info)
|
||||
|
||||
@mock.patch("osprofiler.sqlalchemy.handle_error")
|
||||
@mock.patch("osprofiler.sqlalchemy._before_cursor_execute")
|
||||
@mock.patch("osprofiler.sqlalchemy._after_cursor_execute")
|
||||
def test_add_tracing(self, mock_after_exc, mock_before_exc,
|
||||
mock_handle_error):
|
||||
def test_add_tracing(
|
||||
self, mock_after_exc, mock_before_exc, mock_handle_error
|
||||
):
|
||||
sa = mock.MagicMock()
|
||||
engine = mock.MagicMock()
|
||||
|
||||
@@ -96,8 +92,9 @@ class SqlalchemyTracingTestCase(test.TestCase):
|
||||
@mock.patch("osprofiler.sqlalchemy.handle_error")
|
||||
@mock.patch("osprofiler.sqlalchemy._before_cursor_execute")
|
||||
@mock.patch("osprofiler.sqlalchemy._after_cursor_execute")
|
||||
def test_wrap_session(self, mock_after_exc, mock_before_exc,
|
||||
mock_handle_error):
|
||||
def test_wrap_session(
|
||||
self, mock_after_exc, mock_before_exc, mock_handle_error
|
||||
):
|
||||
sa = mock.MagicMock()
|
||||
|
||||
@contextlib.contextmanager
|
||||
@@ -131,8 +128,9 @@ class SqlalchemyTracingTestCase(test.TestCase):
|
||||
@mock.patch("osprofiler.sqlalchemy._before_cursor_execute")
|
||||
@mock.patch("osprofiler.sqlalchemy._after_cursor_execute")
|
||||
@mock.patch("osprofiler.profiler")
|
||||
def test_with_sql_result(self, mock_profiler, mock_after_exc,
|
||||
mock_before_exc, mock_handle_error):
|
||||
def test_with_sql_result(
|
||||
self, mock_profiler, mock_after_exc, mock_before_exc, mock_handle_error
|
||||
):
|
||||
sa = mock.MagicMock()
|
||||
engine = mock.MagicMock()
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ from osprofiler.tests import test
|
||||
|
||||
|
||||
class UtilsTestCase(test.TestCase):
|
||||
|
||||
def test_split(self):
|
||||
self.assertEqual([1, 2], utils.split([1, 2]))
|
||||
self.assertEqual(["A", "B"], utils.split("A, B"))
|
||||
@@ -35,8 +34,9 @@ class UtilsTestCase(test.TestCase):
|
||||
self.assertRaises(TypeError, utils.split, 1)
|
||||
|
||||
def test_binary_encode_and_decode(self):
|
||||
self.assertEqual("text",
|
||||
utils.binary_decode(utils.binary_encode("text")))
|
||||
self.assertEqual(
|
||||
"text", utils.binary_decode(utils.binary_encode("text"))
|
||||
)
|
||||
|
||||
def test_binary_encode_invalid_type(self):
|
||||
self.assertRaises(TypeError, utils.binary_encode, 1234)
|
||||
|
||||
@@ -29,7 +29,6 @@ def dummy_app(environ, response):
|
||||
|
||||
|
||||
class WebTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
profiler.clean()
|
||||
@@ -43,11 +42,13 @@ class WebTestCase(test.TestCase):
|
||||
def test_get_trace_id_headers(self):
|
||||
profiler.init("key", base_id="y", parent_id="z")
|
||||
headers = web.get_trace_id_headers()
|
||||
self.assertEqual(sorted(headers.keys()),
|
||||
sorted(["X-Trace-Info", "X-Trace-HMAC"]))
|
||||
self.assertEqual(
|
||||
sorted(headers.keys()), sorted(["X-Trace-Info", "X-Trace-HMAC"])
|
||||
)
|
||||
|
||||
trace_info = utils.signed_unpack(headers["X-Trace-Info"],
|
||||
headers["X-Trace-HMAC"], ["key"])
|
||||
trace_info = utils.signed_unpack(
|
||||
headers["X-Trace-Info"], headers["X-Trace-HMAC"], ["key"]
|
||||
)
|
||||
self.assertIn("hmac_key", trace_info)
|
||||
self.assertEqual("key", trace_info.pop("hmac_key"))
|
||||
self.assertEqual({"parent_id": "z", "base_id": "y"}, trace_info)
|
||||
@@ -83,9 +84,9 @@ class WebMiddlewareTestCase(test.TestCase):
|
||||
self.assertTrue(wsgi.enabled)
|
||||
self.assertEqual(wsgi.hmac_keys, [local_conf["hmac_keys"]])
|
||||
|
||||
def _test_wsgi_middleware_with_invalid_trace(self, headers, hmac_key,
|
||||
mock_profiler_init,
|
||||
enabled=True):
|
||||
def _test_wsgi_middleware_with_invalid_trace(
|
||||
self, headers, hmac_key, mock_profiler_init, enabled=True
|
||||
):
|
||||
request = mock.MagicMock()
|
||||
request.get_response.return_value = "yeah!"
|
||||
request.headers = headers
|
||||
@@ -103,21 +104,19 @@ class WebMiddlewareTestCase(test.TestCase):
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": pack[0],
|
||||
"X-Trace-HMAC": pack[1]
|
||||
"X-Trace-HMAC": pack[1],
|
||||
}
|
||||
|
||||
self._test_wsgi_middleware_with_invalid_trace(headers, hmac_key,
|
||||
mock_profiler_init,
|
||||
enabled=False)
|
||||
self._test_wsgi_middleware_with_invalid_trace(
|
||||
headers, hmac_key, mock_profiler_init, enabled=False
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.web.profiler.init")
|
||||
def test_wsgi_middleware_no_trace(self, mock_profiler_init):
|
||||
headers = {
|
||||
"a": "1",
|
||||
"b": "2"
|
||||
}
|
||||
self._test_wsgi_middleware_with_invalid_trace(headers, "secret",
|
||||
mock_profiler_init)
|
||||
headers = {"a": "1", "b": "2"}
|
||||
self._test_wsgi_middleware_with_invalid_trace(
|
||||
headers, "secret", mock_profiler_init
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.web.profiler.init")
|
||||
def test_wsgi_middleware_invalid_trace_headers(self, mock_profiler_init):
|
||||
@@ -125,22 +124,20 @@ class WebMiddlewareTestCase(test.TestCase):
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": "abbababababa",
|
||||
"X-Trace-HMAC": "abbababababa"
|
||||
"X-Trace-HMAC": "abbababababa",
|
||||
}
|
||||
self._test_wsgi_middleware_with_invalid_trace(headers, "secret",
|
||||
mock_profiler_init)
|
||||
self._test_wsgi_middleware_with_invalid_trace(
|
||||
headers, "secret", mock_profiler_init
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.web.profiler.init")
|
||||
def test_wsgi_middleware_no_trace_hmac(self, mock_profiler_init):
|
||||
hmac_key = "secret"
|
||||
pack = utils.signed_pack({"base_id": "1", "parent_id": "2"}, hmac_key)
|
||||
headers = {
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": pack[0]
|
||||
}
|
||||
self._test_wsgi_middleware_with_invalid_trace(headers, hmac_key,
|
||||
mock_profiler_init)
|
||||
headers = {"a": "1", "b": "2", "X-Trace-Info": pack[0]}
|
||||
self._test_wsgi_middleware_with_invalid_trace(
|
||||
headers, hmac_key, mock_profiler_init
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.web.profiler.init")
|
||||
def test_wsgi_middleware_invalid_hmac(self, mock_profiler_init):
|
||||
@@ -150,24 +147,27 @@ class WebMiddlewareTestCase(test.TestCase):
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": pack[0],
|
||||
"X-Trace-HMAC": "not valid hmac"
|
||||
"X-Trace-HMAC": "not valid hmac",
|
||||
}
|
||||
self._test_wsgi_middleware_with_invalid_trace(headers, hmac_key,
|
||||
mock_profiler_init)
|
||||
self._test_wsgi_middleware_with_invalid_trace(
|
||||
headers, hmac_key, mock_profiler_init
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.web.profiler.init")
|
||||
def test_wsgi_middleware_invalid_trace_info(self, mock_profiler_init):
|
||||
hmac_key = "secret"
|
||||
pack = utils.signed_pack([{"base_id": "1"}, {"parent_id": "2"}],
|
||||
hmac_key)
|
||||
pack = utils.signed_pack(
|
||||
[{"base_id": "1"}, {"parent_id": "2"}], hmac_key
|
||||
)
|
||||
headers = {
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": pack[0],
|
||||
"X-Trace-HMAC": pack[1]
|
||||
"X-Trace-HMAC": pack[1],
|
||||
}
|
||||
self._test_wsgi_middleware_with_invalid_trace(headers, hmac_key,
|
||||
mock_profiler_init)
|
||||
self._test_wsgi_middleware_with_invalid_trace(
|
||||
headers, hmac_key, mock_profiler_init
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.web.profiler.init")
|
||||
def test_wsgi_middleware_key_passthrough(self, mock_profiler_init):
|
||||
@@ -187,15 +187,16 @@ class WebMiddlewareTestCase(test.TestCase):
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": pack[0],
|
||||
"X-Trace-HMAC": pack[1]
|
||||
"X-Trace-HMAC": pack[1],
|
||||
}
|
||||
|
||||
middleware = web.WsgiMiddleware("app", "secret1,%s" % hmac_key,
|
||||
enabled=True)
|
||||
middleware = web.WsgiMiddleware(
|
||||
"app", f"secret1,{hmac_key}", enabled=True
|
||||
)
|
||||
self.assertEqual("yeah!", middleware(request))
|
||||
mock_profiler_init.assert_called_once_with(hmac_key=hmac_key,
|
||||
base_id="1",
|
||||
parent_id="2")
|
||||
mock_profiler_init.assert_called_once_with(
|
||||
hmac_key=hmac_key, base_id="1", parent_id="2"
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.web.profiler.init")
|
||||
def test_wsgi_middleware_key_passthrough2(self, mock_profiler_init):
|
||||
@@ -215,15 +216,16 @@ class WebMiddlewareTestCase(test.TestCase):
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": pack[0],
|
||||
"X-Trace-HMAC": pack[1]
|
||||
"X-Trace-HMAC": pack[1],
|
||||
}
|
||||
|
||||
middleware = web.WsgiMiddleware("app", "%s,secret2" % hmac_key,
|
||||
enabled=True)
|
||||
middleware = web.WsgiMiddleware(
|
||||
"app", f"{hmac_key},secret2", enabled=True
|
||||
)
|
||||
self.assertEqual("yeah!", middleware(request))
|
||||
mock_profiler_init.assert_called_once_with(hmac_key=hmac_key,
|
||||
base_id="1",
|
||||
parent_id="2")
|
||||
mock_profiler_init.assert_called_once_with(
|
||||
hmac_key=hmac_key, base_id="1", parent_id="2"
|
||||
)
|
||||
|
||||
@mock.patch("osprofiler.web.profiler.Trace")
|
||||
@mock.patch("osprofiler.web.profiler.init")
|
||||
@@ -244,20 +246,20 @@ class WebMiddlewareTestCase(test.TestCase):
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": pack[0],
|
||||
"X-Trace-HMAC": pack[1]
|
||||
"X-Trace-HMAC": pack[1],
|
||||
}
|
||||
|
||||
middleware = web.WsgiMiddleware("app", hmac_key, enabled=True)
|
||||
self.assertEqual("yeah!", middleware(request))
|
||||
mock_profiler_init.assert_called_once_with(hmac_key=hmac_key,
|
||||
base_id="1",
|
||||
parent_id="2")
|
||||
mock_profiler_init.assert_called_once_with(
|
||||
hmac_key=hmac_key, base_id="1", parent_id="2"
|
||||
)
|
||||
expected_info = {
|
||||
"request": {
|
||||
"path": request.path,
|
||||
"query": request.query_string,
|
||||
"method": request.method,
|
||||
"scheme": request.scheme
|
||||
"scheme": request.scheme,
|
||||
}
|
||||
}
|
||||
mock_profiler_trace.assert_called_once_with("wsgi", info=expected_info)
|
||||
@@ -288,15 +290,15 @@ class WebMiddlewareTestCase(test.TestCase):
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"X-Trace-Info": pack[0],
|
||||
"X-Trace-HMAC": pack[1]
|
||||
"X-Trace-HMAC": pack[1],
|
||||
}
|
||||
|
||||
web.enable("super_secret_key1,super_secret_key2")
|
||||
middleware = web.WsgiMiddleware("app", enabled=True)
|
||||
self.assertEqual("yeah!", middleware(request))
|
||||
mock_profiler_init.assert_called_once_with(hmac_key=hmac_key,
|
||||
base_id="1",
|
||||
parent_id="2")
|
||||
mock_profiler_init.assert_called_once_with(
|
||||
hmac_key=hmac_key, base_id="1", parent_id="2"
|
||||
)
|
||||
|
||||
def test_disable(self):
|
||||
web.disable()
|
||||
|
||||
+14
-10
@@ -37,10 +37,7 @@ def get_trace_id_headers():
|
||||
if p and p.hmac_key:
|
||||
data = {"base_id": p.get_base_id(), "parent_id": p.get_id()}
|
||||
pack = utils.signed_pack(data, p.hmac_key)
|
||||
return {
|
||||
X_TRACE_INFO: pack[0],
|
||||
X_TRACE_HMAC: pack[1]
|
||||
}
|
||||
return {X_TRACE_INFO: pack[0], X_TRACE_HMAC: pack[1]}
|
||||
return {}
|
||||
|
||||
|
||||
@@ -92,6 +89,7 @@ class WsgiMiddleware:
|
||||
def factory(cls, global_conf, **local_conf):
|
||||
def filter_(app):
|
||||
return cls(app, **local_conf)
|
||||
|
||||
return filter_
|
||||
|
||||
def _trace_is_valid(self, trace_info):
|
||||
@@ -106,13 +104,19 @@ class WsgiMiddleware:
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, request):
|
||||
if (_ENABLED is not None and not _ENABLED
|
||||
or _ENABLED is None and not self.enabled):
|
||||
if (
|
||||
_ENABLED is not None
|
||||
and not _ENABLED
|
||||
or _ENABLED is None
|
||||
and not self.enabled
|
||||
):
|
||||
return request.get_response(self.application)
|
||||
|
||||
trace_info = utils.signed_unpack(request.headers.get(X_TRACE_INFO),
|
||||
request.headers.get(X_TRACE_HMAC),
|
||||
_HMAC_KEYS or self.hmac_keys)
|
||||
trace_info = utils.signed_unpack(
|
||||
request.headers.get(X_TRACE_INFO),
|
||||
request.headers.get(X_TRACE_HMAC),
|
||||
_HMAC_KEYS or self.hmac_keys,
|
||||
)
|
||||
|
||||
if not self._trace_is_valid(trace_info):
|
||||
return request.get_response(self.application)
|
||||
@@ -123,7 +127,7 @@ class WsgiMiddleware:
|
||||
"path": request.path,
|
||||
"query": request.query_string,
|
||||
"method": request.method,
|
||||
"scheme": request.scheme
|
||||
"scheme": request.scheme,
|
||||
}
|
||||
}
|
||||
try:
|
||||
|
||||
+11
-2
@@ -65,5 +65,14 @@ packages = [
|
||||
"osprofiler"
|
||||
]
|
||||
|
||||
[tool.bandit]
|
||||
exclude_dirs = ["tests"]
|
||||
[tool.ruff]
|
||||
line-length = 79
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "preserve"
|
||||
docstring-code-format = true
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E4", "E5", "E7", "E9", "F", "G", "LOG", "S", "UP"]
|
||||
external = ["H"]
|
||||
ignore = ["E741"]
|
||||
|
||||
+23
-13
@@ -195,10 +195,8 @@ htmlhelp_basename = 'osprofilerReleaseNotesDoc'
|
||||
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': '',
|
||||
}
|
||||
@@ -207,9 +205,13 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'osprofilerReleaseNotes.tex',
|
||||
'osprofiler Release Notes Documentation',
|
||||
'osprofiler Developers', 'manual'),
|
||||
(
|
||||
'index',
|
||||
'osprofilerReleaseNotes.tex',
|
||||
'osprofiler Release Notes Documentation',
|
||||
'osprofiler Developers',
|
||||
'manual',
|
||||
),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@@ -238,9 +240,13 @@ latex_documents = [
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'osprofilerReleaseNotes',
|
||||
'osprofiler Release Notes Documentation',
|
||||
['osprofiler Developers'], 1)
|
||||
(
|
||||
'index',
|
||||
'osprofilerReleaseNotes',
|
||||
'osprofiler Release Notes Documentation',
|
||||
['osprofiler Developers'],
|
||||
1,
|
||||
)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
@@ -253,11 +259,15 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'osprofilerReleaseNotes',
|
||||
'osprofiler Release Notes Documentation',
|
||||
'osprofiler Developers', 'osprofilerReleaseNotes',
|
||||
'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
'index',
|
||||
'osprofilerReleaseNotes',
|
||||
'osprofiler Release Notes Documentation',
|
||||
'osprofiler Developers',
|
||||
'osprofilerReleaseNotes',
|
||||
'One line description of project.',
|
||||
'Miscellaneous',
|
||||
),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
||||
@@ -14,6 +14,4 @@
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0'],
|
||||
pbr=True)
|
||||
setuptools.setup(setup_requires=['pbr>=2.0'], pbr=True)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ ENABLED_PYLINT_MSGS = ['W0611']
|
||||
|
||||
|
||||
def main(dirpath):
|
||||
enable_opt = '--enable=%s' % ','.join(ENABLED_PYLINT_MSGS)
|
||||
enable_opt = '--enable={}'.format(','.join(ENABLED_PYLINT_MSGS))
|
||||
lint.Run(['--reports=n', '--disable=all', enable_opt, dirpath])
|
||||
|
||||
|
||||
|
||||
+16
-11
@@ -29,18 +29,23 @@ def main(argv):
|
||||
|
||||
venv = os.environ['VIRTUAL_ENV']
|
||||
|
||||
pip_requires = first_file([
|
||||
os.path.join(root, 'requirements.txt'),
|
||||
os.path.join(root, 'tools', 'pip-requires'),
|
||||
])
|
||||
test_requires = first_file([
|
||||
os.path.join(root, 'test-requirements.txt'),
|
||||
os.path.join(root, 'tools', 'test-requires'),
|
||||
])
|
||||
py_version = "python{}.{}".format(sys.version_info[0], sys.version_info[1])
|
||||
pip_requires = first_file(
|
||||
[
|
||||
os.path.join(root, 'requirements.txt'),
|
||||
os.path.join(root, 'tools', 'pip-requires'),
|
||||
]
|
||||
)
|
||||
test_requires = first_file(
|
||||
[
|
||||
os.path.join(root, 'test-requirements.txt'),
|
||||
os.path.join(root, 'tools', 'test-requires'),
|
||||
]
|
||||
)
|
||||
py_version = f"python{sys.version_info[0]}.{sys.version_info[1]}"
|
||||
project = 'oslo'
|
||||
install = install_venv.InstallVenv(root, venv, pip_requires, test_requires,
|
||||
py_version, project)
|
||||
install = install_venv.InstallVenv(
|
||||
root, venv, pip_requires, test_requires, py_version, project
|
||||
)
|
||||
# NOTE(dprince): For Tox we only run post_process, which patches files, etc
|
||||
install.post_process()
|
||||
|
||||
|
||||
@@ -52,16 +52,23 @@ commands =
|
||||
sphinx-build -W --keep-going -b html -d doc/build/doctrees doc/source doc/build/html
|
||||
usedevelop = false
|
||||
|
||||
[testenv:releasenotes]
|
||||
deps =
|
||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
||||
-r{toxinidir}/doc/requirements.txt
|
||||
allowlist_externals = rm
|
||||
commands =
|
||||
rm -rf releasenotes/build
|
||||
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
[flake8]
|
||||
show-source = true
|
||||
builtins = _
|
||||
# E741 ambiguous variable name 'l'
|
||||
# W503 line break before binary operator
|
||||
ignore = E741,W503
|
||||
enable-extensions = H211,H214,H215
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools,setup.py,build,releasenotes
|
||||
import-order-style = pep8
|
||||
application-import-names = osprofiler
|
||||
exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,__init__.py
|
||||
# We only enable the hacking (H) and osprofiler (N) checks
|
||||
select = H
|
||||
# H301 Black will put commas after imports that can't fit on one line
|
||||
# H405 Multi-line docstrings are fine
|
||||
ignore = H301,H405
|
||||
|
||||
[flake8:local-plugins]
|
||||
extension =
|
||||
@@ -71,12 +78,3 @@ extension =
|
||||
N353 = checks:check_using_unicode
|
||||
N354 = checks:check_raises
|
||||
paths = ./osprofiler/hacking
|
||||
|
||||
[testenv:releasenotes]
|
||||
deps =
|
||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
||||
-r{toxinidir}/doc/requirements.txt
|
||||
allowlist_externals = rm
|
||||
commands =
|
||||
rm -rf releasenotes/build
|
||||
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
Reference in New Issue
Block a user