]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
add statistics to lxc-info
authorDwight Engen <dwight.engen@oracle.com>
Tue, 5 Nov 2013 18:17:07 +0000 (13:17 -0500)
committerSerge Hallyn <serge.hallyn@ubuntu.com>
Wed, 6 Nov 2013 14:15:13 +0000 (08:15 -0600)
- allow lxc-info to show more than one container, using regex for the name

Signed-off-by: Dwight Engen <dwight.engen@oracle.com>
Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
doc/lxc-info.sgml.in
src/lxc/lxc_info.c

index 819d5cae1af17cc162d40fe003495dada0bd5eb2..791d7809cd0e000fc912bda5f2a887398034db6d 100644 (file)
@@ -47,20 +47,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>lxc-info</command>
-      <arg choice="req">-n <replaceable>name</replaceable></arg>
+      <arg choice="opt">-n <replaceable>name</replaceable></arg>
       <arg choice="opt">-c <replaceable>KEY</replaceable></arg>
       <arg choice="opt">-s</arg>
       <arg choice="opt">-p</arg>
       <arg choice="opt">-i</arg>
       <arg choice="opt">-t <replaceable>state</replaceable></arg>
+      <arg choice="opt">-S</arg>
+      <arg choice="opt">-H</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
     <para>
-      <command>lxc-info</command> queries and shows information about a
-      container.
+      <command>lxc-info</command> queries and shows information about
+      containers.
     </para>
   </refsect1>
 
@@ -70,11 +72,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
       <varlistentry>
         <term>
-          <option>-n <replaceable>name</replaceable></option>
+          <option><optional>-n <replaceable>name</replaceable></optional></option>
         </term>
         <listitem>
           <para>
-            The container name.
+            The container name. It is interpreted as a regular expression,
+            so it is possible to get information on all containers, several
+            of them or just one. See
+            <citerefentry>
+             <refentrytitle><command>regex</command></refentrytitle>
+            <manvolnum>7</manvolnum>
+            </citerefentry> for regular expression syntax. If not specified,
+            <replaceable>name</replaceable> will default to '.*' which
+            will give information on all containers in
+            <command>lxcpath</command>.
           </para>
         </listitem>
       </varlistentry>
@@ -124,6 +135,41 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>
+          <option><optional>-S</optional></option>
+        </term>
+        <listitem>
+          <para>
+            Just print the container's statistics.
+            Note that for performance reasons the kernel does not account
+            kernel memory use unless a kernel memory limit is set. If a limit
+            is not set, <command>lxc-info</command> will display kernel memory
+            use as 0. A limit can be set by specifying
+            <programlisting>
+            lxc.cgroup.memory.kmem.limit_in_bytes = <replaceable>number</replaceable>
+            </programlisting>
+            in your container configuration file, see
+            <citerefentry>
+              <refentrytitle>lxc.conf</refentrytitle>
+              <manvolnum>5</manvolnum>
+            </citerefentry>.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option><optional>-H</optional></option>
+        </term>
+        <listitem>
+          <para>
+            Print the container's statistics in raw, non-humanized form. The
+            default is to print statistics in humanized form.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term>
           <option><optional>-t <replaceable>state</replaceable></optional></option>
@@ -151,6 +197,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>lxc-info -n 'ubuntu.*'</term>
+        <listitem>
+          <para>
+            Show information for all containers whose name starts with ubuntu.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term>lxc-info -n foo -t RUNNING</term>
         <listitem>
index aeaf9a896c750d57621606e42cbf02255efdaf07..ba43f37430cd54f0323e4c819b811fb14bfcb639 100644 (file)
 #include <stdbool.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <regex.h>
+#include <limits.h>
 #include <libgen.h>
 #include <sys/types.h>
 
 #include <lxc/lxc.h>
 #include <lxc/log.h>
+#include <lxc/utils.h>
 #include <lxc/lxccontainer.h>
 
 #include "commands.h"
@@ -38,6 +41,8 @@
 static bool ips;
 static bool state;
 static bool pid;
+static bool stats;
+static bool humanize = true;
 static char *test_state = NULL;
 static char **key = NULL;
 static int keys = 0;
@@ -53,6 +58,8 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
        case 'i': ips = true; break;
        case 's': state = true; break;
        case 'p': pid = true; break;
+       case 'S': stats = true; break;
+       case 'H': humanize = false; break;
        case 't': test_state = arg; break;
        }
        return 0;
@@ -63,6 +70,8 @@ static const struct option my_longopts[] = {
        {"ips", no_argument, 0, 'i'},
        {"state", no_argument, 0, 's'},
        {"pid", no_argument, 0, 'p'},
+       {"stats", no_argument, 0, 'S'},
+       {"no-humanize", no_argument, 0, 'H'},
        {"state-is", required_argument, 0, 't'},
        LXC_COMMON_OPTIONS,
 };
@@ -79,33 +88,177 @@ Options :\n\
   -c, --config=KEY      show configuration variable KEY from running container\n\
   -i, --ips             shows the IP addresses\n\
   -p, --pid             shows the process id of the init container\n\
