From d1f9a992190921783337b71103d3525c3381bedf Mon Sep 17 00:00:00 2001
From: lifeng68 <lifeng68@huawei.com>
Date: Tue, 15 Dec 2020 17:30:01 +0800
Subject: [PATCH 14/14] api: add get container metrics api

Signed-off-by: lifeng68 <lifeng68@huawei.com>
---
 src/lxc/lxccontainer.c | 174 +++++++++++++++++++++++++++++++++++++++++
 src/lxc/lxccontainer.h |  42 ++++++++++
 2 files changed, 216 insertions(+)

diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index cbb67f321..9202b73ff 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -5752,6 +5752,179 @@ static bool do_lxcapi_set_start_timeout(struct lxc_container *c,  unsigned int s
 
 WRAP_API_1(bool, lxcapi_set_start_timeout, unsigned int)
 
+static uint64_t metrics_get_ull(struct lxc_container *c, struct cgroup_ops *cgroup_ops, const char *item)
+{
+    char buf[80] = {0};
+    int len = 0;
+    uint64_t val = 0;
+
+    len = cgroup_ops->get(cgroup_ops, item, buf, sizeof(buf), c->name, c->config_path);
+    if (len <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        return 0;
+    }
+
+    val = strtoull(buf, NULL, 0);
+    return val;
+}
+
+static inline bool is_blk_metrics_read(const char *value)
+{
+    return strcmp(value, "Read") == 0;
+}
+
+static inline bool is_blk_metrics_write(const char *value)
+{
+    return strcmp(value, "Write") == 0;
+}
+
+static inline bool is_blk_metrics_total(const char *value)
+{
+    return strcmp(value, "Total") == 0;
+}
+
+static void metrics_get_blk_stats(struct lxc_container *c, struct cgroup_ops *cgroup_ops, const char *item, struct lxc_blkio_metrics *stats)
+{
+#define BUFSIZE 4096
+    char buf[BUFSIZE] = {0};
+    int i = 0;
+    int len = 0;
+    char **lines = NULL;
+    char **cols = NULL;
+
+    len = cgroup_ops->get(cgroup_ops, item, buf, sizeof(buf), c->name, c->config_path);
+    if (len <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        return;
+    }
+
+    lines = lxc_string_split_and_trim(buf, '\n');
+    if (lines == NULL) {
+        return;
+    }
+
+    (void)memset(stats, 0, sizeof(struct lxc_blkio_metrics));
+
+    for (i = 0; lines[i]; i++) {
+        cols = lxc_string_split_and_trim(lines[i], ' ');
+        if (cols == NULL) {
+            goto err_out;
+        }
+        if (is_blk_metrics_read(cols[1])) {
+            stats->read += strtoull(cols[2], NULL, 0);
+        } else if (is_blk_metrics_write(cols[1])) {
+            stats->write += strtoull(cols[2], NULL, 0);
+        }
+        if (is_blk_metrics_total(cols[0])) {
+            stats->total = strtoull(cols[1], NULL, 0);
+        }
+
+        lxc_free_array((void **)cols, free);
+    }
+err_out:
+    lxc_free_array((void **)lines, free);
+    return;
+}
+
+static uint64_t metrics_match_get_ull(struct lxc_container *c, struct cgroup_ops *cgroup_ops, const char *item, const char *match, int column)
+{
+#define BUFSIZE 4096
+    char buf[BUFSIZE] = {0};
+    int i = 0;
+    int j = 0;
+    int len = 0;
+    uint64_t val = 0;
+    char **lines = NULL;
+    char **cols = NULL;
+    size_t matchlen = 0;
+
+    len = cgroup_ops->get(cgroup_ops, item, buf, sizeof(buf), c->name, c->config_path);
+    if (len <= 0) {
+        DEBUG("unable to read cgroup item %s", item);
+        goto err_out;
+    }
+
+    lines = lxc_string_split_and_trim(buf, '\n');
+    if (lines == NULL) {
+        goto err_out;
+    }
+
+    matchlen = strlen(match);
+    for (i = 0; lines[i]; i++) {
+        if (strncmp(lines[i], match, matchlen) != 0) {
+            continue;
+        }
+
+        cols = lxc_string_split_and_trim(lines[i], ' ');
+        if (cols == NULL) {
+            goto err1;
+        }
+        for (j = 0; cols[j]; j++) {
+            if (j == column) {
+                val = strtoull(cols[j], NULL, 0);
+                break;
+            }
+        }
+        lxc_free_array((void **)cols, free);
+        break;
+    }
+err1:
+    lxc_free_array((void **)lines, free);
+err_out:
+    return val;
+}
+
+/* isulad add get container metrics */
+static bool do_lxcapi_get_container_metrics(struct lxc_container *c,  struct lxc_container_metrics *metrics)
+{
+	call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL;
+	const char *state = NULL;
+	if (c == NULL || c->lxc_conf == NULL || metrics == NULL) {
+		return false;
+	}
+
+	state = c->state(c);
+	metrics->state = state;
+
+	if (!is_stopped(c)) {
+		metrics->init = c->init_pid(c);
+	} else {
+		metrics->init = -1;
+	}
+
+	cgroup_ops = cgroup_init(c->lxc_conf);
+	if (cgroup_ops == NULL) {
+		return false;
+	}
+
+	metrics->cpu_use_nanos = metrics_get_ull(c, cgroup_ops, "cpuacct.usage");
+	metrics->pids_current = metrics_get_ull(c, cgroup_ops, "pids.current");
+
+	metrics->cpu_use_user = metrics_match_get_ull(c, cgroup_ops, "cpuacct.stat", "user", 1);
+	metrics->cpu_use_sys = metrics_match_get_ull(c, cgroup_ops, "cpuacct.stat", "system", 1);
+
+	// Try to read CFQ stats available on all CFQ enabled kernels first
+	metrics_get_blk_stats(c, cgroup_ops, "blkio.io_serviced_recursive", &metrics->io_serviced);
+	if (metrics->io_serviced.read == 0 && metrics->io_serviced.write == 0 && metrics->io_serviced.total == 0) {
+		metrics_get_blk_stats(c, cgroup_ops, "blkio.throttle.io_service_bytes", &metrics->io_service_bytes);
+		metrics_get_blk_stats(c, cgroup_ops, "blkio.throttle.io_serviced", &metrics->io_serviced);
+	} else {
+		metrics_get_blk_stats(c, cgroup_ops, "blkio.io_service_bytes_recursive", &metrics->io_service_bytes);
+	}
+
+	metrics->mem_used = metrics_get_ull(c, cgroup_ops, "memory.usage_in_bytes");
+	metrics->mem_limit = metrics_get_ull(c, cgroup_ops, "memory.limit_in_bytes");
+	metrics->kmem_used = metrics_get_ull(c, cgroup_ops, "memory.kmem.usage_in_bytes");
+	metrics->kmem_limit = metrics_get_ull(c, cgroup_ops, "memory.kmem.limit_in_bytes");
+
+	metrics->cache = metrics_match_get_ull(c, cgroup_ops, "memory.stat", "cache", 1);
+	metrics->cache_total = metrics_match_get_ull(c, cgroup_ops, "memory.stat", "total_cache", 1);
+
+	return true;
+}
+
+WRAP_API_1(bool, lxcapi_get_container_metrics, struct lxc_container_metrics *)
+
 #endif
 
 #ifdef HAVE_ISULAD
@@ -5924,6 +6097,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 	c->clean_container_resource = lxcapi_clean_container_resource;
 	c->get_container_pids = lxcapi_get_container_pids;
 	c->set_start_timeout = lxcapi_set_start_timeout;
+	c->get_container_metrics = lxcapi_get_container_metrics;
 #endif
 	return c;
 
diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
index 2951ac7b4..e30bf6161 100644
--- a/src/lxc/lxccontainer.h
+++ b/src/lxc/lxccontainer.h
@@ -40,6 +40,37 @@ struct lxc_mount {
 	int version;
 };
 
+struct lxc_blkio_metrics {
+	uint64_t read;
+	uint64_t write;
+	uint64_t total;
+};
+
+struct lxc_container_metrics {
+    /* State of container */
+    const char *state;
+    /* The process ID of the init container */
+    pid_t init;
+    /* Current pids */
+    uint64_t pids_current;
+    /* CPU usage */
+    uint64_t cpu_use_nanos;
+    uint64_t cpu_use_user;
+    uint64_t cpu_use_sys;
+    /* BlkIO usage */
+    struct lxc_blkio_metrics io_service_bytes;
+    struct lxc_blkio_metrics io_serviced;
+    /* Memory usage */
+    uint64_t mem_used;
+    uint64_t mem_limit;
+    /* Kernel Memory usage */
+    uint64_t kmem_used;
+    uint64_t kmem_limit;
+    /* Cache usage */
+    uint64_t cache;
+    uint64_t cache_total;
+};
+
 /*!
  * An LXC container.
  *
@@ -976,6 +1007,17 @@ struct lxc_container {
 	 * \return \c true on success, else \c false.
 	 */
 	bool (*set_start_timeout)(struct lxc_container *c, unsigned int start_timeout);
+
+	/*! isulad add
+	 * \brief An API call to set start timeout
+	 *
+	 * \param c Container.
+	 * \param start_timeout Value of start timeout.
+	 *
+	 * \return \c true on success, else \c false.
+	 */
+	bool (*get_container_metrics)(struct lxc_container *c, struct lxc_container_metrics *metrics);
+
 };
 
 /*!
-- 
2.25.1

