The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/2879
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From b407d52b250282f92ace201c01979aa39c84bbfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Mon, 13 Feb 2017 21:18:23 -0500 Subject: [PATCH 01/11] tests: Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- test/main.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/main.sh b/test/main.sh index 66585ca..f6aad33 100755 --- a/test/main.sh +++ b/test/main.sh @@ -455,7 +455,7 @@ fi run_test test_check_deps "checking dependencies" run_test test_static_analysis "static analysis" run_test test_database_update "database schema updates" -run_test test_remote_url "remote url handling" +run_test test_remote_url "remote url handling" run_test test_remote_admin "remote administration" run_test test_remote_usage "remote usage" run_test test_basic_usage "basic usage" From fa35a9d9eaa0fb45f67b23636bb244902ce7df82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 11:27:48 -0500 Subject: [PATCH 02/11] Don't include spaces in translated strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/info.go | 8 ++++---- po/lxd.pot | 34 +++++++++++++++++----------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lxc/info.go b/lxc/info.go index 2300ce0..31f46f2 100644 --- a/lxc/info.go +++ b/lxc/info.go @@ -140,7 +140,7 @@ func (c *infoCmd) containerInfo(d *lxd.Client, name string, showLog bool) error } if diskInfo != "" { - fmt.Println(i18n.G(" Disk usage:")) + fmt.Println(fmt.Sprintf(" %s", i18n.G("Disk usage:"))) fmt.Printf(diskInfo) } @@ -151,7 +151,7 @@ func (c *infoCmd) containerInfo(d *lxd.Client, name string, showLog bool) error } if cpuInfo != "" { - fmt.Println(i18n.G(" CPU usage:")) + fmt.Println(fmt.Sprintf(" %s", i18n.G("CPU usage:"))) fmt.Printf(cpuInfo) } @@ -174,7 +174,7 @@ func (c *infoCmd) containerInfo(d *lxd.Client, name string, showLog bool) error } if memoryInfo != "" { - fmt.Println(i18n.G(" Memory usage:")) + fmt.Println(fmt.Sprintf(" %s", i18n.G("Memory usage:"))) fmt.Printf(memoryInfo) } @@ -191,7 +191,7 @@ func (c *infoCmd) containerInfo(d *lxd.Client, name string, showLog bool) error } if networkInfo != "" { - fmt.Println(i18n.G(" Network usage:")) + fmt.Println(fmt.Sprintf(" %s", i18n.G("Network usage:"))) fmt.Printf(networkInfo) } } diff --git a/po/lxd.pot b/po/lxd.pot index e735b8f..f8106af 100644 --- a/po/lxd.pot +++ b/po/lxd.pot @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: lxd\n" "Report-Msgid-Bugs-To: lxc-devel@lists.linuxcontainers.org\n" - "POT-Creation-Date: 2017-02-14 01:13+0100\n" + "POT-Creation-Date: 2017-02-14 16:58-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <l...@li.org>\n" @@ -16,22 +16,6 @@ msgstr "Project-Id-Version: lxd\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: lxc/info.go:154 -msgid " CPU usage:" -msgstr "" - -#: lxc/info.go:143 -msgid " Disk usage:" -msgstr "" - -#: lxc/info.go:177 -msgid " Memory usage:" -msgstr "" - -#: lxc/info.go:194 -msgid " Network usage:" -msgstr "" - #: lxc/storage.go:33 msgid "### This is a yaml representation of a storage pool.\n" "### Any line starting with a '# will be ignored.\n" @@ -197,6 +181,10 @@ msgstr "" msgid "CPU usage (in seconds)" msgstr "" +#: lxc/info.go:154 +msgid "CPU usage:" +msgstr "" + #: lxc/list.go:429 msgid "CREATED AT" msgstr "" @@ -356,6 +344,10 @@ msgstr "" msgid "Device %s removed from %s" msgstr "" +#: lxc/info.go:143 +msgid "Disk usage:" +msgstr "" + #: lxc/list.go:576 msgid "EPHEMERAL" msgstr "" @@ -877,6 +869,10 @@ msgstr "" msgid "Memory (peak)" msgstr "" +#: lxc/info.go:177 +msgid "Memory usage:" +msgstr "" + #: lxc/help.go:87 msgid "Missing summary." msgstr "" @@ -947,6 +943,10 @@ msgstr "" msgid "Network name" msgstr "" +#: lxc/info.go:194 +msgid "Network usage:" +msgstr "" + #: lxc/image.go:168 lxc/publish.go:35 msgid "New alias to define at target" msgstr "" From b209a944e950b6fb72ae7aa755aa711d8b6a8bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 11:41:34 -0500 Subject: [PATCH 03/11] tests: Add golint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- Makefile | 1 + test/suites/static_analysis.sh | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 3970a11..5d092ed 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,7 @@ protobuf: check: default go get -v -x github.com/rogpeppe/godeps go get -v -x github.com/remyoudompheng/go-misc/deadcode + go get -v -x github.com/golang/lint/golint go test -v $(TAGS) $(DEBUG) ./... cd test && ./main.sh diff --git a/test/suites/static_analysis.sh b/test/suites/static_analysis.sh index 878bce7..f17ade4 100644 --- a/test/suites/static_analysis.sh +++ b/test/suites/static_analysis.sh @@ -25,9 +25,7 @@ test_static_analysis() { fi ## go vet, if it exists - have_go_vet=1 - go help vet > /dev/null 2>&1 || have_go_vet=0 - if [ "${have_go_vet}" -eq 1 ]; then + if go help vet >/dev/null 2>&1; then go vet ./... fi @@ -36,6 +34,11 @@ test_static_analysis() { vet --all . fi + ## golint + if which golint >/dev/null 2>&1; then + golint -set_exit_status shared/api/ + fi + ## deadcode if which deadcode >/dev/null 2>&1; then for path in . fuidshift lxc lxd lxd/types shared shared/api shared/i18n shared/ioprogress shared/logging shared/osarch shared/simplestreams shared/termios shared/version test/lxd-benchmark; do From cbbed2577ed8b19c2345a0132650d55b1a8812c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 14:27:22 -0500 Subject: [PATCH 04/11] Use a tmpfs for shmounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids some information leakage from the host. Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/daemon.go | 21 +++++++-------------- shared/util.go | 36 ------------------------------------ 2 files changed, 7 insertions(+), 50 deletions(-) diff --git a/lxd/daemon.go b/lxd/daemon.go index 4509703..f37bdb0 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -402,35 +402,28 @@ var sharedMounted bool var sharedMountsLock sync.Mutex func setupSharedMounts() error { + // Check if we already went through this if sharedMounted { return nil } + // Get a lock to prevent races sharedMountsLock.Lock() defer sharedMountsLock.Unlock() - if sharedMounted { - return nil - } - + // Check if already setup path := shared.VarPath("shmounts") - - isShared, err := shared.IsOnSharedMount(path) - if err != nil { - return err - } - - if isShared { - // / may already be ms-shared, or shmounts may have - // been mounted by a previous lxd run + if shared.IsMountPoint(path) { sharedMounted = true return nil } - if err := syscall.Mount(path, path, "none", syscall.MS_BIND, ""); err != nil { + // Mount a new tmpfs + if err := syscall.Mount("tmpfs", path, "tmpfs", 0, "size=100k,mode=0711"); err != nil { return err } + // Mark as MS_SHARED and MS_REC var flags uintptr = syscall.MS_SHARED | syscall.MS_REC if err := syscall.Mount(path, path, "none", flags, ""); err != nil { return err diff --git a/shared/util.go b/shared/util.go index 0474e4d..69f83ac 100644 --- a/shared/util.go +++ b/shared/util.go @@ -430,42 +430,6 @@ func IsTrue(value string) bool { return false } -func IsOnSharedMount(pathName string) (bool, error) { - file, err := os.Open("/proc/self/mountinfo") - if err != nil { - return false, err - } - defer file.Close() - - absPath, err := filepath.Abs(pathName) - if err != nil { - return false, err - } - - expPath, err := os.Readlink(absPath) - if err != nil { - expPath = absPath - } - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - rows := strings.Fields(line) - - if rows[4] != expPath { - continue - } - - if strings.HasPrefix(rows[6], "shared:") { - return true, nil - } else { - return false, nil - } - } - - return false, nil -} - func IsBlockdev(fm os.FileMode) bool { return ((fm&os.ModeDevice != 0) && (fm&os.ModeCharDevice == 0)) } From 17a4ad1a240a78fd82013101466311f5080af4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 15:01:10 -0500 Subject: [PATCH 05/11] Mount a tmpfs under devlxd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2877 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/daemon.go | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lxd/daemon.go b/lxd/daemon.go index f37bdb0..2de8487 100644 --- a/lxd/daemon.go +++ b/lxd/daemon.go @@ -854,6 +854,17 @@ func (d *Daemon) Init() error { daemonConfig["core.proxy_ignore_hosts"].Get(), ) + /* Setup some mounts (nice to have) */ + if !d.MockMode { + // Attempt to mount the shmounts tmpfs + setupSharedMounts() + + // Attempt to Mount the devlxd tmpfs + if !shared.IsMountPoint(shared.VarPath("devlxd")) { + syscall.Mount("tmpfs", shared.VarPath("devlxd"), "tmpfs", 0, "size=100k,mode=0755") + } + } + /* Setup /dev/lxd */ shared.LogInfof("Starting /dev/lxd handler") d.devlxd, err = createAndBindDevLxd() @@ -1161,23 +1172,24 @@ func (d *Daemon) Stop() error { } } + shared.LogInfof("Stopping /dev/lxd handler") + d.devlxd.Close() + shared.LogInfof("Stopped /dev/lxd handler") + if n, err := d.numRunningContainers(); err != nil || n == 0 { - shared.LogInfof("Unmounting shmounts") + shared.LogInfof("Unmounting temporary filesystems") + syscall.Unmount(shared.VarPath("devlxd"), syscall.MNT_DETACH) syscall.Unmount(shared.VarPath("shmounts"), syscall.MNT_DETACH) - shared.LogInfof("Done unmounting shmounts") + shared.LogInfof("Done unmounting temporary filesystems") } else { - shared.LogDebugf("Not unmounting shmounts (containers are still running)") + shared.LogDebugf("Not unmounting temporary filesystems (containers are still running)") } shared.LogInfof("Closing the database") d.db.Close() - shared.LogInfof("Stopping /dev/lxd handler") - d.devlxd.Close() - shared.LogInfof("Stopped /dev/lxd handler") - shared.LogInfof("Saving simplestreams cache") imageSaveStreamCache() shared.LogInfof("Saved simplestreams cache") From c77f2052231fc4b775df742289b644e2234f23f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 15:27:03 -0500 Subject: [PATCH 06/11] Allow setting network interface name on attach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2873 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/network.go | 16 ++++++++++++---- po/lxd.pot | 38 +++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/lxc/network.go b/lxc/network.go index 0966173..9e890d7 100644 --- a/lxc/network.go +++ b/lxc/network.go @@ -61,8 +61,8 @@ lxc network edit [<remote>:]<network> Example: lxc network edit <network> # launch editor cat network.yaml | lxc network edit <network> # read from network.yaml -lxc network attach [<remote>:]<network> <container> [device name] -lxc network attach-profile [<remote>:]<network> <profile> [device name] +lxc network attach [<remote>:]<network> <container> [device name] [interface name] +lxc network attach-profile [<remote>:]<network> <profile> [device name] [interface name] lxc network detach [<remote>:]<network> <container> [device name] lxc network detach-profile [<remote>:]<network> <container> [device name]`) @@ -118,7 +118,7 @@ func (c *networkCmd) run(config *lxd.Config, args []string) error { } func (c *networkCmd) doNetworkAttach(client *lxd.Client, name string, args []string) error { - if len(args) < 1 || len(args) > 2 { + if len(args) < 1 || len(args) > 3 { return errArgs } @@ -139,6 +139,10 @@ func (c *networkCmd) doNetworkAttach(client *lxd.Client, name string, args []str } props := []string{fmt.Sprintf("nictype=%s", nicType), fmt.Sprintf("parent=%s", name)} + if len(args) > 2 { + props = append(props, fmt.Sprintf("name=%s", args[2])) + } + resp, err := client.ContainerDeviceAdd(container, devName, "nic", props) if err != nil { return err @@ -148,7 +152,7 @@ func (c *networkCmd) doNetworkAttach(client *lxd.Client, name string, args []str } func (c *networkCmd) doNetworkAttachProfile(client *lxd.Client, name string, args []string) error { - if len(args) < 1 || len(args) > 2 { + if len(args) < 1 || len(args) > 3 { return errArgs } @@ -169,6 +173,10 @@ func (c *networkCmd) doNetworkAttachProfile(client *lxd.Client, name string, arg } props := []string{fmt.Sprintf("nictype=%s", nicType), fmt.Sprintf("parent=%s", name)} + if len(args) > 2 { + props = append(props, fmt.Sprintf("name=%s", args[2])) + } + _, err = client.ProfileDeviceAdd(profile, devName, "nic", props) return err } diff --git a/po/lxd.pot b/po/lxd.pot index f8106af..04e1782 100644 --- a/po/lxd.pot +++ b/po/lxd.pot @@ -189,7 +189,7 @@ msgstr "" msgid "CREATED AT" msgstr "" -#: lxc/config.go:114 lxc/network.go:463 +#: lxc/config.go:114 lxc/network.go:471 #, c-format msgid "Can't read from stdin: %s" msgstr "" @@ -199,7 +199,7 @@ msgstr "" msgid "Can't unset key '%s', it's not currently set." msgstr "" -#: lxc/network.go:390 lxc/profile.go:424 lxc/storage.go:522 +#: lxc/network.go:398 lxc/profile.go:424 lxc/storage.go:522 msgid "Cannot provide container name to list" msgstr "" @@ -233,7 +233,7 @@ msgstr "" msgid "Config key/value to apply to the new container" msgstr "" -#: lxc/config.go:535 lxc/config.go:600 lxc/image.go:737 lxc/network.go:346 lxc/profile.go:218 lxc/storage.go:478 lxc/storage.go:824 +#: lxc/config.go:535 lxc/config.go:600 lxc/image.go:737 lxc/network.go:354 lxc/profile.go:218 lxc/storage.go:478 lxc/storage.go:824 #, c-format msgid "Config parsing error: %s" msgstr "" @@ -617,7 +617,7 @@ msgstr "" msgid "Log:" msgstr "" -#: lxc/network.go:428 +#: lxc/network.go:436 msgid "MANAGED" msgstr "" @@ -739,8 +739,8 @@ msgid "Manage networks.\n" " Example: lxc network edit <network> # launch editor\n" " cat network.yaml | lxc network edit <network> # read from network.yaml\n" "\n" - "lxc network attach [<remote>:]<network> <container> [device name]\n" - "lxc network attach-profile [<remote>:]<network> <profile> [device name]\n" + "lxc network attach [<remote>:]<network> <container> [device name] [interface name]\n" + "lxc network attach-profile [<remote>:]<network> <profile> [device name] [interface name]\n" "\n" "lxc network detach [<remote>:]<network> <container> [device name]\n" "lxc network detach-profile [<remote>:]<network> <container> [device name]" @@ -891,7 +891,7 @@ msgid "Monitor activity on the LXD server.\n" " lxc monitor --type=logging" msgstr "" -#: lxc/network.go:216 lxc/network.go:265 lxc/storage.go:309 lxc/storage.go:405 +#: lxc/network.go:224 lxc/network.go:273 lxc/storage.go:309 lxc/storage.go:405 msgid "More than one device matches, specify the device name." msgstr "" @@ -916,11 +916,11 @@ msgstr "" msgid "Must supply container name for: " msgstr "" -#: lxc/list.go:431 lxc/network.go:426 lxc/profile.go:451 lxc/remote.go:380 lxc/storage.go:550 lxc/storage.go:640 lxc/storage.go:673 +#: lxc/list.go:431 lxc/network.go:434 lxc/profile.go:451 lxc/remote.go:380 lxc/storage.go:550 lxc/storage.go:640 lxc/storage.go:673 msgid "NAME" msgstr "" -#: lxc/network.go:412 lxc/remote.go:354 lxc/remote.go:359 +#: lxc/network.go:420 lxc/remote.go:354 lxc/remote.go:359 msgid "NO" msgstr "" @@ -929,12 +929,12 @@ msgstr "" msgid "Name: %s" msgstr "" -#: lxc/network.go:190 +#: lxc/network.go:198 #, c-format msgid "Network %s created" msgstr "" -#: lxc/network.go:293 +#: lxc/network.go:301 #, c-format msgid "Network %s deleted" msgstr "" @@ -955,7 +955,7 @@ msgstr "" msgid "No certificate provided to add" msgstr "" -#: lxc/network.go:225 lxc/network.go:274 +#: lxc/network.go:233 lxc/network.go:282 msgid "No device found for this network" msgstr "" @@ -975,7 +975,7 @@ msgstr "" msgid "Only https:// is supported for remote image import." msgstr "" -#: lxc/network.go:322 lxc/network.go:449 +#: lxc/network.go:330 lxc/network.go:457 msgid "Only managed networks can be modified." msgstr "" @@ -1037,7 +1037,7 @@ msgstr "" msgid "Pid: %d" msgstr "" -#: lxc/network.go:347 lxc/profile.go:219 lxc/storage.go:479 lxc/storage.go:825 +#: lxc/network.go:355 lxc/profile.go:219 lxc/storage.go:479 lxc/storage.go:825 msgid "Press enter to open the editor again" msgstr "" @@ -1303,7 +1303,7 @@ msgstr "" msgid "Swap (peak)" msgstr "" -#: lxc/list.go:436 lxc/network.go:427 lxc/storage.go:641 lxc/storage.go:674 +#: lxc/list.go:436 lxc/network.go:435 lxc/storage.go:641 lxc/storage.go:674 msgid "TYPE" msgstr "" @@ -1332,11 +1332,11 @@ msgstr "" msgid "The opposite of `lxc pause` is `lxc start`." msgstr "" -#: lxc/network.go:230 lxc/network.go:279 lxc/storage.go:323 lxc/storage.go:419 +#: lxc/network.go:238 lxc/network.go:287 lxc/storage.go:323 lxc/storage.go:419 msgid "The specified device doesn't exist" msgstr "" -#: lxc/network.go:234 lxc/network.go:283 +#: lxc/network.go:242 lxc/network.go:291 msgid "The specified device doesn't match the network" msgstr "" @@ -1390,7 +1390,7 @@ msgstr "" msgid "URL" msgstr "" -#: lxc/network.go:429 lxc/profile.go:452 lxc/storage.go:553 lxc/storage.go:642 lxc/storage.go:675 +#: lxc/network.go:437 lxc/profile.go:452 lxc/storage.go:553 lxc/storage.go:642 lxc/storage.go:675 msgid "USED BY" msgstr "" @@ -1424,7 +1424,7 @@ msgstr "" msgid "Whether or not to snapshot the container's running state" msgstr "" -#: lxc/network.go:414 lxc/remote.go:356 lxc/remote.go:361 +#: lxc/network.go:422 lxc/remote.go:356 lxc/remote.go:361 msgid "YES" msgstr "" From 59bbab88ec8c1f2a4c7772eba5640a72c9f7bc4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 16:08:46 -0500 Subject: [PATCH 07/11] Fix error handling on FileRemove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/container_lxc.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index 05f252f..46600bf 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -4597,6 +4597,8 @@ func (c *containerLXC) FilePush(srcpath string, dstpath string, uid int, gid int } func (c *containerLXC) FileRemove(path string) error { + var errStr string + // Setup container storage if needed if !c.IsRunning() { err := c.StorageStart() @@ -4623,13 +4625,24 @@ func (c *containerLXC) FileRemove(path string) error { } // Process forkremovefile response - if string(out) != "" { - if strings.HasPrefix(string(out), "error:") { - return fmt.Errorf(strings.TrimPrefix(strings.TrimSuffix(string(out), "\n"), "error: ")) + for _, line := range strings.Split(strings.TrimRight(string(out), "\n"), "\n") { + if line == "" { + continue } - for _, line := range strings.Split(strings.TrimRight(string(out), "\n"), "\n") { - shared.LogDebugf("forkremovefile: %s", line) + // Extract errors + if strings.HasPrefix(line, "error: ") { + errStr = strings.TrimPrefix(line, "error: ") + continue + } + + if strings.HasPrefix(line, "errno: ") { + errno := strings.TrimPrefix(line, "errno: ") + if errno == "2" { + return os.ErrNotExist + } + + return fmt.Errorf(errStr) } } From b9d73d05bfc439c6e740fd18cb77554b499f54dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 16:09:03 -0500 Subject: [PATCH 08/11] Implement file DELETE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2868 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- client.go | 14 ++++++++++++++ doc/api-extensions.md | 3 +++ doc/rest-api.md | 12 ++++++++++++ lxc/file.go | 29 +++++++++++++++++++++++++++++ lxd/api_1.0.go | 1 + lxd/container_file.go | 11 +++++++++++ lxd/containers.go | 7 ++++--- 7 files changed, 74 insertions(+), 3 deletions(-) diff --git a/client.go b/client.go index e013499..7232b7b 100644 --- a/client.go +++ b/client.go @@ -1962,6 +1962,20 @@ func (c *Client) RecursivePullFile(container string, p string, targetDir string) return nil } +func (c *Client) DeleteFile(container string, p string) error { + if c.Remote.Public { + return fmt.Errorf("This function isn't supported by public remotes.") + } + + query := url.Values{"path": []string{p}} + _, err := c.delete(fmt.Sprintf("containers/%s/files?%s", container, query.Encode()), nil, api.SyncResponse) + if err != nil { + return err + } + + return nil +} + func (c *Client) GetMigrationSourceWS(container string) (*api.Response, error) { if c.Remote.Public { return nil, fmt.Errorf("This function isn't supported by public remotes.") diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 55a91d3..b6a9d23 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -202,3 +202,6 @@ This includes: * DELETE /1.0/storage-pools/<pool>/volumes/<volume_type>/<name> (see rest-api.md for details) - All storage configuration options (see configuration.md for details) + +## file\_delete +Implements DELETE in /1.0/containers/\<name\>/files diff --git a/doc/rest-api.md b/doc/rest-api.md index 65302a1..8a07bef 100644 --- a/doc/rest-api.md +++ b/doc/rest-api.md @@ -819,6 +819,18 @@ The following headers may be set by the client: This is designed to be easily usable from the command line or even a web browser. +### DELETE (?path=/path/inside/the/container) + * Description: delete a file in the container + * Introduced: with API extension "file\_delete" + * Authentication: trusted + * Operation: sync + * Return: standard return value or standard error + +Input (none at present): + + { + } + ## /1.0/containers/\<name\>/snapshots ### GET * Description: List of snapshots diff --git a/lxc/file.go b/lxc/file.go index 1fbb65c..9760bee 100644 --- a/lxc/file.go +++ b/lxc/file.go @@ -38,6 +38,7 @@ func (c *fileCmd) usage() string { lxc file pull [-r|--recursive] [<remote>:]<container> [[<remote>:]<container>...] <target path> lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--mode=MODE] <source path> [<source path>...] [<remote>:]<container> +lxc file delete [<remote>:]<container> [[<remote>:]<container>...] lxc file edit [<remote>:]<container>/<path> <source> in the case of pull, <target> in the case of push and <file> in the case of edit are <container name>/<path> @@ -340,6 +341,32 @@ func (c *fileCmd) pull(config *lxd.Config, args []string) error { return nil } +func (c *fileCmd) delete(config *lxd.Config, args []string) error { + if len(args) < 1 { + return errArgs + } + + for _, f := range args[:len(args)] { + pathSpec := strings.SplitN(f, "/", 2) + if len(pathSpec) != 2 { + return fmt.Errorf(i18n.G("Invalid path %s"), f) + } + + remote, container := config.ParseRemoteAndContainer(pathSpec[0]) + d, err := lxd.NewClient(config, remote) + if err != nil { + return err + } + + err = d.DeleteFile(container, pathSpec[1]) + if err != nil { + return err + } + } + + return nil +} + func (c *fileCmd) edit(config *lxd.Config, args []string) error { if len(args) != 1 { return errArgs @@ -390,6 +417,8 @@ func (c *fileCmd) run(config *lxd.Config, args []string) error { return c.push(config, true, args[1:]) case "pull": return c.pull(config, args[1:]) + case "delete": + return c.delete(config, args[1:]) case "edit": return c.edit(config, args[1:]) default: diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go index 3951f94..963c815 100644 --- a/lxd/api_1.0.go +++ b/lxd/api_1.0.go @@ -105,6 +105,7 @@ func api10Get(d *Daemon, r *http.Request) Response { "network_firewall_filtering", "network_routes", "storage", + "file_delete", }, APIStatus: "stable", APIVersion: version.APIVersion, diff --git a/lxd/container_file.go b/lxd/container_file.go index 7e9d20b..a4c1714 100644 --- a/lxd/container_file.go +++ b/lxd/container_file.go @@ -30,6 +30,8 @@ func containerFileHandler(d *Daemon, r *http.Request) Response { return containerFileGet(c, path, r) case "POST": return containerFilePut(c, path, r) + case "DELETE": + return containerFileDelete(c, path, r) default: return NotFound } @@ -117,3 +119,12 @@ func containerFilePut(c container, path string, r *http.Request) Response { return InternalError(fmt.Errorf("bad file type %s", type_)) } } + +func containerFileDelete(c container, path string, r *http.Request) Response { + err := c.FileRemove(path) + if err != nil { + return SmartError(err) + } + + return EmptySyncResponse +} diff --git a/lxd/containers.go b/lxd/containers.go index d5305d3..3bb06ae 100644 --- a/lxd/containers.go +++ b/lxd/containers.go @@ -33,9 +33,10 @@ var containerStateCmd = Command{ } var containerFileCmd = Command{ - name: "containers/{name}/files", - get: containerFileHandler, - post: containerFileHandler, + name: "containers/{name}/files", + get: containerFileHandler, + post: containerFileHandler, + delete: containerFileHandler, } var containerSnapshotsCmd = Command{ From a3b28399a72627a29e118a128af798a67987ef7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 16:15:05 -0500 Subject: [PATCH 09/11] doc: Clarify PUT vs PATCH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2873 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- doc/rest-api.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/rest-api.md b/doc/rest-api.md index 8a07bef..4c3f5e0 100644 --- a/doc/rest-api.md +++ b/doc/rest-api.md @@ -161,6 +161,21 @@ It's recommended that the client always subscribes to the operations notification type before triggering remote operations so that it doesn't have to then poll for their status. +# PUT vs PATCH +The LXD API supports both PUT and PATCH to modify existing objects. + +PUT replaces the entire object with a new definition, it's typically +called after the current object state was retrieved through GET. + +To avoid race conditions, the Etag header should be read from the GET +response and sent as If-Match for the PUT request. This will cause LXD +to fail the request if the object was modified between GET and PUT. + +PATCH can be used to modify a single field inside an object by only +specifying the property that you want to change. To unset a key, setting +it to empty will usually do the trick, but there are cases where PATCH +won't work and PUT needs to be used instead. + # API structure * / * /1.0 From e33dc0715dc335d8dd8bc246a687c4abbe50e1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 16:36:47 -0500 Subject: [PATCH 10/11] Make it possible to append to a file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2871 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- client.go | 2 +- doc/api-extensions.md | 3 +++ doc/rest-api.md | 1 + lxd/api_1.0.go | 1 + lxd/container.go | 2 +- lxd/container_file.go | 12 ++++++++---- lxd/container_lxc.go | 6 ++++-- lxd/main_nsexec.go | 25 ++++++++++++++++++------- shared/util.go | 12 ++++++++++-- 9 files changed, 47 insertions(+), 17 deletions(-) diff --git a/client.go b/client.go index 7232b7b..6893165 100644 --- a/client.go +++ b/client.go @@ -1896,7 +1896,7 @@ func (c *Client) PullFile(container string, p string) (int, int, int, string, io return 0, 0, 0, "", nil, nil, err } - uid, gid, mode, type_ := shared.ParseLXDFileHeaders(r.Header) + uid, gid, mode, type_, _ := shared.ParseLXDFileHeaders(r.Header) if type_ == "directory" { resp, err := HoistResponse(r, api.SyncResponse) if err != nil { diff --git a/doc/api-extensions.md b/doc/api-extensions.md index b6a9d23..9c76606 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -205,3 +205,6 @@ This includes: ## file\_delete Implements DELETE in /1.0/containers/\<name\>/files + +## file\_append +Implements the X-LXD-write header which can be one of "overwrite" or "append". diff --git a/doc/rest-api.md b/doc/rest-api.md index 4c3f5e0..d883338 100644 --- a/doc/rest-api.md +++ b/doc/rest-api.md @@ -830,6 +830,7 @@ The following headers may be set by the client: * X-LXD-uid: 0 * X-LXD-gid: 0 * X-LXD-mode: 0700 + * X-LXD-write: overwrite (or append, introduced with API extension "file\_append") This is designed to be easily usable from the command line or even a web browser. diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go index 963c815..215f4a9 100644 --- a/lxd/api_1.0.go +++ b/lxd/api_1.0.go @@ -106,6 +106,7 @@ func api10Get(d *Daemon, r *http.Request) Response { "network_routes", "storage", "file_delete", + "file_append", }, APIStatus: "stable", APIVersion: version.APIVersion, diff --git a/lxd/container.go b/lxd/container.go index 20a41dd..e4ef23c 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -370,7 +370,7 @@ type container interface { // File handling FileExists(path string) error FilePull(srcpath string, dstpath string) (int, int, os.FileMode, string, []string, error) - FilePush(srcpath string, dstpath string, uid int, gid int, mode int) error + FilePush(srcpath string, dstpath string, uid int, gid int, mode int, write string) error FileRemove(path string) error /* Command execution: diff --git a/lxd/container_file.go b/lxd/container_file.go index a4c1714..eb443a3 100644 --- a/lxd/container_file.go +++ b/lxd/container_file.go @@ -84,7 +84,11 @@ func containerFileGet(c container, path string, r *http.Request) Response { func containerFilePut(c container, path string, r *http.Request) Response { // Extract file ownership and mode from headers - uid, gid, mode, type_ := shared.ParseLXDFileHeaders(r.Header) + uid, gid, mode, type_, write := shared.ParseLXDFileHeaders(r.Header) + + if !shared.StringInSlice(write, []string{"overwrite", "append"}) { + return BadRequest(fmt.Errorf("Bad file write mode: %s", write)) + } if type_ == "file" { // Write file content to a tempfile @@ -103,20 +107,20 @@ func containerFilePut(c container, path string, r *http.Request) Response { } // Transfer the file into the container - err = c.FilePush(temp.Name(), path, uid, gid, mode) + err = c.FilePush(temp.Name(), path, uid, gid, mode, write) if err != nil { return InternalError(err) } return EmptySyncResponse } else if type_ == "directory" { - err := c.FilePush("", path, uid, gid, mode) + err := c.FilePush("", path, uid, gid, mode, write) if err != nil { return InternalError(err) } return EmptySyncResponse } else { - return InternalError(fmt.Errorf("bad file type %s", type_)) + return BadRequest(fmt.Errorf("Bad file type: %s", type_)) } } diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index 46600bf..8a62d9f 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -4505,7 +4505,7 @@ func (c *containerLXC) FilePull(srcpath string, dstpath string) (int, int, os.Fi return uid, gid, os.FileMode(mode), type_, dirEnts, nil } -func (c *containerLXC) FilePush(srcpath string, dstpath string, uid int, gid int, mode int) error { +func (c *containerLXC) FilePush(srcpath string, dstpath string, uid int, gid int, mode int, write string) error { var rootUid = 0 var rootGid = 0 var errStr string @@ -4545,6 +4545,7 @@ func (c *containerLXC) FilePush(srcpath string, dstpath string, uid int, gid int fmt.Sprintf("%d", rootUid), fmt.Sprintf("%d", rootGid), fmt.Sprintf("%d", int(os.FileMode(0640)&os.ModePerm)), + write, ).CombinedOutput() // Tear down container storage if needed @@ -4579,7 +4580,7 @@ func (c *containerLXC) FilePush(srcpath string, dstpath string, uid int, gid int if err != nil { return fmt.Errorf( - "Error calling 'lxd forkputfile %s %d %s %s %d %d %d %d %d %d': err='%v'", + "Error calling 'lxd forkputfile %s %d %s %s %d %d %d %d %d %d %s': err='%v'", c.RootfsPath(), c.InitPID(), srcpath, @@ -4590,6 +4591,7 @@ func (c *containerLXC) FilePush(srcpath string, dstpath string, uid int, gid int rootUid, rootGid, int(os.FileMode(0640)&os.ModePerm), + write, err) } diff --git a/lxd/main_nsexec.go b/lxd/main_nsexec.go index a1239f4..67c3c14 100644 --- a/lxd/main_nsexec.go +++ b/lxd/main_nsexec.go @@ -87,16 +87,21 @@ int mkdir_p(const char *dir, mode_t mode) return 0; } -int copy(int target, int source) +int copy(int target, int source, bool append) { ssize_t n; char buf[1024]; - if (ftruncate(target, 0) < 0) { + if (!append && ftruncate(target, 0) < 0) { error("error: truncate"); return -1; } + if (append && lseek(target, 0, SEEK_END) < 0) { + error("error: seek"); + return -1; + } + while ((n = read(source, buf, 1024)) > 0) { if (write(target, buf, n) != n) { error("error: write"); @@ -175,7 +180,7 @@ void attach_userns(int pid) { } } -int manip_file_in_ns(char *rootfs, int pid, char *host, char *container, bool is_put, uid_t uid, gid_t gid, mode_t mode, uid_t defaultUid, gid_t defaultGid, mode_t defaultMode) { +int manip_file_in_ns(char *rootfs, int pid, char *host, char *container, bool is_put, uid_t uid, gid_t gid, mode_t mode, uid_t defaultUid, gid_t defaultGid, mode_t defaultMode, bool append) { int host_fd = -1, container_fd = -1; int ret = -1; int container_open_flags; @@ -273,7 +278,7 @@ int manip_file_in_ns(char *rootfs, int pid, char *host, char *container, bool is } } - if (copy(container_fd, host_fd) < 0) { + if (copy(container_fd, host_fd, append) < 0) { error("error: copy"); goto close_container; } @@ -333,7 +338,7 @@ int manip_file_in_ns(char *rootfs, int pid, char *host, char *container, bool is goto close_host; } else { fprintf(stderr, "type: file\n"); - ret = copy(host_fd, container_fd); + ret = copy(host_fd, container_fd, false); } fprintf(stderr, "type: %s", S_ISDIR(st.st_mode) ? "directory" : "file"); } @@ -497,8 +502,9 @@ void forkdofile(char *buf, char *cur, bool is_put, ssize_t size) { uid_t defaultUid = 0; gid_t defaultGid = 0; mode_t defaultMode = 0; - char *command = cur, *rootfs = NULL, *source = NULL, *target = NULL; + char *command = cur, *rootfs = NULL, *source = NULL, *target = NULL, *writeMode = NULL; pid_t pid; + bool append = false; ADVANCE_ARG_REQUIRED(); rootfs = cur; @@ -530,9 +536,14 @@ void forkdofile(char *buf, char *cur, bool is_put, ssize_t size) { ADVANCE_ARG_REQUIRED(); defaultMode = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + if (strcmp(cur, "append") == 0) { + append = true; + } } - _exit(manip_file_in_ns(rootfs, pid, source, target, is_put, uid, gid, mode, defaultUid, defaultGid, defaultMode)); + _exit(manip_file_in_ns(rootfs, pid, source, target, is_put, uid, gid, mode, defaultUid, defaultGid, defaultMode, append)); } void forkcheckfile(char *buf, char *cur, bool is_put, ssize_t size) { diff --git a/shared/util.go b/shared/util.go index 69f83ac..1e0b878 100644 --- a/shared/util.go +++ b/shared/util.go @@ -123,7 +123,7 @@ func LogPath(path ...string) string { return filepath.Join(items...) } -func ParseLXDFileHeaders(headers http.Header) (uid int, gid int, mode int, type_ string) { +func ParseLXDFileHeaders(headers http.Header) (uid int, gid int, mode int, type_ string, write string) { uid, err := strconv.Atoi(headers.Get("X-LXD-uid")) if err != nil { uid = -1 @@ -152,7 +152,15 @@ func ParseLXDFileHeaders(headers http.Header) (uid int, gid int, mode int, type_ type_ = "file" } - return uid, gid, mode, type_ + write = headers.Get("X-LXD-write") + /* backwards compat: before "write" was introduced, we could only + * overwrite files + */ + if write == "" { + write = "overwrite" + } + + return uid, gid, mode, type_, write } func ReadToJSON(r io.Reader, req interface{}) error { From a9e04686f712303d691afab8463df1675048f439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 14 Feb 2017 16:57:55 -0500 Subject: [PATCH 11/11] network: Implement configurable DHCP lease time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2835 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- doc/api-extensions.md | 3 +++ doc/configuration.md | 2 ++ lxd/api_1.0.go | 1 + lxd/networks.go | 18 ++++++++++++++---- lxd/networks_config.go | 2 ++ 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 9c76606..dbf18c6 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -208,3 +208,6 @@ Implements DELETE in /1.0/containers/\<name\>/files ## file\_append Implements the X-LXD-write header which can be one of "overwrite" or "append". + +## network\_dhcp\_expiry +Introduces "ipv4.dhcp.expiry" and "ipv6.dhcp.expiry" allowing to set the DHCP lease expiry time. diff --git a/doc/configuration.md b/doc/configuration.md index 981647e..b791fbd 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -355,6 +355,7 @@ tunnel.NAME.id | integer | vxlan | 0 ipv4.address | string | standard mode | random unused subnet | IPv4 address for the bridge (CIDR notation). Use "none" to turn off IPv4 or "auto" to generate a new one ipv4.nat | boolean | ipv4 address | false | Whether to NAT (will default to true if unset and a random ipv4.address is generated) ipv4.dhcp | boolean | ipv4 address | true | Whether to allocate addresses using DHCP +ipv4.dhcp.expiry | string | ipv4 dhcp | 1h | When to expire DHCP leases ipv4.dhcp.ranges | string | ipv4 dhcp | all addresses | Comma separated list of IP ranges to use for DHCP (FIRST-LAST format) ipv4.firewall | boolean | ipv4 address | true | Whether to generate filtering firewall rules for this network ipv4.routes | string | ipv4 address | - | Comma separated list of additional IPv4 CIDR subnets to route to the bridge @@ -362,6 +363,7 @@ ipv4.routing | boolean | ipv4 address | true ipv6.address | string | standard mode | random unused subnet | IPv6 address for the bridge (CIDR notation). Use "none" to turn off IPv6 or "auto" to generate a new one ipv6.nat | boolean | ipv6 address | false | Whether to NAT (will default to true if unset and a random ipv6.address is generated) ipv6.dhcp | boolean | ipv6 address | true | Whether to provide additional network configuration over DHCP +ipv6.dhcp.expiry | string | ipv6 dhcp | 1h | When to expire DHCP leases ipv6.dhcp.stateful | boolean | ipv6 dhcp | false | Whether to allocate addresses using DHCP ipv6.dhcp.ranges | string | ipv6 stateful dhcp | all addresses | Comma separated list of IPv6 ranges to use for DHCP (FIRST-LAST format) ipv6.firewall | boolean | ipv6 address | true | Whether to generate filtering firewall rules for this network diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go index 215f4a9..77bbc3e 100644 --- a/lxd/api_1.0.go +++ b/lxd/api_1.0.go @@ -107,6 +107,7 @@ func api10Get(d *Daemon, r *http.Request) Response { "storage", "file_delete", "file_append", + "network_dhcp_expiry", }, APIStatus: "stable", APIVersion: version.APIVersion, diff --git a/lxd/networks.go b/lxd/networks.go index 63a298c..8a03d56 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -742,13 +742,18 @@ func (n *network) Start() error { dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-no-override", "--dhcp-authoritative", fmt.Sprintf("--dhcp-leasefile=%s", shared.VarPath("networks", n.name, "dnsmasq.leases")), fmt.Sprintf("--dhcp-hostsfile=%s", shared.VarPath("networks", n.name, "dnsmasq.hosts"))}...) } + expiry := "1h" + if n.config["ipv4.dhcp.expiry"] != "" { + expiry = n.config["ipv4.dhcp.expiry"] + } + if n.config["ipv4.dhcp.ranges"] != "" { for _, dhcpRange := range strings.Split(n.config["ipv4.dhcp.ranges"], ",") { dhcpRange = strings.TrimSpace(dhcpRange) - dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", strings.Replace(dhcpRange, "-", ",", -1)}...) + dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s", strings.Replace(dhcpRange, "-", ",", -1), expiry)}...) } } else { - dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s", networkGetIP(subnet, 2).String(), networkGetIP(subnet, -2).String())}...) + dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s,%s", networkGetIP(subnet, 2).String(), networkGetIP(subnet, -2).String(), expiry)}...) } } @@ -821,14 +826,19 @@ func (n *network) Start() error { dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-no-override", "--dhcp-authoritative", fmt.Sprintf("--dhcp-leasefile=%s", shared.VarPath("networks", n.name, "dnsmasq.leases")), fmt.Sprintf("--dhcp-hostsfile=%s", shared.VarPath("networks", n.name, "dnsmasq.hosts"))}...) } + expiry := "1h" + if n.config["ipv6.dhcp.expiry"] != "" { + expiry = n.config["ipv6.dhcp.expiry"] + } + if shared.IsTrue(n.config["ipv6.dhcp.stateful"]) { if n.config["ipv6.dhcp.ranges"] != "" { for _, dhcpRange := range strings.Split(n.config["ipv6.dhcp.ranges"], ",") { dhcpRange = strings.TrimSpace(dhcpRange) - dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s", strings.Replace(dhcpRange, "-", ",", -1))}...) + dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s", strings.Replace(dhcpRange, "-", ",", -1), expiry)}...) } } else { - dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s", networkGetIP(subnet, 2), networkGetIP(subnet, -1))}...) + dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("%s,%s,%s", networkGetIP(subnet, 2), networkGetIP(subnet, -1), expiry)}...) } } else { dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("::,constructor:%s,ra-stateless,ra-names", n.name)}...) diff --git a/lxd/networks_config.go b/lxd/networks_config.go index 0270045..fd6b17f 100644 --- a/lxd/networks_config.go +++ b/lxd/networks_config.go @@ -62,6 +62,7 @@ var networkConfigKeys = map[string]func(value string) error{ "ipv4.firewall": shared.IsBool, "ipv4.nat": shared.IsBool, "ipv4.dhcp": shared.IsBool, + "ipv4.dhcp.expiry": shared.IsAny, "ipv4.dhcp.ranges": shared.IsAny, "ipv4.routes": shared.IsAny, "ipv4.routing": shared.IsBool, @@ -76,6 +77,7 @@ var networkConfigKeys = map[string]func(value string) error{ "ipv6.firewall": shared.IsBool, "ipv6.nat": shared.IsBool, "ipv6.dhcp": shared.IsBool, + "ipv6.dhcp.expiry": shared.IsAny, "ipv6.dhcp.stateful": shared.IsBool, "ipv6.dhcp.ranges": shared.IsAny, "ipv6.routes": shared.IsAny,
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel