[lxc-devel] [lxd/master] Various bugfix and minor features
stgraber on Github
lxc-bot at linuxcontainers.org
Tue Feb 14 22:26:52 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170214/3d0d46ef/attachment.bin>
-------------- next part --------------
From b407d52b250282f92ace201c01979aa39c84bbfb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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 at 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 at ADDRESS>\n"
"Language-Team: LANGUAGE <LL at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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,
More information about the lxc-devel
mailing list