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.

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

Reply via email to