[lxc-devel] [go-lxc/v2] Support backing store options

dinesh on Github lxc-bot at linuxcontainers.org
Tue Aug 4 12:05:18 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 459 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200804/fea4d11b/attachment.bin>
-------------- next part --------------
From 0f17eb8db8fefc6f0596f9d2a9076c3b2155ff6b Mon Sep 17 00:00:00 2001
From: dinesh <dinesh1042 at gmail.com>
Date: Tue, 4 Aug 2020 17:30:00 +0530
Subject: [PATCH] support backing store options

---
 container.go              | 75 ++++++++++++++++++++++++++++++-----
 examples/create/create.go | 28 +++++++++++++
 lxc-binding.c             |  6 +--
 lxc-binding.h             |  2 +-
 options.go                | 19 +++++++++
 type.go                   | 82 ++++++++++++++++++++++++++++++++++++++-
 6 files changed, 197 insertions(+), 15 deletions(-)

diff --git a/container.go b/container.go
index 30c5ef6..0fe2440 100644
--- a/container.go
+++ b/container.go
@@ -407,17 +407,12 @@ func (c *Container) Create(options TemplateOptions) error {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
-	// FIXME: Support bdev_specs
-	//
-	// bdev_specs:
-	// zfs requires zfsroot
-	// lvm requires lvname/vgname/thinpool as well as fstype and fssize
-	// btrfs requires nothing
-	// dir requires nothing
 	if err := c.makeSure(isNotDefined); err != nil {
 		return err
 	}
 
+	bdevspecs := buildBdevSpecs(options.BackendSpecs)
+
 	// use download template if not set
 	if options.Template == "" {
 		options.Template = "download"
@@ -489,9 +484,9 @@ func (c *Container) Create(options TemplateOptions) error {
 		}
 		defer freeNullTerminatedArgs(cargs, len(args))
 
-		ret = bool(C.go_lxc_create(c.container, ctemplate, cbackend, C.int(c.verbosity), cargs))
+		ret = bool(C.go_lxc_create(c.container, ctemplate, cbackend, bdevspecs, C.int(c.verbosity), cargs))
 	} else {
-		ret = bool(C.go_lxc_create(c.container, ctemplate, cbackend, C.int(c.verbosity), nil))
+		ret = bool(C.go_lxc_create(c.container, ctemplate, cbackend, bdevspecs, C.int(c.verbosity), nil))
 	}
 
 	if !ret {
@@ -1950,3 +1945,65 @@ func (c *Container) ErrorNum() int {
 	cError := C.go_lxc_error_num(c.container)
 	return int(cError)
 }
+
+func buildBdevSpecs(o *BackendStoreSpecs) *C.struct_bdev_specs {
+	if o == nil {
+		return nil
+	}
+
+	// bdev_specs:
+	// zfs requires zfsroot
+	// lvm requires lvname/vgname/thinpool as well as fstype and fssize
+	// btrfs requires nothing
+	// dir requires nothing
+
+	specs := C.struct_bdev_specs{}
+
+	if o.FSType != "" {
+		fstype := C.CString(o.FSType)
+		specs.fstype = fstype 
+		defer C.free(unsafe.Pointer(fstype))
+	}
+
+	if o.FSSize > 0  {
+		specs.fssize = C.uint64_t(o.FSSize)
+	}
+
+	if o.ZFS.Root != "" { 
+		zfsroot := C.CString(o.ZFS.Root)
+		specs.zfs.zfsroot = zfsroot
+		defer C.free(unsafe.Pointer(zfsroot))
+	}
+	
+	if o.LVM.VG != "" {
+		vg := C.CString(o.LVM.VG)
+		specs.lvm.vg = vg
+		defer C.free(unsafe.Pointer(vg))
+	}
+
+	if o.LVM.Thinpool != "" {
+		lv := C.CString(o.LVM.Thinpool)
+		specs.lvm.thinpool = lv
+		defer C.free(unsafe.Pointer(lv))
+	}
+
+	if o.RBD.Name != "" {
+		lv := C.CString(o.RBD.Name)
+		specs.rbd.rbdname = lv
+		defer C.free(unsafe.Pointer(lv))
+	}
+
+	if o.RBD.Pool != "" {
+		lv := C.CString(o.RBD.Pool)
+		specs.rbd.rbdpool = lv
+		defer C.free(unsafe.Pointer(lv))
+	}
+
+	if o.Dir != nil {
+		dir := C.CString(*o.Dir)
+		specs.dir = dir
+		defer C.free(unsafe.Pointer(dir))
+	}
+
+	return &specs
+}
\ No newline at end of file
diff --git a/examples/create/create.go b/examples/create/create.go
index 1e71d25..cd5cc75 100644
--- a/examples/create/create.go
+++ b/examples/create/create.go
@@ -23,6 +23,8 @@ var (
 	verbose    bool
 	flush      bool
 	validation bool
+	fssize     string
+	bdevtype   string
 )
 
 func init() {
@@ -35,6 +37,11 @@ func init() {
 	flag.BoolVar(&verbose, "verbose", false, "Verbose output")
 	flag.BoolVar(&flush, "flush", false, "Flush the cache")
 	flag.BoolVar(&validation, "validation", false, "GPG validation")
+
+	flag.StringVar(&bdevtype, "bdev", "dir", "backing store type")
+	flag.StringVar(&fssize, "fssize", "", "backing store size")
+	// TODO support more flags for zfs, lvm, or rbd
+
 	flag.Parse()
 }
 
@@ -50,6 +57,20 @@ func main() {
 		c.SetVerbosity(lxc.Verbose)
 	}
 
+	var backend lxc.BackendStore
+	if err := (&backend).Set(bdevtype); err != nil {
+		log.Fatalf("ERROR: %s\n", err.Error())
+	}
+
+	var bdevSize lxc.ByteSize
+	if fssize != "" {
+		var err error
+		bdevSize, err = lxc.ParseBytes(fssize)
+		if err != nil {
+			log.Fatalf("ERROR: %s\n", err.Error())
+		}
+	}
+
 	options := lxc.TemplateOptions{
 		Template:             template,
 		Distro:               distro,
@@ -57,8 +78,15 @@ func main() {
 		Arch:                 arch,
 		FlushCache:           flush,
 		DisableGPGValidation: validation,
+		Backend:              backend,
+		BackendSpecs: &lxc.BackendStoreSpecs{
+			FSSize: uint64(bdevSize),
+		},
 	}
 
+	c.SetLogFile("log")
+	c.SetLogLevel(lxc.DEBUG)
+
 	if err := c.Create(options); err != nil {
 		log.Printf("ERROR: %s\n", err.Error())
 	}
diff --git a/lxc-binding.c b/lxc-binding.c
index 2c26162..ce21e15 100644
--- a/lxc-binding.c
+++ b/lxc-binding.c
@@ -75,11 +75,11 @@ bool go_lxc_want_close_all_fds(struct lxc_container *c, bool state) {
 	return c->want_close_all_fds(c, state);
 }
 
-bool go_lxc_create(struct lxc_container *c, const char *t, const char *bdevtype, int flags, char * const argv[]) {
+bool go_lxc_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char * const argv[]) {
 	if (strncmp(t, "none", strlen(t)) == 0) {
-		return c->create(c, NULL, bdevtype, NULL, !!(flags & LXC_CREATE_QUIET), argv);
+		return c->create(c, NULL, bdevtype, specs, !!(flags & LXC_CREATE_QUIET), argv);
 	}
-	return c->create(c, t, bdevtype, NULL, !!(flags & LXC_CREATE_QUIET), argv);
+	return c->create(c, t, bdevtype, specs, !!(flags & LXC_CREATE_QUIET), argv);
 }
 
 bool go_lxc_start(struct lxc_container *c, int useinit, char * const argv[]) {
diff --git a/lxc-binding.h b/lxc-binding.h
index b52e2cd..8f7f427 100644
--- a/lxc-binding.h
+++ b/lxc-binding.h
@@ -12,7 +12,7 @@ extern void go_lxc_clear_config(struct lxc_container *c);
 extern bool go_lxc_clear_config_item(struct lxc_container *c, const char *key);
 extern bool go_lxc_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype);
 extern bool go_lxc_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape);
-extern bool go_lxc_create(struct lxc_container *c, const char *t, const char *bdevtype, int flags, char * const argv[]);
+extern bool go_lxc_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char * const argv[]);
 extern bool go_lxc_defined(struct lxc_container *c);
 extern bool go_lxc_destroy(struct lxc_container *c);
 extern bool go_lxc_destroy_with_snapshots(struct lxc_container *c);
diff --git a/options.go b/options.go
index ae6b6fd..060abd5 100644
--- a/options.go
+++ b/options.go
@@ -71,6 +71,8 @@ type TemplateOptions struct {
 	// Backend specifies the type of the backend.
 	Backend BackendStore
 
+	BackendSpecs *BackendStoreSpecs
+
 	// Distro specifies the name of the distribution.
 	Distro string
 
@@ -105,6 +107,23 @@ type TemplateOptions struct {
 	ExtraArgs []string
 }
 
+
+type BackendStoreSpecs struct {
+	FSType string
+	FSSize uint64
+	Dir *string
+	ZFS struct {
+		Root string
+	}
+	LVM struct {
+		VG, LV, Thinpool string
+	}
+	RBD struct {
+		Name, Pool string
+	}
+}
+
+
 // DownloadTemplateOptions is a convenient set of options for "download" template.
 var DownloadTemplateOptions = TemplateOptions{
 	Template: "download",
diff --git a/type.go b/type.go
index ced3c33..c966531 100644
--- a/type.go
+++ b/type.go
@@ -9,7 +9,13 @@ package lxc
 // #include <lxc/lxccontainer.h>
 import "C"
 
-import "fmt"
+import (
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+	"unicode"
+)
 
 // Verbosity type
 type Verbosity int
@@ -150,7 +156,7 @@ func (t State) String() string {
 type ByteSize float64
 
 const (
-	_ = iota
+	B = iota
 	// KB - kilobyte
 	KB ByteSize = 1 << (10 * iota)
 	// MB - megabyte
@@ -191,6 +197,78 @@ func (b ByteSize) String() string {
 	return fmt.Sprintf("%.2fB", b)
 }
 
+// Used to convert user input to ByteSize
+var unitMap = map[string]ByteSize{
+	"B":     B,
+	"BYTE":  B,
+	"BYTES": B,
+
+	"KB":        KB,
+	"KILOBYTE":  KB,
+	"KILOBYTES": KB,
+
+	"MB":        MB,
+	"MEGABYTE":  MB,
+	"MEGABYTES": MB,
+
+	"GB":        GB,
+	"GIGABYTE":  GB,
+	"GIGABYTES": GB,
+
+	"TB":        TB,
+	"TERABYTE":  TB,
+	"TERABYTES": TB,
+
+	"PB":        PB,
+	"PETABYTE":  PB,
+	"PETABYTES": PB,
+
+	"EB":       EB,
+	"EXABYTE":  EB,
+	"EXABYTES": EB,
+}
+
+// Inspired from https://github.com/inhies/go-bytesize
+
+// ParseBytes parses a byte size string. A byte size string is a number followed by
+// a unit suffix, such as "1024B" or "1 MB". Valid byte units are "B", "KB",
+// "MB", "GB", "TB", "PB" and "EB". You can also use the long
+// format of units, such as "kilobyte" or "kilobytes".
+func ParseBytes(s string) (ByteSize, error) {
+	// Remove leading and trailing whitespace
+	s = strings.TrimSpace(s)
+
+	split := make([]string, 0)
+	for i, r := range s {
+		if !unicode.IsDigit(r) {
+			// Split the string by digit and size designator, remove whitespace
+			split = append(split, strings.TrimSpace(string(s[:i])))
+			split = append(split, strings.TrimSpace(string(s[i:])))
+			break
+		}
+	}
+
+	// Check to see if we split successfully
+	if len(split) != 2 {
+		return 0, errors.New("Unrecognized size suffix")
+	}
+
+	// Check for MB, MEGABYTE, and MEGABYTES
+	unit, ok := unitMap[strings.ToUpper(split[1])]
+	if !ok {
+		return 0, errors.New("Unrecognized size suffix " + split[1])
+
+	}
+
+	value, err := strconv.ParseFloat(split[0], 64)
+	if err != nil {
+		return 0, err
+	}
+
+	bytesize := ByteSize(value * float64(unit))
+	return bytesize, nil
+}
+
 // LogLevel type specifies possible log levels.
 type LogLevel int
 


More information about the lxc-devel mailing list