Skip to content

Commit 6d3caba

Browse files
committed
mgr: add new API's to fetch latest perf counter values
New API's to fetch the latest values of performance counters have been added. These API's support fetching the values for labeled performance counters. We also make changes to `insert` and `create` API of perf_counters_key to remove any empty labels. This change helps us construct perf_counters_key when the key labels are constructed dynamically. Signed-off-by: Naveen Naidu <[email protected]>
1 parent a4aebd8 commit 6d3caba

File tree

7 files changed

+213
-13
lines changed

7 files changed

+213
-13
lines changed

doc/mgr/modules.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,8 @@ function. This will result in a circular locking exception.
508508
.. automethod:: MgrModule.get_unlabeled_perf_schema
509509
.. automethod:: MgrModule.get_unlabeled_counter
510510
.. automethod:: MgrModule.get_latest_unlabeled_counter
511-
.. automethod:: MgrModule.get_perf_schema
511+
.. automethod:: MgrModule.get_perf_schema
512+
.. automethod:: MgrModule.get_latest_counter
512513
.. automethod:: MgrModule.get_mgr_id
513514
.. automethod:: MgrModule.get_daemon_health_metrics
514515

src/mgr/ActivePyModules.cc

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,76 @@ PyObject* ActivePyModules::with_unlabled_perf_counters(
877877
return f.get();
878878
}
879879

