[lxc-devel] [PATCH] add lua binding for the lxc API

Stéphane Graber stgraber at ubuntu.com
Tue Jan 22 23:39:00 UTC 2013


On 01/22/2013 11:03 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.engen at oracle.com>

I was just about to ask about the status of your lua bindings when I saw
that one in my mailbox ;)

I wrote a couple of comments inline below.

I'm also assuming that if we merge this we can count on you to keep the
binding up to date as we do API changes?

We try not to break it and I don't think we actually ever broke it since
it was first pushed to the staging branch, however that may still happen
until we decide that it's good and release 1.0.

> ---
>  Makefile.am                  |   6 +-
>  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      |  30 ++++
>  src/lua-lxc/core.c           | 382 +++++++++++++++++++++++++++++++++++++++
>  src/lua-lxc/lxc-top          | 242 +++++++++++++++++++++++++
>  src/lua-lxc/lxc.lua          | 412 +++++++++++++++++++++++++++++++++++++++++++
>  src/lua-lxc/test/apitest.lua | 302 +++++++++++++++++++++++++++++++
>  11 files changed, 1574 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 100644 src/lua-lxc/lxc-top
>  create mode 100644 src/lua-lxc/lxc.lua
>  create mode 100644 src/lua-lxc/test/apitest.lua
> 
> diff --git a/Makefile.am b/Makefile.am
> index 7b32326..89260e5 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -6,6 +6,10 @@ SUBDIRS = config src templates doc
>  DIST_SUBDIRS = config src templates doc
>  EXTRA_DIST = autogen.sh lxc.spec CONTRIBUTING MAINTAINERS ChangeLog
>  
> +if ENABLE_LUA
> +RPMARGS = --with lua
> +endif
> +

Should we have something similar for python? (in a separate patch obviously)

>  pcdatadir = $(libdir)/pkgconfig
>  pcdata_DATA = lxc.pc
>  
> @@ -17,4 +21,4 @@ ChangeLog::
>  	@touch ChangeLog
>  
>  rpm: dist
> -	rpmbuild --clean -ta ${distdir}.tar.gz
> +	rpmbuild --clean -ta ${distdir}.tar.gz $(RPMARGS)
> 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.engen at 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.engen at 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 c7470b8..e77cbc3 100644
> --- a/lxc.spec.in
> +++ b/lxc.spec.in
> @@ -32,6 +32,13 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-build
>  Requires: libcap openssl rsync
>  BuildRequires: libcap libcap-devel docbook2X
>  
> +%define with_lua %{?_with_lua: 1} %{?!_with_lua: 0}
> +%if %{with_lua}
> +Requires: lua-filesystem
> +BuildRequires: lua-devel
> +%define enable_lua --enable-lua
> +%endif
> +
>  %description
>  
>  The package "%{name}" provides the command lines to create and manage
> @@ -62,7 +69,7 @@ development of the linux containers.
>  %prep
>  %setup
>  %build
> -PATH=$PATH:/usr/sbin:/sbin %configure $args --disable-rpath
> +PATH=$PATH:/usr/sbin:/sbin %configure $args --disable-rpath %{?enable_lua}
>  make %{?_smp_mflags}
>  
>  %install
> @@ -98,6 +105,10 @@ rm -rf %{buildroot}
>  %{_libdir}/*.so.*
>  %{_libdir}/%{name}
>  %{_localstatedir}/*
> +%if %{with_lua}
> +%{_datadir}/lua
> +%{_libdir}/lua
> +%endif
>  %attr(4555,root,root) %{_libexecdir}/%{name}/lxc-init
>  
>  %files devel
> 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..3db3b7c
> --- /dev/null
> +++ b/src/lua-lxc/Makefile.am
> @@ -0,0 +1,30 @@
> +if ENABLE_LUA
> +
> +luadir=$(datadir)/lua/5.1
> +sodir=$(libdir)/lua/5.1/lxc
> +
> +lua_SCRIPTS=lxc.lua
> +EXTRA_DIST=lxc.lua lxc-top

Any reason not to put lxc-top in src/lxc/ similarly to what was done for
some of the python-based tools?

> +so_PROGRAMS = core.so
> +
> +core_so_SOURCES = core.c
> +
> +bin_SCRIPTS = lxc-top
> +
> +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:
> +
> +lxc-top:
> +
> +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.engen at 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-top b/src/lua-lxc/lxc-top
> new file mode 100644
> index 0000000..31aaecf
> --- /dev/null
> +++ b/src/lua-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.engen at 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
> diff --git a/src/lua-lxc/lxc.lua b/src/lua-lxc/lxc.lua
> new file mode 100644
> 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.engen at 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 100644
> 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.engen at 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")
> 


-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 901 bytes
Desc: OpenPGP digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20130122/5e107f8b/attachment.pgp>


More information about the lxc-devel mailing list