1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2016 John Spray <john.spray@redhat.com>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 * The interface we present to python code that runs within
16 * ceph-mgr. This is implemented as a Python class from which
17 * all modules must inherit -- access to the Ceph state is then
18 * available as methods on that object.
25 #include "mon/MonClient.h"
26 #include "common/errno.h"
27 #include "common/version.h"
29 #include "BaseMgrModule.h"
34 #define dout_context g_ceph_context
35 #define dout_subsys ceph_subsys_mgr
37 #define PLACEHOLDER ""
42 ActivePyModules
*py_modules
;
43 ActivePyModule
*this_module
;
46 class MonCommandCompletion
: public Context
48 ActivePyModules
*py_modules
;
49 PyObject
*python_completion
;
50 const std::string tag
;
51 SafeThreadState pThreadState
;
58 ActivePyModules
*py_modules_
, PyObject
* ev
,
59 const std::string
&tag_
, PyThreadState
*ts_
)
60 : py_modules(py_modules_
), python_completion(ev
),
61 tag(tag_
), pThreadState(ts_
)
63 ceph_assert(python_completion
!= nullptr);
64 Py_INCREF(python_completion
);
67 ~MonCommandCompletion() override
69 if (python_completion
) {
70 // Usually do this in finish(): this path is only for if we're
71 // being destroyed without completing.
72 Gil
gil(pThreadState
, true);
73 Py_DECREF(python_completion
);
74 python_completion
= nullptr;
78 void finish(int r
) override
80 ceph_assert(python_completion
!= nullptr);
82 dout(10) << "MonCommandCompletion::finish()" << dendl
;
84 // Scoped so the Gil is released before calling notify_all()
85 // Create new thread state because this is called via the MonClient
86 // Finisher, not the PyModules finisher.
87 Gil
gil(pThreadState
, true);
89 auto set_fn
= PyObject_GetAttrString(python_completion
, "complete");
90 ceph_assert(set_fn
!= nullptr);
92 auto pyR
= PyInt_FromLong(r
);
93 auto pyOutBl
= PyString_FromString(outbl
.to_str().c_str());
94 auto pyOutS
= PyString_FromString(outs
.c_str());
95 auto args
= PyTuple_Pack(3, pyR
, pyOutBl
, pyOutS
);
100 auto rtn
= PyObject_CallObject(set_fn
, args
);
101 if (rtn
!= nullptr) {
107 Py_DECREF(python_completion
);
108 python_completion
= nullptr;
110 py_modules
->notify_all("command", tag
);
116 ceph_send_command(BaseMgrModule
*self
, PyObject
*args
)
118 // Like mon, osd, mds
119 char *type
= nullptr;
121 // Like "23" for an OSD or "myid" for an MDS
122 char *name
= nullptr;
124 char *cmd_json
= nullptr;
126 PyObject
*completion
= nullptr;
127 if (!PyArg_ParseTuple(args
, "Ossss:ceph_send_command",
128 &completion
, &type
, &name
, &cmd_json
, &tag
)) {
132 auto set_fn
= PyObject_GetAttrString(completion
, "complete");
133 if (set_fn
== nullptr) {
134 ceph_abort(); // TODO raise python exception instead
136 ceph_assert(PyCallable_Check(set_fn
));
140 MonCommandCompletion
*command_c
= new MonCommandCompletion(self
->py_modules
,
141 completion
, tag
, PyThreadState_Get());
143 PyThreadState
*tstate
= PyEval_SaveThread();
145 if (std::string(type
) == "mon") {
147 // Wait for the latest OSDMap after each command we send to
148 // the mons. This is a heavy-handed hack to make life simpler
149 // for python module authors, so that they know whenever they
150 // run a command they've gt a fresh OSDMap afterwards.
151 // TODO: enhance MCommand interface so that it returns
152 // latest cluster map versions on completion, and callers
153 // can wait for those.
154 auto c
= new FunctionContext([command_c
, self
](int command_r
){
155 self
->py_modules
->get_objecter().wait_for_latest_osdmap(
156 new FunctionContext([command_c
, command_r
](int wait_r
){
157 command_c
->complete(command_r
);
162 self
->py_modules
->get_monc().start_mon_command(
168 new C_OnFinisher(c
, &self
->py_modules
->cmd_finisher
));
169 } else if (std::string(type
) == "osd") {
171 uint64_t osd_id
= strict_strtoll(name
, 10, &err
);
174 string
msg("invalid osd_id: ");
175 msg
.append("\"").append(name
).append("\"");
176 PyEval_RestoreThread(tstate
);
177 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
182 self
->py_modules
->get_objecter().osd_command(
189 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
190 } else if (std::string(type
) == "mds") {
191 int r
= self
->py_modules
->get_client().mds_command(
197 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
199 string
msg("failed to send command to mds: ");
200 msg
.append(cpp_strerror(r
));
201 PyEval_RestoreThread(tstate
);
202 PyErr_SetString(PyExc_RuntimeError
, msg
.c_str());
205 } else if (std::string(type
) == "pg") {
207 if (!pgid
.parse(name
)) {
209 string
msg("invalid pgid: ");
210 msg
.append("\"").append(name
).append("\"");
211 PyEval_RestoreThread(tstate
);
212 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
217 self
->py_modules
->get_objecter().pg_command(
224 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
225 PyEval_RestoreThread(tstate
);
229 string
msg("unknown service type: ");
231 PyEval_RestoreThread(tstate
);
232 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
236 PyEval_RestoreThread(tstate
);
241 ceph_set_health_checks(BaseMgrModule
*self
, PyObject
*args
)
243 PyObject
*checks
= NULL
;
244 if (!PyArg_ParseTuple(args
, "O:ceph_set_health_checks", &checks
)) {
247 if (!PyDict_Check(checks
)) {
248 derr
<< __func__
<< " arg not a dict" << dendl
;
251 PyObject
*checksls
= PyDict_Items(checks
);
252 health_check_map_t out_checks
;
253 for (int i
= 0; i
< PyList_Size(checksls
); ++i
) {
254 PyObject
*kv
= PyList_GET_ITEM(checksls
, i
);
255 char *check_name
= nullptr;
256 PyObject
*check_info
= nullptr;
257 if (!PyArg_ParseTuple(kv
, "sO:pair", &check_name
, &check_info
)) {
258 derr
<< __func__
<< " dict item " << i
259 << " not a size 2 tuple" << dendl
;
262 if (!PyDict_Check(check_info
)) {
263 derr
<< __func__
<< " item " << i
<< " " << check_name
264 << " value not a dict" << dendl
;
267 health_status_t severity
= HEALTH_OK
;
270 PyObject
*infols
= PyDict_Items(check_info
);
271 for (int j
= 0; j
< PyList_Size(infols
); ++j
) {
272 PyObject
*pair
= PyList_GET_ITEM(infols
, j
);
273 if (!PyTuple_Check(pair
)) {
274 derr
<< __func__
<< " item " << i
<< " pair " << j
275 << " not a tuple" << dendl
;
279 PyObject
*v
= nullptr;
280 if (!PyArg_ParseTuple(pair
, "sO:pair", &k
, &v
)) {
281 derr
<< __func__
<< " item " << i
<< " pair " << j
282 << " not a size 2 tuple" << dendl
;
286 if (ks
== "severity") {
287 if (!PyString_Check(v
)) {
288 derr
<< __func__
<< " check " << check_name
289 << " severity value not string" << dendl
;
292 string
vs(PyString_AsString(v
));
293 if (vs
== "warning") {
294 severity
= HEALTH_WARN
;
295 } else if (vs
== "error") {
296 severity
= HEALTH_ERR
;
298 } else if (ks
== "summary") {
299 if (!PyString_Check(v
)) {
300 derr
<< __func__
<< " check " << check_name
301 << " summary value not string" << dendl
;
304 summary
= PyString_AsString(v
);
305 } else if (ks
== "detail") {
306 if (!PyList_Check(v
)) {
307 derr
<< __func__
<< " check " << check_name
308 << " detail value not list" << dendl
;
311 for (int k
= 0; k
< PyList_Size(v
); ++k
) {
312 PyObject
*di
= PyList_GET_ITEM(v
, k
);
313 if (!PyString_Check(di
)) {
314 derr
<< __func__
<< " check " << check_name
315 << " detail item " << k
<< " not a string" << dendl
;
318 detail
.push_back(PyString_AsString(di
));
321 derr
<< __func__
<< " check " << check_name
322 << " unexpected key " << k
<< dendl
;
325 auto& d
= out_checks
.add(check_name
, severity
, summary
);
326 d
.detail
.swap(detail
);
329 JSONFormatter
jf(true);
330 dout(10) << "module " << self
->this_module
->get_name()
331 << " health checks:\n";
332 out_checks
.dump(&jf
);
336 PyThreadState
*tstate
= PyEval_SaveThread();
337 self
->py_modules
->set_health_checks(self
->this_module
->get_name(),
338 std::move(out_checks
));
339 PyEval_RestoreThread(tstate
);
346 ceph_state_get(BaseMgrModule
*self
, PyObject
*args
)
349 if (!PyArg_ParseTuple(args
, "s:ceph_state_get", &what
)) {
353 return self
->py_modules
->get_python(what
);
358 ceph_get_server(BaseMgrModule
*self
, PyObject
*args
)
360 char *hostname
= NULL
;
361 if (!PyArg_ParseTuple(args
, "z:ceph_get_server", &hostname
)) {
366 return self
->py_modules
->get_server_python(hostname
);
368 return self
->py_modules
->list_servers_python();
373 ceph_get_mgr_id(BaseMgrModule
*self
, PyObject
*args
)
375 return PyString_FromString(g_conf()->name
.get_id().c_str());
379 ceph_option_get(BaseMgrModule
*self
, PyObject
*args
)
381 char *what
= nullptr;
382 if (!PyArg_ParseTuple(args
, "s:ceph_option_get", &what
)) {
383 derr
<< "Invalid args!" << dendl
;
388 int r
= g_conf().get_val(string(what
), &value
);
390 dout(10) << "ceph_option_get " << what
<< " found: " << value
<< dendl
;
391 return PyString_FromString(value
.c_str());
393 dout(4) << "ceph_option_get " << what
<< " not found " << dendl
;
399 ceph_get_module_option(BaseMgrModule
*self
, PyObject
*args
)
401 char *module
= nullptr;
403 char *prefix
= nullptr;
404 if (!PyArg_ParseTuple(args
, "ss|s:ceph_get_module_option", &module
, &key
,
406 derr
<< "Invalid args!" << dendl
;
409 std::string str_prefix
;
413 assert(self
->this_module
->py_module
);
414 auto pResult
= self
->py_modules
->get_typed_config(module
, key
, str_prefix
);
419 ceph_store_get_prefix(BaseMgrModule
*self
, PyObject
*args
)
421 char *prefix
= nullptr;
422 if (!PyArg_ParseTuple(args
, "s:ceph_store_get_prefix", &prefix
)) {
423 derr
<< "Invalid args!" << dendl
;
427 return self
->py_modules
->get_store_prefix(self
->this_module
->get_name(),
432 ceph_set_module_option(BaseMgrModule
*self
, PyObject
*args
)
434 char *module
= nullptr;
436 char *value
= nullptr;
437 if (!PyArg_ParseTuple(args
, "ssz:ceph_set_module_option",
438 &module
, &key
, &value
)) {
439 derr
<< "Invalid args!" << dendl
;
442 boost::optional
<string
> val
;
446 PyThreadState
*tstate
= PyEval_SaveThread();
447 self
->py_modules
->set_config(module
, key
, val
);
448 PyEval_RestoreThread(tstate
);
454 ceph_store_get(BaseMgrModule
*self
, PyObject
*args
)
456 char *what
= nullptr;
457 if (!PyArg_ParseTuple(args
, "s:ceph_store_get", &what
)) {
458 derr
<< "Invalid args!" << dendl
;
463 bool found
= self
->py_modules
->get_store(self
->this_module
->get_name(),
466 dout(10) << "ceph_store_get " << what
<< " found: " << value
.c_str() << dendl
;
467 return PyString_FromString(value
.c_str());
469 dout(4) << "ceph_store_get " << what
<< " not found " << dendl
;
475 ceph_store_set(BaseMgrModule
*self
, PyObject
*args
)
478 char *value
= nullptr;
479 if (!PyArg_ParseTuple(args
, "sz:ceph_store_set", &key
, &value
)) {
482 boost::optional
<string
> val
;
486 PyThreadState
*tstate
= PyEval_SaveThread();
487 self
->py_modules
->set_store(self
->this_module
->get_name(), key
, val
);
488 PyEval_RestoreThread(tstate
);
494 get_metadata(BaseMgrModule
*self
, PyObject
*args
)
496 char *svc_name
= NULL
;
498 if (!PyArg_ParseTuple(args
, "ss:get_metadata", &svc_name
, &svc_id
)) {
501 return self
->py_modules
->get_metadata_python(svc_name
, svc_id
);
505 get_daemon_status(BaseMgrModule
*self
, PyObject
*args
)
507 char *svc_name
= NULL
;
509 if (!PyArg_ParseTuple(args
, "ss:get_daemon_status", &svc_name
,
513 return self
->py_modules
->get_daemon_status_python(svc_name
, svc_id
);
517 ceph_log(BaseMgrModule
*self
, PyObject
*args
)
520 char *record
= nullptr;
521 if (!PyArg_ParseTuple(args
, "is:log", &level
, &record
)) {
525 ceph_assert(self
->this_module
);
527 self
->this_module
->log(level
, record
);
533 ceph_cluster_log(BaseMgrModule
*self
, PyObject
*args
)
536 char *channel
= nullptr;
537 char *message
= nullptr;
538 std::vector
<std::string
> channels
= { "audit", "cluster" };
540 if (!PyArg_ParseTuple(args
, "sis:ceph_cluster_log", &channel
, &prio
, &message
)) {
544 if (std::find(channels
.begin(), channels
.end(), std::string(channel
)) == channels
.end()) {
545 std::string
msg("Unknown channel: ");
547 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
551 PyThreadState
*tstate
= PyEval_SaveThread();
552 self
->py_modules
->cluster_log(channel
, (clog_type
)prio
, message
);
553 PyEval_RestoreThread(tstate
);
559 ceph_get_version(BaseMgrModule
*self
, PyObject
*args
)
561 return PyString_FromString(pretty_version_to_str().c_str());
565 ceph_get_context(BaseMgrModule
*self
, PyObject
*args
)
567 return self
->py_modules
->get_context();
571 get_counter(BaseMgrModule
*self
, PyObject
*args
)
573 char *svc_name
= nullptr;
574 char *svc_id
= nullptr;
575 char *counter_path
= nullptr;
576 if (!PyArg_ParseTuple(args
, "sss:get_counter", &svc_name
,
577 &svc_id
, &counter_path
)) {
580 return self
->py_modules
->get_counter_python(
581 svc_name
, svc_id
, counter_path
);
585 get_latest_counter(BaseMgrModule
*self
, PyObject
*args
)
587 char *svc_name
= nullptr;
588 char *svc_id
= nullptr;
589 char *counter_path
= nullptr;
590 if (!PyArg_ParseTuple(args
, "sss:get_counter", &svc_name
,
591 &svc_id
, &counter_path
)) {
594 return self
->py_modules
->get_latest_counter_python(
595 svc_name
, svc_id
, counter_path
);
599 get_perf_schema(BaseMgrModule
*self
, PyObject
*args
)
601 char *type_str
= nullptr;
602 char *svc_id
= nullptr;
603 if (!PyArg_ParseTuple(args
, "ss:get_perf_schema", &type_str
,
608 return self
->py_modules
->get_perf_schema_python(type_str
, svc_id
);
612 ceph_get_osdmap(BaseMgrModule
*self
, PyObject
*args
)
614 return self
->py_modules
->get_osdmap();
618 ceph_set_uri(BaseMgrModule
*self
, PyObject
*args
)
620 char *svc_str
= nullptr;
621 if (!PyArg_ParseTuple(args
, "s:ceph_advertize_service",
626 // We call down into PyModules even though we have a MgrPyModule
627 // reference here, because MgrPyModule's fields are protected
628 // by PyModules' lock.
629 PyThreadState
*tstate
= PyEval_SaveThread();
630 self
->py_modules
->set_uri(self
->this_module
->get_name(), svc_str
);
631 PyEval_RestoreThread(tstate
);
637 ceph_have_mon_connection(BaseMgrModule
*self
, PyObject
*args
)
639 if (self
->py_modules
->get_monc().is_connected()) {
647 ceph_update_progress_event(BaseMgrModule
*self
, PyObject
*args
)
649 char *evid
= nullptr;
650 char *desc
= nullptr;
651 float progress
= 0.0;
652 if (!PyArg_ParseTuple(args
, "ssf:ceph_update_progress_event",
653 &evid
, &desc
, &progress
)) {
657 PyThreadState
*tstate
= PyEval_SaveThread();
658 self
->py_modules
->update_progress_event(evid
, desc
, progress
);
659 PyEval_RestoreThread(tstate
);
665 ceph_complete_progress_event(BaseMgrModule
*self
, PyObject
*args
)
667 char *evid
= nullptr;
668 if (!PyArg_ParseTuple(args
, "s:ceph_complete_progress_event",
673 PyThreadState
*tstate
= PyEval_SaveThread();
674 self
->py_modules
->complete_progress_event(evid
);
675 PyEval_RestoreThread(tstate
);
681 ceph_clear_all_progress_events(BaseMgrModule
*self
, PyObject
*args
)
683 PyThreadState
*tstate
= PyEval_SaveThread();
684 self
->py_modules
->clear_all_progress_events();
685 PyEval_RestoreThread(tstate
);
693 ceph_dispatch_remote(BaseMgrModule
*self
, PyObject
*args
)
695 char *other_module
= nullptr;
696 char *method
= nullptr;
697 PyObject
*remote_args
= nullptr;
698 PyObject
*remote_kwargs
= nullptr;
699 if (!PyArg_ParseTuple(args
, "ssOO:ceph_dispatch_remote",
700 &other_module
, &method
, &remote_args
, &remote_kwargs
)) {
704 // Early error handling, because if the module doesn't exist then we
705 // won't be able to use its thread state to set python error state
706 // inside dispatch_remote().
707 if (!self
->py_modules
->module_exists(other_module
)) {
708 derr
<< "no module '" << other_module
<< "'" << dendl
;
709 PyErr_SetString(PyExc_ImportError
, "Module not found");
713 // Drop GIL from calling python thread state, it will be taken
714 // both for checking for method existence and for executing method.
715 PyThreadState
*tstate
= PyEval_SaveThread();
717 if (!self
->py_modules
->method_exists(other_module
, method
)) {
718 PyEval_RestoreThread(tstate
);
719 PyErr_SetString(PyExc_NameError
, "Method not found");
724 auto result
= self
->py_modules
->dispatch_remote(other_module
, method
,
725 remote_args
, remote_kwargs
, &err
);
727 PyEval_RestoreThread(tstate
);
729 if (result
== nullptr) {
730 std::stringstream ss
;
731 ss
<< "Remote method threw exception: " << err
;
732 PyErr_SetString(PyExc_RuntimeError
, ss
.str().c_str());
733 derr
<< ss
.str() << dendl
;
740 ceph_add_osd_perf_query(BaseMgrModule
*self
, PyObject
*args
)
742 static const std::string NAME_KEY_DESCRIPTOR
= "key_descriptor";
743 static const std::string NAME_COUNTERS_DESCRIPTORS
=
744 "performance_counter_descriptors";
745 static const std::string NAME_LIMIT
= "limit";
746 static const std::string NAME_SUB_KEY_TYPE
= "type";
747 static const std::string NAME_SUB_KEY_REGEX
= "regex";
748 static const std::string NAME_LIMIT_ORDER_BY
= "order_by";
749 static const std::string NAME_LIMIT_MAX_COUNT
= "max_count";
750 static const std::map
<std::string
, OSDPerfMetricSubKeyType
> sub_key_types
= {
751 {"client_id", OSDPerfMetricSubKeyType::CLIENT_ID
},
752 {"client_address", OSDPerfMetricSubKeyType::CLIENT_ADDRESS
},
753 {"pool_id", OSDPerfMetricSubKeyType::POOL_ID
},
754 {"namespace", OSDPerfMetricSubKeyType::NAMESPACE
},
755 {"osd_id", OSDPerfMetricSubKeyType::OSD_ID
},
756 {"pg_id", OSDPerfMetricSubKeyType::PG_ID
},
757 {"object_name", OSDPerfMetricSubKeyType::OBJECT_NAME
},
758 {"snap_id", OSDPerfMetricSubKeyType::SNAP_ID
},
760 static const std::map
<std::string
, PerformanceCounterType
> counter_types
= {
761 {"ops", PerformanceCounterType::OPS
},
762 {"write_ops", PerformanceCounterType::WRITE_OPS
},
763 {"read_ops", PerformanceCounterType::READ_OPS
},
764 {"bytes", PerformanceCounterType::BYTES
},
765 {"write_bytes", PerformanceCounterType::WRITE_BYTES
},
766 {"read_bytes", PerformanceCounterType::READ_BYTES
},
767 {"latency", PerformanceCounterType::LATENCY
},
768 {"write_latency", PerformanceCounterType::WRITE_LATENCY
},
769 {"read_latency", PerformanceCounterType::READ_LATENCY
},
772 PyObject
*py_query
= nullptr;
773 if (!PyArg_ParseTuple(args
, "O:ceph_add_osd_perf_query", &py_query
)) {
774 derr
<< "Invalid args!" << dendl
;
777 if (!PyDict_Check(py_query
)) {
778 derr
<< __func__
<< " arg not a dict" << dendl
;
782 PyObject
*query_params
= PyDict_Items(py_query
);
783 OSDPerfMetricQuery query
;
784 std::optional
<OSDPerfMetricLimit
> limit
;
787 // 'key_descriptor': [
788 // {'type': subkey_type, 'regex': regex_pattern},
791 // 'performance_counter_descriptors': [
792 // list, of, descriptor, types
794 // 'limit': {'order_by': performance_counter_type, 'max_count': n},
797 for (int i
= 0; i
< PyList_Size(query_params
); ++i
) {
798 PyObject
*kv
= PyList_GET_ITEM(query_params
, i
);
799 char *query_param_name
= nullptr;
800 PyObject
*query_param_val
= nullptr;
801 if (!PyArg_ParseTuple(kv
, "sO:pair", &query_param_name
, &query_param_val
)) {
802 derr
<< __func__
<< " dict item " << i
<< " not a size 2 tuple" << dendl
;
805 if (query_param_name
== NAME_KEY_DESCRIPTOR
) {
806 if (!PyList_Check(query_param_val
)) {
807 derr
<< __func__
<< " " << query_param_name
<< " not a list" << dendl
;
810 for (int j
= 0; j
< PyList_Size(query_param_val
); j
++) {
811 PyObject
*sub_key
= PyList_GET_ITEM(query_param_val
, j
);
812 if (!PyDict_Check(sub_key
)) {
813 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
814 << " not a dict" << dendl
;
817 OSDPerfMetricSubKeyDescriptor d
;
818 PyObject
*sub_key_params
= PyDict_Items(sub_key
);
819 for (int k
= 0; k
< PyList_Size(sub_key_params
); ++k
) {
820 PyObject
*pair
= PyList_GET_ITEM(sub_key_params
, k
);
821 if (!PyTuple_Check(pair
)) {
822 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
823 << " pair " << k
<< " not a tuple" << dendl
;
826 char *param_name
= nullptr;
827 PyObject
*param_value
= nullptr;
828 if (!PyArg_ParseTuple(pair
, "sO:pair", ¶m_name
, ¶m_value
)) {
829 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
830 << " pair " << k
<< " not a size 2 tuple" << dendl
;
833 if (param_name
== NAME_SUB_KEY_TYPE
) {
834 if (!PyString_Check(param_value
)) {
835 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
836 << " contains invalid param " << param_name
<< dendl
;
839 auto type
= PyString_AsString(param_value
);
840 auto it
= sub_key_types
.find(type
);
841 if (it
== sub_key_types
.end()) {
842 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
843 << " contains invalid type " << dendl
;
847 } else if (param_name
== NAME_SUB_KEY_REGEX
) {
848 if (!PyString_Check(param_value
)) {
849 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
850 << " contains invalid param " << param_name
<< dendl
;
853 d
.regex_str
= PyString_AsString(param_value
);
855 d
.regex
= {d
.regex_str
.c_str()};
856 } catch (const std::regex_error
& e
) {
857 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
858 << " contains invalid regex " << d
.regex_str
<< dendl
;
861 if (d
.regex
.mark_count() == 0) {
862 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
863 << " regex " << d
.regex_str
<< ": no capturing groups"
868 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
869 << " contains invalid param " << param_name
<< dendl
;
873 if (d
.type
== static_cast<OSDPerfMetricSubKeyType
>(-1) ||
874 d
.regex_str
.empty()) {
875 derr
<< __func__
<< " query " << query_param_name
<< " item " << i
876 << " invalid" << dendl
;
879 query
.key_descriptor
.push_back(d
);
881 } else if (query_param_name
== NAME_COUNTERS_DESCRIPTORS
) {
882 if (!PyList_Check(query_param_val
)) {
883 derr
<< __func__
<< " " << query_param_name
<< " not a list" << dendl
;
886 for (int j
= 0; j
< PyList_Size(query_param_val
); j
++) {
887 PyObject
*py_type
= PyList_GET_ITEM(query_param_val
, j
);
888 if (!PyString_Check(py_type
)) {
889 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
890 << " not a string" << dendl
;
893 auto type
= PyString_AsString(py_type
);
894 auto it
= counter_types
.find(type
);
895 if (it
== counter_types
.end()) {
896 derr
<< __func__
<< " query " << query_param_name
<< " item " << type
897 << " is not valid type" << dendl
;
900 query
.performance_counter_descriptors
.push_back(it
->second
);
902 } else if (query_param_name
== NAME_LIMIT
) {
903 if (!PyDict_Check(query_param_val
)) {
904 derr
<< __func__
<< " query " << query_param_name
<< " not a dict"
909 limit
= OSDPerfMetricLimit();
910 PyObject
*limit_params
= PyDict_Items(query_param_val
);
912 for (int j
= 0; j
< PyList_Size(limit_params
); ++j
) {
913 PyObject
*kv
= PyList_GET_ITEM(limit_params
, j
);
914 char *limit_param_name
= nullptr;
915 PyObject
*limit_param_val
= nullptr;
916 if (!PyArg_ParseTuple(kv
, "sO:pair", &limit_param_name
,
918 derr
<< __func__
<< " limit item " << j
<< " not a size 2 tuple"
923 if (limit_param_name
== NAME_LIMIT_ORDER_BY
) {
924 if (!PyString_Check(limit_param_val
)) {
925 derr
<< __func__
<< " " << limit_param_name
<< " not a string"
929 auto order_by
= PyString_AsString(limit_param_val
);
930 auto it
= counter_types
.find(order_by
);
931 if (it
== counter_types
.end()) {
932 derr
<< __func__
<< " limit " << limit_param_name
933 << " not a valid counter type" << dendl
;
936 limit
->order_by
= it
->second
;
937 } else if (limit_param_name
== NAME_LIMIT_MAX_COUNT
) {
938 #if PY_MAJOR_VERSION <= 2
939 if (!PyInt_Check(limit_param_val
) && !PyLong_Check(limit_param_val
)) {
941 if (!PyLong_Check(limit_param_val
)) {
943 derr
<< __func__
<< " " << limit_param_name
<< " not an int"
947 limit
->max_count
= PyLong_AsLong(limit_param_val
);
949 derr
<< __func__
<< " unknown limit param: " << limit_param_name
955 derr
<< __func__
<< " unknown query param: " << query_param_name
<< dendl
;
960 if (query
.key_descriptor
.empty() ||
961 query
.performance_counter_descriptors
.empty()) {
962 derr
<< __func__
<< " invalid query" << dendl
;
967 auto &ds
= query
.performance_counter_descriptors
;
968 if (std::find(ds
.begin(), ds
.end(), limit
->order_by
) == ds
.end()) {
969 derr
<< __func__
<< " limit order_by " << limit
->order_by
970 << " not in performance_counter_descriptors" << dendl
;
975 auto query_id
= self
->py_modules
->add_osd_perf_query(query
, limit
);
976 return PyLong_FromLong(query_id
);
980 ceph_remove_osd_perf_query(BaseMgrModule
*self
, PyObject
*args
)
982 OSDPerfMetricQueryID query_id
;
983 if (!PyArg_ParseTuple(args
, "i:ceph_remove_osd_perf_query", &query_id
)) {
984 derr
<< "Invalid args!" << dendl
;
988 self
->py_modules
->remove_osd_perf_query(query_id
);
993 ceph_get_osd_perf_counters(BaseMgrModule
*self
, PyObject
*args
)
995 OSDPerfMetricQueryID query_id
;
996 if (!PyArg_ParseTuple(args
, "i:ceph_get_osd_perf_counters", &query_id
)) {
997 derr
<< "Invalid args!" << dendl
;
1001 return self
->py_modules
->get_osd_perf_counters(query_id
);
1004 PyMethodDef BaseMgrModule_methods
[] = {
1005 {"_ceph_get", (PyCFunction
)ceph_state_get
, METH_VARARGS
,
1006 "Get a cluster object"},
1008 {"_ceph_get_server", (PyCFunction
)ceph_get_server
, METH_VARARGS
,
1009 "Get a server object"},
1011 {"_ceph_get_metadata", (PyCFunction
)get_metadata
, METH_VARARGS
,
1012 "Get a service's metadata"},
1014 {"_ceph_get_daemon_status", (PyCFunction
)get_daemon_status
, METH_VARARGS
,
1015 "Get a service's status"},
1017 {"_ceph_send_command", (PyCFunction
)ceph_send_command
, METH_VARARGS
,
1018 "Send a mon command"},
1020 {"_ceph_set_health_checks", (PyCFunction
)ceph_set_health_checks
, METH_VARARGS
,
1021 "Set health checks for this module"},
1023 {"_ceph_get_mgr_id", (PyCFunction
)ceph_get_mgr_id
, METH_NOARGS
,
1024 "Get the name of the Mgr daemon where we are running"},
1026 {"_ceph_get_option", (PyCFunction
)ceph_option_get
, METH_VARARGS
,
1027 "Get a native configuration option value"},
1029 {"_ceph_get_module_option", (PyCFunction
)ceph_get_module_option
, METH_VARARGS
,
1030 "Get a module configuration option value"},
1032 {"_ceph_get_store_prefix", (PyCFunction
)ceph_store_get_prefix
, METH_VARARGS
,
1033 "Get all KV store values with a given prefix"},
1035 {"_ceph_set_module_option", (PyCFunction
)ceph_set_module_option
, METH_VARARGS
,
1036 "Set a module configuration option value"},
1038 {"_ceph_get_store", (PyCFunction
)ceph_store_get
, METH_VARARGS
,
1039 "Get a stored field"},
1041 {"_ceph_set_store", (PyCFunction
)ceph_store_set
, METH_VARARGS
,
1042 "Set a stored field"},
1044 {"_ceph_get_counter", (PyCFunction
)get_counter
, METH_VARARGS
,
1045 "Get a performance counter"},
1047 {"_ceph_get_latest_counter", (PyCFunction
)get_latest_counter
, METH_VARARGS
,
1048 "Get the latest performance counter"},
1050 {"_ceph_get_perf_schema", (PyCFunction
)get_perf_schema
, METH_VARARGS
,
1051 "Get the performance counter schema"},
1053 {"_ceph_log", (PyCFunction
)ceph_log
, METH_VARARGS
,
1054 "Emit a (local) log message"},
1056 {"_ceph_cluster_log", (PyCFunction
)ceph_cluster_log
, METH_VARARGS
,
1057 "Emit a cluster log message"},
1059 {"_ceph_get_version", (PyCFunction
)ceph_get_version
, METH_VARARGS
,
1060 "Get the ceph version of this process"},
1062 {"_ceph_get_context", (PyCFunction
)ceph_get_context
, METH_NOARGS
,
1063 "Get a CephContext* in a python capsule"},
1065 {"_ceph_get_osdmap", (PyCFunction
)ceph_get_osdmap
, METH_NOARGS
,
1066 "Get an OSDMap* in a python capsule"},
1068 {"_ceph_set_uri", (PyCFunction
)ceph_set_uri
, METH_VARARGS
,
1069 "Advertize a service URI served by this module"},
1071 {"_ceph_have_mon_connection", (PyCFunction
)ceph_have_mon_connection
,
1072 METH_NOARGS
, "Find out whether this mgr daemon currently has "
1073 "a connection to a monitor"},
1075 {"_ceph_update_progress_event", (PyCFunction
)ceph_update_progress_event
,
1076 METH_VARARGS
, "Update status of a progress event"},
1077 {"_ceph_complete_progress_event", (PyCFunction
)ceph_complete_progress_event
,
1078 METH_VARARGS
, "Complete a progress event"},
1079 {"_ceph_clear_all_progress_events", (PyCFunction
)ceph_clear_all_progress_events
,
1080 METH_NOARGS
, "Clear all progress events"},
1082 {"_ceph_dispatch_remote", (PyCFunction
)ceph_dispatch_remote
,
1083 METH_VARARGS
, "Dispatch a call to another module"},
1085 {"_ceph_add_osd_perf_query", (PyCFunction
)ceph_add_osd_perf_query
,
1086 METH_VARARGS
, "Add an osd perf query"},
1088 {"_ceph_remove_osd_perf_query", (PyCFunction
)ceph_remove_osd_perf_query
,
1089 METH_VARARGS
, "Remove an osd perf query"},
1091 {"_ceph_get_osd_perf_counters", (PyCFunction
)ceph_get_osd_perf_counters
,
1092 METH_VARARGS
, "Get osd perf counters"},
1094 {NULL
, NULL
, 0, NULL
}
1099 BaseMgrModule_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
)
1101 BaseMgrModule
*self
;
1103 self
= (BaseMgrModule
*)type
->tp_alloc(type
, 0);
1105 return (PyObject
*)self
;
1109 BaseMgrModule_init(BaseMgrModule
*self
, PyObject
*args
, PyObject
*kwds
)
1111 PyObject
*py_modules_capsule
= nullptr;
1112 PyObject
*this_module_capsule
= nullptr;
1113 static const char *kwlist
[] = {"py_modules", "this_module", NULL
};
1115 if (! PyArg_ParseTupleAndKeywords(args
, kwds
, "OO",
1116 const_cast<char**>(kwlist
),
1117 &py_modules_capsule
,
1118 &this_module_capsule
)) {
1122 self
->py_modules
= static_cast<ActivePyModules
*>(PyCapsule_GetPointer(
1123 py_modules_capsule
, nullptr));
1124 ceph_assert(self
->py_modules
);
1125 self
->this_module
= static_cast<ActivePyModule
*>(PyCapsule_GetPointer(
1126 this_module_capsule
, nullptr));
1127 ceph_assert(self
->this_module
);
1132 PyTypeObject BaseMgrModuleType
= {
1133 PyVarObject_HEAD_INIT(NULL
, 0)
1134 "ceph_module.BaseMgrModule", /* tp_name */
1135 sizeof(BaseMgrModule
), /* tp_basicsize */
1136 0, /* tp_itemsize */
1143 0, /* tp_as_number */
1144 0, /* tp_as_sequence */
1145 0, /* tp_as_mapping */
1149 0, /* tp_getattro */
1150 0, /* tp_setattro */
1151 0, /* tp_as_buffer */
1152 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
, /* tp_flags */
1153 "ceph-mgr Python Plugin", /* tp_doc */
1154 0, /* tp_traverse */
1156 0, /* tp_richcompare */
1157 0, /* tp_weaklistoffset */
1159 0, /* tp_iternext */
1160 BaseMgrModule_methods
, /* tp_methods */
1165 0, /* tp_descr_get */
1166 0, /* tp_descr_set */
1167 0, /* tp_dictoffset */
1168 (initproc
)BaseMgrModule_init
, /* tp_init */
1170 BaseMgrModule_new
, /* tp_new */