[lxc-devel] [PATCH] add zfs support to lxc-create and lxc-destroy

Serge Hallyn serge.hallyn at ubuntu.com
Thu Apr 25 20:18:25 UTC 2013


This is based on patch from Papp Tamas (thanks).  It also does some
reorganizing of lxc-create to commonize some of the backingstore handling.

I played with it using:

	sudo lvcreate -L 100G -n zfs vg0
	sudo zpool create lxc /dev/vg0/zfs
	sudo lxc-create -B zfs --zfsroot lxc -t ubuntu -n dir2

or you could

	qemu-img create zfs.img 100G
	sudo qemu-nbd -c /dev/nbd0 zfs.img
	sudo zpool create lxc /dev/nbd0
	sudo lxc-create -B zfs --zfsroot lxc -t ubuntu -n dir2

I'll write the bdev.c handler and hook up lxc-clone next.

This also fixses a bug in the sed expression to extract the rootfs from
container config, which prepended an extra '/' to the rootdev.  (That
caused the zfs list entry not to match at destroy)

Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
Cc: Papp Tamas <tompos at martos.bme.hu>
---
 src/lxc/lxc-create.in  |  167 ++++++++++++++++++++++++++++++++----------------
 src/lxc/lxc-destroy.in |   13 +++-
 2 files changed, 124 insertions(+), 56 deletions(-)

diff --git a/src/lxc/lxc-create.in b/src/lxc/lxc-create.in
index fbb566f..3367a89 100644
--- a/src/lxc/lxc-create.in
+++ b/src/lxc/lxc-create.in
@@ -22,6 +22,59 @@
 
 . @DATADIR@/lxc/lxc.functions
 
+verify_btrfs() {
+    if which btrfs >/dev/null 2>&1 && \
+        btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then
+        echo "btrfs"
+    else
+        echo "no"
+    fi
+}
+
+verify_zfs() {
+    if which zfs >/dev/null 2>&1 && zfs get all "$zfs_root" >/dev/null 2>&1; then
+        echo zfs
+    else
+        echo no
+    fi
+}
+
+verify_lvm() {
+    which vgscan > /dev/null 2>&1
+    if [ $? -ne 0 ]; then
+        echo "vgscan not found (is lvm2 installed?)" >&2
+        echo no
+        return
+    fi
+
+    grep -q "\<$fstype\>" /proc/filesystems
+    if [ $? -ne 0 ]; then
+        echo "$fstype is not listed in /proc/filesystems" >&2
+        echo no
+        return
+    fi
+
+    vgscan | grep -q "Found volume group \"$vgname\""
+    if [ $? -ne 0 ]; then
+        echo "could not find volume group \"$vgname\"" >&2
+        echo no
+        return
+    fi
+
+    echo lvm
+}
+
+# if no backingstore is specified, auto-detect if $lxc_path is btrfs or zfs
+detect_backingstore() {
+    if [ `verify_btrfs` = "btrfs" ]; then
+        echo btrfs
+    elif [ `verify_zfs` = "zfs" ]; then
+        echo zfs
+    else
+        echo none
+    fi
+}
+
 usage() {
     echo "usage: $(basename $0) -n NAME [-f CONFIG_FILE] [-t TEMPLATE] [FS_OPTIONS] --" >&2
     echo "         [-P lxcpath] [TEMPLATE_OPTIONS]" >&2
@@ -32,6 +85,7 @@ usage() {
     echo "  -B lvm [--lvname LV_NAME] [--vgname VG_NAME] [--fstype FS_TYPE]" >&2
     echo "    [--fssize FS_SIZE]" >&2
     echo "  -B btrfs" >&2
+    echo "  -B zfs [--zfsroot PATH]" >&2
 }
 
 help() {
@@ -51,6 +105,7 @@ help() {
     echo "  --vgname VG_NAME   specify the LVM volume group name (default: lxc)" >&2
     echo "  --fstype FS_TYPE   specify the filesystem type (default: ext4)" >&2
     echo "  --fssize FS_SIZE   specify the filesystem size (default: 500M)" >&2
+    echo "  --zfsroot PATH     specify the zfs path for lxcpath (default: tank/lxc)" >&2
     echo >&2
     if [ -z "$lxc_template" ]; then
         echo "To see template-specific options, specify a template. For example:" >&2
@@ -140,6 +195,11 @@ while [ $# -gt 0 ]; do
             fssize=$1
             shift
             ;;
+        --zfsroot)
+            optarg_check $opt "$1"
+            zfs_root=$1
+            shift
+            ;;
         --)
             break;;
         -?)
@@ -187,6 +247,10 @@ if [ -z "$lvname" ]; then
     lvname="$lxc_name"
 fi
 
+if [ -z "$zfs_root" ]; then
+    zfs_root="tank/lxc"
+fi
+
 if [ "$(id -u)" != "0" ]; then
    echo "$(basename $0): must be run as root" >&2
    exit 1
@@ -196,10 +260,39 @@ if [ -n "$custom_rootfs" ] && [ "$backingstore" != "dir" ]; then
    echo "--dir is only valid with -B dir"
 fi
 
+# detect / verify backing store
 case "$backingstore" in
-    dir|lvm|none|btrfs|_unset) :;;
+    btrfs)
+        if [ `verify_btrfs` != 'btrfs' ]; then
+            echo "missing 'btrfs' command or $lxc_path is not btrfs" >&2
+            exit 1
+        fi
+        ;;
+    zfs)
+        if [ `verify_zfs` != 'zfs' ]; then
+            echo "missing 'zfs' command or $zfs_root is not zfs" >&2
+            exit 1
+        fi
+        ;;
+    lvm)
+        if [ `verify_lvm` != 'lvm' ]; then
+            echo "system is missing 'lvm' support, or VG does not exist." >&2
+            exit 1
+        fi
+        rootdev=/dev/$vgname/$lvname
+        lvdisplay $rootdev > /dev/null 2>&1
+        if [ $? -eq 0 ]; then
+            echo "LV $rootdev already exists" >&2
+            exit 1
+        fi
+        ;;
+    _unset)
+        backingstore=`detect_backingstore`
+        ;;
+    dir|lvm|none)
+        :;;
     *)
