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"
28 #include "mgr/Types.h"
31 #include "BaseMgrModule.h"
36 #define dout_context g_ceph_context
37 #define dout_subsys ceph_subsys_mgr
39 #define PLACEHOLDER ""
44 ActivePyModules
*py_modules
;
45 ActivePyModule
*this_module
;
48 class MonCommandCompletion
: public Context
50 ActivePyModules
*py_modules
;
51 PyObject
*python_completion
;
52 const std::string tag
;
53 SafeThreadState pThreadState
;
60 ActivePyModules
*py_modules_
, PyObject
* ev
,
61 const std::string
&tag_
, PyThreadState
*ts_
)
62 : py_modules(py_modules_
), python_completion(ev
),
63 tag(tag_
), pThreadState(ts_
)
65 ceph_assert(python_completion
!= nullptr);
66 Py_INCREF(python_completion
);
69 ~MonCommandCompletion() override
71 if (python_completion
) {
72 // Usually do this in finish(): this path is only for if we're
73 // being destroyed without completing.
74 Gil
gil(pThreadState
, true);
75 Py_DECREF(python_completion
);
76 python_completion
= nullptr;
80 void finish(int r
) override
82 ceph_assert(python_completion
!= nullptr);
84 dout(10) << "MonCommandCompletion::finish()" << dendl
;
86 // Scoped so the Gil is released before calling notify_all()
87 // Create new thread state because this is called via the MonClient
88 // Finisher, not the PyModules finisher.
89 Gil
gil(pThreadState
, true);
91 auto set_fn
= PyObject_GetAttrString(python_completion
, "complete");
92 ceph_assert(set_fn
!= nullptr);
94 auto pyR
= PyLong_FromLong(r
);
95 auto pyOutBl
= PyUnicode_FromString(outbl
.to_str().c_str());
96 auto pyOutS
= PyUnicode_FromString(outs
.c_str());
97 auto args
= PyTuple_Pack(3, pyR
, pyOutBl
, pyOutS
);
102 auto rtn
= PyObject_CallObject(set_fn
, args
);
103 if (rtn
!= nullptr) {
109 Py_DECREF(python_completion
);
110 python_completion
= nullptr;
112 py_modules
->notify_all("command", tag
);
118 ceph_send_command(BaseMgrModule
*self
, PyObject
*args
)
120 // Like mon, osd, mds
121 char *type
= nullptr;
123 // Like "23" for an OSD or "myid" for an MDS
124 char *name
= nullptr;
126 char *cmd_json
= nullptr;
128 char *inbuf_ptr
= nullptr;
129 Py_ssize_t inbuf_len
= 0;
130 bufferlist inbuf
= {};
132 PyObject
*completion
= nullptr;
133 if (!PyArg_ParseTuple(args
, "Ossssz#:ceph_send_command",
134 &completion
, &type
, &name
, &cmd_json
, &tag
, &inbuf_ptr
, &inbuf_len
)) {
139 inbuf
.append(inbuf_ptr
, (unsigned)inbuf_len
);
142 auto set_fn
= PyObject_GetAttrString(completion
, "complete");
143 if (set_fn
== nullptr) {
144 ceph_abort(); // TODO raise python exception instead
146 ceph_assert(PyCallable_Check(set_fn
));
150 MonCommandCompletion
*command_c
= new MonCommandCompletion(self
->py_modules
,
151 completion
, tag
, PyThreadState_Get());
153 PyThreadState
*tstate
= PyEval_SaveThread();
155 if (std::string(type
) == "mon") {
157 // Wait for the latest OSDMap after each command we send to
158 // the mons. This is a heavy-handed hack to make life simpler
159 // for python module authors, so that they know whenever they
160 // run a command they've gt a fresh OSDMap afterwards.
161 // TODO: enhance MCommand interface so that it returns
162 // latest cluster map versions on completion, and callers
163 // can wait for those.
164 auto c
= new LambdaContext([command_c
, self
](int command_r
){
165 self
->py_modules
->get_objecter().wait_for_latest_osdmap(
166 new LambdaContext([command_c
, command_r
](int wait_r
){
167 command_c
->complete(command_r
);
172 self
->py_modules
->get_monc().start_mon_command(
178 new C_OnFinisher(c
, &self
->py_modules
->cmd_finisher
));
179 } else if (std::string(type
) == "osd") {
181 uint64_t osd_id
= strict_strtoll(name
, 10, &err
);
184 string
msg("invalid osd_id: ");
185 msg
.append("\"").append(name
).append("\"");
186 PyEval_RestoreThread(tstate
);
187 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
192 self
->py_modules
->get_objecter().osd_command(
199 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
200 } else if (std::string(type
) == "mds") {
201 int r
= self
->py_modules
->get_client().mds_command(
207 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
209 string
msg("failed to send command to mds: ");
210 msg
.append(cpp_strerror(r
));
211 PyEval_RestoreThread(tstate
);
212 PyErr_SetString(PyExc_RuntimeError
, msg
.c_str());
215 } else if (std::string(type
) == "pg") {
217 if (!pgid
.parse(name
)) {
219 string
msg("invalid pgid: ");
220 msg
.append("\"").append(name
).append("\"");
221 PyEval_RestoreThread(tstate
);
222 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
227 self
->py_modules
->get_objecter().pg_command(
234 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
235 PyEval_RestoreThread(tstate
);
239 string
msg("unknown service type: ");
241 PyEval_RestoreThread(tstate
);
242 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
246 PyEval_RestoreThread(tstate
);
251 ceph_set_health_checks(BaseMgrModule
*self
, PyObject
*args
)
253 PyObject
*checks
= NULL
;
254 if (!PyArg_ParseTuple(args
, "O:ceph_set_health_checks", &checks
)) {
257 if (!PyDict_Check(checks
)) {
258 derr
<< __func__
<< " arg not a dict" << dendl
;
261 PyObject
*checksls
= PyDict_Items(checks
);
262 health_check_map_t out_checks
;
263 for (int i
= 0; i
< PyList_Size(checksls
); ++i
) {
264 PyObject
*kv
= PyList_GET_ITEM(checksls
, i
);
265 char *check_name
= nullptr;
266 PyObject
*check_info
= nullptr;
267 if (!PyArg_ParseTuple(kv
, "sO:pair", &check_name
, &check_info
)) {
268 derr
<< __func__
<< " dict item " << i
269 << " not a size 2 tuple" << dendl
;
272 if (!PyDict_Check(check_info
)) {
273 derr
<< __func__
<< " item " << i
<< " " << check_name
274 << " value not a dict" << dendl
;
277 health_status_t severity
= HEALTH_OK
;
281 PyObject
*infols
= PyDict_Items(check_info
);
282 for (int j
= 0; j
< PyList_Size(infols
); ++j
) {
283 PyObject
*pair
= PyList_GET_ITEM(infols
, j
);
284 if (!PyTuple_Check(pair
)) {
285 derr
<< __func__
<< " item " << i
<< " pair " << j
286 << " not a tuple" << dendl
;
290 PyObject
*v
= nullptr;
291 if (!PyArg_ParseTuple(pair
, "sO:pair", &k
, &v
)) {
292 derr
<< __func__
<< " item " << i
<< " pair " << j
293 << " not a size 2 tuple" << dendl
;
297 if (ks
== "severity") {
298 if (!PyUnicode_Check(v
)) {
299 derr
<< __func__
<< " check " << check_name
300 << " severity value not string" << dendl
;
303 if (const string vs
= PyUnicode_AsUTF8(v
); vs
== "warning") {
304 severity
= HEALTH_WARN
;
305 } else if (vs
== "error") {
306 severity
= HEALTH_ERR
;
308 } else if (ks
== "summary") {
309 if (!PyUnicode_Check(v
)) {
310 derr
<< __func__
<< " check " << check_name
311 << " summary value not [unicode] string" << dendl
;
314 summary
= PyUnicode_AsUTF8(v
);
316 } else if (ks
== "count") {
317 if (PyLong_Check(v
)) {
318 count
= PyLong_AsLong(v
);
320 derr
<< __func__
<< " check " << check_name
321 << " count value not int" << dendl
;
324 } else if (ks
== "detail") {
325 if (!PyList_Check(v
)) {
326 derr
<< __func__
<< " check " << check_name
327 << " detail value not list" << dendl
;
330 for (int k
= 0; k
< PyList_Size(v
); ++k
) {
331 PyObject
*di
= PyList_GET_ITEM(v
, k
);
332 if (!PyUnicode_Check(di
)) {
333 derr
<< __func__
<< " check " << check_name
334 << " detail item " << k
<< " not a [unicode] string" << dendl
;
337 detail
.push_back(PyUnicode_AsUTF8(di
));
341 derr
<< __func__
<< " check " << check_name
342 << " unexpected key " << k
<< dendl
;
345 auto& d
= out_checks
.add(check_name
, severity
, summary
, count
);
346 d
.detail
.swap(detail
);
349 JSONFormatter
jf(true);
350 dout(10) << "module " << self
->this_module
->get_name()
351 << " health checks:\n";
352 out_checks
.dump(&jf
);
356 PyThreadState
*tstate
= PyEval_SaveThread();
357 self
->py_modules
->set_health_checks(self
->this_module
->get_name(),
358 std::move(out_checks
));
359 PyEval_RestoreThread(tstate
);
366 ceph_state_get(BaseMgrModule
*self
, PyObject
*args
)
369 if (!PyArg_ParseTuple(args
, "s:ceph_state_get", &what
)) {
373 return self
->py_modules
->get_python(what
);
378 ceph_get_server(BaseMgrModule
*self
, PyObject
*args
)
380 char *hostname
= NULL
;
381 if (!PyArg_ParseTuple(args
, "z:ceph_get_server", &hostname
)) {
386 return self
->py_modules
->get_server_python(hostname
);
388 return self
->py_modules
->list_servers_python();
393 ceph_get_mgr_id(BaseMgrModule
*self
, PyObject
*args
)
395 return PyUnicode_FromString(g_conf()->name
.get_id().c_str());
399 ceph_option_get(BaseMgrModule
*self
, PyObject
*args
)
401 char *what
= nullptr;
402 if (!PyArg_ParseTuple(args
, "s:ceph_option_get", &what
)) {
403 derr
<< "Invalid args!" << dendl
;
407 const Option
*opt
= g_conf().find_option(string(what
));
410 switch (int r
= g_conf().get_val(string(what
), &value
); r
) {
415 PyErr_SetString(PyExc_ValueError
, "value too long");
421 dout(10) << "ceph_option_get " << what
<< " found: " << value
<< dendl
;
422 return get_python_typed_option_value(opt
->type
, value
);
424 dout(4) << "ceph_option_get " << what
<< " not found " << dendl
;
425 PyErr_Format(PyExc_KeyError
, "option not found: %s", what
);
431 ceph_get_module_option(BaseMgrModule
*self
, PyObject
*args
)
433 char *module
= nullptr;
435 char *prefix
= nullptr;
436 if (!PyArg_ParseTuple(args
, "ss|s:ceph_get_module_option", &module
, &key
,
438 derr
<< "Invalid args!" << dendl
;
441 std::string str_prefix
;
445 assert(self
->this_module
->py_module
);
446 auto pResult
= self
->py_modules
->get_typed_config(module
, key
, str_prefix
);
451 ceph_store_get_prefix(BaseMgrModule
*self
, PyObject
*args
)
453 char *prefix
= nullptr;
454 if (!PyArg_ParseTuple(args
, "s:ceph_store_get_prefix", &prefix
)) {
455 derr
<< "Invalid args!" << dendl
;
459 return self
->py_modules
->get_store_prefix(self
->this_module
->get_name(),
464 ceph_set_module_option(BaseMgrModule
*self
, PyObject
*args
)
466 char *module
= nullptr;
468 char *value
= nullptr;
469 if (!PyArg_ParseTuple(args
, "ssz:ceph_set_module_option",
470 &module
, &key
, &value
)) {
471 derr
<< "Invalid args!" << dendl
;
474 boost::optional
<string
> val
;
478 PyThreadState
*tstate
= PyEval_SaveThread();
479 self
->py_modules
->set_config(module
, key
, val
);
480 PyEval_RestoreThread(tstate
);
486 ceph_store_get(BaseMgrModule
*self
, PyObject
*args
)
488 char *what
= nullptr;
489 if (!PyArg_ParseTuple(args
, "s:ceph_store_get", &what
)) {
490 derr
<< "Invalid args!" << dendl
;
495 bool found
= self
->py_modules
->get_store(self
->this_module
->get_name(),
498 dout(10) << "ceph_store_get " << what
<< " found: " << value
.c_str() << dendl
;
499 return PyUnicode_FromString(value
.c_str());
501 dout(4) << "ceph_store_get " << what
<< " not found " << dendl
;
507 ceph_store_set(BaseMgrModule
*self
, PyObject
*args
)
510 char *value
= nullptr;
511 if (!PyArg_ParseTuple(args
, "sz:ceph_store_set", &key
, &value
)) {
514 boost::optional
<string
> val
;
518 PyThreadState
*tstate
= PyEval_SaveThread();
519 self
->py_modules
->set_store(self
->this_module
->get_name(), key
, val
);
520 PyEval_RestoreThread(tstate
);
526 get_metadata(BaseMgrModule
*self
, PyObject
*args
)
528 char *svc_name
= NULL
;
530 if (!PyArg_ParseTuple(args
, "ss:get_metadata", &svc_name
, &svc_id
)) {
533 return self
->py_modules
->get_metadata_python(svc_name
, svc_id
);
537 get_daemon_status(BaseMgrModule
*self
, PyObject
*args
)
539 char *svc_name
= NULL
;
541 if (!PyArg_ParseTuple(args
, "ss:get_daemon_status", &svc_name
,
545 return self
->py_modules
->get_daemon_status_python(svc_name
, svc_id
);
549 ceph_log(BaseMgrModule
*self
, PyObject
*args
)
551 char *record
= nullptr;
552 if (!PyArg_ParseTuple(args
, "s:log", &record
)) {
556 ceph_assert(self
->this_module
);
558 self
->this_module
->log(record
);
564 ceph_cluster_log(BaseMgrModule
*self
, PyObject
*args
)
567 char *channel
= nullptr;
568 char *message
= nullptr;
570 if (!PyArg_ParseTuple(args
, "sis:ceph_cluster_log", &channel
, &prio
, &message
)) {
574 PyThreadState
*tstate
= PyEval_SaveThread();
575 self
->py_modules
->cluster_log(channel
, (clog_type
)prio
, message
);
576 PyEval_RestoreThread(tstate
);
582 ceph_get_version(BaseMgrModule
*self
, PyObject
*args
)
584 return PyUnicode_FromString(pretty_version_to_str().c_str());
588 ceph_get_release_name(BaseMgrModule
*self
, PyObject
*args
)
590 return PyUnicode_FromString(ceph_release_to_str());
594 ceph_get_context(BaseMgrModule
*self
)
596 return self
->py_modules
->get_context();
600 get_counter(BaseMgrModule
*self
, PyObject
*args
)
602 char *svc_name
= nullptr;
603 char *svc_id
= nullptr;
604 char *counter_path
= nullptr;
605 if (!PyArg_ParseTuple(args
, "sss:get_counter", &svc_name
,
606 &svc_id
, &counter_path
)) {
609 return self
->py_modules
->get_counter_python(
610 svc_name
, svc_id
, counter_path
);
614 get_latest_counter(BaseMgrModule
*self
, PyObject
*args
)
616 char *svc_name
= nullptr;
617 char *svc_id
= nullptr;
618 char *counter_path
= nullptr;
619 if (!PyArg_ParseTuple(args
, "sss:get_counter", &svc_name
,
620 &svc_id
, &counter_path
)) {
623 return self
->py_modules
->get_latest_counter_python(
624 svc_name
, svc_id
, counter_path
);
628 get_perf_schema(BaseMgrModule
*self
, PyObject
*args
)
630 char *type_str
= nullptr;
631 char *svc_id
= nullptr;
632 if (!PyArg_ParseTuple(args
, "ss:get_perf_schema", &type_str
,
637 return self
->py_modules
->get_perf_schema_python(type_str
, svc_id
);
641 ceph_get_osdmap(BaseMgrModule
*self
, PyObject
*args
)
643 return self
->py_modules
->get_osdmap();
647 ceph_set_uri(BaseMgrModule
*self
, PyObject
*args
)
649 char *svc_str
= nullptr;
650 if (!PyArg_ParseTuple(args
, "s:ceph_advertize_service",
655 // We call down into PyModules even though we have a MgrPyModule
656 // reference here, because MgrPyModule's fields are protected
657 // by PyModules' lock.
658 PyThreadState
*tstate
= PyEval_SaveThread();
659 self
->py_modules
->set_uri(self
->this_module
->get_name(), svc_str
);
660 PyEval_RestoreThread(tstate
);
666 ceph_have_mon_connection(BaseMgrModule
*self
, PyObject
*args
)
668 if (self
->py_modules
->get_monc().is_connected()) {
676 ceph_update_progress_event(BaseMgrModule
*self
, PyObject
*args
)
678 char *evid
= nullptr;
679 char *desc
= nullptr;
680 float progress
= 0.0;
681 if (!PyArg_ParseTuple(args
, "ssf:ceph_update_progress_event",
682 &evid
, &desc
, &progress
)) {
686 PyThreadState
*tstate
= PyEval_SaveThread();
687 self
->py_modules
->update_progress_event(evid
, desc
, progress
);
688 PyEval_RestoreThread(tstate
);
694 ceph_complete_progress_event(BaseMgrModule
*self
, PyObject
*args
)
696 char *evid
= nullptr;
697 if (!PyArg_ParseTuple(args
, "s:ceph_complete_progress_event",
702 PyThreadState
*tstate
= PyEval_SaveThread();
703 self
->py_modules
->complete_progress_event(evid
);
704 PyEval_RestoreThread(tstate
);
710 ceph_clear_all_progress_events(BaseMgrModule
*self
, PyObject
*args
)
712 PyThreadState
*tstate
= PyEval_SaveThread();
713 self
->py_modules
->clear_all_progress_events();
714 PyEval_RestoreThread(tstate
);
722 ceph_dispatch_remote(BaseMgrModule
*self
, PyObject
*args
)
724 char *other_module
= nullptr;
725 char *method
= nullptr;
726 PyObject
*remote_args
= nullptr;
727 PyObject
*remote_kwargs
= nullptr;
728 if (!PyArg_ParseTuple(args
, "ssOO:ceph_dispatch_remote",
729 &other_module
, &method
, &remote_args
, &remote_kwargs
)) {
733 // Early error handling, because if the module doesn't exist then we
734 // won't be able to use its thread state to set python error state
735 // inside dispatch_remote().
736 if (!self
->py_modules
->module_exists(other_module
)) {
737 derr
<< "no module '" << other_module
<< "'" << dendl
;
738 PyErr_SetString(PyExc_ImportError
, "Module not found");
742 // Drop GIL from calling python thread state, it will be taken
743 // both for checking for method existence and for executing method.
744 PyThreadState
*tstate
= PyEval_SaveThread();
746 if (!self
->py_modules
->method_exists(other_module
, method
)) {
747 PyEval_RestoreThread(tstate
);
748 PyErr_SetString(PyExc_NameError
, "Method not found");
753 auto result
= self
->py_modules
->dispatch_remote(other_module
, method
,
754 remote_args
, remote_kwargs
, &err
);
756 PyEval_RestoreThread(tstate
);
758 if (result
== nullptr) {
759 std::stringstream ss
;
760 ss
<< "Remote method threw exception: " << err
;
761 PyErr_SetString(PyExc_RuntimeError
, ss
.str().c_str());
762 derr
<< ss
.str() << dendl
;
769 ceph_add_osd_perf_query(BaseMgrModule
*self
, PyObject
*args
)
771 static const std::string NAME_KEY_DESCRIPTOR
= "key_descriptor";
772 static const std::string NAME_COUNTERS_DESCRIPTORS
=
773 "performance_counter_descriptors";
774 static const std::string NAME_LIMIT
= "limit";
775 static const std::string NAME_SUB_KEY_TYPE
= "type";
776 static const std::string NAME_SUB_KEY_REGEX
= "regex";
777 static const std::string NAME_LIMIT_ORDER_BY
= "order_by";
778 static const std::string NAME_LIMIT_MAX_COUNT
= "max_count";
779 static const std::map
<std::string
, OSDPerfMetricSubKeyType
> sub_key_types
= {
780 {"client_id", OSDPerfMetricSubKeyType::CLIENT_ID
},
781 {"client_address", OSDPerfMetricSubKeyType::CLIENT_ADDRESS
},
782 {"pool_id", OSDPerfMetricSubKeyType::POOL_ID
},
783 {"namespace", OSDPerfMetricSubKeyType::NAMESPACE
},
784 {"osd_id", OSDPerfMetricSubKeyType::OSD_ID
},
785 {"pg_id", OSDPerfMetricSubKeyType::PG_ID
},
786 {"object_name", OSDPerfMetricSubKeyType::OBJECT_NAME
},
787 {"snap_id", OSDPerfMetricSubKeyType::SNAP_ID
},
789 static const std::map
<std::string
, PerformanceCounterType
> counter_types
= {
790 {"ops", PerformanceCounterType::OPS
},
791 {"write_ops", PerformanceCounterType::WRITE_OPS
},
792 {"read_ops", PerformanceCounterType::READ_OPS
},
793 {"bytes", PerformanceCounterType::BYTES
},
794 {"write_bytes", PerformanceCounterType::WRITE_BYTES
},
795 {"read_bytes", PerformanceCounterType::READ_BYTES
},
796 {"latency", PerformanceCounterType::LATENCY
},
797 {"write_latency", PerformanceCounterType::WRITE_LATENCY
},
798 {"read_latency", PerformanceCounterType::READ_LATENCY
},
801 PyObject
*py_query
= nullptr;
802 if (!PyArg_ParseTuple(args
, "O:ceph_add_osd_perf_query", &py_query
)) {
803 derr
<< "Invalid args!" << dendl
;
806 if (!PyDict_Check(py_query
)) {
807 derr
<< __func__
<< " arg not a dict" << dendl
;
811 PyObject
*query_params
= PyDict_Items(py_query
);
812 OSDPerfMetricQuery query
;
813 std::optional
<OSDPerfMetricLimit
> limit
;
816 // 'key_descriptor': [
817 // {'type': subkey_type, 'regex': regex_pattern},
820 // 'performance_counter_descriptors': [
821 // list, of, descriptor, types
823 // 'limit': {'order_by': performance_counter_type, 'max_count': n},
826 for (int i
= 0; i
< PyList_Size(query_params
); ++i
) {
827 PyObject
*kv
= PyList_GET_ITEM(query_params
, i
);
828 char *query_param_name
= nullptr;
829 PyObject
*query_param_val
= nullptr;
830 if (!PyArg_ParseTuple(kv
, "sO:pair", &query_param_name
, &query_param_val
)) {
831 derr
<< __func__
<< " dict item " << i
<< " not a size 2 tuple" << dendl
;
834 if (query_param_name
== NAME_KEY_DESCRIPTOR
) {
835 if (!PyList_Check(query_param_val
)) {
836 derr
<< __func__
<< " " << query_param_name
<< " not a list" << dendl
;
839 for (int j
= 0; j
< PyList_Size(query_param_val
); j
++) {
840 PyObject
*sub_key
= PyList_GET_ITEM(query_param_val
, j
);
841 if (!PyDict_Check(sub_key
)) {
842 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
843 << " not a dict" << dendl
;
846 OSDPerfMetricSubKeyDescriptor d
;
847 PyObject
*sub_key_params
= PyDict_Items(sub_key
);
848 for (int k
= 0; k
< PyList_Size(sub_key_params
); ++k
) {
849 PyObject
*pair
= PyList_GET_ITEM(sub_key_params
, k
);
850 if (!PyTuple_Check(pair
)) {
851 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
852 << " pair " << k
<< " not a tuple" << dendl
;
855 char *param_name
= nullptr;
856 PyObject
*param_value
= nullptr;
857 if (!PyArg_ParseTuple(pair
, "sO:pair", ¶m_name
, ¶m_value
)) {
858 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
859 << " pair " << k
<< " not a size 2 tuple" << dendl
;
862 if (param_name
== NAME_SUB_KEY_TYPE
) {
863 if (!PyUnicode_Check(param_value
)) {
864 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
865 << " contains invalid param " << param_name
<< dendl
;
868 auto type
= PyUnicode_AsUTF8(param_value
);
869 auto it
= sub_key_types
.find(type
);
870 if (it
== sub_key_types
.end()) {
871 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
872 << " contains invalid type " << dendl
;
876 } else if (param_name
== NAME_SUB_KEY_REGEX
) {
877 if (!PyUnicode_Check(param_value
)) {
878 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
879 << " contains invalid param " << param_name
<< dendl
;
882 d
.regex_str
= PyUnicode_AsUTF8(param_value
);
884 d
.regex
= d
.regex_str
.c_str();
885 } catch (const std::regex_error
& e
) {
886 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
887 << " contains invalid regex " << d
.regex_str
<< dendl
;
890 if (d
.regex
.mark_count() == 0) {
891 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
892 << " regex " << d
.regex_str
<< ": no capturing groups"
897 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
898 << " contains invalid param " << param_name
<< dendl
;
902 if (d
.type
== static_cast<OSDPerfMetricSubKeyType
>(-1) ||
903 d
.regex_str
.empty()) {
904 derr
<< __func__
<< " query " << query_param_name
<< " item " << i
905 << " invalid" << dendl
;
908 query
.key_descriptor
.push_back(d
);
910 } else if (query_param_name
== NAME_COUNTERS_DESCRIPTORS
) {
911 if (!PyList_Check(query_param_val
)) {
912 derr
<< __func__
<< " " << query_param_name
<< " not a list" << dendl
;
915 for (int j
= 0; j
< PyList_Size(query_param_val
); j
++) {
916 PyObject
*py_type
= PyList_GET_ITEM(query_param_val
, j
);
917 if (!PyUnicode_Check(py_type
)) {
918 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
919 << " not a string" << dendl
;
922 auto type
= PyUnicode_AsUTF8(py_type
);
923 auto it
= counter_types
.find(type
);
924 if (it
== counter_types
.end()) {
925 derr
<< __func__
<< " query " << query_param_name
<< " item " << type
926 << " is not valid type" << dendl
;
929 query
.performance_counter_descriptors
.push_back(it
->second
);
931 } else if (query_param_name
== NAME_LIMIT
) {
932 if (!PyDict_Check(query_param_val
)) {
933 derr
<< __func__
<< " query " << query_param_name
<< " not a dict"
938 limit
= OSDPerfMetricLimit();
939 PyObject
*limit_params
= PyDict_Items(query_param_val
);
941 for (int j
= 0; j
< PyList_Size(limit_params
); ++j
) {
942 PyObject
*kv
= PyList_GET_ITEM(limit_params
, j
);
943 char *limit_param_name
= nullptr;
944 PyObject
*limit_param_val
= nullptr;
945 if (!PyArg_ParseTuple(kv
, "sO:pair", &limit_param_name
,
947 derr
<< __func__
<< " limit item " << j
<< " not a size 2 tuple"
952 if (limit_param_name
== NAME_LIMIT_ORDER_BY
) {
953 if (!PyUnicode_Check(limit_param_val
)) {
954 derr
<< __func__
<< " " << limit_param_name
<< " not a string"
958 auto order_by
= PyUnicode_AsUTF8(limit_param_val
);
959 auto it
= counter_types
.find(order_by
);
960 if (it
== counter_types
.end()) {
961 derr
<< __func__
<< " limit " << limit_param_name
962 << " not a valid counter type" << dendl
;
965 limit
->order_by
= it
->second
;
966 } else if (limit_param_name
== NAME_LIMIT_MAX_COUNT
) {
967 if (!PyLong_Check(limit_param_val
)) {
968 derr
<< __func__
<< " " << limit_param_name
<< " not an int"
972 limit
->max_count
= PyLong_AsLong(limit_param_val
);
974 derr
<< __func__
<< " unknown limit param: " << limit_param_name
980 derr
<< __func__
<< " unknown query param: " << query_param_name
<< dendl
;
985 if (query
.key_descriptor
.empty() ||
986 query
.performance_counter_descriptors
.empty()) {
987 derr
<< __func__
<< " invalid query" << dendl
;
992 auto &ds
= query
.performance_counter_descriptors
;
993 if (std::find(ds
.begin(), ds
.end(), limit
->order_by
) == ds
.end()) {
994 derr
<< __func__
<< " limit order_by " << limit
->order_by
995 << " not in performance_counter_descriptors" << dendl
;
1000 auto query_id
= self
->py_modules
->add_osd_perf_query(query
, limit
);
1001 return PyLong_FromLong(query_id
);
1005 ceph_remove_osd_perf_query(BaseMgrModule
*self
, PyObject
*args
)
1007 MetricQueryID query_id
;
1008 if (!PyArg_ParseTuple(args
, "i:ceph_remove_osd_perf_query", &query_id
)) {
1009 derr
<< "Invalid args!" << dendl
;
1013 self
->py_modules
->remove_osd_perf_query(query_id
);
1018 ceph_get_osd_perf_counters(BaseMgrModule
*self
, PyObject
*args
)
1020 MetricQueryID query_id
;
1021 if (!PyArg_ParseTuple(args
, "i:ceph_get_osd_perf_counters", &query_id
)) {
1022 derr
<< "Invalid args!" << dendl
;
1026 return self
->py_modules
->get_osd_perf_counters(query_id
);
1030 ceph_is_authorized(BaseMgrModule
*self
, PyObject
*args
)
1032 PyObject
*args_dict
= NULL
;
1033 if (!PyArg_ParseTuple(args
, "O:ceph_is_authorized", &args_dict
)) {
1037 if (!PyDict_Check(args_dict
)) {
1038 derr
<< __func__
<< " arg not a dict" << dendl
;
1042 std::map
<std::string
, std::string
> arguments
;
1044 PyObject
*args_list
= PyDict_Items(args_dict
);
1045 for (int i
= 0; i
< PyList_Size(args_list
); ++i
) {
1046 PyObject
*kv
= PyList_GET_ITEM(args_list
, i
);
1048 char *arg_key
= nullptr;
1049 char *arg_value
= nullptr;
1050 if (!PyArg_ParseTuple(kv
, "ss:pair", &arg_key
, &arg_value
)) {
1051 derr
<< __func__
<< " dict item " << i
<< " not a size 2 tuple" << dendl
;
1055 arguments
[arg_key
] = arg_value
;
1058 PyThreadState
*tstate
= PyEval_SaveThread();
1059 bool r
= self
->this_module
->is_authorized(arguments
);
1060 PyEval_RestoreThread(tstate
);
1069 ceph_register_client(BaseMgrModule
*self
, PyObject
*args
)
1071 char *addrs
= nullptr;
1072 if (!PyArg_ParseTuple(args
, "s:ceph_register_client", &addrs
)) {
1075 PyThreadState
*tstate
= PyEval_SaveThread();
1076 self
->py_modules
->register_client(self
->this_module
->get_name(), addrs
);
1077 PyEval_RestoreThread(tstate
);
1082 ceph_unregister_client(BaseMgrModule
*self
, PyObject
*args
)
1084 char *addrs
= nullptr;
1085 if (!PyArg_ParseTuple(args
, "s:ceph_unregister_client", &addrs
)) {
1088 PyThreadState
*tstate
= PyEval_SaveThread();
1089 self
->py_modules
->unregister_client(self
->this_module
->get_name(), addrs
);
1090 PyEval_RestoreThread(tstate
);
1094 PyMethodDef BaseMgrModule_methods
[] = {
1095 {"_ceph_get", (PyCFunction
)ceph_state_get
, METH_VARARGS
,
1096 "Get a cluster object"},
1098 {"_ceph_get_server", (PyCFunction
)ceph_get_server
, METH_VARARGS
,
1099 "Get a server object"},
1101 {"_ceph_get_metadata", (PyCFunction
)get_metadata
, METH_VARARGS
,
1102 "Get a service's metadata"},
1104 {"_ceph_get_daemon_status", (PyCFunction
)get_daemon_status
, METH_VARARGS
,
1105 "Get a service's status"},
1107 {"_ceph_send_command", (PyCFunction
)ceph_send_command
, METH_VARARGS
,
1108 "Send a mon command"},
1110 {"_ceph_set_health_checks", (PyCFunction
)ceph_set_health_checks
, METH_VARARGS
,
1111 "Set health checks for this module"},
1113 {"_ceph_get_mgr_id", (PyCFunction
)ceph_get_mgr_id
, METH_NOARGS
,
1114 "Get the name of the Mgr daemon where we are running"},
1116 {"_ceph_get_option", (PyCFunction
)ceph_option_get
, METH_VARARGS
,
1117 "Get a native configuration option value"},
1119 {"_ceph_get_module_option", (PyCFunction
)ceph_get_module_option
, METH_VARARGS
,
1120 "Get a module configuration option value"},
1122 {"_ceph_get_store_prefix", (PyCFunction
)ceph_store_get_prefix
, METH_VARARGS
,
1123 "Get all KV store values with a given prefix"},
1125 {"_ceph_set_module_option", (PyCFunction
)ceph_set_module_option
, METH_VARARGS
,
1126 "Set a module configuration option value"},
1128 {"_ceph_get_store", (PyCFunction
)ceph_store_get
, METH_VARARGS
,
1129 "Get a stored field"},
1131 {"_ceph_set_store", (PyCFunction
)ceph_store_set
, METH_VARARGS
,
1132 "Set a stored field"},
1134 {"_ceph_get_counter", (PyCFunction
)get_counter
, METH_VARARGS
,
1135 "Get a performance counter"},
1137 {"_ceph_get_latest_counter", (PyCFunction
)get_latest_counter
, METH_VARARGS
,
1138 "Get the latest performance counter"},
1140 {"_ceph_get_perf_schema", (PyCFunction
)get_perf_schema
, METH_VARARGS
,
1141 "Get the performance counter schema"},
1143 {"_ceph_log", (PyCFunction
)ceph_log
, METH_VARARGS
,
1144 "Emit a (local) log message"},
1146 {"_ceph_cluster_log", (PyCFunction
)ceph_cluster_log
, METH_VARARGS
,
1147 "Emit a cluster log message"},
1149 {"_ceph_get_version", (PyCFunction
)ceph_get_version
, METH_NOARGS
,
1150 "Get the ceph version of this process"},
1152 {"_ceph_get_release_name", (PyCFunction
)ceph_get_release_name
, METH_NOARGS
,
1153 "Get the ceph release name of this process"},
1155 {"_ceph_get_context", (PyCFunction
)ceph_get_context
, METH_NOARGS
,
1156 "Get a CephContext* in a python capsule"},
1158 {"_ceph_get_osdmap", (PyCFunction
)ceph_get_osdmap
, METH_NOARGS
,
1159 "Get an OSDMap* in a python capsule"},
1161 {"_ceph_set_uri", (PyCFunction
)ceph_set_uri
, METH_VARARGS
,
1162 "Advertize a service URI served by this module"},
1164 {"_ceph_have_mon_connection", (PyCFunction
)ceph_have_mon_connection
,
1165 METH_NOARGS
, "Find out whether this mgr daemon currently has "
1166 "a connection to a monitor"},
1168 {"_ceph_update_progress_event", (PyCFunction
)ceph_update_progress_event
,
1169 METH_VARARGS
, "Update status of a progress event"},
1170 {"_ceph_complete_progress_event", (PyCFunction
)ceph_complete_progress_event
,
1171 METH_VARARGS
, "Complete a progress event"},
1172 {"_ceph_clear_all_progress_events", (PyCFunction
)ceph_clear_all_progress_events
,
1173 METH_NOARGS
, "Clear all progress events"},
1175 {"_ceph_dispatch_remote", (PyCFunction
)ceph_dispatch_remote
,
1176 METH_VARARGS
, "Dispatch a call to another module"},
1178 {"_ceph_add_osd_perf_query", (PyCFunction
)ceph_add_osd_perf_query
,
1179 METH_VARARGS
, "Add an osd perf query"},
1181 {"_ceph_remove_osd_perf_query", (PyCFunction
)ceph_remove_osd_perf_query
,
1182 METH_VARARGS
, "Remove an osd perf query"},
1184 {"_ceph_get_osd_perf_counters", (PyCFunction
)ceph_get_osd_perf_counters
,
1185 METH_VARARGS
, "Get osd perf counters"},
1187 {"_ceph_is_authorized", (PyCFunction
)ceph_is_authorized
,
1188 METH_VARARGS
, "Verify the current session caps are valid"},
1190 {"_ceph_register_client", (PyCFunction
)ceph_register_client
,
1191 METH_VARARGS
, "Register RADOS instance for potential blacklisting"},
1193 {"_ceph_unregister_client", (PyCFunction
)ceph_unregister_client
,
1194 METH_VARARGS
, "Unregister RADOS instance for potential blacklisting"},
1196 {NULL
, NULL
, 0, NULL
}
1201 BaseMgrModule_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
)
1203 BaseMgrModule
*self
;
1205 self
= (BaseMgrModule
*)type
->tp_alloc(type
, 0);
1207 return (PyObject
*)self
;
1211 BaseMgrModule_init(BaseMgrModule
*self
, PyObject
*args
, PyObject
*kwds
)
1213 PyObject
*py_modules_capsule
= nullptr;
1214 PyObject
*this_module_capsule
= nullptr;
1215 static const char *kwlist
[] = {"py_modules", "this_module", NULL
};
1217 if (! PyArg_ParseTupleAndKeywords(args
, kwds
, "OO",
1218 const_cast<char**>(kwlist
),
1219 &py_modules_capsule
,
1220 &this_module_capsule
)) {
1224 self
->py_modules
= static_cast<ActivePyModules
*>(PyCapsule_GetPointer(
1225 py_modules_capsule
, nullptr));
1226 ceph_assert(self
->py_modules
);
1227 self
->this_module
= static_cast<ActivePyModule
*>(PyCapsule_GetPointer(
1228 this_module_capsule
, nullptr));
1229 ceph_assert(self
->this_module
);
1234 PyTypeObject BaseMgrModuleType
= {
1235 PyVarObject_HEAD_INIT(NULL
, 0)
1236 "ceph_module.BaseMgrModule", /* tp_name */
1237 sizeof(BaseMgrModule
), /* tp_basicsize */
1238 0, /* tp_itemsize */
1245 0, /* tp_as_number */
1246 0, /* tp_as_sequence */
1247 0, /* tp_as_mapping */
1251 0, /* tp_getattro */
1252 0, /* tp_setattro */
1253 0, /* tp_as_buffer */
1254 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
, /* tp_flags */
1255 "ceph-mgr Python Plugin", /* tp_doc */
1256 0, /* tp_traverse */
1258 0, /* tp_richcompare */
1259 0, /* tp_weaklistoffset */
1261 0, /* tp_iternext */
1262 BaseMgrModule_methods
, /* tp_methods */
1267 0, /* tp_descr_get */
1268 0, /* tp_descr_set */
1269 0, /* tp_dictoffset */
1270 (initproc
)BaseMgrModule_init
, /* tp_init */
1272 BaseMgrModule_new
, /* tp_new */