+  -S, --stats           shows usage stats\n\
+  -H, --no-humanize     shows stats as raw numbers, not humanized\n\
   -s, --state           shows the state of the container\n\
   -t, --state-is=STATE  test if current state is STATE\n\
                         returns success if it matches, false otherwise\n",
+       .name     = ".*",
        .options  = my_longopts,
        .parser   = my_parser,
        .checker  = NULL,
 };
 
-int main(int argc, char *argv[])
+static void str_chomp(char *buf)
 {
-       struct lxc_container *c;
+       char *ch;
 
-       int i;
+       /* remove trailing whitespace from buf */
+       for(ch = &buf[strlen(buf)-1];
+           ch >= buf && (*ch == '\t' || *ch == '\n' || *ch == ' ');
+           ch--)
+               *ch = '\0';
+}
 
-       if (lxc_arguments_parse(&my_args, argc, argv))
-               return -1;
+static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
+{
+       if (val > 1 << 30) {
+               snprintf(buf, bufsz, "%u.%2.2u GiB",
+                           (int)(val >> 30),
+                           (int)(val & ((1 << 30) - 1)) / 10737419);
+       } else if (val > 1 << 20) {
+               int x = val + 5243;  /* for rounding */
+               snprintf(buf, bufsz, "%u.%2.2u MiB",
+                           x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
+       } else if (val > 1 << 10) {
+               int x = val + 5;  /* for rounding */
+               snprintf(buf, bufsz, "%u.%2.2u KiB",
+                           x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
+       } else {
+               snprintf(buf, bufsz, "%u bytes", (int)val);
+       }
+}
 
-       if (!my_args.log_file)
-               my_args.log_file = "none";
+static unsigned long long str_size_humanize(char *iobuf, size_t iobufsz)
+{
+       unsigned long long val;
+       char *end = NULL;
 
-       if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
-                        my_args.progname, my_args.quiet, my_args.lxcpath[0]))
-               return -1;
+       val = strtoull(iobuf, &end, 0);
+       if (humanize) {
+               if (*end == '\0' || *end == '\n')
+                       size_humanize(val, iobuf, iobufsz);
+               else
+                       *iobuf = '\0';
+       }
+       return val;
+}
+
+static void print_net_stats(const char *name, const char *lxcpath)
+{
+       int rc,netnr;
+       unsigned long long rx_bytes = 0, tx_bytes = 0;
+       char *ifname, *type;
+       char path[PATH_MAX];
+       char buf[256];
+
+       for(netnr = 0; ;netnr++) {
+               sprintf(buf, "lxc.network.%d.type", netnr);
+               type = lxc_cmd_get_config_item(name, buf, lxcpath);
+               if (!type)
+                       break;
 
-       c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
-       if (!c)
+               if (!strcmp(type, "veth")) {
+                       sprintf(buf, "lxc.network.%d.veth.pair", netnr);
+               } else {
+                       sprintf(buf, "lxc.network.%d.link", netnr);
+               }
+               free(type);
+               ifname = lxc_cmd_get_config_item(name, buf, lxcpath);
+               if (!ifname)
+                       return;
+               printf("%-15s %s\n", "Link:", ifname);
+
+               /* XXX: tx and rx are reversed from the host vs container
+                * perspective, print them from the container perspective
+                */
+               snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/rx_bytes", ifname);
+               rc = lxc_read_from_file(path, buf, sizeof(buf));
+               if (rc > 0) {
+                       str_chomp(buf);
+                       rx_bytes = str_size_humanize(buf, sizeof(buf));
+                       printf("%-15s %s\n", " TX bytes:", buf);
+               }
+
+               snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/tx_bytes", ifname);
+               rc = lxc_read_from_file(path, buf, sizeof(buf));
+               if (rc > 0) {
+                       str_chomp(buf);
+                       tx_bytes = str_size_humanize(buf, sizeof(buf));
+                       printf("%-15s %s\n", " RX bytes:", buf);
+               }
+
+               sprintf(buf, "%llu", rx_bytes + tx_bytes);
+               str_size_humanize(buf, sizeof(buf));
+               printf("%-15s %s\n", " Total bytes:", buf);
+               free(ifname);
+       }
+}
+
+static void print_stats(struct lxc_container *c)
+{
+       int i, ret;
+       char buf[256];
+
+       ret = c->get_cgroup_item(c, "cpuacct.usage", buf, sizeof(buf));
+       if (ret > 0 && ret < sizeof(buf)) {
+               str_chomp(buf);
+               if (humanize) {
+                       float seconds = strtof(buf, NULL) / 1000000000.0;
+                       printf("%-15s %.2f seconds\n", "CPU use:", seconds);
+               } else {
+                       printf("%-15s %s\n", "CPU use:", buf);
+               }
+       }
+
+       ret = c->get_cgroup_item(c, "blkio.throttle.io_service_bytes", buf, sizeof(buf));
+       if (ret > 0 && ret < sizeof(buf)) {
+               char *ch;
+
+               /* put ch on last "Total" line */
+               str_chomp(buf);
+               for(ch = &buf[strlen(buf)-1]; ch > buf && *ch != '\n'; ch--)
+                       ;
+               if (*ch == '\n')
+                       ch++;
+
+               if (strncmp(ch, "Total", 5) == 0) {
+                       ch += 6;
+                       memmove(buf, ch, strlen(ch)+1);
+                       str_size_humanize(buf, sizeof(buf));
+                       printf("%-15s %s\n", "BlkIO use:", buf);
+               }
+       }
+
+       static const struct {
+               const char *name;
+               const char *file;
+       } lxstat[] = {
+               { "Memory use:", "memory.usage_in_bytes" },
+               { "KMem use:",   "memory.kmem.usage_in_bytes" },
+               { NULL, NULL },
+       };
+
+       for (i = 0; lxstat[i].name; i++) {
+               ret = c->get_cgroup_item(c, lxstat[i].file, buf, sizeof(buf));
+               if (ret > 0 && ret < sizeof(buf)) {
+                       str_chomp(buf);
+                       str_size_humanize(buf, sizeof(buf));
+                       printf("%-15s %s\n", lxstat[i].name, buf);
+               }
+       }
+}
+
+static int print_info(const char *name, const char *lxcpath)
+{
+       int i;
+       struct lxc_container *c;
+
+       c = lxc_container_new(name, lxcpath);
+       if (!c) {
+               fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
                return -1;
+       }
 
        if (!c->may_control(c)) {
                fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
@@ -113,14 +266,16 @@ int main(int argc, char *argv[])
                return -1;
        }
 
-       if (!state && !pid && !ips && keys <= 0)
-               state = pid = ips = true;
+       if (!state && !pid && !ips && !stats && keys <= 0)
+               state = pid = ips = stats = true;
+
+       printf("%-15s %s\n", "Name:", c->name);
 
        if (state || test_state) {
                if (test_state)
                        return strcmp(c->state(c), test_state) != 0;
 
-               printf("state: \t%s\n", c->state(c));
+               printf("%-15s %s\n", "State:", c->state(c));
        }
 
        if (pid) {
@@ -128,7 +283,7 @@ int main(int argc, char *argv[])
 
                initpid = c->init_pid(c);
                if (initpid >= 0)
-                       printf("pid: \t%d\n", initpid);
+                       printf("%-15s %d\n", "Pid:", initpid);
        }
 
        if (ips) {
@@ -138,12 +293,17 @@ int main(int argc, char *argv[])
                        i = 0;
                        while (addresses[i]) {
                                address = addresses[i];
-                               printf("ip: \t%s\n", address);
+                               printf("%-15s %s\n", "IP:", address);
                                i++;
                        }
                }
        }
 