880+
// Holds a list of label pairs for a counter, [(level, shallow), (pooltype, replicated)]
881+
typedef std::vector<pair<std::string_view, std::string_view>> perf_counter_label_pairs;
882+
883+
PyObject* ActivePyModules::with_perf_counters(
884+
std::function<void(
885+
PerfCounterInstance &counter_instance,
886+
PerfCounterType &counter_type,
887+
PyFormatter& f)> fct,
888+
const std::string& svc_name,
889+
const std::string& svc_id,
890+
std::string_view counter_name,
891+
std::string_view sub_counter_name,
892+
const perf_counter_label_pairs& labels) const
893+
{
894+
PyFormatter f;
895+
/*
896+
The resolved counter path, they are of the format
897+
<counter_name>.<sub_counter_name> If the counter name has labels, then they
898+
are segregated via NULL delimters.
899+
900+
Eg:
901+
- labeled counter:
902+
"osd_scrub_sh_repl^@level^@shallow^@pooltype^@replicated^@.successful_scrubs_elapsed"
903+
- unlabeled counter: "osd.stat_bytes"
904+
*/
905+
std::string resolved_path;
906+
Formatter::ArraySection perf_counter_value_section(f, counter_name);
907+
908+
// Construct the resolved path
909+
if (labels.empty()) {
910+
resolved_path =
911+
std::string(counter_name) + "." + std::string(sub_counter_name);
912+
} else {
913+
perf_counter_label_pairs perf_counter_labels = labels;
914+
std::string counter_name_with_labels = ceph::perf_counters::detail::create(
915+
counter_name.data(), perf_counter_labels.data(),
916+
perf_counter_labels.data() + perf_counter_labels.size());
917+
resolved_path = std::string(counter_name_with_labels) + "." +
918+
std::string(sub_counter_name);
919+
}
920+
921+
{
922+
without_gil_t no_gil;
923+
std::lock_guard l(lock);
924+
auto metadata = daemon_state.get(DaemonKey{svc_name, svc_id});
925+
if (metadata) {
926+
std::lock_guard l2(metadata->lock);
927+
if (metadata->perf_counters.instances.count(resolved_path)) {
928+
auto counter_instance =
929+
metadata->perf_counters.instances.at(resolved_path);
930+
auto counter_type = metadata->perf_counters.types.at(resolved_path);
931+
with_gil(no_gil, [&] { fct(counter_instance, counter_type, f); });
932+
} else {
933+
dout(4) << fmt::format(
934+
"Missing counter: '{}' ({}.{})", resolved_path, svc_name,
935+
svc_id)
936+
<< dendl;
937+
dout(20) << "Paths are:" << dendl;
938+
for (const auto& i : metadata->perf_counters.instances) {
939+
dout(20) << i.first << dendl;
940+
}
941+
}
942+
} else {
943+
dout(4) << fmt::format("No daemon state for {}.{}", svc_name, svc_id)
944+
<< dendl;
945+
}
946+
}
947+
return f.get();
948+
}
949+
880950
PyObject* ActivePyModules::get_unlabeled_counter_python(
881951
const std::string &svc_name,
882952
const std::string &svc_id,
@@ -933,6 +1003,32 @@ PyObject* ActivePyModules::get_latest_unlabeled_counter_python(
9331003
return with_unlabled_perf_counters(extract_latest_counters, svc_name, svc_id, path);
9341004
}
9351005

1006+
PyObject* ActivePyModules::get_latest_counter_python(
1007+
const std::string& svc_name,
1008+
const std::string& svc_id,
1009+
std::string_view counter_name,
1010+
std::string_view sub_counter_name,
1011+
const perf_counter_label_pairs& labels)
1012+
{
1013+
auto extract_latest_counters = [](PerfCounterInstance& counter_instance,
1014+
PerfCounterType& counter_type,
1015+
PyFormatter& f) {
1016+
if (counter_type.type & PERFCOUNTER_LONGRUNAVG) {
1017+
const auto& datapoint = counter_instance.get_latest_data_avg();
1018+
f.dump_float("t", datapoint.t);
1019+
f.dump_unsigned("s", datapoint.s);
1020+
f.dump_unsigned("c", datapoint.c);
1021+
} else {
1022+
const auto& datapoint = counter_instance.get_latest_data();
1023+
f.dump_float("t", datapoint.t);
1024+
f.dump_unsigned("v", datapoint.v);
1025+
}
1026+
};
1027+
return with_perf_counters(
1028+
extract_latest_counters, svc_name, svc_id, counter_name, sub_counter_name,
1029+
labels);
1030+
}
1031+
9361032
PyObject* ActivePyModules::get_unlabeled_perf_schema_python(
9371033
const std::string &svc_type,
9381034
const std::string &svc_id)
@@ -995,9 +1091,6 @@ PyObject* ActivePyModules::get_unlabeled_perf_schema_python(
9951091
return f.get();
9961092
}
9971093

998-
// Holds a list of label pairs for a counter, [(level, shallow), (pooltype, replicated)]
999-
typedef std::vector<pair<std::string_view, std::string_view>> perf_counter_label_pairs;
1000-
10011094
PyObject* ActivePyModules::get_perf_schema_python(
10021095
const std::string& svc_type,
10031096
const std::string& svc_id)

src/mgr/ActivePyModules.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ class ActivePyModules
9999
const std::string &svc_type,
100100
const std::string &svc_id,
101101
const std::string &path);
102+
PyObject *get_latest_counter_python(
103+
const std::string &svc_type,
104+
const std::string &svc_id,
105+
std::string_view counter_name,
106+
std::string_view sub_counter_name,
107+
const std::vector<std::pair<std::string_view, std::string_view>> &labels);
102108
PyObject *get_unlabeled_perf_schema_python(
103109
const std::string &svc_type,
104110
const std::string &svc_id);
@@ -117,6 +123,18 @@ class ActivePyModules
117123
const std::string &svc_name,
118124
const std::string &svc_id,
119125
const std::string &path) const;
126+
/// @note @c fct is not allowed to acquire locks when holding GIL
127+
PyObject *with_perf_counters(
128+
std::function<void(
129+
PerfCounterInstance &counter_instance,
130+
PerfCounterType &counter_type,
131+
PyFormatter &f)> fct,
132+
const std::string &svc_name,
133+
const std::string &svc_id,
134+
std::string_view counter_name,
135+
std::string_view sub_counter_name,
136+
const std::vector<std::pair<std::string_view, std::string_view>> &labels)
137+
const;
120138

121139
MetricQueryID add_osd_perf_query(
122140
const OSDPerfMetricQuery &query,

src/mgr/BaseMgrModule.cc

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,43 @@ get_latest_unlabeled_counter(BaseMgrModule *self, PyObject *args)
667667
svc_name, svc_id, counter_path);
668668
}
669669

