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
) && !PyUnicode_Check(v
)) {
300 derr
<< __func__
<< " check " << check_name
301 << " summary value not [unicode] 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
) && !PyUnicode_Check(di
)) {
314 derr
<< __func__
<< " check " << check_name
315 << " detail item " << k
<< " not a [unicode] 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_release_name(BaseMgrModule
*self
, PyObject
*args
)
567 return PyString_FromString(ceph_release_to_str());
571 ceph_get_context(BaseMgrModule
*self
)
573 return self
->py_modules
->get_context();
577 get_counter(BaseMgrModule
*self
, PyObject
*args
)
579 char *svc_name
= nullptr;
580 char *svc_id
= nullptr;
581 char *counter_path
= nullptr;
582 if (!PyArg_ParseTuple(args
, "sss:get_counter", &svc_name
,
583 &svc_id
, &counter_path
)) {
586 return self
->py_modules
->get_counter_python(
587 svc_name
, svc_id
, counter_path
);
591 get_latest_counter(BaseMgrModule
*self
, PyObject
*args
)
593 char *svc_name
= nullptr;
594 char *svc_id
= nullptr;
595 char *counter_path
= nullptr;
596 if (!PyArg_ParseTuple(args
, "sss:get_counter", &svc_name
,
597 &svc_id
, &counter_path
)) {
600 return self
->py_modules
->get_latest_counter_python(
601 svc_name
, svc_id
, counter_path
);
605 get_perf_schema(BaseMgrModule
*self
, PyObject
*args
)
607 char *type_str
= nullptr;
608 char *svc_id
= nullptr;
609 if (!PyArg_ParseTuple(args
, "ss:get_perf_schema", &type_str
,
614 return self
->py_modules
->get_perf_schema_python(type_str
, svc_id
);
618 ceph_get_osdmap(BaseMgrModule
*self
, PyObject
*args
)
620 return self
->py_modules
->get_osdmap();
624 ceph_set_uri(BaseMgrModule
*self
, PyObject
*args
)
626 char *svc_str
= nullptr;
627 if (!PyArg_ParseTuple(args
, "s:ceph_advertize_service",
632 // We call down into PyModules even though we have a MgrPyModule
633 // reference here, because MgrPyModule's fields are protected
634 // by PyModules' lock.
635 PyThreadState
*tstate
= PyEval_SaveThread();
636 self
->py_modules
->set_uri(self
->this_module
->get_name(), svc_str
);
637 PyEval_RestoreThread(tstate
);
643 ceph_have_mon_connection(BaseMgrModule
*self
, PyObject
*args
)
645 if (self
->py_modules
->get_monc().is_connected()) {
653 ceph_update_progress_event(BaseMgrModule
*self
, PyObject
*args
)
655 char *evid
= nullptr;
656 char *desc
= nullptr;
657 float progress
= 0.0;
658 if (!PyArg_ParseTuple(args
, "ssf:ceph_update_progress_event",
659 &evid
, &desc
, &progress
)) {
663 PyThreadState
*tstate
= PyEval_SaveThread();
664 self
->py_modules
->update_progress_event(evid
, desc
, progress
);
665 PyEval_RestoreThread(tstate
);
671 ceph_complete_progress_event(BaseMgrModule
*self
, PyObject
*args
)
673 char *evid
= nullptr;
674 if (!PyArg_ParseTuple(args
, "s:ceph_complete_progress_event",
679 PyThreadState
*tstate
= PyEval_SaveThread();
680 self
->py_modules
->complete_progress_event(evid
);
681 PyEval_RestoreThread(tstate
);
687 ceph_clear_all_progress_events(BaseMgrModule
*self
, PyObject
*args
)
689 PyThreadState
*tstate
= PyEval_SaveThread();
690 self
->py_modules
->clear_all_progress_events();
691 PyEval_RestoreThread(tstate
);
699 ceph_dispatch_remote(BaseMgrModule
*self
, PyObject
*args
)
701 char *other_module
= nullptr;
702 char *method
= nullptr;
703 PyObject
*remote_args
= nullptr;
704 PyObject
*remote_kwargs
= nullptr;
705 if (!PyArg_ParseTuple(args
, "ssOO:ceph_dispatch_remote",
706 &other_module
, &method
, &remote_args
, &remote_kwargs
)) {
710 // Early error handling, because if the module doesn't exist then we
711 // won't be able to use its thread state to set python error state
712 // inside dispatch_remote().
713 if (!self
->py_modules
->module_exists(other_module
)) {
714 derr
<< "no module '" << other_module
<< "'" << dendl
;
715 PyErr_SetString(PyExc_ImportError
, "Module not found");
719 // Drop GIL from calling python thread state, it will be taken
720 // both for checking for method existence and for executing method.
721 PyThreadState
*tstate
= PyEval_SaveThread();
723 if (!self
->py_modules
->method_exists(other_module
, method
)) {
724 PyEval_RestoreThread(tstate
);
725 PyErr_SetString(PyExc_NameError
, "Method not found");
730 auto result
= self
->py_modules
->dispatch_remote(other_module
, method
,
731 remote_args
, remote_kwargs
, &err
);
733 PyEval_RestoreThread(tstate
);
735 if (result
== nullptr) {
736 std::stringstream ss
;
737 ss
<< "Remote method threw exception: " << err
;
738 PyErr_SetString(PyExc_RuntimeError
, ss
.str().c_str());
739 derr
<< ss
.str() << dendl
;
746 ceph_add_osd_perf_query(BaseMgrModule
*self
, PyObject
*args
)
748 static const std::string NAME_KEY_DESCRIPTOR
= "key_descriptor";
749 static const std::string NAME_COUNTERS_DESCRIPTORS
=
750 "performance_counter_descriptors";
751 static const std::string NAME_LIMIT
= "limit";
752 static const std::string NAME_SUB_KEY_TYPE
= "type";
753 static const std::string NAME_SUB_KEY_REGEX
= "regex";
754 static const std::string NAME_LIMIT_ORDER_BY
= "order_by";
755 static const std::string NAME_LIMIT_MAX_COUNT
= "max_count";
756 static const std::map
<std::string
, OSDPerfMetricSubKeyType
> sub_key_types
= {
757 {"client_id", OSDPerfMetricSubKeyType::CLIENT_ID
},
758 {"client_address", OSDPerfMetricSubKeyType::CLIENT_ADDRESS
},
759 {"pool_id", OSDPerfMetricSubKeyType::POOL_ID
},
760 {"namespace", OSDPerfMetricSubKeyType::NAMESPACE
},
761 {"osd_id", OSDPerfMetricSubKeyType::OSD_ID
},
762 {"pg_id", OSDPerfMetricSubKeyType::PG_ID
},
763 {"object_name", OSDPerfMetricSubKeyType::OBJECT_NAME
},
764 {"snap_id", OSDPerfMetricSubKeyType::SNAP_ID
},
766 static const std::map
<std::string
, PerformanceCounterType
> counter_types
= {
767 {"ops", PerformanceCounterType::OPS
},
768 {"write_ops", PerformanceCounterType::WRITE_OPS
},
769 {"read_ops", PerformanceCounterType::READ_OPS
},
770 {"bytes", PerformanceCounterType::BYTES
},
771 {"write_bytes", PerformanceCounterType::WRITE_BYTES
},
772 {"read_bytes", PerformanceCounterType::READ_BYTES
},
773 {"latency", PerformanceCounterType::LATENCY
},
774 {"write_latency", PerformanceCounterType::WRITE_LATENCY
},
775 {"read_latency", PerformanceCounterType::READ_LATENCY
},
778 PyObject
*py_query
= nullptr;
779 if (!PyArg_ParseTuple(args
, "O:ceph_add_osd_perf_query", &py_query
)) {
780 derr
<< "Invalid args!" << dendl
;
783 if (!PyDict_Check(py_query
)) {
784 derr
<< __func__
<< " arg not a dict" << dendl
;
788 PyObject
*query_params
= PyDict_Items(py_query
);
789 OSDPerfMetricQuery query
;
790 std::optional
<OSDPerfMetricLimit
> limit
;
793 // 'key_descriptor': [
794 // {'type': subkey_type, 'regex': regex_pattern},
797 // 'performance_counter_descriptors': [
798 // list, of, descriptor, types
800 // 'limit': {'order_by': performance_counter_type, 'max_count': n},
803 for (int i
= 0; i
< PyList_Size(query_params
); ++i
) {
804 PyObject
*kv
= PyList_GET_ITEM(query_params
, i
);
805 char *query_param_name
= nullptr;
806 PyObject
*query_param_val
= nullptr;
807 if (!PyArg_ParseTuple(kv
, "sO:pair", &query_param_name
, &query_param_val
)) {
808 derr
<< __func__
<< " dict item " << i
<< " not a size 2 tuple" << dendl
;
811 if (query_param_name
== NAME_KEY_DESCRIPTOR
) {
812 if (!PyList_Check(query_param_val
)) {
813 derr
<< __func__
<< " " << query_param_name
<< " not a list" << dendl
;
816 for (int j
= 0; j
< PyList_Size(query_param_val
); j
++) {
817 PyObject
*sub_key
= PyList_GET_ITEM(query_param_val
, j
);
818 if (!PyDict_Check(sub_key
)) {
819 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
820 << " not a dict" << dendl
;
823 OSDPerfMetricSubKeyDescriptor d
;
824 PyObject
*sub_key_params
= PyDict_Items(sub_key
);
825 for (int k
= 0; k
< PyList_Size(sub_key_params
); ++k
) {
826 PyObject
*pair
= PyList_GET_ITEM(sub_key_params
, k
);
827 if (!PyTuple_Check(pair
)) {
828 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
829 << " pair " << k
<< " not a tuple" << dendl
;
832 char *param_name
= nullptr;
833 PyObject
*param_value
= nullptr;
834 if (!PyArg_ParseTuple(pair
, "sO:pair", ¶m_name
, ¶m_value
)) {
835 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
836 << " pair " << k
<< " not a size 2 tuple" << dendl
;
839 if (param_name
== NAME_SUB_KEY_TYPE
) {
840 if (!PyString_Check(param_value
)) {
841 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
842 << " contains invalid param " << param_name
<< dendl
;
845 auto type
= PyString_AsString(param_value
);
846 auto it
= sub_key_types
.find(type
);
847 if (it
== sub_key_types
.end()) {
848 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
849 << " contains invalid type " << dendl
;
853 } else if (param_name
== NAME_SUB_KEY_REGEX
) {
854 if (!PyString_Check(param_value
)) {
855 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
856 << " contains invalid param " << param_name
<< dendl
;
859 d
.regex_str
= PyString_AsString(param_value
);
861 d
.regex
= {d
.regex_str
.c_str()};
862 } catch (const std::regex_error
& e
) {
863 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
864 << " contains invalid regex " << d
.regex_str
<< dendl
;
867 if (d
.regex
.mark_count() == 0) {
868 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
869 << " regex " << d
.regex_str
<< ": no capturing groups"
874 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
875 << " contains invalid param " << param_name
<< dendl
;
879 if (d
.type
== static_cast<OSDPerfMetricSubKeyType
>(-1) ||
880 d
.regex_str
.empty()) {
881 derr
<< __func__
<< " query " << query_param_name
<< " item " << i
882 << " invalid" << dendl
;
885 query
.key_descriptor
.push_back(d
);
887 } else if (query_param_name
== NAME_COUNTERS_DESCRIPTORS
) {
888 if (!PyList_Check(query_param_val
)) {
889 derr
<< __func__
<< " " << query_param_name
<< " not a list" << dendl
;
892 for (int j
= 0; j
< PyList_Size(query_param_val
); j
++) {
893 PyObject
*py_type
= PyList_GET_ITEM(query_param_val
, j
);
894 if (!PyString_Check(py_type
)) {
895 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
896 << " not a string" << dendl
;
899 auto type
= PyString_AsString(py_type
);
900 auto it
= counter_types
.find(type
);
901 if (it
== counter_types
.end()) {
902 derr
<< __func__
<< " query " << query_param_name
<< " item " << type
903 << " is not valid type" << dendl
;
906 query
.performance_counter_descriptors
.push_back(it
->second
);
908 } else if (query_param_name
== NAME_LIMIT
) {
909 if (!PyDict_Check(query_param_val
)) {
910 derr
<< __func__
<< " query " << query_param_name
<< " not a dict"
915 limit
= OSDPerfMetricLimit();
916 PyObject
*limit_params
= PyDict_Items(query_param_val
);
918 for (int j
= 0; j
< PyList_Size(limit_params
); ++j
) {
919 PyObject
*kv
= PyList_GET_ITEM(limit_params
, j
);
920 char *limit_param_name
= nullptr;
921 PyObject
*limit_param_val
= nullptr;
922 if (!PyArg_ParseTuple(kv
, "sO:pair", &limit_param_name
,
924 derr
<< __func__
<< " limit item " << j
<< " not a size 2 tuple"
929 if (limit_param_name
== NAME_LIMIT_ORDER_BY
) {
930 if (!PyString_Check(limit_param_val
)) {
931 derr
<< __func__
<< " " << limit_param_name
<< " not a string"
935 auto order_by
= PyString_AsString(limit_param_val
);
936 auto it
= counter_types
.find(order_by
);
937 if (it
== counter_types
.end()) {
938 derr
<< __func__
<< " limit " << limit_param_name
939 << " not a valid counter type" << dendl
;
942 limit
->order_by
= it
->second
;
943 } else if (limit_param_name
== NAME_LIMIT_MAX_COUNT
) {
944 #if PY_MAJOR_VERSION <= 2
945 if (!PyInt_Check(limit_param_val
) && !PyLong_Check(limit_param_val
)) {
947 if (!PyLong_Check(limit_param_val
)) {
949 derr
<< __func__
<< " " << limit_param_name
<< " not an int"
953 limit
->max_count
= PyLong_AsLong(limit_param_val
);
955 derr
<< __func__
<< " unknown limit param: " << limit_param_name
961 derr
<< __func__
<< " unknown query param: " << query_param_name
<< dendl
;
966 if (query
.key_descriptor
.empty() ||
967 query
.performance_counter_descriptors
.empty()) {
968 derr
<< __func__
<< " invalid query" << dendl
;
973 auto &ds
= query
.performance_counter_descriptors
;
974 if (std::find(ds
.begin(), ds
.end(), limit
->order_by
) == ds
.end()) {
975 derr
<< __func__
<< " limit order_by " << limit
->order_by
976 << " not in performance_counter_descriptors" << dendl
;
981 auto query_id
= self
->py_modules
->add_osd_perf_query(query
, limit
);
982 return PyLong_FromLong(query_id
);
986 ceph_remove_osd_perf_query(BaseMgrModule
*self
, PyObject
*args
)
988 OSDPerfMetricQueryID query_id
;
989 if (!PyArg_ParseTuple(args
, "i:ceph_remove_osd_perf_query", &query_id
)) {
990 derr
<< "Invalid args!" << dendl
;
994 self
->py_modules
->remove_osd_perf_query(query_id
);
999 ceph_get_osd_perf_counters(BaseMgrModule
*self
, PyObject
*args
)
1001 OSDPerfMetricQueryID query_id
;
1002 if (!PyArg_ParseTuple(args
, "i:ceph_get_osd_perf_counters", &query_id
)) {
1003 derr
<< "Invalid args!" << dendl
;
1007 return self
->py_modules
->get_osd_perf_counters(query_id
);
1011 ceph_is_authorized(BaseMgrModule
*self
, PyObject
*args
)
1013 PyObject
*args_dict
= NULL
;
1014 if (!PyArg_ParseTuple(args
, "O:ceph_is_authorized", &args_dict
)) {
1018 if (!PyDict_Check(args_dict
)) {
1019 derr
<< __func__
<< " arg not a dict" << dendl
;
1023 std::map
<std::string
, std::string
> arguments
;
1025 PyObject
*args_list
= PyDict_Items(args_dict
);
1026 for (int i
= 0; i
< PyList_Size(args_list
); ++i
) {
1027 PyObject
*kv
= PyList_GET_ITEM(args_list
, i
);
1029 char *arg_key
= nullptr;
1030 char *arg_value
= nullptr;
1031 if (!PyArg_ParseTuple(kv
, "ss:pair", &arg_key
, &arg_value
)) {
1032 derr
<< __func__
<< " dict item " << i
<< " not a size 2 tuple" << dendl
;
1036 arguments
[arg_key
] = arg_value
;
1039 if (self
->this_module
->is_authorized(arguments
)) {
1046 PyMethodDef BaseMgrModule_methods
[] = {
1047 {"_ceph_get", (PyCFunction
)ceph_state_get
, METH_VARARGS
,
1048 "Get a cluster object"},
1050 {"_ceph_get_server", (PyCFunction
)ceph_get_server
, METH_VARARGS
,
1051 "Get a server object"},
1053 {"_ceph_get_metadata", (PyCFunction
)get_metadata
, METH_VARARGS
,
1054 "Get a service's metadata"},
1056 {"_ceph_get_daemon_status", (PyCFunction
)get_daemon_status
, METH_VARARGS
,
1057 "Get a service's status"},
1059 {"_ceph_send_command", (PyCFunction
)ceph_send_command
, METH_VARARGS
,
1060 "Send a mon command"},
1062 {"_ceph_set_health_checks", (PyCFunction
)ceph_set_health_checks
, METH_VARARGS
,
1063 "Set health checks for this module"},
1065 {"_ceph_get_mgr_id", (PyCFunction
)ceph_get_mgr_id
, METH_NOARGS
,
1066 "Get the name of the Mgr daemon where we are running"},
1068 {"_ceph_get_option", (PyCFunction
)ceph_option_get
, METH_VARARGS
,
1069 "Get a native configuration option value"},
1071 {"_ceph_get_module_option", (PyCFunction
)ceph_get_module_option
, METH_VARARGS
,
1072 "Get a module configuration option value"},
1074 {"_ceph_get_store_prefix", (PyCFunction
)ceph_store_get_prefix
, METH_VARARGS
,
1075 "Get all KV store values with a given prefix"},
1077 {"_ceph_set_module_option", (PyCFunction
)ceph_set_module_option
, METH_VARARGS
,
1078 "Set a module configuration option value"},
1080 {"_ceph_get_store", (PyCFunction
)ceph_store_get
, METH_VARARGS
,
1081 "Get a stored field"},
1083 {"_ceph_set_store", (PyCFunction
)ceph_store_set
, METH_VARARGS
,
1084 "Set a stored field"},
1086 {"_ceph_get_counter", (PyCFunction
)get_counter
, METH_VARARGS
,
1087 "Get a performance counter"},
1089 {"_ceph_get_latest_counter", (PyCFunction
)get_latest_counter
, METH_VARARGS
,
1090 "Get the latest performance counter"},
1092 {"_ceph_get_perf_schema", (PyCFunction
)get_perf_schema
, METH_VARARGS
,
1093 "Get the performance counter schema"},
1095 {"_ceph_log", (PyCFunction
)ceph_log
, METH_VARARGS
,
1096 "Emit a (local) log message"},
1098 {"_ceph_cluster_log", (PyCFunction
)ceph_cluster_log
, METH_VARARGS
,
1099 "Emit a cluster log message"},
1101 {"_ceph_get_version", (PyCFunction
)ceph_get_version
, METH_NOARGS
,
1102 "Get the ceph version of this process"},
1104 {"_ceph_get_release_name", (PyCFunction
)ceph_get_release_name
, METH_NOARGS
,
1105 "Get the ceph release name of this process"},
1107 {"_ceph_get_context", (PyCFunction
)ceph_get_context
, METH_NOARGS
,
1108 "Get a CephContext* in a python capsule"},
1110 {"_ceph_get_osdmap", (PyCFunction
)ceph_get_osdmap
, METH_NOARGS
,
1111 "Get an OSDMap* in a python capsule"},
1113 {"_ceph_set_uri", (PyCFunction
)ceph_set_uri
, METH_VARARGS
,
1114 "Advertize a service URI served by this module"},
1116 {"_ceph_have_mon_connection", (PyCFunction
)ceph_have_mon_connection
,
1117 METH_NOARGS
, "Find out whether this mgr daemon currently has "
1118 "a connection to a monitor"},
1120 {"_ceph_update_progress_event", (PyCFunction
)ceph_update_progress_event
,
1121 METH_VARARGS
, "Update status of a progress event"},
1122 {"_ceph_complete_progress_event", (PyCFunction
)ceph_complete_progress_event
,
1123 METH_VARARGS
, "Complete a progress event"},
1124 {"_ceph_clear_all_progress_events", (PyCFunction
)ceph_clear_all_progress_events
,
1125 METH_NOARGS
, "Clear all progress events"},
1127 {"_ceph_dispatch_remote", (PyCFunction
)ceph_dispatch_remote
,
1128 METH_VARARGS
, "Dispatch a call to another module"},
1130 {"_ceph_add_osd_perf_query", (PyCFunction
)ceph_add_osd_perf_query
,
1131 METH_VARARGS
, "Add an osd perf query"},
1133 {"_ceph_remove_osd_perf_query", (PyCFunction
)ceph_remove_osd_perf_query
,
1134 METH_VARARGS
, "Remove an osd perf query"},
1136 {"_ceph_get_osd_perf_counters", (PyCFunction
)ceph_get_osd_perf_counters
,
1137 METH_VARARGS
, "Get osd perf counters"},
1139 {"_ceph_is_authorized", (PyCFunction
)ceph_is_authorized
,
1140 METH_VARARGS
, "Verify the current session caps are valid"},
1142 {NULL
, NULL
, 0, NULL
}
1147 BaseMgrModule_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
)
1149 BaseMgrModule
*self
;
1151 self
= (BaseMgrModule
*)type
->tp_alloc(type
, 0);
1153 return (PyObject
*)self
;
1157 BaseMgrModule_init(BaseMgrModule
*self
, PyObject
*args
, PyObject
*kwds
)
1159 PyObject
*py_modules_capsule
= nullptr;
1160 PyObject
*this_module_capsule
= nullptr;
1161 static const char *kwlist
[] = {"py_modules", "this_module", NULL
};
1163 if (! PyArg_ParseTupleAndKeywords(args
, kwds
, "OO",
1164 const_cast<char**>(kwlist
),
1165 &py_modules_capsule
,
1166 &this_module_capsule
)) {
1170 self
->py_modules
= static_cast<ActivePyModules
*>(PyCapsule_GetPointer(
1171 py_modules_capsule
, nullptr));
1172 ceph_assert(self
->py_modules
);
1173 self
->this_module
= static_cast<ActivePyModule
*>(PyCapsule_GetPointer(
1174 this_module_capsule
, nullptr));
1175 ceph_assert(self
->this_module
);
1180 PyTypeObject BaseMgrModuleType
= {
1181 PyVarObject_HEAD_INIT(NULL
, 0)
1182 "ceph_module.BaseMgrModule", /* tp_name */
1183 sizeof(BaseMgrModule
), /* tp_basicsize */
1184 0, /* tp_itemsize */
1191 0, /* tp_as_number */
1192 0, /* tp_as_sequence */
1193 0, /* tp_as_mapping */
1197 0, /* tp_getattro */
1198 0, /* tp_setattro */
1199 0, /* tp_as_buffer */
1200 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
, /* tp_flags */
1201 "ceph-mgr Python Plugin", /* tp_doc */
1202 0, /* tp_traverse */
1204 0, /* tp_richcompare */
1205 0, /* tp_weaklistoffset */
1207 0, /* tp_iternext */
1208 BaseMgrModule_methods
, /* tp_methods */
1213 0, /* tp_descr_get */
1214 0, /* tp_descr_set */
1215 0, /* tp_dictoffset */
1216 (initproc
)BaseMgrModule_init
, /* tp_init */
1218 BaseMgrModule_new
, /* tp_new */