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

Attachment: 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

Reply via email to