670+
static PyObject*
671+
get_latest_counter(BaseMgrModule *self, PyObject *args)
672+
{
673+
char *svc_name = nullptr;
674+
char *svc_id = nullptr;
675+
char *counter_name = nullptr;
676+
char *sub_counter_name = nullptr;
677+
PyObject *labels_list = nullptr; //labels = [("level", "deep"), ("pooltype", "ec")]
678+
if (!PyArg_ParseTuple(args, "ssssO:get_latest_counter", &svc_name,
679+
&svc_id, &counter_name, &sub_counter_name,
680+
&labels_list)) {
681+
return nullptr;
682+
}
683+
684+
if (!PyList_Check(labels_list)) {
685+
derr << __func__ << " labels_list not a list" << dendl;
686+
Py_RETURN_FALSE;
687+
}
688+
689+
std::vector<std::pair<std::string_view, std::string_view>> labels;
690+
for (int i = 0; i < PyList_Size(labels_list); ++i) {
691+
// Get the tuple element of labels list ("level", "deep")
692+
PyObject *label_key_value = PyList_GET_ITEM(labels_list, i);
693+
694+
char *label_key = nullptr;
695+
char *label_value = nullptr;
696+
if (!PyArg_ParseTuple(label_key_value, "ss:label_pair", &label_key, &label_value)) {
697+
derr << fmt::format("{} list item {} not a size 2 tuple", __func__, i) << dendl;
698+
continue;
699+
}
700+
labels.push_back(std::make_pair<std::string_view, std::string_view>(label_key, label_value));
701+
}
702+
703+
return self->py_modules->get_latest_counter_python(
704+
svc_name, svc_id, counter_name, sub_counter_name, labels);
705+
}
706+
670707
static PyObject*
671708
get_unlabeled_perf_schema(BaseMgrModule *self, PyObject *args)
672709
{
@@ -1493,16 +1530,19 @@ PyMethodDef BaseMgrModule_methods[] = {
14931530
"Set a stored field"},
14941531

14951532
{"_ceph_get_unlabeled_counter", (PyCFunction)get_unlabeled_counter, METH_VARARGS,
1496-
"Get a performance counter"},
1533+
"Get a performance counter"},
14971534

14981535
{"_ceph_get_latest_unlabeled_counter", (PyCFunction)get_latest_unlabeled_counter, METH_VARARGS,
1499-
"Fetch (or get) the latest (or updated) value of an unlabeled counter"},
1536+
"Fetch (or get) the latest (or updated) value of an unlabeled counter"},
1537+
1538+
{"_ceph_get_latest_counter", (PyCFunction)get_latest_counter, METH_VARARGS,
1539+
"Fetch (or get) the latest (or updated) value of a performance counter"},
15001540

15011541
{"_ceph_get_unlabeled_perf_schema", (PyCFunction)get_unlabeled_perf_schema, METH_VARARGS,
1502-
"Get the unlabeled performance counter schema"},
1542+
"Get the unlabeled performance counter schema"},
15031543

15041544
{"_ceph_get_perf_schema", (PyCFunction)get_perf_schema, METH_VARARGS,
1505-
"Get the performance counter schema"},
1545+
"Get the performance counter schema"},
15061546

15071547
{"_ceph_get_rocksdb_version", (PyCFunction)ceph_get_rocksdb_version, METH_NOARGS,
15081548
"Get the current RocksDB version number"},

src/pybind/ceph_argparse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def _compound_type_to_argdesc(tp, attrs, positional):
173173
assert len(type_args) == 1
174174
attrs['n'] = 'N'
175175
return CephArgtype.to_argdesc(type_args[0], attrs, positional=positional)
176-
elif orig_type is Tuple:
176+
elif orig_type in (Tuple, tuple):
177177
assert len(type_args) >= 1
178178
inner_tp = type_args[0]
179179
assert type_args.count(inner_tp) == len(type_args), \

