[lxc-devel] [RFC 1/1] snapshots in api: define lxcapi_snapshot
Stéphane Graber
stgraber at ubuntu.com
Wed Sep 4 17:07:35 UTC 2013
On Wed, Sep 04, 2013 at 11:05:02AM -0500, Serge Hallyn wrote:
> Hi,
>
> before I go on with the snapshots-in-api patchset, I wanted to floar
> this and ask a few questions, as I'm having a hard time deciding how
> we want to talk over the API.
>
> (Though as I've been typing this out, I think I now see how I want it
> to look.)
>
> First, the basics: if you have lxcpath=/var/lib/lxc and take a first
> snapshot of container c1, then the container will be
> /var/lib/lxcsnaps/c1/snap0. Next snapshot will be /var/lib/lxcsnaps/c1/snap1.
> You can pass a text file containing a commit comment, which will simply be
> stored at /var/lib/lxc/lxcsnaps/c0/snap0/comment, and a timestamp is
> created at /var/lib/lxc/lxcsnaps/c0/snap0/tx.
>
> To restore that snap1 as container c2, I'm thinking you would
>
> c = lxc_container_new("c0", "/var/lib/lxc");
> c2 = c->restore(c, "snap1", "c2");
> lxc_container_put(c2);
> lxc_container_put(c);
>
> (Note this doesn't match what's in the patch below). There are other
> ways it could be done. For instance we could open the snapshot as
> its own container and call restore on that, i.e.
>
> c = lxc_container_new("snap1", "/var/lib/lxcsnaps/c1");
> c2 = c->restore(c, "c2");
>
> I think I like the first option better though as it keeps callers
> from digging into the storage details. Thoughts?
>
> In addition, I'll add lxcapi_snapshot_destroy(), which will look like:
>
> c = lxc_container_new("c0", "/var/lib/lxc");
> c->snapshot_destroy(c, "snap1");
> lxc_container_put(c);
>
> As for snapshot_list, I'm thinking it will just look like:
>
> c = lxc_container_new("c0", "/var/lib/lxc");
> ns = c->snapshot_entries(c, NULL, 0);
> for (i=0; i<ns; i++) {
> c2 = c->get_snapshot(c, i);
> printf("name is %s, lxcpath %s\n", c->name, c->config_path);
> lxc_container_put(c2);
> }
> lxc_container_put(c);
>
> with 'timestamp' and 'comment_file' fields being added to struct
> container_struct, usually both NULL.
So I think that makes sense and I also prefer the first option you
described. Upon first read of your description it felt a bit weird
having this called restore() since it doesn't actually restore the
snapshot so much as create a new container based on it, but I suppose
that's fine since restore(c, "snpa1", "c0") will probably do what you'd
expect (restore previous state of a container from a snapshot name).
>
> Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
>
> ---
> src/lxc/lxccontainer.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++
> src/lxc/lxccontainer.h | 14 +++++++
> src/tests/Makefile.am | 7 +++-
> src/tests/snapshot.c | 97 ++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 222 insertions(+), 2 deletions(-)
> create mode 100644 src/tests/snapshot.c
>
> diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
> index f4d1f8e..65e7ebe 100644
> --- a/src/lxc/lxccontainer.c
> +++ b/src/lxc/lxccontainer.c
> @@ -68,6 +68,13 @@ static bool file_exists(char *f)
> return stat(f, &statbuf) == 0;
> }
>
> +static void remove_trailing_slashes(char *p)
> +{
> + int l = strlen(p);
> + while (--l >= 0 && (p[l] == '/' || p[l] == '\n'))
> + p[l] = '\0';
> +}
> +
> /*
> * A few functions to help detect when a container creation failed.
> * If a container creation was killed partway through, then trying
> @@ -2189,6 +2196,101 @@ static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t
> return lxc_wait_for_pid_status(pid);
> }
>
> +int get_next_index(const char *lxcpath, char *cname)
> +{
> + char *fname;
> + struct stat sb;
> + int i = 0, ret;
> +
> + fname = alloca(strlen(lxcpath) + strlen(cname) + 20);
> + while (1) {
> + sprintf(fname, "%s/%s_%d", lxcpath, cname, i);
> + ret = stat(fname, &sb);
> + if (ret != 0)
> + return i;
> + i++;
> + }
> +}
> +
> +static bool lxcapi_snapshot(struct lxc_container *c, char *commentfile)
> +{
> + int i, flags, ret;
> + struct lxc_container *c2;
> + char snappath[MAXPATHLEN], newname[20];
> +
> + // /var/lib/lxc -> /var/lib/lxcsnaps \0
> + ret = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
> + if (ret < 0 || ret >= MAXPATHLEN)
> + return false;
> + i = get_next_index(snappath, c->name);
> +
> + if (mkdir_p(snappath, 0755) < 0) {
> + ERROR("Failed to create snapshot directory %s", snappath);
> + return false;
> + }
> +
> + ret = snprintf(newname, 20, "snap%d", i);
> + if (ret < 0 || ret >= 20)
> + return false;
> +
> + flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_KEEPMACADDR | LXC_CLONE_KEEPNAME;
> + c2 = c->clone(c, newname, snappath, flags, NULL, NULL, 0, NULL);
> + if (!c2) {
> + ERROR("clone of %s:%s failed\n", c->config_path, c->name);
> + return false;
> + }
> +
> + lxc_container_put(c2);
> +
> + // Now write down the creation time
> + time_t timer;
> + char buffer[25];
> + struct tm* tm_info;
> +
> + time(&timer);
> + tm_info = localtime(&timer);
> +
> + strftime(buffer, 25, "%Y:%m:%d %H:%M:%S", tm_info);
> +
> + char *dfnam = alloca(strlen(snappath) + strlen(newname) + 5);
> + sprintf(dfnam, "%s/%s/ts", snappath, newname);
> + FILE *f = fopen(dfnam, "w");
> + if (!f) {
> + ERROR("Failed to open %s\n", dfnam);
> + return false;
> + }
> + if (fprintf(f, "%s", buffer) < 0) {
> + SYSERROR("Writing timestamp");
> + fclose(f);
> + return false;
> + }
> + if (fclose(f) != 0) {
> + SYSERROR("Writing timestamp");
> + return false;
> + }
> +
> + if (commentfile) {
> + // $p / $name / comment \0
> + int len = strlen(snappath) + strlen(newname) + 10;
> + char *path = alloca(len);
> + sprintf(path, "%s/%s/comment", snappath, newname);
> + return copy_file(commentfile, path) < 0 ? false : true;
> + }
> +
> + return true;
> +}
> +
> +static bool lxcapi_snapshot_list(struct lxc_container *c, char *dest, int *len)
> +{
> + return false; // not yet implemented
> +}
> +
> +static bool lxcapi_snapshot_restore(struct lxc_container *c, char *newname)
> +{
> + return false;
> +
> +}
> +
> static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...)
> {
> va_list ap;
> @@ -2234,6 +2336,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
> goto err;
> }
>
> + remove_trailing_slashes(c->config_path);
> c->name = malloc(strlen(name)+1);
> if (!c->name) {
> fprintf(stderr, "Error allocating lxc_container name\n");
> @@ -2302,6 +2405,9 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
> c->attach = lxcapi_attach;
> c->attach_run_wait = lxcapi_attach_run_wait;
> c->attach_run_waitl = lxcapi_attach_run_waitl;
> + c->snapshot = lxcapi_snapshot;
> + c->snapshot_list = lxcapi_snapshot_list;
> + c->snapshot_restore = lxcapi_snapshot_restore;
>
> /* we'll allow the caller to update these later */
> if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) {
> diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
> index ad6afa1..121eede 100644
> --- a/src/lxc/lxccontainer.h
> +++ b/src/lxc/lxccontainer.h
> @@ -177,6 +177,20 @@ struct lxc_container {
> /* run program in container, wait for it to exit */
> int (*attach_run_wait)(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char * const argv[]);
> int (*attach_run_waitl)(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...);
> +
> + /*
> + * snapshot api:
> + * If you have /var/lib/lxc/c1 and ask to create the first snapshot, it will
> + * be created as /var/lib/lxcsnaps/c1/snap1
> + * The second will be /var/lib/lxcsnaps/c1/snap2, etc.
> + *
> + * This will want some extensions but until we use it I'm not sure what
> + * those will be. We'll want at least a way to get comment details for
> + * snapshots
> + */
> + bool (*snapshot)(struct lxc_container *c, char *commentfile);
> + bool (*snapshot_list)(struct lxc_container *c, char *dest, int *len);
> + bool (*snapshot_restore)(struct lxc_container *c, char *newname);
> };
>
> struct lxc_container *lxc_container_new(const char *name, const char *configpath);
> diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
> index 76b38f9..a6dacab 100644
> --- a/src/tests/Makefile.am
> +++ b/src/tests/Makefile.am
> @@ -17,6 +17,7 @@ lxc_test_clonetest_SOURCES = clonetest.c
> lxc_test_console_SOURCES = console.c
> lxc_usernic_test_SOURCES = ../lxc/lxc_user_nic.c ../lxc/nl.c
> lxc_usernic_test_CFLAGS = -DISTEST
> +lxc_test_snapshot_SOURCES = snapshot.c
>
> AM_CFLAGS=-I$(top_srcdir)/src \
> -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
> @@ -28,7 +29,8 @@ AM_CFLAGS=-I$(top_srcdir)/src \
> bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \
> lxc-test-destroytest lxc-test-saveconfig lxc-test-createtest \
> lxc-test-shutdowntest lxc-test-get_item lxc-test-getkeys lxc-test-lxcpath \
> - lxc-test-cgpath lxc-test-clonetest lxc-test-console lxc-usernic-test
> + lxc-test-cgpath lxc-test-clonetest lxc-test-console lxc-usernic-test \
> + lxc-test-snapshot
>
> bin_SCRIPTS = lxc-test-usernic
>
> @@ -48,4 +50,5 @@ EXTRA_DIST = \
> clonetest.c \
> startone.c \
> console.c \
> - lxc-test-usernic
> + lxc-test-usernic \
> + snapshot.c
> diff --git a/src/tests/snapshot.c b/src/tests/snapshot.c
> new file mode 100644
> index 0000000..11e905e
> --- /dev/null
> +++ b/src/tests/snapshot.c
> @@ -0,0 +1,97 @@
> +/* liblxcapi
> + *
> + * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program 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.
> + */
> +#include "../lxc/lxccontainer.h"
> +
> +#include <errno.h>
> +#include <stdlib.h>
> +#include "../lxc/lxc.h"
> +
> +#define MYNAME "snapxxx1"
> +
> +void try_to_remove()
> +{
> + struct lxc_container *c;
> + char snappath[1024];
> + snprintf(snappath, 1024, "%ssnaps/%s", lxc_get_default_config_path(), MYNAME);
> + c = lxc_container_new("snap0", snappath);
> + if (c) {
> + if (c->is_defined(c))
> + c->destroy(c);
> + lxc_container_put(c);
> + }
> + c = lxc_container_new(MYNAME, NULL);
> + if (c) {
> + if (c->is_defined(c))
> + c->destroy(c);
> + lxc_container_put(c);
> + }
> +}
> +
> +int main()
> +{
> + struct lxc_container *c;
> +
> + try_to_remove();
> + c = lxc_container_new(MYNAME, NULL);
> + if (!c) {
> + fprintf(stderr, "%s: %d: failed to load first container\n", __FILE__, __LINE__);
> + exit(1);
> + }
> +
> + if (c->is_defined(c)) {
> + fprintf(stderr, "%d: %s thought it was defined\n", __LINE__, MYNAME);
> + (void) c->destroy(c);
> + }
> + if (!c->set_config_item(c, "lxc.network.type", "empty")) {
> + fprintf(stderr, "%s: %d: failed to set network type\n", __FILE__, __LINE__);
> + goto err;
> + }
> + c->save_config(c, NULL);
> + if (!c->createl(c, "busybox", NULL, NULL, 0, NULL)) {
> + fprintf(stderr, "%s: %d: failed to create busybox container\n", __FILE__, __LINE__);
> + goto err;
> + }
> + c->load_config(c, NULL);
> +
> + if (!c->snapshot(c, NULL)) {
> + fprintf(stderr, "%s: %d: failed to create snapsot\n", __FILE__, __LINE__);
> + goto err;
> + }
> +
> + // rootfs should be ${lxcpath}snaps/${lxcname}/snap0/rootfs
> + struct stat sb;
> + int ret;
> + char path[1024];
> + snprintf(path, 1024, "%ssnaps/%s/snap0/rootfs", lxc_get_default_config_path(), MYNAME);
> + ret = stat(path, &sb);
> + if (ret != 0) {
> + fprintf(stderr, "%s: %d: snapshot was not actually created\n", __FILE__, __LINE__);
> + goto err;
> + }
> +
> + printf("All tests passed\n");
> + lxc_container_put(c);
> + exit(0);
> +
> +err:
> + lxc_container_put(c);
> + fprintf(stderr, "Exiting on error\n");
> + try_to_remove();
> + exit(1);
> +}
> --
> 1.8.3.2
>
>
> ------------------------------------------------------------------------------
> Learn the latest--Visual Studio 2012, SharePoint 2013, SQL 2012, more!
> Discover the easy way to master current and previous Microsoft technologies
> and advance your career. Get an incredible 1,500+ hours of step-by-step
> tutorial videos with LearnDevNow. Subscribe today and save!
> http://pubads.g.doubleclick.net/gampad/clk?id=58040911&iu=/4140/ostg.clktrk
> _______________________________________________
> Lxc-devel mailing list
> Lxc-devel at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/lxc-devel
--
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: 836 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20130904/073af602/attachment.pgp>
More information about the lxc-devel
mailing list