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 PyObject
*completion
= nullptr;
129 if (!PyArg_ParseTuple(args
, "Ossss:ceph_send_command",
130 &completion
, &type
, &name
, &cmd_json
, &tag
)) {
134 auto set_fn
= PyObject_GetAttrString(completion
, "complete");
135 if (set_fn
== nullptr) {
136 ceph_abort(); // TODO raise python exception instead
138 ceph_assert(PyCallable_Check(set_fn
));
142 MonCommandCompletion
*command_c
= new MonCommandCompletion(self
->py_modules
,
143 completion
, tag
, PyThreadState_Get());
145 PyThreadState
*tstate
= PyEval_SaveThread();
147 if (std::string(type
) == "mon") {
149 // Wait for the latest OSDMap after each command we send to
150 // the mons. This is a heavy-handed hack to make life simpler
151 // for python module authors, so that they know whenever they
152 // run a command they've gt a fresh OSDMap afterwards.
153 // TODO: enhance MCommand interface so that it returns
154 // latest cluster map versions on completion, and callers
155 // can wait for those.
156 auto c
= new LambdaContext([command_c
, self
](int command_r
){
157 self
->py_modules
->get_objecter().wait_for_latest_osdmap(
158 new LambdaContext([command_c
, command_r
](int wait_r
){
159 command_c
->complete(command_r
);
164 self
->py_modules
->get_monc().start_mon_command(
170 new C_OnFinisher(c
, &self
->py_modules
->cmd_finisher
));
171 } else if (std::string(type
) == "osd") {
173 uint64_t osd_id
= strict_strtoll(name
, 10, &err
);
176 string
msg("invalid osd_id: ");
177 msg
.append("\"").append(name
).append("\"");
178 PyEval_RestoreThread(tstate
);
179 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
184 self
->py_modules
->get_objecter().osd_command(
191 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
192 } else if (std::string(type
) == "mds") {
193 int r
= self
->py_modules
->get_client().mds_command(
199 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
201 string
msg("failed to send command to mds: ");
202 msg
.append(cpp_strerror(r
));
203 PyEval_RestoreThread(tstate
);
204 PyErr_SetString(PyExc_RuntimeError
, msg
.c_str());
207 } else if (std::string(type
) == "pg") {
209 if (!pgid
.parse(name
)) {
211 string
msg("invalid pgid: ");
212 msg
.append("\"").append(name
).append("\"");
213 PyEval_RestoreThread(tstate
);
214 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
219 self
->py_modules
->get_objecter().pg_command(
226 new C_OnFinisher(command_c
, &self
->py_modules
->cmd_finisher
));
227 PyEval_RestoreThread(tstate
);
231 string
msg("unknown service type: ");
233 PyEval_RestoreThread(tstate
);
234 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
238 PyEval_RestoreThread(tstate
);
243 ceph_set_health_checks(BaseMgrModule
*self
, PyObject
*args
)
245 PyObject
*checks
= NULL
;
246 if (!PyArg_ParseTuple(args
, "O:ceph_set_health_checks", &checks
)) {
249 if (!PyDict_Check(checks
)) {
250 derr
<< __func__
<< " arg not a dict" << dendl
;
253 PyObject
*checksls
= PyDict_Items(checks
);
254 health_check_map_t out_checks
;
255 for (int i
= 0; i
< PyList_Size(checksls
); ++i
) {
256 PyObject
*kv
= PyList_GET_ITEM(checksls
, i
);
257 char *check_name
= nullptr;
258 PyObject
*check_info
= nullptr;
259 if (!PyArg_ParseTuple(kv
, "sO:pair", &check_name
, &check_info
)) {
260 derr
<< __func__
<< " dict item " << i
261 << " not a size 2 tuple" << dendl
;
264 if (!PyDict_Check(check_info
)) {
265 derr
<< __func__
<< " item " << i
<< " " << check_name
266 << " value not a dict" << dendl
;
269 health_status_t severity
= HEALTH_OK
;
273 PyObject
*infols
= PyDict_Items(check_info
);
274 for (int j
= 0; j
< PyList_Size(infols
); ++j
) {
275 PyObject
*pair
= PyList_GET_ITEM(infols
, j
);
276 if (!PyTuple_Check(pair
)) {
277 derr
<< __func__
<< " item " << i
<< " pair " << j
278 << " not a tuple" << dendl
;
282 PyObject
*v
= nullptr;
283 if (!PyArg_ParseTuple(pair
, "sO:pair", &k
, &v
)) {
284 derr
<< __func__
<< " item " << i
<< " pair " << j
285 << " not a size 2 tuple" << dendl
;
289 if (ks
== "severity") {
290 if (!PyUnicode_Check(v
)) {
291 derr
<< __func__
<< " check " << check_name
292 << " severity value not string" << dendl
;
295 if (const string vs
= PyUnicode_AsUTF8(v
); vs
== "warning") {
296 severity
= HEALTH_WARN
;
297 } else if (vs
== "error") {
298 severity
= HEALTH_ERR
;
300 } else if (ks
== "summary") {
301 if (!PyUnicode_Check(v
)) {
302 derr
<< __func__
<< " check " << check_name
303 << " summary value not [unicode] string" << dendl
;
306 summary
= PyUnicode_AsUTF8(v
);
308 } else if (ks
== "count") {
309 if (PyLong_Check(v
)) {
310 count
= PyLong_AsLong(v
);
312 derr
<< __func__
<< " check " << check_name
313 << " count value not int" << dendl
;
316 } else if (ks
== "detail") {
317 if (!PyList_Check(v
)) {
318 derr
<< __func__
<< " check " << check_name
319 << " detail value not list" << dendl
;
322 for (int k
= 0; k
< PyList_Size(v
); ++k
) {
323 PyObject
*di
= PyList_GET_ITEM(v
, k
);
324 if (!PyUnicode_Check(di
)) {
325 derr
<< __func__
<< " check " << check_name
326 << " detail item " << k
<< " not a [unicode] string" << dendl
;
329 detail
.push_back(PyUnicode_AsUTF8(di
));
333 derr
<< __func__
<< " check " << check_name
334 << " unexpected key " << k
<< dendl
;
337 auto& d
= out_checks
.add(check_name
, severity
, summary
, count
);
338 d
.detail
.swap(detail
);
341 JSONFormatter
jf(true);
342 dout(10) << "module " << self
->this_module
->get_name()
343 << " health checks:\n";
344 out_checks
.dump(&jf
);
348 PyThreadState
*tstate
= PyEval_SaveThread();
349 self
->py_modules
->set_health_checks(self
->this_module
->get_name(),
350 std::move(out_checks
));
351 PyEval_RestoreThread(tstate
);
358 ceph_state_get(BaseMgrModule
*self
, PyObject
*args
)
361 if (!PyArg_ParseTuple(args
, "s:ceph_state_get", &what
)) {
365 return self
->py_modules
->get_python(what
);
370 ceph_get_server(BaseMgrModule
*self
, PyObject
*args
)
372 char *hostname
= NULL
;
373 if (!PyArg_ParseTuple(args
, "z:ceph_get_server", &hostname
)) {
378 return self
->py_modules
->get_server_python(hostname
);
380 return self
->py_modules
->list_servers_python();
385 ceph_get_mgr_id(BaseMgrModule
*self
, PyObject
*args
)
387 return PyUnicode_FromString(g_conf()->name
.get_id().c_str());
391 ceph_option_get(BaseMgrModule
*self
, PyObject
*args
)
393 char *what
= nullptr;
394 if (!PyArg_ParseTuple(args
, "s:ceph_option_get", &what
)) {
395 derr
<< "Invalid args!" << dendl
;
399 const Option
*opt
= g_conf().find_option(string(what
));
402 switch (int r
= g_conf().get_val(string(what
), &value
); r
) {
407 PyErr_SetString(PyExc_ValueError
, "value too long");
413 dout(10) << "ceph_option_get " << what
<< " found: " << value
<< dendl
;
414 return get_python_typed_option_value(opt
->type
, value
);
416 dout(4) << "ceph_option_get " << what
<< " not found " << dendl
;
417 PyErr_Format(PyExc_KeyError
, "option not found: %s", what
);
423 ceph_get_module_option(BaseMgrModule
*self
, PyObject
*args
)
425 char *module
= nullptr;
427 char *prefix
= nullptr;
428 if (!PyArg_ParseTuple(args
, "ss|s:ceph_get_module_option", &module
, &key
,
430 derr
<< "Invalid args!" << dendl
;
433 std::string str_prefix
;
437 assert(self
->this_module
->py_module
);
438 auto pResult
= self
->py_modules
->get_typed_config(module
, key
, str_prefix
);
443 ceph_store_get_prefix(BaseMgrModule
*self
, PyObject
*args
)
445 char *prefix
= nullptr;
446 if (!PyArg_ParseTuple(args
, "s:ceph_store_get_prefix", &prefix
)) {
447 derr
<< "Invalid args!" << dendl
;
451 return self
->py_modules
->get_store_prefix(self
->this_module
->get_name(),
456 ceph_set_module_option(BaseMgrModule
*self
, PyObject
*args
)
458 char *module
= nullptr;
460 char *value
= nullptr;
461 if (!PyArg_ParseTuple(args
, "ssz:ceph_set_module_option",
462 &module
, &key
, &value
)) {
463 derr
<< "Invalid args!" << dendl
;
466 boost::optional
<string
> val
;
470 PyThreadState
*tstate
= PyEval_SaveThread();
471 self
->py_modules
->set_config(module
, key
, val
);
472 PyEval_RestoreThread(tstate
);
478 ceph_store_get(BaseMgrModule
*self
, PyObject
*args
)
480 char *what
= nullptr;
481 if (!PyArg_ParseTuple(args
, "s:ceph_store_get", &what
)) {
482 derr
<< "Invalid args!" << dendl
;
487 bool found
= self
->py_modules
->get_store(self
->this_module
->get_name(),
490 dout(10) << "ceph_store_get " << what
<< " found: " << value
.c_str() << dendl
;
491 return PyUnicode_FromString(value
.c_str());
493 dout(4) << "ceph_store_get " << what
<< " not found " << dendl
;
499 ceph_store_set(BaseMgrModule
*self
, PyObject
*args
)
502 char *value
= nullptr;
503 if (!PyArg_ParseTuple(args
, "sz:ceph_store_set", &key
, &value
)) {
506 boost::optional
<string
> val
;
510 PyThreadState
*tstate
= PyEval_SaveThread();
511 self
->py_modules
->set_store(self
->this_module
->get_name(), key
, val
);
512 PyEval_RestoreThread(tstate
);
518 get_metadata(BaseMgrModule
*self
, PyObject
*args
)
520 char *svc_name
= NULL
;
522 if (!PyArg_ParseTuple(args
, "ss:get_metadata", &svc_name
, &svc_id
)) {
525 return self
->py_modules
->get_metadata_python(svc_name
, svc_id
);
529 get_daemon_status(BaseMgrModule
*self
, PyObject
*args
)
531 char *svc_name
= NULL
;
533 if (!PyArg_ParseTuple(args
, "ss:get_daemon_status", &svc_name
,
537 return self
->py_modules
->get_daemon_status_python(svc_name
, svc_id
);
541 ceph_log(BaseMgrModule
*self
, PyObject
*args
)
543 char *record
= nullptr;
544 if (!PyArg_ParseTuple(args
, "s:log", &record
)) {
548 ceph_assert(self
->this_module
);
550 self
->this_module
->log(record
);
556 ceph_cluster_log(BaseMgrModule
*self
, PyObject
*args
)
559 char *channel
= nullptr;
560 char *message
= nullptr;
562 if (!PyArg_ParseTuple(args
, "sis:ceph_cluster_log", &channel
, &prio
, &message
)) {
566 PyThreadState
*tstate
= PyEval_SaveThread();
567 self
->py_modules
->cluster_log(channel
, (clog_type
)prio
, message
);
568 PyEval_RestoreThread(tstate
);
574 ceph_get_version(BaseMgrModule
*self
, PyObject
*args
)
576 return PyUnicode_FromString(pretty_version_to_str().c_str());
580 ceph_get_release_name(BaseMgrModule
*self
, PyObject
*args
)
582 return PyUnicode_FromString(ceph_release_to_str());
586 ceph_get_context(BaseMgrModule
*self
)
588 return self
->py_modules
->get_context();
592 get_counter(BaseMgrModule
*self
, PyObject
*args
)
594 char *svc_name
= nullptr;
595 char *svc_id
= nullptr;
596 char *counter_path
= nullptr;
597 if (!PyArg_ParseTuple(args
, "sss:get_counter", &svc_name
,
598 &svc_id
, &counter_path
)) {
601 return self
->py_modules
->get_counter_python(
602 svc_name
, svc_id
, counter_path
);
606 get_latest_counter(BaseMgrModule
*self
, PyObject
*args
)
608 char *svc_name
= nullptr;
609 char *svc_id
= nullptr;
610 char *counter_path
= nullptr;
611 if (!PyArg_ParseTuple(args
, "sss:get_counter", &svc_name
,
612 &svc_id
, &counter_path
)) {
615 return self
->py_modules
->get_latest_counter_python(
616 svc_name
, svc_id
, counter_path
);
620 get_perf_schema(BaseMgrModule
*self
, PyObject
*args
)
622 char *type_str
= nullptr;
623 char *svc_id
= nullptr;
624 if (!PyArg_ParseTuple(args
, "ss:get_perf_schema", &type_str
,
629 return self
->py_modules
->get_perf_schema_python(type_str
, svc_id
);
633 ceph_get_osdmap(BaseMgrModule
*self
, PyObject
*args
)
635 return self
->py_modules
->get_osdmap();
639 ceph_set_uri(BaseMgrModule
*self
, PyObject
*args
)
641 char *svc_str
= nullptr;
642 if (!PyArg_ParseTuple(args
, "s:ceph_advertize_service",
647 // We call down into PyModules even though we have a MgrPyModule
648 // reference here, because MgrPyModule's fields are protected
649 // by PyModules' lock.
650 PyThreadState
*tstate
= PyEval_SaveThread();
651 self
->py_modules
->set_uri(self
->this_module
->get_name(), svc_str
);
652 PyEval_RestoreThread(tstate
);
658 ceph_have_mon_connection(BaseMgrModule
*self
, PyObject
*args
)
660 if (self
->py_modules
->get_monc().is_connected()) {
668 ceph_update_progress_event(BaseMgrModule
*self
, PyObject
*args
)
670 char *evid
= nullptr;
671 char *desc
= nullptr;
672 float progress
= 0.0;
673 if (!PyArg_ParseTuple(args
, "ssf:ceph_update_progress_event",
674 &evid
, &desc
, &progress
)) {
678 PyThreadState
*tstate
= PyEval_SaveThread();
679 self
->py_modules
->update_progress_event(evid
, desc
, progress
);
680 PyEval_RestoreThread(tstate
);
686 ceph_complete_progress_event(BaseMgrModule
*self
, PyObject
*args
)
688 char *evid
= nullptr;
689 if (!PyArg_ParseTuple(args
, "s:ceph_complete_progress_event",
694 PyThreadState
*tstate
= PyEval_SaveThread();
695 self
->py_modules
->complete_progress_event(evid
);
696 PyEval_RestoreThread(tstate
);
702 ceph_clear_all_progress_events(BaseMgrModule
*self
, PyObject
*args
)
704 PyThreadState
*tstate
= PyEval_SaveThread();
705 self
->py_modules
->clear_all_progress_events();
706 PyEval_RestoreThread(tstate
);
714 ceph_dispatch_remote(BaseMgrModule
*self
, PyObject
*args
)
716 char *other_module
= nullptr;
717 char *method
= nullptr;
718 PyObject
*remote_args
= nullptr;
719 PyObject
*remote_kwargs
= nullptr;
720 if (!PyArg_ParseTuple(args
, "ssOO:ceph_dispatch_remote",
721 &other_module
, &method
, &remote_args
, &remote_kwargs
)) {
725 // Early error handling, because if the module doesn't exist then we
726 // won't be able to use its thread state to set python error state
727 // inside dispatch_remote().
728 if (!self
->py_modules
->module_exists(other_module
)) {
729 derr
<< "no module '" << other_module
<< "'" << dendl
;
730 PyErr_SetString(PyExc_ImportError
, "Module not found");
734 // Drop GIL from calling python thread state, it will be taken
735 // both for checking for method existence and for executing method.
736 PyThreadState
*tstate
= PyEval_SaveThread();
738 if (!self
->py_modules
->method_exists(other_module
, method
)) {
739 PyEval_RestoreThread(tstate
);
740 PyErr_SetString(PyExc_NameError
, "Method not found");
745 auto result
= self
->py_modules
->dispatch_remote(other_module
, method
,
746 remote_args
, remote_kwargs
, &err
);
748 PyEval_RestoreThread(tstate
);
750 if (result
== nullptr) {
751 std::stringstream ss
;
752 ss
<< "Remote method threw exception: " << err
;
753 PyErr_SetString(PyExc_RuntimeError
, ss
.str().c_str());
754 derr
<< ss
.str() << dendl
;
761 ceph_add_osd_perf_query(BaseMgrModule
*self
, PyObject
*args
)
763 static const std::string NAME_KEY_DESCRIPTOR
= "key_descriptor";
764 static const std::string NAME_COUNTERS_DESCRIPTORS
=
765 "performance_counter_descriptors";
766 static const std::string NAME_LIMIT
= "limit";
767 static const std::string NAME_SUB_KEY_TYPE
= "type";
768 static const std::string NAME_SUB_KEY_REGEX
= "regex";
769 static const std::string NAME_LIMIT_ORDER_BY
= "order_by";
770 static const std::string NAME_LIMIT_MAX_COUNT
= "max_count";
771 static const std::map
<std::string
, OSDPerfMetricSubKeyType
> sub_key_types
= {
772 {"client_id", OSDPerfMetricSubKeyType::CLIENT_ID
},
773 {"client_address", OSDPerfMetricSubKeyType::CLIENT_ADDRESS
},
774 {"pool_id", OSDPerfMetricSubKeyType::POOL_ID
},
775 {"namespace", OSDPerfMetricSubKeyType::NAMESPACE
},
776 {"osd_id", OSDPerfMetricSubKeyType::OSD_ID
},
777 {"pg_id", OSDPerfMetricSubKeyType::PG_ID
},
778 {"object_name", OSDPerfMetricSubKeyType::OBJECT_NAME
},
779 {"snap_id", OSDPerfMetricSubKeyType::SNAP_ID
},
781 static const std::map
<std::string
, PerformanceCounterType
> counter_types
= {
782 {"ops", PerformanceCounterType::OPS
},
783 {"write_ops", PerformanceCounterType::WRITE_OPS
},
784 {"read_ops", PerformanceCounterType::READ_OPS
},
785 {"bytes", PerformanceCounterType::BYTES
},
786 {"write_bytes", PerformanceCounterType::WRITE_BYTES
},
787 {"read_bytes", PerformanceCounterType::READ_BYTES
},
788 {"latency", PerformanceCounterType::LATENCY
},
789 {"write_latency", PerformanceCounterType::WRITE_LATENCY
},
790 {"read_latency", PerformanceCounterType::READ_LATENCY
},
793 PyObject
*py_query
= nullptr;
794 if (!PyArg_ParseTuple(args
, "O:ceph_add_osd_perf_query", &py_query
)) {
795 derr
<< "Invalid args!" << dendl
;
798 if (!PyDict_Check(py_query
)) {
799 derr
<< __func__
<< " arg not a dict" << dendl
;
803 PyObject
*query_params
= PyDict_Items(py_query
);
804 OSDPerfMetricQuery query
;
805 std::optional
<OSDPerfMetricLimit
> limit
;
808 // 'key_descriptor': [
809 // {'type': subkey_type, 'regex': regex_pattern},
812 // 'performance_counter_descriptors': [
813 // list, of, descriptor, types
815 // 'limit': {'order_by': performance_counter_type, 'max_count': n},
818 for (int i
= 0; i
< PyList_Size(query_params
); ++i
) {
819 PyObject
*kv
= PyList_GET_ITEM(query_params
, i
);
820 char *query_param_name
= nullptr;
821 PyObject
*query_param_val
= nullptr;
822 if (!PyArg_ParseTuple(kv
, "sO:pair", &query_param_name
, &query_param_val
)) {
823 derr
<< __func__
<< " dict item " << i
<< " not a size 2 tuple" << dendl
;
826 if (query_param_name
== NAME_KEY_DESCRIPTOR
) {
827 if (!PyList_Check(query_param_val
)) {
828 derr
<< __func__
<< " " << query_param_name
<< " not a list" << dendl
;
831 for (int j
= 0; j
< PyList_Size(query_param_val
); j
++) {
832 PyObject
*sub_key
= PyList_GET_ITEM(query_param_val
, j
);
833 if (!PyDict_Check(sub_key
)) {
834 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
835 << " not a dict" << dendl
;
838 OSDPerfMetricSubKeyDescriptor d
;
839 PyObject
*sub_key_params
= PyDict_Items(sub_key
);
840 for (int k
= 0; k
< PyList_Size(sub_key_params
); ++k
) {
841 PyObject
*pair
= PyList_GET_ITEM(sub_key_params
, k
);
842 if (!PyTuple_Check(pair
)) {
843 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
844 << " pair " << k
<< " not a tuple" << dendl
;
847 char *param_name
= nullptr;
848 PyObject
*param_value
= nullptr;
849 if (!PyArg_ParseTuple(pair
, "sO:pair", ¶m_name
, ¶m_value
)) {
850 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
851 << " pair " << k
<< " not a size 2 tuple" << dendl
;
854 if (param_name
== NAME_SUB_KEY_TYPE
) {
855 if (!PyUnicode_Check(param_value
)) {
856 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
857 << " contains invalid param " << param_name
<< dendl
;
860 auto type
= PyUnicode_AsUTF8(param_value
);
861 auto it
= sub_key_types
.find(type
);
862 if (it
== sub_key_types
.end()) {
863 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
864 << " contains invalid type " << dendl
;
868 } else if (param_name
== NAME_SUB_KEY_REGEX
) {
869 if (!PyUnicode_Check(param_value
)) {
870 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
871 << " contains invalid param " << param_name
<< dendl
;
874 d
.regex_str
= PyUnicode_AsUTF8(param_value
);
876 d
.regex
= d
.regex_str
.c_str();
877 } catch (const std::regex_error
& e
) {
878 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
879 << " contains invalid regex " << d
.regex_str
<< dendl
;
882 if (d
.regex
.mark_count() == 0) {
883 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
884 << " regex " << d
.regex_str
<< ": no capturing groups"
889 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
890 << " contains invalid param " << param_name
<< dendl
;
894 if (d
.type
== static_cast<OSDPerfMetricSubKeyType
>(-1) ||
895 d
.regex_str
.empty()) {
896 derr
<< __func__
<< " query " << query_param_name
<< " item " << i
897 << " invalid" << dendl
;
900 query
.key_descriptor
.push_back(d
);
902 } else if (query_param_name
== NAME_COUNTERS_DESCRIPTORS
) {
903 if (!PyList_Check(query_param_val
)) {
904 derr
<< __func__
<< " " << query_param_name
<< " not a list" << dendl
;
907 for (int j
= 0; j
< PyList_Size(query_param_val
); j
++) {
908 PyObject
*py_type
= PyList_GET_ITEM(query_param_val
, j
);
909 if (!PyUnicode_Check(py_type
)) {
910 derr
<< __func__
<< " query " << query_param_name
<< " item " << j
911 << " not a string" << dendl
;
914 auto type
= PyUnicode_AsUTF8(py_type
);
915 auto it
= counter_types
.find(type
);
916 if (it
== counter_types
.end()) {
917 derr
<< __func__
<< " query " << query_param_name
<< " item " << type
918 << " is not valid type" << dendl
;
921 query
.performance_counter_descriptors
.push_back(it
->second
);
923 } else if (query_param_name
== NAME_LIMIT
) {
924 if (!PyDict_Check(query_param_val
)) {
925 derr
<< __func__
<< " query " << query_param_name
<< " not a dict"
930 limit
= OSDPerfMetricLimit();
931 PyObject
*limit_params
= PyDict_Items(query_param_val
);
933 for (int j
= 0; j
< PyList_Size(limit_params
); ++j
) {
934 PyObject
*kv
= PyList_GET_ITEM(limit_params
, j
);
935 char *limit_param_name
= nullptr;
936 PyObject
*limit_param_val
= nullptr;
937 if (!PyArg_ParseTuple(kv
, "sO:pair", &limit_param_name
,
939 derr
<< __func__
<< " limit item " << j
<< " not a size 2 tuple"
944 if (limit_param_name
== NAME_LIMIT_ORDER_BY
) {
945 if (!PyUnicode_Check(limit_param_val
)) {
946 derr
<< __func__
<< " " << limit_param_name
<< " not a string"
950 auto order_by
= PyUnicode_AsUTF8(limit_param_val
);
951 auto it
= counter_types
.find(order_by
);
952 if (it
== counter_types
.end()) {
953 derr
<< __func__
<< " limit " << limit_param_name
954 << " not a valid counter type" << dendl
;
957 limit
->order_by
= it
->second
;
958 } else if (limit_param_name
== NAME_LIMIT_MAX_COUNT
) {
959 if (!PyLong_Check(limit_param_val
)) {
960 derr
<< __func__
<< " " << limit_param_name
<< " not an int"
964 limit
->max_count
= PyLong_AsLong(limit_param_val
);
966 derr
<< __func__
<< " unknown limit param: " << limit_param_name
972 derr
<< __func__
<< " unknown query param: " << query_param_name
<< dendl
;
977 if (query
.key_descriptor
.empty() ||
978 query
.performance_counter_descriptors
.empty()) {
979 derr
<< __func__
<< " invalid query" << dendl
;
984 auto &ds
= query
.performance_counter_descriptors
;
985 if (std::find(ds
.begin(), ds
.end(), limit
->order_by
) == ds
.end()) {
986 derr
<< __func__
<< " limit order_by " << limit
->order_by
987 << " not in performance_counter_descriptors" << dendl
;
992 auto query_id
= self
->py_modules
->add_osd_perf_query(query
, limit
);
993 return PyLong_FromLong(query_id
);
997 ceph_remove_osd_perf_query(BaseMgrModule
*self
, PyObject
*args
)
999 MetricQueryID query_id
;
1000 if (!PyArg_ParseTuple(args
, "i:ceph_remove_osd_perf_query", &query_id
)) {
1001 derr
<< "Invalid args!" << dendl
;
1005 self
->py_modules
->remove_osd_perf_query(query_id
);
1010 ceph_get_osd_perf_counters(BaseMgrModule
*self
, PyObject
*args
)
1012 MetricQueryID query_id
;
1013 if (!PyArg_ParseTuple(args
, "i:ceph_get_osd_perf_counters", &query_id
)) {
1014 derr
<< "Invalid args!" << dendl
;
1018 return self
->py_modules
->get_osd_perf_counters(query_id
);
1022 ceph_is_authorized(BaseMgrModule
*self
, PyObject
*args
)
1024 PyObject
*args_dict
= NULL
;
1025 if (!PyArg_ParseTuple(args
, "O:ceph_is_authorized", &args_dict
)) {
1029 if (!PyDict_Check(args_dict
)) {
1030 derr
<< __func__
<< " arg not a dict" << dendl
;
1034 std::map
<std::string
, std::string
> arguments
;
1036 PyObject
*args_list
= PyDict_Items(args_dict
);
1037 for (int i
= 0; i
< PyList_Size(args_list
); ++i
) {
1038 PyObject
*kv
= PyList_GET_ITEM(args_list
, i
);
1040 char *arg_key
= nullptr;
1041 char *arg_value
= nullptr;
1042 if (!PyArg_ParseTuple(kv
, "ss:pair", &arg_key
, &arg_value
)) {
1043 derr
<< __func__
<< " dict item " << i
<< " not a size 2 tuple" << dendl
;
1047 arguments
[arg_key
] = arg_value
;
1050 PyThreadState
*tstate
= PyEval_SaveThread();
1051 bool r
= self
->this_module
->is_authorized(arguments
);
1052 PyEval_RestoreThread(tstate
);
1061 ceph_register_client(BaseMgrModule
*self
, PyObject
*args
)
1063 char *addrs
= nullptr;
1064 if (!PyArg_ParseTuple(args
, "s:ceph_register_client", &addrs
)) {
1067 PyThreadState
*tstate
= PyEval_SaveThread();
1068 self
->py_modules
->register_client(self
->this_module
->get_name(), addrs
);
1069 PyEval_RestoreThread(tstate
);
1074 ceph_unregister_client(BaseMgrModule
*self
, PyObject
*args
)
1076 char *addrs
= nullptr;
1077 if (!PyArg_ParseTuple(args
, "s:ceph_unregister_client", &addrs
)) {
1080 PyThreadState
*tstate
= PyEval_SaveThread();
1081 self
->py_modules
->unregister_client(self
->this_module
->get_name(), addrs
);
1082 PyEval_RestoreThread(tstate
);
1086 PyMethodDef BaseMgrModule_methods
[] = {
1087 {"_ceph_get", (PyCFunction
)ceph_state_get
, METH_VARARGS
,
1088 "Get a cluster object"},
1090 {"_ceph_get_server", (PyCFunction
)ceph_get_server
, METH_VARARGS
,
1091 "Get a server object"},
1093 {"_ceph_get_metadata", (PyCFunction
)get_metadata
, METH_VARARGS
,
1094 "Get a service's metadata"},
1096 {"_ceph_get_daemon_status", (PyCFunction
)get_daemon_status
, METH_VARARGS
,
1097 "Get a service's status"},
1099 {"_ceph_send_command", (PyCFunction
)ceph_send_command
, METH_VARARGS
,
1100 "Send a mon command"},
1102 {"_ceph_set_health_checks", (PyCFunction
)ceph_set_health_checks
, METH_VARARGS
,
1103 "Set health checks for this module"},
1105 {"_ceph_get_mgr_id", (PyCFunction
)ceph_get_mgr_id
, METH_NOARGS
,
1106 "Get the name of the Mgr daemon where we are running"},
1108 {"_ceph_get_option", (PyCFunction
)ceph_option_get
, METH_VARARGS
,
1109 "Get a native configuration option value"},
1111 {"_ceph_get_module_option", (PyCFunction
)ceph_get_module_option
, METH_VARARGS
,
1112 "Get a module configuration option value"},
1114 {"_ceph_get_store_prefix", (PyCFunction
)ceph_store_get_prefix
, METH_VARARGS
,
1115 "Get all KV store values with a given prefix"},
1117 {"_ceph_set_module_option", (PyCFunction
)ceph_set_module_option
, METH_VARARGS
,
1118 "Set a module configuration option value"},
1120 {"_ceph_get_store", (PyCFunction
)ceph_store_get
, METH_VARARGS
,
1121 "Get a stored field"},
1123 {"_ceph_set_store", (PyCFunction
)ceph_store_set
, METH_VARARGS
,
1124 "Set a stored field"},
1126 {"_ceph_get_counter", (PyCFunction
)get_counter
, METH_VARARGS
,
1127 "Get a performance counter"},
1129 {"_ceph_get_latest_counter", (PyCFunction
)get_latest_counter
, METH_VARARGS
,
1130 "Get the latest performance counter"},
1132 {"_ceph_get_perf_schema", (PyCFunction
)get_perf_schema
, METH_VARARGS
,
1133 "Get the performance counter schema"},
1135 {"_ceph_log", (PyCFunction
)ceph_log
, METH_VARARGS
,
1136 "Emit a (local) log message"},
1138 {"_ceph_cluster_log", (PyCFunction
)ceph_cluster_log
, METH_VARARGS
,
1139 "Emit a cluster log message"},
1141 {"_ceph_get_version", (PyCFunction
)ceph_get_version
, METH_NOARGS
,
1142 "Get the ceph version of this process"},
1144 {"_ceph_get_release_name", (PyCFunction
)ceph_get_release_name
, METH_NOARGS
,
1145 "Get the ceph release name of this process"},
1147 {"_ceph_get_context", (PyCFunction
)ceph_get_context
, METH_NOARGS
,
1148 "Get a CephContext* in a python capsule"},
1150 {"_ceph_get_osdmap", (PyCFunction
)ceph_get_osdmap
, METH_NOARGS
,
1151 "Get an OSDMap* in a python capsule"},
1153 {"_ceph_set_uri", (PyCFunction
)ceph_set_uri
, METH_VARARGS
,
1154 "Advertize a service URI served by this module"},
1156 {"_ceph_have_mon_connection", (PyCFunction
)ceph_have_mon_connection
,
1157 METH_NOARGS
, "Find out whether this mgr daemon currently has "
1158 "a connection to a monitor"},
1160 {"_ceph_update_progress_event", (PyCFunction
)ceph_update_progress_event
,
1161 METH_VARARGS
, "Update status of a progress event"},
1162 {"_ceph_complete_progress_event", (PyCFunction
)ceph_complete_progress_event
,
1163 METH_VARARGS
, "Complete a progress event"},
1164 {"_ceph_clear_all_progress_events", (PyCFunction
)ceph_clear_all_progress_events
,
1165 METH_NOARGS
, "Clear all progress events"},
1167 {"_ceph_dispatch_remote", (PyCFunction
)ceph_dispatch_remote
,
1168 METH_VARARGS
, "Dispatch a call to another module"},
1170 {"_ceph_add_osd_perf_query", (PyCFunction
)ceph_add_osd_perf_query
,
1171 METH_VARARGS
, "Add an osd perf query"},
1173 {"_ceph_remove_osd_perf_query", (PyCFunction
)ceph_remove_osd_perf_query
,
1174 METH_VARARGS
, "Remove an osd perf query"},
1176 {"_ceph_get_osd_perf_counters", (PyCFunction
)ceph_get_osd_perf_counters
,
1177 METH_VARARGS
, "Get osd perf counters"},
1179 {"_ceph_is_authorized", (PyCFunction
)ceph_is_authorized
,
1180 METH_VARARGS
, "Verify the current session caps are valid"},
1182 {"_ceph_register_client", (PyCFunction
)ceph_register_client
,
1183 METH_VARARGS
, "Register RADOS instance for potential blacklisting"},
1185 {"_ceph_unregister_client", (PyCFunction
)ceph_unregister_client
,
1186 METH_VARARGS
, "Unregister RADOS instance for potential blacklisting"},
1188 {NULL
, NULL
, 0, NULL
}
1193 BaseMgrModule_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
)
1195 BaseMgrModule
*self
;
1197 self
= (BaseMgrModule
*)type
->tp_alloc(type
, 0);
1199 return (PyObject
*)self
;
1203 BaseMgrModule_init(BaseMgrModule
*self
, PyObject
*args
, PyObject
*kwds
)
1205 PyObject
*py_modules_capsule
= nullptr;
1206 PyObject
*this_module_capsule
= nullptr;
1207 static const char *kwlist
[] = {"py_modules", "this_module", NULL
};
1209 if (! PyArg_ParseTupleAndKeywords(args
, kwds
, "OO",
1210 const_cast<char**>(kwlist
),
1211 &py_modules_capsule
,
1212 &this_module_capsule
)) {
1216 self
->py_modules
= static_cast<ActivePyModules
*>(PyCapsule_GetPointer(
1217 py_modules_capsule
, nullptr));
1218 ceph_assert(self
->py_modules
);
1219 self
->this_module
= static_cast<ActivePyModule
*>(PyCapsule_GetPointer(
1220 this_module_capsule
, nullptr));
1221 ceph_assert(self
->this_module
);
1226 PyTypeObject BaseMgrModuleType
= {
1227 PyVarObject_HEAD_INIT(NULL
, 0)
1228 "ceph_module.BaseMgrModule", /* tp_name */
1229 sizeof(BaseMgrModule
), /* tp_basicsize */
1230 0, /* tp_itemsize */
1237 0, /* tp_as_number */
1238 0, /* tp_as_sequence */
1239 0, /* tp_as_mapping */
1243 0, /* tp_getattro */
1244 0, /* tp_setattro */
1245 0, /* tp_as_buffer */
1246 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
, /* tp_flags */
1247 "ceph-mgr Python Plugin", /* tp_doc */
1248 0, /* tp_traverse */
1250 0, /* tp_richcompare */
1251 0, /* tp_weaklistoffset */
1253 0, /* tp_iternext */
1254 BaseMgrModule_methods
, /* tp_methods */
1259 0, /* tp_descr_get */
1260 0, /* tp_descr_set */
1261 0, /* tp_dictoffset */
1262 (initproc
)BaseMgrModule_init
, /* tp_init */
1264 BaseMgrModule_new
, /* tp_new */