-        echo "$(basename $0): '$backingstore' is not known (try 'none', 'dir', 'lvm', 'btrfs')" >&2
+        echo "$(basename $0): '$backingstore' is not known (try 'none', 'dir', 'lvm', 'btrfs', 'zfs')" >&2
         usage
         exit 1
         ;;
@@ -212,61 +305,14 @@ fi
 
 rootfs="$lxc_path/$lxc_name/rootfs"
 
-if [ "$backingstore" = "_unset" ] || [ "$backingstore" = "btrfs" ]; then
-# if no backing store was given, then see if btrfs would work
-    if which btrfs >/dev/null 2>&1 && \
-        btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then
-        backingstore="btrfs"
-    else
-        if [ "$backingstore" = "btrfs" ]; then
-            echo "$(basename $0): missing 'btrfs' command or $lxc_path is not btrfs" >&2
-            exit 1;
-        fi
-        backingstore="none"
-    fi
-fi
-
-if [ "$backingstore" = "lvm" ]; then
-    which vgscan > /dev/null 2>&1
-    if [ $? -ne 0 ]; then
-        echo "$(basename $0): vgscan not found (is lvm2 installed?)" >&2
-        exit 1
-    fi
-
-    grep -q "\<$fstype\>" /proc/filesystems
-    if [ $? -ne 0 ]; then
-        echo "$(basename $0): $fstype is not listed in /proc/filesystems" >&2
-        exit 1
-    fi
-
-    vgscan | grep -q "Found volume group \"$vgname\""
-    if [ $? -ne 0 ]; then
-        echo "$(basename $0): could not find volume group \"$vgname\"" >&2
-        exit 1
-    fi
-
-    rootdev=/dev/$vgname/$lvname
-    lvdisplay $rootdev > /dev/null 2>&1
-    if [ $? -eq 0 ]; then
-        echo "$(basename $0): backing store already exists: $rootdev" >&2
-        echo "please delete it (using \"lvremove $rootdev\") and try again" >&2
-        exit 1
-    fi
-
-elif [ "$backingstore" = "btrfs" ]; then
-    mkdir "$lxc_path/$lxc_name"
-    if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then
-        echo "$(basename $0): failed to create subvolume in $rootfs: $out" >&2
-        exit 1;
-    fi
-fi
-
 cleanup() {
     if [ "$backingstore" = "lvm" ]; then
-        umount $rootfs
-        lvremove -f $rootdev
+        umount -l $rootfs || true
+        lvremove -f $rootdev || true
     elif [ "$backingstore" = "btrfs" ]; then
-        btrfs subvolume delete "$rootfs"
+        btrfs subvolume delete "$rootfs" || true
+    elif [ "$backingstore" = "zfs" ]; then
+        zfs destroy "$zfs_root/$lxc_name" || true
     fi
 
     ${bindir}/lxc-destroy -n $lxc_name
@@ -276,7 +322,18 @@ cleanup() {
 
 trap cleanup HUP INT TERM
 
-mkdir -p $lxc_path/$lxc_name
+# set up container dir per backing store
+if [ "$backingstore" = "zfs" ]; then
+    zfs create -omountpoint=$lxc_path/$lxc_name/rootfs "$zfs_root/$lxc_name"
+elif [ "$backingstore" = "btrfs" ]; then
+    mkdir "$lxc_path/$lxc_name"
+    if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then
+        echo "$(basename $0): failed to create subvolume in $rootfs: $out" >&2
+        exit 1;
+    fi
+else
+    mkdir -p $lxc_path/$lxc_name
+fi
 
 if [ -z "$lxc_config" ]; then
     lxc_config="@SYSCONFDIR@/lxc/default.conf"
diff --git a/src/lxc/lxc-destroy.in b/src/lxc/lxc-destroy.in
index 6514085..fc164c2 100644
--- a/src/lxc/lxc-destroy.in
+++ b/src/lxc/lxc-destroy.in
@@ -46,6 +46,15 @@ usage_err() {
     exit 1
 }
 
+verify_zfs() {
+    path=$1
+    if which zfs >/dev/null 2>&1 && zfs list | grep -q $path; then
+        echo zfs
+    else
+        echo no
+    fi
+}
+
 optarg_check() {
     if [ -z "$2" ]; then
         usage_err "option '$1' requires an argument"
@@ -123,7 +132,7 @@ fi
 # Deduce the type of rootfs
 # If LVM partition, destroy it. For btrfs, we delete the subvolue. If anything
 # 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/^[^/]*/\//'`
+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
@@ -131,6 +140,8 @@ if [ -n "$rootdev" ]; then
             echo "removing backing store: $rootdev"
             lvremove -f $rootdev
         fi
+    elif [ `verify_zfs $rootdev` = "zfs" ]; then
+        zfs destroy $(zfs list | grep $rootdev | awk '{ print $1 }')
     elif [ -h "$rootdev" -o -d "$rootdev" ]; then
         if which btrfs >/dev/null 2>&1 &&
            btrfs subvolume list "$rootdev" >/dev/null 2>&1; then
-- 
1.7.9.5





More information about the lxc-devel mailing list