Files
python-observabilityclient/observabilityclient/utils/metric_utils.py
Jaromir Wysoglad f1dd649a39 Rename Aetos's service type to metric-storage
Also it's no longer required for the service name to be 'aetos',
which better aligns to how other services work and a default
443 port is used for https.

Depends-On: https://review.opendev.org/c/openstack/service-types-authority/+/955782

Change-Id: I0f53830cf7ace12d44812a0b2c52aaa7e2ac6d07
Signed-off-by: Jaromir Wysoglad <jwysogla@redhat.com>
2025-07-25 04:17:46 -04:00

164 lines
5.3 KiB
Python

# Copyright 2023 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import os
from urllib import parse
from keystoneauth1 import adapter
from keystoneauth1.exceptions import catalog as keystone_exception
from oslo_utils import netutils
import yaml
from observabilityclient.prometheus_client import PrometheusAPIClient
DEFAULT_CONFIG_LOCATIONS = (
[os.path.join(os.environ["HOME"], ".config/openstack/"), "/etc/openstack/"]
if "HOME" in os.environ
else ["/etc/openstack/"]
)
CONFIG_FILE_NAME = "prometheus.yaml"
LOG = logging.getLogger(__name__)
class ConfigurationError(Exception):
pass
def get_config_file():
if os.path.exists(CONFIG_FILE_NAME):
LOG.debug("Using %s as prometheus configuration", CONFIG_FILE_NAME)
return open(CONFIG_FILE_NAME, "r")
for path in DEFAULT_CONFIG_LOCATIONS:
full_filename = path + CONFIG_FILE_NAME
if os.path.exists(full_filename):
LOG.debug("Using %s as prometheus configuration", full_filename)
return open(full_filename, "r")
return None
def get_prometheus_client(session=None, adapter_options={}):
host = None
port = None
ca_cert = None
root_path = ""
is_aetos = False
conf_file = get_config_file()
if conf_file is not None:
conf = yaml.safe_load(conf_file)
if 'host' in conf:
host = conf['host']
if 'port' in conf:
port = conf['port']
if 'ca_cert' in conf:
ca_cert = conf['ca_cert']
if 'root_path' in conf:
root_path = conf['root_path']
conf_file.close()
if session is not None and (host is None or port is None):
try:
endpoint = adapter.Adapter(
session=session, **adapter_options
).get_endpoint()
parsed_url = parse.urlparse(endpoint)
host = parsed_url.hostname
root_path = parsed_url.path.strip('/')
if parsed_url.scheme == "https" and ca_cert is None:
# NOTE(jwysogla): Use the default CA certs if the scheme
# is https, but keep the original value if already set,
# so that a custom certificate can be set in the config
# file, while the endpoint is retrieved from keystone.
ca_cert = True
if parsed_url.port is not None:
port = parsed_url.port
elif parsed_url.scheme == "https":
port = 443
else:
port = 80
is_aetos = True
except keystone_exception.EndpointNotFound:
# NOTE(jwysogla): Don't do anything here. It's still possible
# to get the correct endpoint configuration from the env vars.
# If that doesn't work, the same error message is part of the
# exception raised below.
pass
# NOTE(jwysogla): We allow to overide the prometheus.yaml by
# the environment variables
if 'PROMETHEUS_HOST' in os.environ:
host = os.environ['PROMETHEUS_HOST']
if 'PROMETHEUS_PORT' in os.environ:
port = os.environ['PROMETHEUS_PORT']
if 'PROMETHEUS_CA_CERT' in os.environ:
ca_cert = os.environ['PROMETHEUS_CA_CERT']
if 'PROMETHEUS_ROOT_PATH' in os.environ:
root_path = os.environ['PROMETHEUS_ROOT_PATH']
if host is None or port is None:
raise ConfigurationError("Can't find prometheus host and "
"port configuration and endpoint for "
"metric-storage not found.")
escaped_host = netutils.escape_ipv6(host)
if is_aetos:
client = PrometheusAPIClient(
f"{escaped_host}:{port}", session, root_path
)
else:
client = PrometheusAPIClient(
f"{escaped_host}:{port}", None, root_path
)
if ca_cert is not None:
client.set_ca_cert(ca_cert)
return client
def get_client(obj):
return obj.app.client_manager.observabilityclient
def format_labels(d: dict) -> str:
def replace_doubled_quotes(string):
if "''" in string:
string = string.replace("''", "'")
if '""' in string:
string = string.replace('""', '"')
return string
ret = ""
for key, value in d.items():
ret += "{}='{}', ".format(key, value)
ret = ret[0:-2]
old = ""
while ret != old:
old = ret
ret = replace_doubled_quotes(ret)
return ret
def metrics2cols(m):
# get all label keys
cols = list(set().union(*(d.labels.keys() for d in m)))
cols.sort()
cols.append("value")
fields = []
for metric in m:
row = [""] * len(cols)
for key, value in metric.labels.items():
row[cols.index(key)] = value
row[cols.index("value")] = metric.value
fields.append(row)
return cols, fields