+       if (stats) {
+               print_stats(c);
+               print_net_stats(name, lxcpath);
+       }
+
        for(i = 0; i < keys; i++) {
                int len = c->get_config_item(c, key[i], NULL, 0);
 
@@ -164,3 +324,66 @@ int main(int argc, char *argv[])
        lxc_container_put(c);
        return 0;
 }
+
+int main(int argc, char *argv[])
+{
+       int rc, i, len, ret = EXIT_FAILURE;
+       char *regexp;
+       regex_t preg;
+       int ct_cnt;
+       char **ct_name;
+       bool printed;
+
+       if (lxc_arguments_parse(&my_args, argc, argv))
+               goto err1;
+
+       if (!my_args.log_file)
+               my_args.log_file = "none";
+
+       if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+                        my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+               goto err1;
+
+       len = strlen(my_args.name) + 3;
+       regexp = malloc(len + 3);
+       if (!regexp) {
+               fprintf(stderr, "failed to allocate memory");
+               goto err1;
+       }
+       rc = snprintf(regexp, len, "^%s$", my_args.name);
+       if (rc < 0 || rc >= len) {
+               fprintf(stderr, "Name too long");
+               goto err2;
+       }
+
+       if (regcomp(&preg, regexp, REG_NOSUB|REG_EXTENDED)) {
+               fprintf(stderr, "failed to compile the regex '%s'", my_args.name);
+               goto err2;
+       }
+
+       printed = false;
+       ct_cnt = list_all_containers(my_args.lxcpath[0], &ct_name, NULL);
+       if (ct_cnt < 0)
+               goto err3;
+
+       for (i = 0; i < ct_cnt; i++) {
+               if (regexec(&preg, ct_name[i], 0, NULL, 0) == 0)
+               {
+                       if (printed)
+                               printf("\n");
+                       print_info(ct_name[i], my_args.lxcpath[0]);
+                       printed = true;
+               }
+               free(ct_name[i]);
+       }
+       if (ct_name)
+               free(ct_name);
+       ret = EXIT_SUCCESS;
+
+err3:
+       regfree(&preg);
+err2:
+       free(regexp);
+err1:
+       return ret;
+}