On 01/24/2013 11:42 AM, Dwight Engen wrote: > The lua binding is based closely on the python binding. Also included are > a test program for excercising the binding, and an lxc-top utility for > showing statistics on running containers. > > Signed-off-by: Dwight Engen <dwight.en...@oracle.com>
Test build passed and the configure.ac/automake integration looks good. I haven't looked at the code in much more depth than that, but it looks clean, self-contained and builds, so good enough for me :) Acked-by: Stéphane Graber <stgra...@ubuntu.com> Pushed to staging. Thanks! > --- > Makefile.am | 7 +- > configure.ac | 20 +++ > doc/Makefile.am | 4 + > doc/lxc-top.sgml.in | 164 +++++++++++++++++ > lxc.spec.in | 13 ++ > src/Makefile.am | 2 +- > src/lua-lxc/Makefile.am | 26 +++ > src/lua-lxc/core.c | 382 +++++++++++++++++++++++++++++++++++++++ > src/lua-lxc/lxc.lua | 412 > +++++++++++++++++++++++++++++++++++++++++++ > src/lua-lxc/test/apitest.lua | 302 +++++++++++++++++++++++++++++++ > src/lxc/Makefile.am | 8 +- > src/lxc/lxc-top | 242 +++++++++++++++++++++++++ > 12 files changed, 1579 insertions(+), 3 deletions(-) > create mode 100644 doc/lxc-top.sgml.in > create mode 100644 src/lua-lxc/Makefile.am > create mode 100644 src/lua-lxc/core.c > create mode 100755 src/lua-lxc/lxc.lua > create mode 100755 src/lua-lxc/test/apitest.lua > create mode 100755 src/lxc/lxc-top > > diff --git a/Makefile.am b/Makefile.am > index 3fb453e..53473ee 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -5,9 +5,14 @@ ACLOCAL_AMFLAGS = -I config > SUBDIRS = config src templates doc > DIST_SUBDIRS = config src templates doc > EXTRA_DIST = autogen.sh lxc.spec CONTRIBUTING MAINTAINERS ChangeLog > +RPMARGS = > + > +if ENABLE_LUA > +RPMARGS += --with lua > +endif > > if ENABLE_PYTHON > -RPMARGS = --with python > +RPMARGS += --with python > endif > > pcdatadir = $(libdir)/pkgconfig > diff --git a/configure.ac b/configure.ac > index d1f5ad9..02c75a1 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -137,6 +137,23 @@ AM_COND_IF([ENABLE_PYTHON], > PKG_CHECK_MODULES([PYTHONDEV], [python3 >= 3.2],[],[AC_MSG_ERROR([You > must install python3-dev])]) > AC_DEFINE_UNQUOTED([ENABLE_PYTHON], 1, [Python3 is available])]) > > +# Lua module and scripts > +if test x"$with_distro" = "xdebian" -o x"$with_distro" = "xubuntu" ; then > + LUAPKGCONFIG=lua5.1 > +else > + LUAPKGCONFIG=lua > +fi > + > +AC_ARG_ENABLE([lua], > + [AC_HELP_STRING([--enable-lua], [enable lua binding])], > + [enable_lua=yes], [enable_lua=no]) > + > +AM_CONDITIONAL([ENABLE_LUA], [test "x$enable_lua" = "xyes"]) > + > +AM_COND_IF([ENABLE_LUA], > + [PKG_CHECK_MODULES([LUA], [$LUAPKGCONFIG >= 5.1],[],[AC_MSG_ERROR([You > must install lua-devel for lua 5.1])]) > + AC_DEFINE_UNQUOTED([ENABLE_LUA], 1, [Lua is available])]) > + > # Optional test binaries > AC_ARG_ENABLE([tests], > [AC_HELP_STRING([--enable-tests], [build test/example binaries])], > @@ -268,6 +285,7 @@ AC_CONFIG_FILES([ > doc/lxc-wait.sgml > doc/lxc-ls.sgml > doc/lxc-ps.sgml > + doc/lxc-top.sgml > doc/lxc-cgroup.sgml > doc/lxc-kill.sgml > doc/lxc-attach.sgml > @@ -321,6 +339,8 @@ AC_CONFIG_FILES([ > src/python-lxc/lxc/__init__.py > src/python-lxc/examples/api_test.py > > + src/lua-lxc/Makefile > + > src/tests/Makefile > ]) > AC_CONFIG_COMMANDS([default],[[]],[[]]) > diff --git a/doc/Makefile.am b/doc/Makefile.am > index 86de2fe..e539254 100644 > --- a/doc/Makefile.am > +++ b/doc/Makefile.am > @@ -34,6 +34,10 @@ else > man_MANS += legacy/lxc-ls.1 > endif > > +if ENABLE_LUA > + man_MANS += lxc-top.1 > +endif > + > %.1 : %.sgml > $(db2xman) $< > test "$(shell basename $@)" != "$@" && mv $(shell basename $@) $@ || > true > diff --git a/doc/lxc-top.sgml.in b/doc/lxc-top.sgml.in > new file mode 100644 > index 0000000..2a4f835 > --- /dev/null > +++ b/doc/lxc-top.sgml.in > @@ -0,0 +1,164 @@ > +<!-- > + > +Copyright © 2012 Oracle. > + > +Authors: > +Dwight Engen <dwight.en...@oracle.com> > + > +This library is free software; you can redistribute it and/or > +modify it under the terms of the GNU Lesser General Public > +License as published by the Free Software Foundation; either > +version 2.1 of the License, or (at your option) any later version. > + > +This library is distributed in the hope that it will be useful, > +but WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > +Lesser General Public License for more details. > + > +You should have received a copy of the GNU Lesser General Public > +License along with this library; if not, write to the Free Software > +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + > +--> > + > +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" > "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ > + > +<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml"> > +]> > + > +<refentry> > + > + <docinfo><date>@LXC_GENERATE_DATE@</date></docinfo> > + > + <refmeta> > + <refentrytitle>lxc-top</refentrytitle> > + <manvolnum>1</manvolnum> > + </refmeta> > + > + <refnamediv> > + <refname>lxc-top</refname> > + > + <refpurpose> > + monitor container statistics > + </refpurpose> > + </refnamediv> > + > + <refsynopsisdiv> > + <cmdsynopsis> > + <command>lxc-top</command> > + <arg choice="opt">--help</arg> > + <arg choice="opt">--max <replaceable>count</replaceable></arg> > + <arg choice="opt">--delay <replaceable>delay</replaceable></arg> > + <arg choice="opt">--sort <replaceable>sortby</replaceable></arg> > + <arg choice="opt">--reverse</arg> > + </cmdsynopsis> > + </refsynopsisdiv> > + > + <refsect1> > + <title>Description</title> > + <para> > + <command>lxc-top</command> displays container statistics. The output > + is updated every <replaceable>delay</replaceable> seconds, and is > + ordered according to the <replaceable>sortby</replaceable> value > + given. Specifying <replaceable>count</replaceable> will limit the > + number of containers displayed, otherwise <command>lxc-top</command> > + will display as many containers as can fit in your terminal. > + </para> > + </refsect1> > + > + <refsect1> > + <title>Options</title> > + <variablelist> > + > + <varlistentry> > + <term> > + <option><optional>-m, --max > <replaceable>count</replaceable></optional></option> > + </term> > + <listitem> > + <para> > + Limit the number of containers displayed to > + <replaceable>count</replaceable>. > + </para> > + </listitem> > + </varlistentry> > + > + <varlistentry> > + <term> > + <option><optional>-d, --delay > <replaceable>delay</replaceable></optional></option> > + </term> > + <listitem> > + <para> > + Amount of time in seconds to delay between screen updates. > + This can be specified as less than a second by giving a > + rational number, for example 0.5 for a half second delay. The > + default is 3 seconds. > + </para> > + </listitem> > + </varlistentry> > + <varlistentry> > + <term> > + <option><optional>-s, --sort > <replaceable>sortby</replaceable></optional></option> > + </term> > + <listitem> > + <para> > + Sort the containers by name, cpu use, or memory use. The > + <replaceable>sortby</replaceable> argument should be one of > + the letters n,c,d,m to sort by name, cpu use, disk I/O, or > + memory use respectively. The default is 'n'. > + </para> > + </listitem> > + </varlistentry> > + <varlistentry> > + <term> > + <option><optional>-r, --reverse</optional></option> > + </term> > + <listitem> > + <para> > + Reverse the default sort order. By default, names sort in > + ascending alphabetical order and values sort in descending > + amounts (ie. largest value first). > + </para> > + </listitem> > + </varlistentry> > + </variablelist> > + </refsect1> > + > + <refsect1> > + <title>Example</title> > + <variablelist> > + <varlistentry> > + <term>lxc-top --delay 1 --sort m</term> > + <listitem> > + <para> > + Display containers, updating every second, sorted by memory use. > + </para> > + </listitem> > + </varlistentry> > + </variablelist> > + </refsect1> > + > + &seealso; > + > + <refsect1> > + <title>Author</title> > + <para>Dwight Engen <email>dwight.en...@oracle.com</email></para> > + </refsect1> > + > +</refentry> > + > +<!-- Keep this comment at the end of the file > +Local variables: > +mode: sgml > +sgml-omittag:t > +sgml-shorttag:t > +sgml-minimize-attributes:nil > +sgml-always-quote-attributes:t > +sgml-indent-step:2 > +sgml-indent-data:t > +sgml-parent-document:nil > +sgml-default-dtd-file:nil > +sgml-exposed-tags:nil > +sgml-local-catalogs:nil > +sgml-local-ecat-files:nil > +End: > +--> > diff --git a/lxc.spec.in b/lxc.spec.in > index 65997d9..9fbd6b0 100644 > --- a/lxc.spec.in > +++ b/lxc.spec.in > @@ -38,6 +38,12 @@ Requires: python3 > BuildRequires: python3-devel > %endif > > +%define with_lua %{?_with_lua: 1} %{?!_with_lua: 0} > +%if %{with_lua} > +Requires: lua-filesystem > +BuildRequires: lua-devel > +%endif > + > %description > > The package "%{name}" provides the command lines to create and manage > @@ -69,6 +75,9 @@ development of the linux containers. > %setup > %build > PATH=$PATH:/usr/sbin:/sbin %configure $args \ > +%if %{with_lua} > + --enable-lua \ > +%endif > %if %{with_python} > --enable-python \ > %endif > @@ -107,6 +116,10 @@ rm -rf %{buildroot} > %defattr(-,root,root) > %{_libdir}/*.so.* > %{_libdir}/%{name} > +%if %{with_lua} > +%{_datadir}/lua > +%{_libdir}/lua > +%endif > %if %{with_python} > %{_libdir}/python* > %endif > diff --git a/src/Makefile.am b/src/Makefile.am > index 4e4d66b..c96cbe7 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -1 +1 @@ > -SUBDIRS = lxc tests python-lxc > +SUBDIRS = lxc tests python-lxc lua-lxc > diff --git a/src/lua-lxc/Makefile.am b/src/lua-lxc/Makefile.am > new file mode 100644 > index 0000000..f05eb72 > --- /dev/null > +++ b/src/lua-lxc/Makefile.am > @@ -0,0 +1,26 @@ > +if ENABLE_LUA > + > +luadir=$(datadir)/lua/5.1 > +sodir=$(libdir)/lua/5.1/lxc > + > +lua_SCRIPTS=lxc.lua > +EXTRA_DIST=lxc.lua > + > +so_PROGRAMS = core.so > + > +core_so_SOURCES = core.c > + > +AM_CFLAGS=-I$(top_srcdir)/src $(LUA_CFLAGS) -DVERSION=\"$(VERSION)\" > -DLXCPATH=\"$(LXCPATH)\" > + > +core_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) > + > +core_so_LDFLAGS = \ > + -shared \ > + -L$(top_srcdir)/src/lxc \ > + -Wl,-soname,core.so.$(firstword $(subst ., ,$(VERSION))) > + > +core_so_LDADD = -llxc $(LUA_LIBS) > + > +lxc.lua: > + > +endif > diff --git a/src/lua-lxc/core.c b/src/lua-lxc/core.c > new file mode 100644 > index 0000000..5c47aed > --- /dev/null > +++ b/src/lua-lxc/core.c > @@ -0,0 +1,382 @@ > +/* > + * lua-lxc: lua bindings for lxc > + * > + * Copyright © 2012 Oracle. > + * > + * Authors: > + * Dwight Engen <dwight.en...@oracle.com> > + * > + * This library is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2, as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License along > + * with this program; if not, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > + > +#define LUA_LIB > +#define _GNU_SOURCE > +#include <lua.h> > +#include <lauxlib.h> > +#include <string.h> > +#include <lxc/lxccontainer.h> > + > +#ifdef NO_CHECK_UDATA > +#define checkudata(L,i,tname) lua_touserdata(L, i) > +#else > +#define checkudata(L,i,tname) luaL_checkudata(L, i, tname) > +#endif > + > +#define lua_boxpointer(L,u) \ > + (*(void **) (lua_newuserdata(L, sizeof(void *))) = (u)) > + > +#define lua_unboxpointer(L,i,tname) \ > + (*(void **) (checkudata(L, i, tname))) > + > +#define CONTAINER_TYPENAME "lxc.container" > + > +static int container_new(lua_State *L) > +{ > + const char *name = luaL_checkstring(L, 1); > + struct lxc_container *c = lxc_container_new(name); > + > + if (c) { > + lua_boxpointer(L, c); > + luaL_getmetatable(L, CONTAINER_TYPENAME); > + lua_setmetatable(L, -2); > + } else { > + lua_pushnil(L); > + } > + return 1; > +} > + > +static int container_gc(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + /* XXX what to do if this fails? */ > + lxc_container_put(c); > + return 0; > +} > + > +static int container_config_file_name(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + char *config_file_name; > + > + config_file_name = c->config_file_name(c); > + lua_pushstring(L, config_file_name); > + free(config_file_name); > + return 1; > +} > + > +static int container_defined(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushboolean(L, !!c->is_defined(c)); > + return 1; > +} > + > +static int container_name(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushstring(L, c->name); > + return 1; > +} > + > +static int container_create(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + char *template_name = strdupa(luaL_checkstring(L, 2)); > + int argc = lua_gettop(L); > + char **argv; > + int i; > + > + argv = alloca((argc+1) * sizeof(char *)); > + for (i = 0; i < argc-2; i++) > + argv[i] = strdupa(luaL_checkstring(L, i+3)); > + argv[i] = NULL; > + > + lua_pushboolean(L, !!c->create(c, template_name, argv)); > + return 1; > +} > + > +static int container_destroy(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushboolean(L, !!c->destroy(c)); > + return 1; > +} > + > +/* container state */ > +static int container_start(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + int argc = lua_gettop(L); > + char **argv = NULL; > + int i,j; > + int useinit = 0; > + > + if (argc > 1) { > + argv = alloca((argc+1) * sizeof(char *)); > + for (i = 0, j = 0; i < argc-1; i++) { > + const char *arg = luaL_checkstring(L, i+2); > + > + if (!strcmp(arg, "useinit")) > + useinit = 1; > + else > + argv[j++] = strdupa(arg); > + } > + argv[j] = NULL; > + } > + > + c->want_daemonize(c); > + lua_pushboolean(L, !!c->start(c, useinit, argv)); > + return 1; > +} > + > +static int container_stop(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushboolean(L, !!c->stop(c)); > + return 1; > +} > + > +static int container_shutdown(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + int timeout = luaL_checkinteger(L, 2); > + > + lua_pushboolean(L, !!c->shutdown(c, timeout)); > + return 1; > +} > + > +static int container_wait(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + const char *state = luaL_checkstring(L, 2); > + int timeout = luaL_checkinteger(L, 3); > + > + lua_pushboolean(L, !!c->wait(c, state, timeout)); > + return 1; > +} > + > +static int container_freeze(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushboolean(L, !!c->freeze(c)); > + return 1; > +} > + > +static int container_unfreeze(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushboolean(L, !!c->unfreeze(c)); > + return 1; > +} > + > +static int container_running(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushboolean(L, !!c->is_running(c)); > + return 1; > +} > + > +static int container_state(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushstring(L, c->state(c)); > + return 1; > +} > + > +static int container_init_pid(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + > + lua_pushinteger(L, c->init_pid(c)); > + return 1; > +} > + > +/* configuration file methods */ > +static int container_load_config(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + int arg_cnt = lua_gettop(L); > + const char *alt_path = NULL; > + > + if (arg_cnt > 1) > + alt_path = luaL_checkstring(L, 2); > + > + lua_pushboolean(L, !!c->load_config(c, alt_path)); > + return 1; > +} > + > +static int container_save_config(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + int arg_cnt = lua_gettop(L); > + const char *alt_path = NULL; > + > + if (arg_cnt > 1) > + alt_path = luaL_checkstring(L, 2); > + > + lua_pushboolean(L, !!c->save_config(c, alt_path)); > + return 1; > +} > + > +static int container_clear_config_item(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + const char *key = luaL_checkstring(L, 2); > + > + lua_pushboolean(L, !!c->clear_config_item(c, key)); > + return 1; > +} > + > +static int container_get_config_item(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + const char *key = luaL_checkstring(L, 2); > + int len; > + char *value; > + > + len = c->get_config_item(c, key, NULL, 0); > + if (len <= 0) > + goto not_found; > + > + value = alloca(sizeof(char)*len + 1); > + if (c->get_config_item(c, key, value, len + 1) != len) > + goto not_found; > + > + lua_pushstring(L, value); > + return 1; > + > +not_found: > + lua_pushnil(L); > + return 1; > +} > + > +static int container_set_config_item(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + const char *key = luaL_checkstring(L, 2); > + const char *value = luaL_checkstring(L, 3); > + > + lua_pushboolean(L, !!c->set_config_item(c, key, value)); > + return 1; > +} > + > +static int container_get_keys(lua_State *L) > +{ > + struct lxc_container *c = lua_unboxpointer(L, 1, CONTAINER_TYPENAME); > + const char *key = NULL; > + int len; > + char *value; > + int arg_cnt = lua_gettop(L); > + > + if (arg_cnt > 1) > + key = luaL_checkstring(L, 2); > + > + len = c->get_keys(c, key, NULL, 0); > + if (len <= 0) > + goto not_found; > + > + value = alloca(sizeof(char)*len + 1); > + if (c->get_keys(c, key, value, len + 1) != len) > + goto not_found; > + > + lua_pushstring(L, value); > + return 1; > + > +not_found: > + lua_pushnil(L); > + return 1; > +} > + > +static luaL_Reg lxc_container_methods[] = > +{ > + {"create", container_create}, > + {"defined", container_defined}, > + {"destroy", container_destroy}, > + {"init_pid", container_init_pid}, > + {"name", container_name}, > + {"running", container_running}, > + {"state", container_state}, > + {"freeze", container_freeze}, > + {"unfreeze", container_unfreeze}, > + {"start", container_start}, > + {"stop", container_stop}, > + {"shutdown", container_shutdown}, > + {"wait", container_wait}, > + > + {"config_file_name", container_config_file_name}, > + {"load_config", container_load_config}, > + {"save_config", container_save_config}, > + {"get_config_item", container_get_config_item}, > + {"set_config_item", container_set_config_item}, > + {"clear_config_item", container_clear_config_item}, > + {"get_keys", container_get_keys}, > + {NULL, NULL} > +}; > + > +static int lxc_version_get(lua_State *L) { > + lua_pushstring(L, VERSION); > + return 1; > +} > + > +static int lxc_path_get(lua_State *L) { > + lua_pushstring(L, LXCPATH); > + return 1; > +} > + > +static luaL_Reg lxc_lib_methods[] = { > + {"version_get", lxc_version_get}, > + {"path_get", lxc_path_get}, > + {"container_new", container_new}, > + {NULL, NULL} > +}; > + > +static int lxc_lib_uninit(lua_State *L) { > + (void) L; > + /* this is where we would fini liblxc.so if we needed to */ > + return 0; > +} > + > +LUALIB_API int luaopen_lxc_core(lua_State *L) { > + /* this is where we would initialize liblxc.so if we needed to */ > + > + luaL_register(L, "lxc", lxc_lib_methods); > + > + lua_newuserdata(L, 0); > + lua_newtable(L); /* metatable */ > + lua_pushvalue(L, -1); > + lua_pushliteral(L, "__gc"); > + lua_pushcfunction(L, lxc_lib_uninit); > + lua_rawset(L, -3); > + lua_setmetatable(L, -3); > + lua_rawset(L, -3); > + > + luaL_newmetatable(L, CONTAINER_TYPENAME); > + lua_pushvalue(L, -1); /* push metatable */ > + lua_pushstring(L, "__gc"); > + lua_pushcfunction(L, container_gc); > + lua_settable(L, -3); > + lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ > + luaL_register(L, NULL, lxc_container_methods); > + lua_pop(L, 1); > + return 1; > +} > diff --git a/src/lua-lxc/lxc.lua b/src/lua-lxc/lxc.lua > new file mode 100755 > index 0000000..c71de48 > --- /dev/null > +++ b/src/lua-lxc/lxc.lua > @@ -0,0 +1,412 @@ > +-- > +-- lua lxc module > +-- > +-- Copyright © 2012 Oracle. > +-- > +-- Authors: > +-- Dwight Engen <dwight.en...@oracle.com> > +-- > +-- This library is free software; you can redistribute it and/or modify > +-- it under the terms of the GNU General Public License version 2, as > +-- published by the Free Software Foundation. > +-- > +-- This program is distributed in the hope that it will be useful, > +-- but WITHOUT ANY WARRANTY; without even the implied warranty of > +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +-- GNU General Public License for more details. > +-- > +-- You should have received a copy of the GNU General Public License along > +-- with this program; if not, write to the Free Software Foundation, Inc., > +-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > +-- > + > +local core = require("lxc.core") > +local lfs = require("lfs") > +local table = require("table") > +local string = require("string") > +local io = require("io") > +module("lxc", package.seeall) > + > +local lxc_path > +local cgroup_path > +local log_level = 3 > + > +-- the following two functions can be useful for debugging > +function printf(...) > + local function wrapper(...) io.write(string.format(...)) end > + local status, result = pcall(wrapper, ...) > + if not status then > + error(result, 2) > + end > +end > + > +function log(level, ...) > + if (log_level >= level) then > + printf(os.date("%Y-%m-%d %T ")) > + printf(...) > + end > +end > + > +function string:split(delim, max_cols) > + local cols = {} > + local start = 1 > + local nextc > + repeat > + nextc = string.find(self, delim, start) > + if (nextc and #cols ~= max_cols - 1) then > + table.insert(cols, string.sub(self, start, nextc-1)) > + start = nextc + #delim > + else > + table.insert(cols, string.sub(self, start, string.len(self))) > + nextc = nil > + end > + until nextc == nil or start > #self > + return cols > +end > + > +function dirname(path) > + local f,output > + f = io.popen("dirname " .. path) > + output = f:read('*all') > + f:close() > + return output:sub(1,-2) > +end > + > +function basename(path, suffix) > + local f,output > + f = io.popen("basename " .. path .. " " .. (suffix or "")) > + output = f:read('*all') > + f:close() > + return output:sub(1,-2) > +end > + > +function cgroup_path_get() > + local f,line,cgroup_path > + > + f = io.open("/proc/mounts", "r") > + if (f) then > + while true do > + local c > + line = f:read() > + c = line:split(" ", 6) > + if (c[1] == "cgroup") then > + cgroup_path = dirname(c[2]) > + break > + end > + end > + f:close() > + end > + if (not cgroup_path) then > + cgroup_path = "/sys/fs/cgroup" > + end > + return cgroup_path > +end > + > +-- container class > +container = {} > +container_mt = {} > +container_mt.__index = container > + > +function container:new(lname) > + local lcore > + local lnetcfg = {} > + local lstats = {} > + > + if lname then > + lcore = core.container_new(lname) > + end > + > + return setmetatable({ctname = lname, core = lcore, netcfg = lnetcfg, > stats = lstats}, container_mt) > +end > + > +-- methods interfacing to core functionality > +function container:config_file_name() > + return self.core:config_file_name() > +end > + > +function container:defined() > + return self.core:defined() > +end > + > +function container:init_pid() > + return self.core:init_pid() > +end > + > +function container:name() > + return self.core:name() > +end > + > +function container:start() > + return self.core:start() > +end > + > +function container:stop() > + return self.core:stop() > +end > + > +function container:shutdown(timeout) > + return self.core:shutdown(timeout) > +end > + > +function container:wait(state, timeout) > + return self.core:wait(state, timeout) > +end > + > +function container:freeze() > + return self.core:freeze() > +end > + > +function container:unfreeze() > + return self.core:unfreeze() > +end > + > +function container:running() > + return self.core:running() > +end > + > +function container:state() > + return self.core:state() > +end > + > +function container:create(template, ...) > + return self.core:create(template, ...) > +end > + > +function container:destroy() > + return self.core:destroy() > +end > + > +function container:append_config_item(key, value) > + return self.core:set_config_item(key, value) > +end > + > +function container:clear_config_item(key) > + return self.core:clear_config_item(key) > +end > + > +function container:get_config_item(key) > + local value > + local vals = {} > + > + value = self.core:get_config_item(key) > + > + -- check if it is a single item > + if (not value or not string.find(value, "\n")) then > + return value > + end > + > + -- it must be a list type item, make a table of it > + vals = value:split("\n", 1000) > + -- make it a "mixed" table, ie both dictionary and list for ease of use > + for _,v in ipairs(vals) do > + vals[v] = true > + end > + return vals > +end > + > +function container:set_config_item(key, value) > + return self.core:set_config_item(key, value) > +end > + > +function container:get_keys(base) > + local ktab = {} > + local keys > + > + if (base) then > + keys = self.core:get_keys(base) > + base = base .. "." > + else > + keys = self.core:get_keys() > + base = "" > + end > + if (keys == nil) then > + return nil > + end > + keys = keys:split("\n", 1000) > + for _,v in ipairs(keys) do > + local config_item = base .. v > + ktab[v] = self.core:get_config_item(config_item) > + end > + return ktab > +end > + > +function container:load_config(alt_path) > + if (alt_path) then > + return self.core:load_config(alt_path) > + else > + return self.core:load_config() > + end > +end > + > +function container:save_config(alt_path) > + if (alt_path) then > + return self.core:save_config(alt_path) > + else > + return self.core:save_config() > + end > +end > + > +-- methods for stats collection from various cgroup files > +-- read integers at given coordinates from a cgroup file > +function container:stat_get_ints(controller, item, coords) > + local f = > io.open(cgroup_path.."/"..controller.."/lxc/"..self.ctname.."/"..item, "r") > + local lines = {} > + local result = {} > + > + if (not f) then > + for k,c in ipairs(coords) do > + table.insert(result, 0) > + end > + else > + for line in f:lines() do > + table.insert(lines, line) > + end > + f:close() > + for k,c in ipairs(coords) do > + local col > + > + col = lines[c[1]]:split(" ", 80) > + local val = tonumber(col[c[2]]) > + table.insert(result, val) > + end > + end > + return unpack(result) > +end > + > +-- read an integer from a cgroup file > +function container:stat_get_int(controller, item) > + local f = > io.open(cgroup_path.."/"..controller.."/lxc/"..self.ctname.."/"..item, "r") > + if (not f) then > + return 0 > + end > + > + local line = f:read() > + f:close() > + -- if line is nil (on an error like Operation not supported because > + -- CONFIG_MEMCG_SWAP_ENABLED isn't enabled) return 0 > + return tonumber(line) or 0 > +end > + > +function container:stat_match_get_int(controller, item, match, column) > + local val > + local f = > io.open(cgroup_path.."/"..controller.."/lxc/"..self.ctname.."/"..item, "r") > + if (not f) then > + return 0 > + end > + > + for line in f:lines() do > + printf("matching line:%s with match:%s\n", line, match) > + if (string.find(line, match)) then > + local col > + > + col = line:split(" ", 80) > + val = tonumber(col[column]) or 0 > + printf("found line!! val:%d\n", val) > + end > + end > + f:close() > + return val > +end > + > +function stats_clear(stat) > + stat.mem_used = 0 > + stat.mem_limit = 0 > + stat.memsw_used = 0 > + stat.memsw_limit = 0 > + stat.cpu_use_nanos = 0 > + stat.cpu_use_user = 0 > + stat.cpu_use_sys = 0 > + stat.blkio = 0 > +end > + > +function container:stats_get(total) > + local stat = {} > + stat.mem_used = self:stat_get_int("memory", > "memory.usage_in_bytes") > + stat.mem_limit = self:stat_get_int("memory", > "memory.limit_in_bytes") > + stat.memsw_used = self:stat_get_int("memory", > "memory.memsw.usage_in_bytes") > + stat.memsw_limit = self:stat_get_int("memory", > "memory.memsw.limit_in_bytes") > + stat.cpu_use_nanos = self:stat_get_int("cpuacct", "cpuacct.usage") > + stat.cpu_use_user, > + stat.cpu_use_sys = self:stat_get_ints("cpuacct", "cpuacct.stat", {{1, > 2}, {2, 2}}) > + stat.blkio = self:stat_match_get_int("blkio", > "blkio.throttle.io_service_bytes", "Total", 2) > + > + if (total) then > + total.mem_used = total.mem_used + stat.mem_used > + total.mem_limit = total.mem_limit + stat.mem_limit > + total.memsw_used = total.memsw_used + stat.memsw_used > + total.memsw_limit = total.memsw_limit + stat.memsw_limit > + total.cpu_use_nanos = total.cpu_use_nanos + stat.cpu_use_nanos > + total.cpu_use_user = total.cpu_use_user + stat.cpu_use_user > + total.cpu_use_sys = total.cpu_use_sys + stat.cpu_use_sys > + total.blkio = total.blkio + stat.blkio > + end > + return stat > +end > + > + > + > +-- return configured containers found in LXC_PATH directory > +function containers_configured(names_only) > + local containers = {} > + > + for dir in lfs.dir(lxc_path) do > + if (dir ~= "." and dir ~= "..") > + then > + local cfgfile = lxc_path .. "/" .. dir .. "/config" > + local cfgattr = lfs.attributes(cfgfile) > + > + if (cfgattr and cfgattr.mode == "file") then > + if (names_only) then > + -- note, this is a "mixed" table, ie both dictionary and > list > + containers[dir] = true > + table.insert(containers, dir) > + else > + local ct = container:new(dir) > + -- note, this is a "mixed" table, ie both dictionary and > list > + containers[dir] = ct > + table.insert(containers, dir) > + end > + end > + end > + end > + table.sort(containers, function (a,b) return (a < b) end) > + return containers > +end > + > +-- return running containers found in cgroup fs > +function containers_running(names_only) > + local containers = {} > + local attr > + > + -- the lxc directory won't exist if no containers has ever been started > + attr = lfs.attributes(cgroup_path .. "/cpu/lxc") > + if (not attr) then > + return containers > + end > + > + for file in lfs.dir(cgroup_path .. "/cpu/lxc") do > + if (file ~= "." and file ~= "..") > + then > + local pathfile = cgroup_path .. "/cpu/lxc/" .. file > + local attr = lfs.attributes(pathfile) > + > + if (attr.mode == "directory") then > + if (names_only) then > + -- note, this is a "mixed" table, ie both dictionary and > list > + containers[file] = true > + table.insert(containers, file) > + else > + local ct = container:new(file) > + -- note, this is a "mixed" table, ie both dictionary and > list > + containers[file] = ct > + table.insert(containers, file) > + end > + end > + end > + end > + table.sort(containers, function (a,b) return (a < b) end) > + return containers > +end > + > +lxc_path = core.path_get() > +cgroup_path = cgroup_path_get() > diff --git a/src/lua-lxc/test/apitest.lua b/src/lua-lxc/test/apitest.lua > new file mode 100755 > index 0000000..14d2a9d > --- /dev/null > +++ b/src/lua-lxc/test/apitest.lua > @@ -0,0 +1,302 @@ > +#!/usr/bin/env lua > +-- > +-- test the lxc lua api > +-- > +-- Copyright © 2012 Oracle. > +-- > +-- Authors: > +-- Dwight Engen <dwight.en...@oracle.com> > +-- > +-- This library is free software; you can redistribute it and/or modify > +-- it under the terms of the GNU General Public License version 2, as > +-- published by the Free Software Foundation. > +-- > +-- This program is distributed in the hope that it will be useful, > +-- but WITHOUT ANY WARRANTY; without even the implied warranty of > +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +-- GNU General Public License for more details. > +-- > +-- You should have received a copy of the GNU General Public License along > +-- with this program; if not, write to the Free Software Foundation, Inc., > +-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > +-- > + > +local lxc = require("lxc") > +local getopt = require("alt_getopt") > + > +local LXC_PATH = lxc.path_get() > + > +local container > +local cfg_containers = {} > +local optarg = {} > +local optind = {} > + > +function printf(...) > + local function wrapper(...) io.write(string.format(...)) end > + local status, result = pcall(wrapper, ...) > + if not status then > + error(result, 2) > + end > +end > + > +function log(level, ...) > + if (optarg["v"] >= level) then > + printf(os.date("%Y-%m-%d %T ")) > + printf(...) > + printf("\n") > + end > +end > + > +function die(...) > + printf(...) > + os.exit(1) > +end > + > +function test_global_info() > + local cfg_containers > + local run_containers > + > + log(0, "%-20s %s", "LXC version:", lxc.version_get()) > + log(0, "%-20s %s", "Container name:", optarg["n"]) > + if (optarg["c"]) then > + log(0, "%-20s %s", "Creating container:", "yes") > + log(0, "%-20s %s", "With template:", optarg["t"]) > + end > + log(0, "%-20s %s", "Containers path:", LXC_PATH) > + > + cfg_containers = lxc.containers_configured() > + log(0, "%-20s", "Containers configured:") > + for _,v in ipairs(cfg_containers) do > + log(0, " %s", v) > + end > + > + run_containers = lxc.containers_running(true) > + log(0, "%-20s", "Containers running:") > + for _,v in ipairs(run_containers) do > + log(0, " %s", v) > + end > +end > + > +function test_container_new() > + container = lxc.container:new(optarg["n"]) > + assert(container ~= nil) > + assert(container:config_file_name() == string.format("%s/%s/config", > LXC_PATH, optarg["n"])) > +end > + > +function test_container_create() > + if (optarg["c"]) then > + log(0, "%-20s %s", "Destroy existing container:", optarg["n"]) > + container:destroy() > + assert(container:defined() == false) > + else > + local cfg_containers = lxc.containers_configured() > + if (cfg_containers[optarg["n"]]) then > + log(0, "%-20s %s", "Use existing container:", optarg["n"]) > + return > + end > + end > + log(0, "%-20s %s", "Creating rootfs using:", optarg["t"]) > + container:create(optarg["t"]) > + assert(container:defined() == true) > + assert(container:name() == optarg["n"]) > +end > + > +function test_container_started() > + local now_running > + log(2, "state:%s pid:%d\n", container:state(), container:init_pid()) > + assert(container:init_pid() > 1) > + assert(container:running() == true) > + assert(container:state() == "RUNNING") > + now_running = lxc.containers_running(true) > + assert(now_running[optarg["n"]] ~= nil) > + log(1, "%-20s %s", "Running, init pid:", container:init_pid()) > +end > + > +function test_container_stopped() > + local now_running > + assert(container:init_pid() == -1) > + assert(container:running() == false) > + assert(container:state() == "STOPPED") > + now_running = lxc.containers_running(true) > + assert(now_running[optarg["n"]] == nil) > +end > + > +function test_container_frozen() > + local now_running > + assert(container:init_pid() > 1) > + assert(container:running() == true) > + assert(container:state() == "FROZEN") > + now_running = lxc.containers_running(true) > + assert(now_running[optarg["n"]] ~= nil) > +end > + > +function test_container_start() > + log(0, "Starting...") > + if (not container:start()) then > + log(1, "Start returned failure, waiting another 10 seconds...") > + container:wait("RUNNING", 10) > + end > + container:wait("RUNNING", 1) > +end > + > +function test_container_stop() > + log(0, "Stopping...") > + if (not container:stop()) then > + log(1, "Stop returned failure, waiting another 10 seconds...") > + container:wait("STOPPED", 10) > + end > + container:wait("STOPPED", 1) > +end > + > +function test_container_freeze() > + log(0, "Freezing...") > + if (not container:freeze()) then > + log(1, "Freeze returned failure, waiting another 10 seconds...") > + container:wait("FROZEN", 10) > + end > +end > + > +function test_container_unfreeze() > + log(0, "Unfreezing...") > + if (not container:unfreeze()) then > + log(1, "Unfreeze returned failure, waiting another 10 seconds...") > + container:wait("RUNNING", 10) > + end > +end > + > +function test_container_shutdown() > + log(0, "Shutting down...") > + container:shutdown(5) > + > + if (container:running()) then > + test_container_stop() > + end > +end > + > +function test_container_in_cfglist(should_find) > + local cfg_containers = lxc.containers_configured() > + > + if (should_find) then > + assert(cfg_containers[container:name()] ~= nil) > + else > + assert(cfg_containers[container:name()] == nil) > + end > +end > + > +function test_config_items() > + log(0, "Test set/clear configuration items...") > + > + -- test setting a 'single type' item > + assert(container:get_config_item("lxc.utsname") == optarg["n"]) > + container:set_config_item("lxc.utsname", "foobar") > + assert(container:get_config_item("lxc.utsname") == "foobar") > + container:set_config_item("lxc.utsname", optarg["n"]) > + assert(container:get_config_item("lxc.utsname") == optarg["n"]) > + > + -- test clearing/setting a 'list type' item > + container:clear_config_item("lxc.cap.drop") > + container:set_config_item("lxc.cap.drop", "new_cap1") > + container:set_config_item("lxc.cap.drop", "new_cap2") > + local cap_drop = container:get_config_item("lxc.cap.drop") > + assert(cap_drop["new_cap1"] ~= nil) > + assert(cap_drop["new_cap2"] ~= nil) > + -- note: clear_config_item only works on list type items > + container:clear_config_item("lxc.cap.drop") > + assert(container:get_config_item("lxc.cap.drop") == nil) > + > + local altname = "/tmp/" .. optarg["n"] .. ".altconfig" > + log(0, "Test saving to an alternate (%s) config file...", altname) > + assert(container:save_config(altname)) > + assert(os.remove(altname)) > +end > + > +function test_config_mount_entries() > + local mntents > + > + -- mount entries are a list type item > + mntents = container:get_config_item("lxc.mount.entry") > + log(0, "Mount entries:") > + for _,v in ipairs(mntents) do > + log(0, " %s", v) > + end > +end > + > +function test_config_keys() > + local keys > + > + keys = container:get_keys() > + log(0, "Top level keys:") > + for k,v in pairs(keys) do > + log(0, " %s = %s", k, v or "") > + end > +end > + > +function test_config_network(net_nr) > + log(0, "Test network %d config...", net_nr) > + local netcfg > + > + netcfg = container:get_keys("lxc.network." .. net_nr) > + if (netcfg == nil) then > + return > + end > + for k,v in pairs(netcfg) do > + log(0, " %s = %s", k, v or "") > + end > + assert(netcfg["flags"] == "up") > + assert(container:get_config_item("lxc.network."..net_nr..".type") == > "veth") > +end > + > + > +function usage() > + die("Usage: apitest <options>\n" .. > + " -v|--verbose increase verbosity with each -v\n" .. > + " -h|--help print help message\n" .. > + " -n|--name name of container to use for testing\n" .. > + " -c|--create create the test container anew\n" .. > + " -l|--login do interactive login test\n" .. > + " -t|--template template to use when creating test container\n" > + ) > +end > + > +local long_opts = { > + verbose = "v", > + help = "h", > + name = "n", > + create = "c", > + template = "t", > +} > + > +optarg,optind = alt_getopt.get_opts (arg, "hvn:ct:", long_opts) > +optarg["v"] = tonumber(optarg["v"]) or 0 > +optarg["n"] = optarg["n"] or "lua-apitest" > +optarg["c"] = optarg["c"] or nil > +optarg["t"] = optarg["t"] or "busybox" > +if (optarg["h"] ~= nil) then > + usage() > +end > + > +test_global_info() > +test_container_new() > +test_container_create() > +test_container_stopped() > +test_container_in_cfglist(true) > + > +test_config_items() > +test_config_keys() > +test_config_mount_entries() > +test_config_network(0) > + > +test_container_start() > +test_container_started() > + > +test_container_freeze() > +test_container_frozen() > +test_container_unfreeze() > +test_container_started() > + > +test_container_shutdown() > +test_container_stopped() > +container:destroy() > +test_container_in_cfglist(false) > + > +log(0, "All tests passed") > diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am > index b55a20c..48234d8 100644 > --- a/src/lxc/Makefile.am > +++ b/src/lxc/Makefile.am > @@ -124,8 +124,9 @@ bin_SCRIPTS = \ > lxc-shutdown \ > lxc-destroy > > +EXTRA_DIST= > if ENABLE_PYTHON > - EXTRA_DIST = lxc-device lxc-ls > + EXTRA_DIST += lxc-device lxc-ls > bin_SCRIPTS += lxc-device > bin_SCRIPTS += lxc-ls > bin_SCRIPTS += lxc-start-ephemeral > @@ -133,6 +134,11 @@ else > bin_SCRIPTS += legacy/lxc-ls > endif > > +if ENABLE_LUA > + EXTRA_DIST += lxc-top > + bin_SCRIPTS += lxc-top > +endif > + > bin_PROGRAMS = \ > lxc-attach \ > lxc-unshare \ > diff --git a/src/lxc/lxc-top b/src/lxc/lxc-top > new file mode 100755 > index 0000000..31aaecf > --- /dev/null > +++ b/src/lxc/lxc-top > @@ -0,0 +1,242 @@ > +#!/usr/bin/env lua > +-- > +-- top(1) like monitor for lxc containers > +-- > +-- Copyright © 2012 Oracle. > +-- > +-- Authors: > +-- Dwight Engen <dwight.en...@oracle.com> > +-- > +-- This library is free software; you can redistribute it and/or modify > +-- it under the terms of the GNU General Public License version 2, as > +-- published by the Free Software Foundation. > +-- > +-- This program is distributed in the hope that it will be useful, > +-- but WITHOUT ANY WARRANTY; without even the implied warranty of > +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +-- GNU General Public License for more details. > +-- > +-- You should have received a copy of the GNU General Public License along > +-- with this program; if not, write to the Free Software Foundation, Inc., > +-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > +-- > + > +local lxc = require("lxc") > +local getopt = require("alt_getopt") > +local lfs = require("lfs") > + > +local USER_HZ = 100 > +local ESC = string.format("%c", 27) > +local TERMCLEAR = ESC.."[H"..ESC.."[J" > +local TERMNORM = ESC.."[0m" > +local TERMBOLD = ESC.."[1m" > +local TERMRVRS = ESC.."[7m" > + > +local containers = {} > +local stats = {} > +local stats_total = {} > +local max_containers > + > +function printf(...) > + local function wrapper(...) io.write(string.format(...)) end > + local status, result = pcall(wrapper, ...) > + if not status then > + error(result, 2) > + end > +end > + > +function string:split(delim, max_cols) > + local cols = {} > + local start = 1 > + local nextc > + repeat > + nextc = string.find(self, delim, start) > + if (nextc and #cols ~= max_cols - 1) then > + table.insert(cols, string.sub(self, start, nextc-1)) > + start = nextc + #delim > + else > + table.insert(cols, string.sub(self, start, string.len(self))) > + nextc = nil > + end > + until nextc == nil or start > #self > + return cols > +end > + > +function strsisize(size, width) > + local KiB = 1024 > + local MiB = 1048576 > + local GiB = 1073741824 > + local TiB = 1099511627776 > + local PiB = 1125899906842624 > + local EiB = 1152921504606846976 > + local ZiB = 1180591620717411303424 > + > + if (size >= ZiB) then > + return string.format("%d.%2.2d ZB", size / ZiB, (math.floor(size % ZiB) > * 100) / ZiB) > + end > + if (size >= EiB) then > + return string.format("%d.%2.2d EB", size / EiB, (math.floor(size % EiB) > * 100) / EiB) > + end > + if (size >= PiB) then > + return string.format("%d.%2.2d PB", size / PiB, (math.floor(size % PiB) > * 100) / PiB) > + end > + if (size >= TiB) then > + return string.format("%d.%2.2d TB", size / TiB, (math.floor(size % TiB) > * 100) / TiB) > + end > + if (size >= GiB) then > + return string.format("%d.%2.2d GB", size / GiB, (math.floor(size % GiB) > * 100) / GiB) > + end > + if (size >= MiB) then > + return string.format("%d.%2.2d MB", size / MiB, (math.floor(size % MiB) > * 1000) / (MiB * 10)) > + end > + if (size >= KiB) then > + return string.format("%d.%2.2d KB", size / KiB, (math.floor(size % KiB) > * 1000) / (KiB * 10)) > + end > + return string.format("%3d.00 ", size) > +end > + > +function usleep(n) > + if (n ~= 0) then > + ret = os.execute("usleep " .. tonumber(n)) > + if (ret ~= 0) then > + os.exit(0) > + end > + end > +end > + > +function tty_lines() > + local rows = 25 > + local f = assert(io.popen("stty -a | head -n 1")) > + for line in f:lines() do > + local stty_rows > + _,_,stty_rows = string.find(line, "rows (%d+)") > + if (stty_rows ~= nil) then > + rows = stty_rows > + break > + end > + end > + f:close() > + return rows > +end > + > +function container_sort(a, b) > + if (optarg["r"]) then > + if (optarg["s"] == "n") then return (a > b) > + elseif (optarg["s"] == "c") then return (stats[a].cpu_use_nanos < > stats[b].cpu_use_nanos) > + elseif (optarg["s"] == "d") then return (stats[a].blkio < > stats[b].blkio) > + elseif (optarg["s"] == "m") then return (stats[a].mem_used < > stats[b].mem_used) > + end > + else > + if (optarg["s"] == "n") then return (a < b) > + elseif (optarg["s"] == "c") then return (stats[a].cpu_use_nanos > > stats[b].cpu_use_nanos) > + elseif (optarg["s"] == "d") then return (stats[a].blkio > > stats[b].blkio) > + elseif (optarg["s"] == "m") then return (stats[a].mem_used > > stats[b].mem_used) > + end > + end > +end > + > +function container_list_update() > + local now_running > + > + lxc.stats_clear(stats_total) > + now_running = lxc.containers_running(true) > + > + -- check for newly started containers > + for _,v in ipairs(now_running) do > + if (containers[v] == nil) then > + local ct = lxc.container:new(v) > + -- note, this is a "mixed" table, ie both dictionary and list > + containers[v] = ct > + table.insert(containers, v) > + end > + end > + > + -- check for newly stopped containers > + local indx = 1 > + while (indx <= #containers) do > + local ctname = containers[indx] > + if (now_running[ctname] == nil) then > + containers[ctname] = nil > + stats[ctname] = nil > + table.remove(containers, indx) > + else > + indx = indx + 1 > + end > + end > + > + -- get stats for all current containers and resort the list > + lxc.stats_clear(stats_total) > + for _,ctname in ipairs(containers) do > + stats[ctname] = containers[ctname]:stats_get(stats_total) > + end > + table.sort(containers, container_sort) > +end > + > +function stats_print_header() > + printf(TERMRVRS .. TERMBOLD) > + printf("%-15s %8s %8s %8s %10s %10s\n", "Container", "CPU", "CPU", > "CPU", "BlkIO", "Mem") > + printf("%-15s %8s %8s %8s %10s %10s\n", "Name", "Used", "Sys", > "User", "Total", "Used") > + printf(TERMNORM) > +end > + > +function stats_print(name, stats) > + printf("%-15s %8.2f %8.2f %8.2f %10s %10s", > + name, > + stats.cpu_use_nanos / 1000000000, > + stats.cpu_use_sys / USER_HZ, > + stats.cpu_use_user / USER_HZ, > + strsisize(stats.blkio), > + strsisize(stats.mem_used)) > +end > + > +function usage() > + printf("Usage: lxc-top [options]\n" .. > + " -h|--help print this help message\n" .. > + " -m|--max display maximum number of containers\n" .. > + " -d|--delay delay in seconds between refreshes (default: 3.0)\n" > .. > + " -s|--sort sort by [n,c,d,m] (default: n) where\n" .. > + " n = Name\n" .. > + " c = CPU use\n" .. > + " d = Disk I/O use\n" .. > + " m = Memory use\n" .. > + " -r|--reverse sort in reverse (descending) order\n" > + ) > + os.exit(1) > +end > + > +local long_opts = { > + help = "h", > + delay = "d", > + max = "m", > + reverse = "r", > + sort = "s", > +} > + > +optarg,optind = alt_getopt.get_opts (arg, "hd:m:rs:", long_opts) > +optarg["d"] = tonumber(optarg["d"]) or 3.0 > +optarg["m"] = tonumber(optarg["m"]) or tonumber(tty_lines() - 3) > +optarg["r"] = optarg["r"] or false > +optarg["s"] = optarg["s"] or "n" > +if (optarg["h"] ~= nil) then > + usage() > +end > + > +while true > +do > + container_list_update() > + -- if some terminal we care about doesn't support the simple escapes, we > + -- may fall back to this, or ncurses. ug. > + --os.execute("tput clear") > + printf(TERMCLEAR) > + stats_print_header() > + for index,ctname in ipairs(containers) do > + stats_print(ctname, stats[ctname]) > + printf("\n") > + if (index >= optarg["m"]) then > + break > + end > + end > + stats_print(string.format("TOTAL (%-2d)", #containers), stats_total) > + io.flush() > + usleep(optarg["d"] * 1000000) > +end > -- Stéphane Graber Ubuntu developer http://www.ubuntu.com
signature.asc
Description: OpenPGP digital signature
------------------------------------------------------------------------------ Master Visual Studio, SharePoint, SQL, ASP.NET, C# 2012, HTML5, CSS, MVC, Windows 8 Apps, JavaScript and much more. Keep your skills current with LearnDevNow - 3,200 step-by-step video tutorials by Microsoft MVPs and experts. ON SALE this month only -- learn more at: http://p.sf.net/sfu/learnnow-d2d
_______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel