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.hal...@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.hal...@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@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/lxc-devel -- Stéphane Graber Ubuntu developer http://www.ubuntu.com
signature.asc
Description: Digital signature
------------------------------------------------------------------------------ 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@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel