]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr: parse OSDPerfMetricQuery spec that comes from python module
authorMykola Golub <mgolub@suse.com>
Thu, 15 Nov 2018 08:59:40 +0000 (10:59 +0200)
committerMykola Golub <mgolub@suse.com>
Tue, 20 Nov 2018 11:18:30 +0000 (13:18 +0200)
Signed-off-by: Mykola Golub <mgolub@suse.com>
src/mgr/BaseMgrModule.cc
src/pybind/mgr/mgr_module.py

index 013d44756173c807314c4f0972352a3c1582256e..a34a6ca4e571d2f473e5cf21c97b9d6e19a43c4c 100644 (file)
@@ -662,21 +662,158 @@ ceph_dispatch_remote(BaseMgrModule *self, PyObject *args)
 static PyObject*
 ceph_add_osd_perf_query(BaseMgrModule *self, PyObject *args)
 {
-  // TODO: parse args to build OSDPerfMetricQuery.
-  // For now it is ignored and can be anything.
-  PyObject *query_ = nullptr;
-  if (!PyArg_ParseTuple(args, "O:ceph_add_osd_perf_query", &query_)) {
+  static const std::string NAME_KEY_DESCRIPTOR = "key_descriptor";
+  static const std::string NAME_COUNTERS_DESCRIPTORS =
+      "performance_counter_descriptors";
+  static const std::string NAME_SUB_KEY_TYPE = "type";
+  static const std::string NAME_SUB_KEY_REGEX = "regex";
+  static const std::map<std::string, OSDPerfMetricSubKeyType> sub_key_types = {
+    {"client_id", OSDPerfMetricSubKeyType::CLIENT_ID},
+    {"pool_id", OSDPerfMetricSubKeyType::POOL_ID},
+    {"object_name", OSDPerfMetricSubKeyType::OBJECT_NAME},
+  };
+  static const std::map<std::string, PerformanceCounterType> counter_types = {
+    {"write_ops", PerformanceCounterType::WRITE_OPS},
+    {"read_ops", PerformanceCounterType::READ_OPS},
+    {"write_bytes", PerformanceCounterType::WRITE_BYTES},
+    {"read_bytes", PerformanceCounterType::READ_BYTES},
+    {"write_latency", PerformanceCounterType::WRITE_LATENCY},
+    {"read_latency", PerformanceCounterType::READ_LATENCY},
+  };
+
+  PyObject *py_query = nullptr;
+  if (!PyArg_ParseTuple(args, "O:ceph_add_osd_perf_query", &py_query)) {
     derr << "Invalid args!" << dendl;
     return nullptr;
   }
+  if (!PyDict_Check(py_query)) {
+    derr << __func__ << " arg not a dict" << dendl;
+    Py_RETURN_NONE;
+  }
 
-  OSDPerfMetricQuery query = {{{OSDPerfMetricSubKeyType::CLIENT_ID, "^.*$"}},
-                              {{PerformanceCounterType::WRITE_OPS},
-                               {PerformanceCounterType::READ_OPS},
-                               {PerformanceCounterType::WRITE_BYTES},
-                               {PerformanceCounterType::READ_BYTES},
-                               {PerformanceCounterType::WRITE_LATENCY},
-                               {PerformanceCounterType::READ_LATENCY}}};
+  PyObject *query_params = PyDict_Items(py_query);
+  OSDPerfMetricQuery query;
+
+  // {
+  //   'key_descriptor': [
+  //     {'type': subkey_type, 'regex': regex_pattern},
+  //     ...
+  //   ],
+  //   'performance_counter_descriptors': [
+  //     list, of, descriptor, types
+  //   ],
+  // }
+
+  for (int i = 0; i < PyList_Size(query_params); ++i) {
+    PyObject *kv = PyList_GET_ITEM(query_params, i);
+    char *query_param_name = nullptr;
+    PyObject *query_param_val = nullptr;
+    if (!PyArg_ParseTuple(kv, "sO:pair", &query_param_name, &query_param_val)) {
+      derr << __func__ << " dict item " << i << " not a size 2 tuple" << dendl;
+      Py_RETURN_NONE;
+    }
+    if (query_param_name == NAME_KEY_DESCRIPTOR) {
+      if (!PyList_Check(query_param_val)) {
+        derr << __func__ << " " << query_param_name << " not a list" << dendl;
+        Py_RETURN_NONE;
+      }
+      for (int j = 0; j < PyList_Size(query_param_val); j++) {
+        PyObject *sub_key = PyList_GET_ITEM(query_param_val, j);
+        if (!PyDict_Check(sub_key)) {
+          derr << __func__ << " query " << query_param_name << " item " << j
+               << " not a dict" << dendl;
+          Py_RETURN_NONE;
+        }
+        OSDPerfMetricSubKeyDescriptor d;
+        PyObject *sub_key_params = PyDict_Items(sub_key);
+        for (int k = 0; k < PyList_Size(sub_key_params); ++k) {
+          PyObject *pair = PyList_GET_ITEM(sub_key_params, k);
+          if (!PyTuple_Check(pair)) {
+            derr << __func__ << " query " << query_param_name << " item " << j
+                 << " pair " << k << " not a tuple" << dendl;
+            Py_RETURN_NONE;
+          }
+          char *param_name = nullptr;
+          PyObject *param_value = nullptr;
+          if (!PyArg_ParseTuple(pair, "sO:pair", &param_name, &param_value)) {
+            derr << __func__ << " query " << query_param_name << " item " << j
+                 << " pair " << k << " not a size 2 tuple" << dendl;
+            Py_RETURN_NONE;
+          }
+          if (param_name == NAME_SUB_KEY_TYPE) {
+            if (!PyString_Check(param_value)) {
+              derr << __func__ << " query " << query_param_name << " item " << j
+                   << " contains invalid param " << param_name << dendl;
+              Py_RETURN_NONE;
+            }
+            auto type = PyString_AsString(param_value);
+            auto it = sub_key_types.find(type);
+            if (it == sub_key_types.end()) {
+              derr << __func__ << " query " << query_param_name << " item " << j
+                   << " contains invalid type " << dendl;
+              Py_RETURN_NONE;
+            }
+            d.type = it->second;
+          } else if (param_name == NAME_SUB_KEY_REGEX) {
+            if (!PyString_Check(param_value)) {
+              derr << __func__ << " query " << query_param_name << " item " << j
+                   << " contains invalid param " << param_name << dendl;
+              Py_RETURN_NONE;
+            }
+            d.regex_str = PyString_AsString(param_value);
+            try {
+              d.regex = {d.regex_str.c_str()};
+            } catch (const std::regex_error& e) {
+              derr << __func__ << " query " << query_param_name << " item " << j
+                   << " contains invalid regex " << d.regex_str << dendl;
+              Py_RETURN_NONE;
+            }
+          } else {
+            derr << __func__ << " query " << query_param_name << " item " << j
+                 << " contains invalid param " << param_name << dendl;
+            Py_RETURN_NONE;
+          }
+        }
+        if (d.type == static_cast<OSDPerfMetricSubKeyType>(-1) ||
+            d.regex_str.empty()) {
+          derr << __func__ << " query " << query_param_name << " item " << i
+               << " invalid" << dendl;
+          Py_RETURN_NONE;
+        }
+        query.key_descriptor.push_back(d);
+      }
+    } else if (query_param_name == NAME_COUNTERS_DESCRIPTORS) {
+      if (!PyList_Check(query_param_val)) {
+        derr << __func__ << " " << query_param_name << " not a list" << dendl;
+        Py_RETURN_NONE;
+      }
+      for (int j = 0; j < PyList_Size(query_param_val); j++) {
+        PyObject *py_type = PyList_GET_ITEM(query_param_val, j);
+        if (!PyString_Check(py_type)) {
+          derr << __func__ << " query " << query_param_name << " item " << j
+               << " not a string" << dendl;
+          Py_RETURN_NONE;
+        }
+        auto type = PyString_AsString(py_type);
+        auto it = counter_types.find(type);
+        if (it == counter_types.end()) {
+          derr << __func__ << " query " << query_param_name << " item " << type
+               << " is not valid type" << dendl;
+          Py_RETURN_NONE;
+        }
+        query.performance_counter_descriptors.push_back(it->second);
+      }
+    } else {
+      derr << "unknown query param: " << query_param_name << dendl;
+      Py_RETURN_NONE;
+    }
+  }
+
+  if (query.key_descriptor.empty() ||
+      query.performance_counter_descriptors.empty()) {
+    derr << "invalid query" << dendl;
+    Py_RETURN_NONE;
+  }
 
   auto query_id = self->py_modules->add_osd_perf_query(query);
   return PyLong_FromLong(query_id);
index a657515368d200508362feb5ac980a26efc08765..d9f9b9ad0a5d1f280bff96d7a27f0b31d32000da 100644 (file)
@@ -917,7 +917,25 @@ class MgrModule(ceph_module.BaseMgrModule):
 
     def add_osd_perf_query(self, query):
         """
-        Fetch the daemon metadata for a particular service.
+        Register an OSD perf query.  Argument is a
+        dict of the query parameters, in this form:
+
+        ::
+
+           {
+             'key_descriptor': [
+               {'type': subkey_type, 'regex': regex_pattern},
+               ...
+             ],
+             'performance_counter_descriptors': [
+               list, of, descriptor, types
+             ],
+           }
+
+        Valid subkey types: 'client_id', 'pool_id', 'object_name'
+        Valid performance counter types:
+           'write_ops', 'read_ops', 'write_bytes', 'read_bytes',
+           'write_latency', 'read_latency'
 
         :param object query: query
         :rtype: int (query id)
@@ -926,8 +944,16 @@ class MgrModule(ceph_module.BaseMgrModule):
 
     def remove_osd_perf_query(self, query_id):
         """
-        Fetch the daemon metadata for a particular service.
+        Unregister an OSD perf query.
 
         :param int query_id: query ID
         """
         return self._ceph_remove_osd_perf_query(query_id)
+
+    def get_osd_perf_counters(self, query_id):
+        """
+        Get stats collected for an OSD perf query.
+
+        :param int query_id: query ID
+        """
+        return self._ceph_get_osd_perf_counters(query_id)