src/pybind/mgr/ceph_module.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class BaseMgrModule(object):
7777
def _ceph_get_rocksdb_version(self) -> str: ...
7878
def _ceph_get_unlabeled_counter(self, svc_type: str, svc_name: str, path: str) -> Dict[str, List[Tuple[float, int]]]: ...
7979
def _ceph_get_latest_unlabeled_counter(self, svc_type, svc_name, path): ...
80-
def _ceph_get_latest_counter(self, svc_type, svc_name, path): ...
80+
def _ceph_get_latest_counter(self, svc_type: str, svc_name: str, counter_name: str, sub_counter_name: str, labels: List[Tuple[str, str]]): ...
8181
def _ceph_get_metadata(self, svc_type, svc_id): ...
8282
def _ceph_get_daemon_status(self, svc_type, svc_id): ...
8383
def _ceph_send_command(self,

src/pybind/mgr/mgr_module.py

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,7 +1641,7 @@ def get_unlabeled_perf_schema(
16411641
:return: list of dicts describing the counters requested
16421642
"""
16431643
return self._ceph_get_unlabeled_perf_schema(svc_type, svc_name)
1644-
1644+
16451645
@API.expose
16461646
def get_perf_schema(self,
16471647
svc_type: str,
@@ -1689,8 +1689,8 @@ def get_latest_unlabeled_counter(
16891689
self, svc_type: str, svc_name: str, path: str
16901690
) -> Dict[str, Union[Tuple[float, int], Tuple[float, int, int]]]:
16911691
"""
1692-
Called by the plugin to fetch only the newest performance counter data
1693-
point for a particular counter on a particular service.
1692+
Called by the plugin to fetch only the newest performance unlabeled counter
1693+
data point for a particular counter on a particular service.
16941694
16951695
:param str svc_type:
16961696
:param str svc_name:
@@ -1702,6 +1702,32 @@ def get_latest_unlabeled_counter(
17021702
"""
17031703
return self._ceph_get_latest_unlabeled_counter(svc_type, svc_name, path)
17041704

1705+
@API.expose
1706+
def get_latest_counter(self,
1707+
svc_type: str,
1708+
svc_name: str,
1709+
counter_name: str,
1710+
sub_counter_name: str,
1711+
labels: List[Tuple[str, str]]) -> Dict[str, Union[Tuple[float, int],
1712+
Tuple[float, int, int]]]:
1713+
"""
1714+
Called by the plugin to fetch only the newest performance counter data
1715+
point for a particular counter on a particular service.
1716+
1717+
:param str svc_type:
1718+
:param str svc_name:
1719+
:param str counter_name: the key_name of the counter, for example
1720+
"osd_scrub_sh_repl"
1721+
:param str sub_counter_name: the counters present under the key_name,
1722+
for example "successful_scrubs_elapsed"
1723+
:param list[(str, str)] labels: the labels associated with the counter,
1724+
for example "[("level", "deep"), ("pooltype", "ec")]"
1725+
:return: A list of two-tuples of (timestamp, value) or three-tuple of
1726+
(timestamp, value, count) is returned. This may be empty if no
1727+
data is available.
1728+
"""
1729+
return self._ceph_get_latest_counter(svc_type, svc_name, counter_name, sub_counter_name, labels)
1730+
17051731
@API.expose
17061732
def list_servers(self) -> List[ServerInfoT]:
17071733
"""
@@ -2214,6 +2240,16 @@ def get_unlabeled_counter_latest(self, daemon_type: str, daemon_name: str, count
22142240
else:
22152241
return 0
22162242

2243+
@API.expose
2244+
def get_counter_latest(self, daemon_type: str, daemon_name: str, counter_name: str,
2245+
sub_counter_name: str, labels: List[Tuple[str, str]]) -> int:
2246+
data = self.get_latest_counter(
2247+
daemon_type, daemon_name, counter_name, sub_counter_name, labels)[counter_name]
2248+
if data:
2249+
return data[1]
2250+
else:
2251+
return 0
2252+
22172253
@API.expose
22182254
def get_unlabeled_counter_latest_avg(self, daemon_type: str, daemon_name: str, counter: str) -> Tuple[int, int]:
22192255
data = self.get_latest_unlabeled_counter(
@@ -2225,6 +2261,18 @@ def get_unlabeled_counter_latest_avg(self, daemon_type: str, daemon_name: str, c
22252261
else:
22262262
return 0, 0
22272263

2264+
@API.expose
2265+
def get_counter_latest_avg(self, daemon_type: str, daemon_name: str, counter_name: str,
2266+
sub_counter_name: str, labels: List[Tuple[str, str]]) -> Tuple[int, int]:
2267+
data = self.get_latest_counter(
2268+
daemon_type, daemon_name, counter_name, sub_counter_name, labels)[counter_name]
2269+
if data:
2270+
# https://github.com/python/mypy/issues/1178
2271+
_, value, count = cast(Tuple[float, int, int], data)
2272+
return value, count
2273+
else:
2274+
return 0, 0
2275+
22282276
@API.expose
22292277
@profile_method()
22302278
def get_unlabeled_perf_counters(

0 commit comments

Comments
 (0)