Add ruff
Change-Id: I04bd29aff7da65093bf7e78c915133d9c7b21f33 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
@@ -1,35 +1,25 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
# Replaces or checks mixed line ending
|
||||
- id: mixed-line-ending
|
||||
args: ['--fix', 'lf']
|
||||
exclude: '.*\.(svg)$'
|
||||
# Forbid files which have a UTF-8 byte-order marker
|
||||
- id: check-byte-order-marker
|
||||
# Checks that non-binary executables have a proper shebang
|
||||
- id: fix-byte-order-marker
|
||||
- id: check-executables-have-shebangs
|
||||
# Check for files that contain merge conflict strings.
|
||||
- id: check-merge-conflict
|
||||
# Check for debugger imports and py37+ breakpoint()
|
||||
# calls in python source
|
||||
- id: debug-statements
|
||||
- id: check-yaml
|
||||
files: .*\.(yaml|yml)$
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.14.7
|
||||
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.7.10
|
||||
hooks:
|
||||
- id: bandit
|
||||
args: ['-x', 'tests']
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.18.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py3-only]
|
||||
|
||||
@@ -34,9 +34,7 @@ openstackdocs_bug_tag = ''
|
||||
# sphinxcontrib.apidoc options
|
||||
apidoc_module_dir = '../../oslo_metrics'
|
||||
apidoc_output_dir = 'reference/api'
|
||||
apidoc_excluded_paths = [
|
||||
'tests'
|
||||
]
|
||||
apidoc_excluded_paths = ['tests']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
@@ -70,16 +68,19 @@ html_theme = 'openstackdocs'
|
||||
# html_static_path = ['static']
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
htmlhelp_basename = f'{project}doc'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
'%s Documentation' % project,
|
||||
'OpenStack Foundation', 'manual'),
|
||||
(
|
||||
'index',
|
||||
f'{project}.tex',
|
||||
f'{project} Documentation',
|
||||
'OpenStack Foundation',
|
||||
'manual',
|
||||
)
|
||||
]
|
||||
|
||||
intersphinx_mapping = {
|
||||
|
||||
@@ -30,18 +30,28 @@ from oslo_metrics import message_router
|
||||
|
||||
|
||||
oslo_metrics_configs = [
|
||||
cfg.StrOpt('metrics_socket_file',
|
||||
default='/var/tmp/metrics_collector.sock', # nosec
|
||||
help='Unix domain socket file to be used'
|
||||
' to send rpc related metrics'),
|
||||
cfg.PortOpt('prometheus_port', default=3000,
|
||||
help='Port number to expose metrics in prometheus format.'),
|
||||
cfg.IntOpt('metrics_socket_perm', default=0o660,
|
||||
help='Permission set to the unix domain socket file'),
|
||||
cfg.BoolOpt('wsgi_silent_server', default=True,
|
||||
help='Whether to silence the WSGI server. If disabled, the '
|
||||
'WSGI server will print all requests it receives on '
|
||||
'STDOUT. This could be very verbose.'),
|
||||
cfg.StrOpt(
|
||||
'metrics_socket_file',
|
||||
default='/var/tmp/metrics_collector.sock', # noqa: S108
|
||||
help='Unix domain socket file to be used to send rpc related metrics',
|
||||
),
|
||||
cfg.PortOpt(
|
||||
'prometheus_port',
|
||||
default=3000,
|
||||
help='Port number to expose metrics in prometheus format.',
|
||||
),
|
||||
cfg.IntOpt(
|
||||
'metrics_socket_perm',
|
||||
default=0o660,
|
||||
help='Permission set to the unix domain socket file',
|
||||
),
|
||||
cfg.BoolOpt(
|
||||
'wsgi_silent_server',
|
||||
default=True,
|
||||
help='Whether to silence the WSGI server. If disabled, the '
|
||||
'WSGI server will print all requests it receives on '
|
||||
'STDOUT. This could be very verbose.',
|
||||
),
|
||||
]
|
||||
cfg.CONF.register_opts(oslo_metrics_configs, group='oslo_metrics')
|
||||
|
||||
@@ -52,8 +62,7 @@ logging.register_options(CONF)
|
||||
logging.setup(CONF, 'oslo-metrics')
|
||||
|
||||
|
||||
class MetricsListener():
|
||||
|
||||
class MetricsListener:
|
||||
def __init__(self, socket_path):
|
||||
self.socket_path = socket_path
|
||||
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
@@ -73,7 +82,8 @@ class MetricsListener():
|
||||
def serve(self):
|
||||
while self.start:
|
||||
readable, writable, exceptional = select.select(
|
||||
[self.socket], [], [], 1)
|
||||
[self.socket], [], [], 1
|
||||
)
|
||||
if len(readable) == 0:
|
||||
continue
|
||||
try:
|
||||
@@ -82,7 +92,7 @@ class MetricsListener():
|
||||
msg = self.socket.recv(65565)
|
||||
LOG.debug("got message")
|
||||
self.router.process(msg)
|
||||
except socket.timeout:
|
||||
except TimeoutError:
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
@@ -122,8 +132,12 @@ def main():
|
||||
try:
|
||||
global httpd
|
||||
if cfg.CONF.oslo_metrics.wsgi_silent_server:
|
||||
httpd = make_server('', CONF.oslo_metrics.prometheus_port, app,
|
||||
handler_class=_SilentHandler)
|
||||
httpd = make_server(
|
||||
'',
|
||||
CONF.oslo_metrics.prometheus_port,
|
||||
app,
|
||||
handler_class=_SilentHandler,
|
||||
)
|
||||
else:
|
||||
httpd = make_server('', CONF.oslo_metrics.prometheus_port, app)
|
||||
signal.signal(signal.SIGTERM, handle_sigterm)
|
||||
|
||||
@@ -21,13 +21,10 @@ from oslo_metrics import message_type
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MODULE_LISTS = [
|
||||
"oslo_metrics.metrics.oslo_messaging",
|
||||
]
|
||||
MODULE_LISTS = ["oslo_metrics.metrics.oslo_messaging"]
|
||||
|
||||
|
||||
class MessageRouter():
|
||||
|
||||
class MessageRouter:
|
||||
def __init__(self):
|
||||
self.modules = {}
|
||||
for m_str in MODULE_LISTS:
|
||||
@@ -61,8 +58,11 @@ class MessageRouter():
|
||||
metric_with_label = getattr(metric_definition, "labels")
|
||||
metric_with_label = metric_with_label(**metric.labels)
|
||||
except AttributeError as e:
|
||||
LOG.error("Failed to load labels func from metrics %s: %s",
|
||||
metric.name, e)
|
||||
LOG.error(
|
||||
"Failed to load labels func from metrics %s: %s",
|
||||
metric.name,
|
||||
e,
|
||||
)
|
||||
return
|
||||
LOG.debug("Get labels with {}: {}", metric.name, metric.labels)
|
||||
|
||||
@@ -74,8 +74,15 @@ class MessageRouter():
|
||||
else:
|
||||
embed_action()
|
||||
except AttributeError as e:
|
||||
LOG.error("Failed to perform metric actionv %s, %s: %s",
|
||||
metric.action.action, metric.action.value, e)
|
||||
LOG.error(
|
||||
"Failed to perform metric actionv %s, %s: %s",
|
||||
metric.action.action,
|
||||
metric.action.value,
|
||||
e,
|
||||
)
|
||||
return
|
||||
LOG.debug("Perform action %s for %s metrics",
|
||||
metric.action.action, metric.name)
|
||||
LOG.debug(
|
||||
"Perform action %s for %s metrics",
|
||||
metric.action.action,
|
||||
metric.name,
|
||||
)
|
||||
|
||||
@@ -26,13 +26,14 @@ class MetricValidationError(Exception):
|
||||
self.message = message
|
||||
|
||||
|
||||
class MetricAction():
|
||||
class MetricAction:
|
||||
actions = ['inc', 'observe']
|
||||
|
||||
def __init__(self, action, value):
|
||||
if action not in self.actions:
|
||||
raise UnSupportedMetricActionError(
|
||||
"%s action is not supported" % action)
|
||||
f"{action} action is not supported"
|
||||
)
|
||||
self.action = action
|
||||
self.value = value
|
||||
|
||||
@@ -44,17 +45,15 @@ class MetricAction():
|
||||
raise MetricValidationError("action need 'action' field")
|
||||
if metric_action_dict["action"] not in cls.actions:
|
||||
raise MetricValidationError(
|
||||
"action should be choosen from %s" % cls.actions)
|
||||
f"action should be choosen from {cls.actions}"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, metric_action_dict):
|
||||
return cls(
|
||||
metric_action_dict["action"],
|
||||
metric_action_dict["value"]
|
||||
)
|
||||
return cls(metric_action_dict["action"], metric_action_dict["value"])
|
||||
|
||||
|
||||
class Metric():
|
||||
class Metric:
|
||||
def __init__(self, module, name, action, **labels):
|
||||
self.module = module
|
||||
self.name = name
|
||||
@@ -67,9 +66,9 @@ class Metric():
|
||||
"name": self.name,
|
||||
"action": {
|
||||
"value": self.action.value,
|
||||
"action": self.action.action
|
||||
"action": self.action.action,
|
||||
},
|
||||
"labels": self.labels
|
||||
"labels": self.labels,
|
||||
}
|
||||
return json.dumps(raw)
|
||||
|
||||
@@ -81,7 +80,8 @@ class Metric():
|
||||
metric_dict["module"],
|
||||
metric_dict["name"],
|
||||
MetricAction.from_dict(metric_dict["action"]),
|
||||
**metric_dict["labels"])
|
||||
**metric_dict["labels"],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _validate(cls, metric_dict):
|
||||
|
||||
@@ -16,17 +16,46 @@
|
||||
import prometheus_client
|
||||
|
||||
rpc_server_common_labels = [
|
||||
'exchange', 'topic', 'server', 'endpoint', 'namespace',
|
||||
'version', 'method', 'process'
|
||||
'exchange',
|
||||
'topic',
|
||||
'server',
|
||||
'endpoint',
|
||||
'namespace',
|
||||
'version',
|
||||
'method',
|
||||
'process',
|
||||
]
|
||||
rpc_client_common_labels = [
|
||||
'call_type', 'exchange', 'topic', 'namespace', 'version',
|
||||
'server', 'fanout', 'process', 'method', 'timeout'
|
||||
'call_type',
|
||||
'exchange',
|
||||
'topic',
|
||||
'namespace',
|
||||
'version',
|
||||
'server',
|
||||
'fanout',
|
||||
'process',
|
||||
'method',
|
||||
'timeout',
|
||||
]
|
||||
|
||||
rpc_processing_seconds_buckets = [
|
||||
0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0,
|
||||
2.5, 5.0, 7.5, 10.0, 25.0, 50.0, 75.0, 100
|
||||
0.01,
|
||||
0.025,
|
||||
0.05,
|
||||
0.075,
|
||||
0.1,
|
||||
0.25,
|
||||
0.5,
|
||||
0.75,
|
||||
1.0,
|
||||
2.5,
|
||||
5.0,
|
||||
7.5,
|
||||
10.0,
|
||||
25.0,
|
||||
50.0,
|
||||
75.0,
|
||||
100,
|
||||
]
|
||||
|
||||
# RPC Server Metrics
|
||||
@@ -34,42 +63,50 @@ rpc_server_invocation_start_total = prometheus_client.Counter(
|
||||
'oslo_messaging_rpc_server_invocation_start_total',
|
||||
'Total number of RPC invocation start. This doesn\'t count'
|
||||
'if rpc server failed to find method from endpoints.',
|
||||
rpc_server_common_labels)
|
||||
rpc_server_common_labels,
|
||||
)
|
||||
|
||||
rpc_server_invocation_end_total = prometheus_client.Counter(
|
||||
'oslo_messaging_rpc_server_invocation_end_total',
|
||||
'Total number of RPC invocation end.',
|
||||
rpc_server_common_labels)
|
||||
rpc_server_common_labels,
|
||||
)
|
||||
|
||||
rpc_server_processing_seconds = prometheus_client.Histogram(
|
||||
'oslo_messaging_rpc_server_processing_seconds',
|
||||
'Duration of RPC processing.',
|
||||
rpc_server_common_labels,
|
||||
buckets=rpc_processing_seconds_buckets)
|
||||
buckets=rpc_processing_seconds_buckets,
|
||||
)
|
||||
|
||||
rpc_server_exception_total = prometheus_client.Counter(
|
||||
'oslo_messaging_rpc_server_exception_total',
|
||||
'Total number of exception while RPC processing.',
|
||||
rpc_server_common_labels + ['exception'])
|
||||
rpc_server_common_labels + ['exception'],
|
||||
)
|
||||
|
||||
# RPC Client Metrics
|
||||
rpc_client_invocation_start_total = prometheus_client.Counter(
|
||||
'oslo_messaging_rpc_client_invocation_start_total',
|
||||
'Total number of RPC invocation start.',
|
||||
rpc_client_common_labels)
|
||||
rpc_client_common_labels,
|
||||
)
|
||||
|
||||
rpc_client_invocation_end_total = prometheus_client.Counter(
|
||||
'oslo_messaging_rpc_client_invocation_end_total',
|
||||
'Total number of RPC invocation end.',
|
||||
rpc_client_common_labels)
|
||||
rpc_client_common_labels,
|
||||
)
|
||||
|
||||
rpc_client_processing_seconds = prometheus_client.Histogram(
|
||||
'oslo_messaging_rpc_client_processing_seconds',
|
||||
'Duration of RPC processing.',
|
||||
rpc_client_common_labels,
|
||||
buckets=rpc_processing_seconds_buckets)
|
||||
buckets=rpc_processing_seconds_buckets,
|
||||
)
|
||||
|
||||
rpc_client_exception_total = prometheus_client.Counter(
|
||||
'oslo_messaging_rpc_client_exception_total',
|
||||
'Total number of exception while RPC processing.',
|
||||
rpc_client_common_labels + ['exception', ])
|
||||
rpc_client_common_labels + ['exception'],
|
||||
)
|
||||
|
||||
@@ -24,7 +24,6 @@ import prometheus_client
|
||||
|
||||
|
||||
class TestProcessMessage(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
@@ -48,9 +47,7 @@ class TestProcessMessage(base.BaseTestCase):
|
||||
}
|
||||
}"""
|
||||
|
||||
with mock.patch.object(
|
||||
prometheus_client.Counter, 'inc',
|
||||
) as mock_inc:
|
||||
with mock.patch.object(prometheus_client.Counter, 'inc') as mock_inc:
|
||||
router = message_router.MessageRouter()
|
||||
router.process(received_json)
|
||||
mock_inc.assert_called_once_with()
|
||||
@@ -78,7 +75,7 @@ class TestProcessMessage(base.BaseTestCase):
|
||||
}"""
|
||||
|
||||
with mock.patch.object(
|
||||
prometheus_client.Histogram, 'observe',
|
||||
prometheus_client.Histogram, 'observe'
|
||||
) as mock_inc:
|
||||
router = message_router.MessageRouter()
|
||||
router.process(received_json)
|
||||
|
||||
@@ -33,37 +33,44 @@ class TestMetricValidation(base.BaseTestCase):
|
||||
self.assertEqual(message, e.message)
|
||||
|
||||
def test_message_validation(self):
|
||||
metric = dict()
|
||||
metric = {}
|
||||
message = "module should be specified"
|
||||
self.assertRaisesWithMessage(
|
||||
message, message_type.Metric.from_json, json.dumps(metric))
|
||||
message, message_type.Metric.from_json, json.dumps(metric)
|
||||
)
|
||||
|
||||
metric['module'] = "test"
|
||||
message = "name should be specified"
|
||||
self.assertRaisesWithMessage(
|
||||
message, message_type.Metric.from_json, json.dumps(metric))
|
||||
message, message_type.Metric.from_json, json.dumps(metric)
|
||||
)
|
||||
|
||||
metric['name'] = "test"
|
||||
message = "action should be specified"
|
||||
self.assertRaisesWithMessage(
|
||||
message, message_type.Metric.from_json, json.dumps(metric))
|
||||
message, message_type.Metric.from_json, json.dumps(metric)
|
||||
)
|
||||
|
||||
metric['action'] = "test"
|
||||
message = "labels should be specified"
|
||||
self.assertRaisesWithMessage(
|
||||
message, message_type.Metric.from_json, json.dumps(metric))
|
||||
message, message_type.Metric.from_json, json.dumps(metric)
|
||||
)
|
||||
|
||||
metric['labels'] = "test_label"
|
||||
message = "action need 'value' field"
|
||||
self.assertRaisesWithMessage(
|
||||
message, message_type.Metric.from_json, json.dumps(metric))
|
||||
message, message_type.Metric.from_json, json.dumps(metric)
|
||||
)
|
||||
|
||||
metric['action'] = {"value": "1"}
|
||||
message = "action need 'action' field"
|
||||
self.assertRaisesWithMessage(
|
||||
message, message_type.Metric.from_json, json.dumps(metric))
|
||||
message, message_type.Metric.from_json, json.dumps(metric)
|
||||
)
|
||||
|
||||
metric['action']['action'] = "test"
|
||||
message = "action should be choosen from ['inc', 'observe']"
|
||||
self.assertRaisesWithMessage(
|
||||
message, message_type.Metric.from_json, json.dumps(metric))
|
||||
message, message_type.Metric.from_json, json.dumps(metric)
|
||||
)
|
||||
|
||||
@@ -36,3 +36,21 @@ Repository = "https://opendev.org/openstack/oslo.metrics"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["oslo_metrics"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 79
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["C4", "E4", "E5", "E7", "E9", "F", "S", "UP"]
|
||||
ignore = [
|
||||
# we only use asserts for type narrowing
|
||||
"S101",
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"oslo_metrics/tests/*" = ["S"]
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "preserve"
|
||||
docstring-code-format = true
|
||||
skip-magic-trailing-comma = true
|
||||
|
||||
@@ -33,10 +33,7 @@
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'openstackdocstheme',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
extensions = ['openstackdocstheme', 'reno.sphinxext']
|
||||
|
||||
# openstackdocstheme options
|
||||
openstackdocs_repo_name = 'openstack/oslo.metrics'
|
||||
@@ -194,10 +191,8 @@ htmlhelp_basename = 'oslo.metricsReleaseNotesDoc'
|
||||
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': '',
|
||||
}
|
||||
@@ -206,9 +201,13 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'oslo.metricsReleaseNotes.tex',
|
||||
'oslo.metrics Release Notes Documentation',
|
||||
'oslo.metrics Developers', 'manual'),
|
||||
(
|
||||
'index',
|
||||
'oslo.metricsReleaseNotes.tex',
|
||||
'oslo.metrics Release Notes Documentation',
|
||||
'oslo.metrics Developers',
|
||||
'manual',
|
||||
)
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@@ -236,9 +235,13 @@ latex_documents = [
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'oslo.metricsReleaseNotes',
|
||||
'oslo.metrics Release Notes Documentation',
|
||||
['oslo.metrics Developers'], 1)
|
||||
(
|
||||
'index',
|
||||
'oslo.metricsReleaseNotes',
|
||||
'oslo.metrics Release Notes Documentation',
|
||||
['oslo.metrics Developers'],
|
||||
1,
|
||||
)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
@@ -250,11 +253,15 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'oslo.metricsReleaseNotes',
|
||||
'oslo.metrics Release Notes Documentation',
|
||||
'oslo.metrics Developers', 'oslo.metricsReleaseNotes',
|
||||
'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
'index',
|
||||
'oslo.metricsReleaseNotes',
|
||||
'oslo.metrics Release Notes Documentation',
|
||||
'oslo.metrics Developers',
|
||||
'oslo.metricsReleaseNotes',
|
||||
'One line description of project.',
|
||||
'Miscellaneous',
|
||||
)
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
||||
4
setup.py
4
setup.py
@@ -15,6 +15,4 @@
|
||||
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
||||
setuptools.setup(setup_requires=['pbr>=2.0.0'], pbr=True)
|
||||
|
||||
5
tox.ini
5
tox.ini
@@ -41,12 +41,11 @@ commands =
|
||||
coverage xml -o cover/coverage.xml
|
||||
|
||||
[flake8]
|
||||
# We only enable the hacking (H) checks
|
||||
select = H
|
||||
show-source = True
|
||||
# H904: Delay string interpolations at logging calls
|
||||
enable-extensions = H904
|
||||
ignore = H405,W504,F405
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
||||
|
||||
[hacking]
|
||||
import_exceptions =
|
||||
|
||||
Reference in New Issue
Block a user