[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