(on top of the clone patch) 1. allow copy clones from other bdevs
2. for lvm and zfs, as we don't yet support passing options, only default VG of 'lxc' and default zfsroot of 'tank' are supported when converting another backing store type. 3. refuse deletion of container which has lvm or zfs snapshots. Note that since a zfs clone must be made from a zfs snapshot, which is made from the original zfs fs, even after we lxc-destroy the snapshotted container we still must manually remove the snapshot. This can be handled automatically, by looking for snapshots where c1 is the original, c2 is the clone, tank/c2 no longer exists, but tank/c1@c2 does. We can then remove tank/c1@c2 and feel free to remove tank/c1. This patch does NOT do that yet. 4. Make sure not to return when we're a forked child. Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com> --- src/lxc/bdev.c | 167 +++++++++++++++++++++++++++++++------------------ src/lxc/bdev.h | 7 +++ src/lxc/lxc-destroy.in | 52 ++++++++++++--- 3 files changed, 158 insertions(+), 68 deletions(-) diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 3df53a5..4ba550c 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -81,7 +81,8 @@ static int do_rsync(const char *src, const char *dest) s[l-2] = '/'; s[l-1] = '\0'; - return execlp("rsync", "rsync", "-a", s, dest, (char *)NULL); + execlp("rsync", "rsync", "-a", s, dest, (char *)NULL); + exit(1); } static int blk_getsize(const char *path, unsigned long *size) @@ -189,7 +190,8 @@ static int do_mkfs(const char *path, const char *fstype) if (pid > 0) return wait_for_pid(pid); - return execlp("mkfs", "mkfs", "-t", fstype, path, NULL); + execlp("mkfs", "mkfs", "-t", fstype, path, NULL); + exit(1); } static char *linkderef(char *path, char *dest) @@ -391,31 +393,25 @@ static int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna const char *cname, const char *oldpath, const char *lxcpath, int snap, unsigned long newsize) { + int len, ret; + if (snap) { ERROR("directories cannot be snapshotted. Try overlayfs."); return -1; } - if (strcmp(orig->type, "dir")) { - ERROR("Directory clone from %s backing store is not supported", - orig->type); - return -1; - } - if (!orig->dest || !orig->src) return -1; - if (orig->data) { - new->data = strdup(orig->data); - if (!new->data) - return -1; - } - new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); - if (!new->dest) - return -1; - new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->src = malloc(len); if (!new->src) return -1; + ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) + return -1; + if ((new->dest = strdup(new->src)) == NULL) + return -1; return 0; } @@ -499,8 +495,10 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, int ret; pid_t pid; - if (zfs_list_entry(opath, output) < 0) - return -1; + if (!zfs_list_entry(opath, output)) + // default is tank. I'd prefer lxc, but apparently this is + // tradition. + sprintf(output, "tank"); if ((p = index(output, ' ')) == NULL) return -1; @@ -524,7 +522,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, ret = snprintf(dev, MAXPATHLEN, "%s/%s", output, nname); if (ret < 0 || ret >= MAXPATHLEN) exit(1); - return execlp("zfs", "zfs", "create", option, dev, NULL); + execlp("zfs", "zfs", "create", option, dev, NULL); + exit(1); } return wait_for_pid(pid); } else { @@ -543,7 +542,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, if ((pid = fork()) < 0) return -1; if (!pid) { - return execlp("zfs", "zfs", "destroy", path1, NULL); + execlp("zfs", "zfs", "destroy", path1, NULL); + exit(1); } // it probably doesn't exist so destroy probably will fail. (void) wait_for_pid(pid); @@ -552,7 +552,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, if ((pid = fork()) < 0) return -1; if (!pid) { - return execlp("zfs", "zfs", "snapshot", path1, NULL); + execlp("zfs", "zfs", "snapshot", path1, NULL); + exit(1); } if (wait_for_pid(pid) < 0) return -1; @@ -561,7 +562,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname, if ((pid = fork()) < 0) return -1; if (!pid) { - return execlp("zfs", "zfs", "clone", option, path1, path2, NULL); + execlp("zfs", "zfs", "clone", option, path1, path2, NULL); + exit(1); } return wait_for_pid(pid); } @@ -571,27 +573,26 @@ static int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna const char *cname, const char *oldpath, const char *lxcpath, int snap, unsigned long newsize) { + int len, ret; + if (!orig->src || !orig->dest) return -1; - if (strcmp(orig->type, "zfs")) { - ERROR("zfs clone from %s backing store is not supported", + if (snap && strcmp(orig->type, "zfs")) { + ERROR("zfs snapshot from %s backing store is not supported", orig->type); return -1; } - if (orig->data) { - new->data = strdup(orig->data); - if (!new->data) - return -1; - } - new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); - if (!new->dest) - return -1; - - new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->src = malloc(len); if (!new->src) return -1; + ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) + return -1; + if ((new->dest = strdup(new->src)) == NULL) + return -1; return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap); } @@ -699,9 +700,9 @@ static int lvm_create(const char *path, unsigned long size) if (!vg) exit(1); vg++; - ret = execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL); + execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL); free(pathdup); - return ret; + exit(1); } static int lvm_snapshot(const char *orig, const char *path, unsigned long size) @@ -733,7 +734,16 @@ static int lvm_snapshot(const char *orig, const char *path, unsigned long size) ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL); free(pathdup); - return ret; + exit(1); +} + +// this will return 1 for physical disks, qemu-nbd, loop, etc +// right now only lvm is a block device +static int is_blktype(struct bdev *b) +{ + if (strcmp(b->type, "lvm") == 0) + return 1; + return 0; } static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, @@ -742,14 +752,30 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna { char fstype[100]; unsigned long size = newsize; + int len, ret; if (!orig->src || !orig->dest) return -1; if (strcmp(orig->type, "lvm")) { - ERROR("LVM clone from %s backing store is not supported", - orig->type); - return -1; + if (snap) { + ERROR("LVM snapshot from %s backing store is not supported", + orig->type); + return -1; + } + // Use VG 'lxc' by default + // We will want to support custom VGs, at least as specified through + // /etc/lxc/lxc.conf, preferably also over cmdline + len = strlen("/dev/lxc/") + strlen(cname) + 1; + if ((new->src = malloc(len)) == NULL) + return -1; + ret = snprintf(new->src, len, "/dev/lxc/%s", cname); + if (ret < 0 || ret >= len) + return -1; + } else { + new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); + if (!new->src) + return -1; } if (orig->data) { @@ -757,21 +783,32 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna if (!new->data) return -1; } - new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath); + + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->dest = malloc(len); if (!new->dest) return -1; - if (mkdir_p(new->dest, 0755) < 0) + ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) return -1; - - - new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath); - if (!new->src) + if (mkdir_p(new->dest, 0755) < 0) return -1; - if (!newsize && blk_getsize(orig->src, &size) < 0) { - ERROR("Error getting size of %s", orig->src); - return -1; + if (is_blktype(orig)) { + if (!newsize && blk_getsize(orig->src, &size) < 0) { + ERROR("Error getting size of %s", orig->src); + return -1; + } + if (detect_fs(orig, fstype, 100) < 0) { + INFO("could not find fstype for %s, using ext3", orig->src); + return -1; + } + } else { + sprintf(fstype, "ext3"); + if (!newsize) + size = 1000000000; // default to 1G } + if (snap) { if (lvm_snapshot(orig->src, new->src, size) < 0) { ERROR("could not create %s snapshot of %s", new->src, orig->src); @@ -782,10 +819,6 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna ERROR("Error creating new lvm blockdev"); return -1; } - if (detect_fs(orig, fstype, 100) < 0) { - ERROR("could not find fstype for %s", orig->src); - return -1; - } if (do_mkfs(new->src, fstype) < 0) { ERROR("Error creating filesystem type %s on %s", fstype, new->src); @@ -997,15 +1030,27 @@ static int btrfs_clonepaths(struct bdev *orig, struct bdev *new, const char *old return -1; if (strcmp(orig->type, "btrfs")) { - ERROR("btrfs cloen from %s backing store is not supported", - orig->type); - return -1; - } + int len, ret; + if (snap) { + ERROR("btrfs snapshot from %s backing store is not supported", + orig->type); + return -1; + } + len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3; + new->src = malloc(len); + if (!new->src) + return -1; + ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname); + if (ret < 0 || ret >= len) + return -1; + } else { + // in case rootfs is in custom path, reuse it + if ((new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath)) == NULL) + return -1; - if ((new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath)) == NULL) - return -1; + } - if ((new->src = strdup(new->dest)) == NULL) + if ((new->dest = strdup(new->src)) == NULL) return -1; if (orig->data && (new->data = strdup(orig->data)) == NULL) diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index 131f158..cc03592 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -22,6 +22,13 @@ struct bdev_ops { int snap, unsigned long newsize); }; +/* + * When lxc-start (conf.c) is mounting a rootfs, then src will be the + * 'lxc.rootfs' value, dest will be mount dir (i.e. $libdir/lxc) When clone + * or create is doing so, then dest will be $lxcpath/$lxcname/rootfs, since + * we may need to rsync from one to the other. + * data is so far unused. + */ struct bdev { struct bdev_ops *ops; char *type; diff --git a/src/lxc/lxc-destroy.in b/src/lxc/lxc-destroy.in index 2e7a486..de29909 100644 --- a/src/lxc/lxc-destroy.in +++ b/src/lxc/lxc-destroy.in @@ -40,6 +40,8 @@ help() { echo " -P lxcpath container is in specified lxcpath" >&2 } +. @DATADIR@/lxc/lxc.functions + usage_err() { [ -n "$1" ] && echo "$1" >&2 usage @@ -47,21 +49,46 @@ usage_err() { } verify_zfs() { - path=$1 - if which zfs >/dev/null 2>&1 && zfs list | grep -q $path; then + local path=$1 + which zfs > /dev/null 2>&1 || { echo no; return; } + if zfs list -H $path >/dev/null 2>&1; then echo zfs else echo no fi } +busy_zfs() { + local path=$1 + local dev + dev=`zfs list -H $path 2>/dev/null | awk '{ print $1 }'` + if zfs list -t snapshot | grep -q "$dev"; then + echo busy + else + echo zfs + fi +} + +verify_lvm() { + local path=$1 + if [ -b $path -o -h $path ]; then + lvdisplay $path > /dev/null 2>&1 && { echo lvm; return; } + fi + echo no +} + +busy_lvm() { + local path=$1 + lvdisplay $path | grep -q "LV snapshot status.*source of" && { echo busy; return; } + echo lvm +} + optarg_check() { if [ -z "$2" ]; then usage_err "option '$1' requires an argument" fi } -. @DATADIR@/lxc/lxc.functions force=0 while [ $# -gt 0 ]; do @@ -134,14 +161,25 @@ fi # else, ignore it. We'll support deletion of others later. rootdev=`grep lxc.rootfs $lxc_path/$lxc_name/config 2>/dev/null | sed -e 's/^[^/]*//'` if [ -n "$rootdev" ]; then - if [ -b "$rootdev" -o -h "$rootdev" ]; then - lvdisplay $rootdev > /dev/null 2>&1 - if [ $? -eq 0 ]; then + if [ `verify_lvm $rootdev` = "lvm" ]; then + if [ `busy_lvm $rootdev` = "busy" ]; then + echo "$rootdev has lvm snapshots - not deleting" + exit 1 + else echo "removing backing store: $rootdev" lvremove -f $rootdev fi elif [ `verify_zfs $rootdev` = "zfs" ]; then - zfs destroy $(zfs list | grep $rootdev | awk '{ print $1 }') + if [ `busy_zfs $rootdev` = "busy" ]; then + echo "$rootdev has zfs snapshots - not deleting" + exit 1 + else + zfs destroy $(zfs list | grep $rootdev | awk '{ print $1 }') + if [ $? -ne 0 ]; then + echo "zfs destroy failed - please wait a bit and try again" + exit 1 + fi + fi elif [ -h "$rootdev" -o -d "$rootdev" ]; then if which btrfs >/dev/null 2>&1 && btrfs subvolume list "$rootdev" >/dev/null 2>&1; then -- 1.8.1.2 ------------------------------------------------------------------------------ Try New Relic Now & We'll Send You this Cool Shirt New Relic is the only SaaS-based application performance monitoring service that delivers powerful full stack analytics. Optimize and monitor your browser, app, & servers with just a few lines of code. Try New Relic and get this awesome Nerd Life shirt! http://p.sf.net/sfu/newrelic_d2d_apr _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel