diff -Nru lxd-2.6.2/client.go lxd-2.7/client.go --- lxd-2.6.2/client.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/client.go 2016-12-21 00:52:23.000000000 +0000 @@ -2,7 +2,6 @@ import ( "bytes" - "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" @@ -25,6 +24,9 @@ "github.com/gorilla/websocket" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/ioprogress" + "github.com/lxc/lxd/shared/simplestreams" + "github.com/lxc/lxd/shared/version" ) // Client can talk to a LXD daemon. @@ -39,7 +41,7 @@ Http http.Client websocketDialer websocket.Dialer - simplestreams *shared.SimpleStreams + simplestreams *simplestreams.SimpleStreams } type ResponseType string @@ -338,11 +340,20 @@ } if info.RemoteConfig.Protocol == "simplestreams" { - ss, err := shared.SimpleStreamsClient(c.Remote.Addr, shared.ProxyFromEnvironment) + tlsconfig, err := shared.GetTLSConfig("", "", "", nil) if err != nil { return nil, err } + tr := &http.Transport{ + TLSClientConfig: tlsconfig, + Dial: shared.RFC3493Dialer, + Proxy: shared.ProxyFromEnvironment, + DisableKeepAlives: true, + } + c.Http.Transport = tr + + ss := simplestreams.NewClient(c.Remote.Addr, c.Http, version.UserAgent) c.simplestreams = ss } @@ -382,7 +393,7 @@ } func (c *Client) get(base string) (*Response, error) { - uri := c.url(shared.APIVersion, base) + uri := c.url(version.APIVersion, base) return c.baseGet(uri) } @@ -393,7 +404,7 @@ return nil, err } - req.Header.Set("User-Agent", shared.UserAgent) + req.Header.Set("User-Agent", version.UserAgent) resp, err := c.Http.Do(req) if err != nil { @@ -403,8 +414,8 @@ return HoistResponse(resp, Sync) } -func (c *Client) put(base string, args interface{}, rtype ResponseType) (*Response, error) { - uri := c.url(shared.APIVersion, base) +func (c *Client) doUpdateMethod(method string, base string, args interface{}, rtype ResponseType) (*Response, error) { + uri := c.url(version.APIVersion, base) buf := bytes.Buffer{} err := json.NewEncoder(&buf).Encode(args) @@ -412,13 +423,13 @@ return nil, err } - shared.LogDebugf("Putting %s to %s", buf.String(), uri) + shared.LogDebugf("%s %s to %s", method, buf.String(), uri) - req, err := http.NewRequest("PUT", uri, &buf) + req, err := http.NewRequest(method, uri, &buf) if err != nil { return nil, err } - req.Header.Set("User-Agent", shared.UserAgent) + req.Header.Set("User-Agent", version.UserAgent) req.Header.Set("Content-Type", "application/json") resp, err := c.Http.Do(req) @@ -429,30 +440,20 @@ return HoistResponse(resp, rtype) } -func (c *Client) post(base string, args interface{}, rtype ResponseType) (*Response, error) { - uri := c.url(shared.APIVersion, base) - - buf := bytes.Buffer{} - err := json.NewEncoder(&buf).Encode(args) - if err != nil { - return nil, err - } - - shared.LogDebugf("Posting %s to %s", buf.String(), uri) +func (c *Client) put(base string, args interface{}, rtype ResponseType) (*Response, error) { + return c.doUpdateMethod("PUT", base, args, rtype) +} - req, err := http.NewRequest("POST", uri, &buf) - if err != nil { - return nil, err - } - req.Header.Set("User-Agent", shared.UserAgent) - req.Header.Set("Content-Type", "application/json") +func (c *Client) patch(base string, args interface{}, rtype ResponseType) (*Response, error) { + return c.doUpdateMethod("PATCH", base, args, rtype) +} - resp, err := c.Http.Do(req) - if err != nil { - return nil, err - } +func (c *Client) post(base string, args interface{}, rtype ResponseType) (*Response, error) { + return c.doUpdateMethod("POST", base, args, rtype) +} - return HoistResponse(resp, rtype) +func (c *Client) delete(base string, args interface{}, rtype ResponseType) (*Response, error) { + return c.doUpdateMethod("DELETE", base, args, rtype) } func (c *Client) getRaw(uri string) (*http.Response, error) { @@ -460,7 +461,7 @@ if err != nil { return nil, err } - req.Header.Set("User-Agent", shared.UserAgent) + req.Header.Set("User-Agent", version.UserAgent) raw, err := c.Http.Do(req) if err != nil { @@ -479,32 +480,6 @@ return raw, nil } -func (c *Client) delete(base string, args interface{}, rtype ResponseType) (*Response, error) { - uri := c.url(shared.APIVersion, base) - - buf := bytes.Buffer{} - err := json.NewEncoder(&buf).Encode(args) - if err != nil { - return nil, err - } - - shared.LogDebugf("Deleting %s to %s", buf.String(), uri) - - req, err := http.NewRequest("DELETE", uri, &buf) - if err != nil { - return nil, err - } - req.Header.Set("User-Agent", shared.UserAgent) - req.Header.Set("Content-Type", "application/json") - - resp, err := c.Http.Do(req) - if err != nil { - return nil, err - } - - return HoistResponse(resp, rtype) -} - func (c *Client) Websocket(operation string, secret string) (*websocket.Conn, error) { query := url.Values{"secret": []string{secret}} url := c.BaseWSURL + path.Join(operation, "websocket") + "?" + query.Encode() @@ -547,7 +522,7 @@ return nil, fmt.Errorf("This function isn't supported by simplestreams remote.") } - return c.baseGet(c.url(shared.APIVersion)) + return c.baseGet(c.url(version.APIVersion)) } // GetLocalLXDErr determines whether or not an error is likely due to a @@ -795,7 +770,7 @@ return c.simplestreams.ExportImage(image, target) } - uri := c.url(shared.APIVersion, "images", image, "export") + uri := c.url(version.APIVersion, "images", image, "export") raw, err := c.getRaw(uri) if err != nil { return "", err @@ -1003,7 +978,7 @@ return "", fmt.Errorf("This function isn't supported by public remotes.") } - uri := c.url(shared.APIVersion, "images") + uri := c.url(version.APIVersion, "images") var err error var fImage *os.File @@ -1065,9 +1040,9 @@ return "", err } - progress := &shared.ProgressReader{ + progress := &ioprogress.ProgressReader{ ReadCloser: body, - Tracker: &shared.ProgressTracker{ + Tracker: &ioprogress.ProgressTracker{ Length: size, Handler: progressHandler, }, @@ -1087,9 +1062,9 @@ return "", err } - progress := &shared.ProgressReader{ + progress := &ioprogress.ProgressReader{ ReadCloser: fImage, - Tracker: &shared.ProgressTracker{ + Tracker: &ioprogress.ProgressTracker{ Length: stat.Size(), Handler: progressHandler, }, @@ -1103,7 +1078,7 @@ if err != nil { return "", err } - req.Header.Set("User-Agent", shared.UserAgent) + req.Header.Set("User-Agent", version.UserAgent) if public { req.Header.Set("X-LXD-public", "1") @@ -1712,10 +1687,10 @@ // Fill in certificate fingerprint if not provided if ss.Environment.CertificateFingerprint == "" && ss.Environment.Certificate != "" { - pemCertificate, _ := pem.Decode([]byte(ss.Environment.Certificate)) - if pemCertificate != nil { - digest := sha256.Sum256(pemCertificate.Bytes) - ss.Environment.CertificateFingerprint = fmt.Sprintf("%x", digest) + var err error + ss.Environment.CertificateFingerprint, err = shared.CertFingerprintStr(ss.Environment.Certificate) + if err != nil { + return nil, err } } @@ -1765,7 +1740,7 @@ return nil, fmt.Errorf("This function isn't supported by public remotes.") } - uri := c.url(shared.APIVersion, "containers", container, "logs", log) + uri := c.url(version.APIVersion, "containers", container, "logs", log) resp, err := c.getRaw(uri) if err != nil { return nil, err @@ -1799,13 +1774,13 @@ } query := url.Values{"path": []string{p}} - uri := c.url(shared.APIVersion, "containers", container, "files") + "?" + query.Encode() + uri := c.url(version.APIVersion, "containers", container, "files") + "?" + query.Encode() req, err := http.NewRequest("POST", uri, buf) if err != nil { return err } - req.Header.Set("User-Agent", shared.UserAgent) + req.Header.Set("User-Agent", version.UserAgent) req.Header.Set("X-LXD-type", "file") if mode != "" { @@ -1833,14 +1808,14 @@ } query := url.Values{"path": []string{p}} - uri := c.url(shared.APIVersion, "containers", container, "files") + "?" + query.Encode() + uri := c.url(version.APIVersion, "containers", container, "files") + "?" + query.Encode() req, err := http.NewRequest("POST", uri, nil) if err != nil { return err } - req.Header.Set("User-Agent", shared.UserAgent) + req.Header.Set("User-Agent", version.UserAgent) req.Header.Set("X-LXD-type", "directory") req.Header.Set("X-LXD-mode", fmt.Sprintf("%04o", mode.Perm())) @@ -1903,7 +1878,12 @@ return fmt.Errorf("got error sending path %s: %s", p, err) } - targetPath := path.Join(target, p[len(sourceDir):]) + appendLen := len(sourceDir) + if sourceDir == "." { + appendLen-- + } + + targetPath := path.Join(target, p[appendLen:]) if fInfo.IsDir() { return c.Mkdir(container, targetPath, fInfo.Mode()) } @@ -1927,7 +1907,7 @@ return 0, 0, 0, "", nil, nil, fmt.Errorf("This function isn't supported by public remotes.") } - uri := c.url(shared.APIVersion, "containers", container, "files") + uri := c.url(version.APIVersion, "containers", container, "files") query := url.Values{"path": []string{p}} r, err := c.getRaw(uri + "?" + query.Encode()) diff -Nru lxd-2.6.2/debian/changelog lxd-2.7/debian/changelog --- lxd-2.6.2/debian/changelog 2016-12-05 12:33:41.000000000 +0000 +++ lxd-2.7/debian/changelog 2016-12-21 08:47:38.000000000 +0000 @@ -1,3 +1,81 @@ +lxd (2.7-0ubuntu2) zesty; urgency=medium + + * Cherry-pick upstream fix: + - tests: Fix shellcheck being confused by cd + * Add lxc-to-lxd to lxd-tools. + * Re-enable Go shared libraries. + + -- Stéphane Graber Wed, 21 Dec 2016 03:28:42 -0500 + +lxd (2.7-0ubuntu1) zesty; urgency=medium + + * New upstream release (2.7): + - New "ipv4.firewall" and "ipv6.firewall" network attributes + controlling the generation of iptables FORWARD rules + - New "ipv4.routes" and "ipv6.routes" network attributes allowing + for additional static routes to be set to the network. + - New "lxd import" command allowing importing of containers when all + that exists is the "containers" directory. + + - client: Commonize update methods and add PATCH + - extra/lxc-to-lxd: Add more unsupported config keys + - extra/lxc-to-lxd: All properties must be strings + - extra/lxc-to-lxd: Copy rootfs by default, do not move + - extra/lxc-to-lxd: Show nicer error on missing python3-lxc + - extra/lxc-to-lxd: Switch to using whitelist + - i18n: Update french translation + - lxc/file: Fix off by one error in push + - lxc: Improve help messages + - lxc/init: Fix example + - lxc/launch: Just use init.go's flags() + - lxd: Common codepath for http client + - lxd: Don't set InsecureSkipVerify on daemon's tls config + - lxd: Log daemon version + - lxd: Make LXD_DIR 711 by default (needed for unprivileged containers) + - lxd: Only mark daemon ready once containers are up + - lxd: Properly validate daemon keys on unset + - lxd: Refactoring of sub-command code + - lxd: Use our custom http server when updating HTTPS address too + - lxd/containers: Add basic logging to container creation + - lxd/containers: Avoid race condition in network fill function + - lxd/containers: Blacklist lxc.syslog and lxc.ephemeral + - lxd/containers: Cleanup leftover temp file + - lxd/containers: Detect background tasks to allow clean exit on exec + - lxd/containers: Do mounts in the right order + - lxd/containers: Don't record last_state.power twice + - lxd/containers: Fix container state recording + - lxd/containers: Fix device hotplug with major/minor set + - lxd/containers: Fix file push error handling + - lxd/containers: Fix logging for file_manip commands + - lxd/containers: Move FromLXCState out of shared + - lxd/containers: Return a clear error when replacing a directory + - lxd/containers: Rework EEXISTS detection on create + - lxd/networks: Allow for network-specific lease updates + - lxd/networks: DHCP over TCP has never been implemented + - lxd/nsexec: Also call setgroups + - lxd/seccomp: Fix generated seccomp profile + - lxd/storage: Change ContainerStart to take the name and path to start + - Makefile: Rework "make dist" + - shared: Give Architecture handling its own package + - shared: Give IO progress tracker its own package + - shared: Give simplestreams client its own package + - shared: Give version handling its own package + - shared: Introduce our own formatter + - shared: Make a helper to compute cert fingerprint + - shared: Make PrintStack print at the Error level + - shared: Move WebsocketUpgrader to network.go + - shared: Rename idmapset_test_linux.go to idmapset_linux_test.go + - shared/idmap: Drop debugging code + - shared/idmap: Fix intersection test + - shared/simplestreams: Don't depend on custom http handler + - shared/simplestreams: Pass UserAgent as argument + - tests: Add pki test + - tests: Only attach lxdbr0 if it is present on the host + - tests: Simplify testsuite spawn code + - tests: Test lxd shutdown + + -- Stéphane Graber Wed, 21 Dec 2016 00:21:11 -0500 + lxd (2.6.2-0ubuntu3) zesty; urgency=medium * Fix container last-state recording (LP: #1647312) diff -Nru lxd-2.6.2/debian/control lxd-2.7/debian/control --- lxd-2.6.2/debian/control 2016-12-05 12:33:40.000000000 +0000 +++ lxd-2.7/debian/control 2016-12-21 08:42:08.000000000 +0000 @@ -33,7 +33,7 @@ libsqlite3-dev, lxc-dev (>= 1.1.0~), pkg-config, - protobuf-compiler + python3-lxc Package: lxd-client Architecture: any @@ -86,7 +86,7 @@ Package: lxd-tools Architecture: any -Depends: ${misc:Depends}, ${shlibs:Depends} +Depends: ${misc:Depends}, ${shlibs:Depends}, python3, python3-lxc Built-Using: ${misc:Built-Using} Description: Container hypervisor based on LXC - extra tools LXD offers a REST API to remotely manage containers over the network, @@ -94,6 +94,7 @@ . This package contains extra tools provided with LXD. - fuidshift - A tool to map/unmap filesystem uids/gids + - lxc-to-lxd - A tool to migrate LXC containers to LXD Package: golang-github-lxc-lxd-dev Architecture: all diff -Nru lxd-2.6.2/debian/.git-dpm lxd-2.7/debian/.git-dpm --- lxd-2.6.2/debian/.git-dpm 2016-12-05 12:33:40.000000000 +0000 +++ lxd-2.7/debian/.git-dpm 2016-12-21 08:47:38.000000000 +0000 @@ -1,8 +1,8 @@ # see git-dpm(1) from git-dpm package -e9073535ab9e6ec39b7e0f21e92ca09d1da4868d -e9073535ab9e6ec39b7e0f21e92ca09d1da4868d -9e019e8aba546882141159a6250bb5e3689d6a79 -9e019e8aba546882141159a6250bb5e3689d6a79 -lxd_2.6.2.orig.tar.gz -9bd332243e0da2d34c4016151a9693af11a4cb4e -5110845 +5b259e3fcb914db70c68a2381ef11bbef6637382 +5b259e3fcb914db70c68a2381ef11bbef6637382 +307876fb50559741d36cf024f3e0e4f0fe656f69 +307876fb50559741d36cf024f3e0e4f0fe656f69 +lxd_2.7.orig.tar.gz +7bf8984c2d7b95111ac56af9b20c1eadf728a5dc +5138212 diff -Nru lxd-2.6.2/debian/lxd-tools.install lxd-2.7/debian/lxd-tools.install --- lxd-2.6.2/debian/lxd-tools.install 2016-12-05 12:33:40.000000000 +0000 +++ lxd-2.7/debian/lxd-tools.install 2016-12-21 08:42:08.000000000 +0000 @@ -1,2 +1,5 @@ usr/bin/fuidshift usr/share/man/man1/fuidshift.1 + +usr/bin/lxc-to-lxd +usr/share/man/man1/lxc-to-lxd.1 diff -Nru lxd-2.6.2/debian/patches/0001-Fix-container-state-recording.patch lxd-2.7/debian/patches/0001-Fix-container-state-recording.patch --- lxd-2.6.2/debian/patches/0001-Fix-container-state-recording.patch 2016-12-05 12:33:40.000000000 +0000 +++ lxd-2.7/debian/patches/0001-Fix-container-state-recording.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -From 20831569f48a6f4114b574307e8d03ad62c7d5f6 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?St=C3=A9phane=20Graber?= -Date: Mon, 5 Dec 2016 11:09:44 +0100 -Subject: Fix container state recording -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Closes #2686 - -Signed-off-by: Stéphane Graber ---- - lxd/containers.go | 9 +++++---- - 1 file changed, 5 insertions(+), 4 deletions(-) - -diff --git a/lxd/containers.go b/lxd/containers.go -index 206456c..a002c8e 100644 ---- a/lxd/containers.go -+++ b/lxd/containers.go -@@ -167,10 +167,7 @@ func containersShutdown(d *Daemon) error { - } - - // Record the current state -- err = c.ConfigKeySet("volatile.last_state.power", c.State()) -- if err != nil { -- return err -- } -+ lastState := c.State() - - // Stop the container - if c.IsRunning() { -@@ -188,8 +185,12 @@ func containersShutdown(d *Daemon) error { - go func() { - c.Shutdown(time.Second * time.Duration(timeoutSeconds)) - c.Stop(false) -+ c.ConfigKeySet("volatile.last_state.power", lastState) -+ - wg.Done() - }() -+ } else { -+ c.ConfigKeySet("volatile.last_state.power", lastState) - } - } - wg.Wait() diff -Nru lxd-2.6.2/debian/patches/0001-tests-Fix-shellcheck-being-confused-by-cd.patch lxd-2.7/debian/patches/0001-tests-Fix-shellcheck-being-confused-by-cd.patch --- lxd-2.6.2/debian/patches/0001-tests-Fix-shellcheck-being-confused-by-cd.patch 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/debian/patches/0001-tests-Fix-shellcheck-being-confused-by-cd.patch 2016-12-21 08:47:38.000000000 +0000 @@ -0,0 +1,26 @@ +From 5b259e3fcb914db70c68a2381ef11bbef6637382 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?St=C3=A9phane=20Graber?= +Date: Wed, 21 Dec 2016 03:26:54 -0500 +Subject: tests: Fix shellcheck being confused by cd +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Stéphane Graber +--- + test/suites/filemanip.sh | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/test/suites/filemanip.sh b/test/suites/filemanip.sh +index 9f970e3..ab7ade6 100644 +--- a/test/suites/filemanip.sh ++++ b/test/suites/filemanip.sh +@@ -1,6 +1,8 @@ + #!/bin/sh + + test_filemanip() { ++ # Workaround for shellcheck getting confused by "cd" ++ set -e + ensure_import_testimage + ensure_has_localhost_remote "${LXD_ADDR}" + diff -Nru lxd-2.6.2/debian/patches/0002-tests-Test-lxd-shutdown.patch lxd-2.7/debian/patches/0002-tests-Test-lxd-shutdown.patch --- lxd-2.6.2/debian/patches/0002-tests-Test-lxd-shutdown.patch 2016-12-05 12:33:40.000000000 +0000 +++ lxd-2.7/debian/patches/0002-tests-Test-lxd-shutdown.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -From f6f8ae94af86ff50971e6b4c54144fba558f5089 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?St=C3=A9phane=20Graber?= -Date: Mon, 5 Dec 2016 11:25:45 +0100 -Subject: tests: Test lxd shutdown -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Stéphane Graber ---- - test/suites/basic.sh | 25 +++++++++++++++++++++++-- - 1 file changed, 23 insertions(+), 2 deletions(-) - -diff --git a/test/suites/basic.sh b/test/suites/basic.sh -index 9dcd9d3..a0f4674 100644 ---- a/test/suites/basic.sh -+++ b/test/suites/basic.sh -@@ -215,8 +215,9 @@ test_basic_usage() { - [ "$(my_curl "https://${LXD_ADDR}/1.0/containers/configtest" | jq -r .metadata.config[\"raw.lxc\"])" = "lxc.hook.clone=/bin/true" ] - lxc delete configtest - -- # Test socket activation -+ # Test activateifneeded/shutdown - LXD_ACTIVATION_DIR=$(mktemp -d -p "${TEST_DIR}" XXX) -+ chmod +x "${LXD_ACTIVATION_DIR}" - spawn_lxd "${LXD_ACTIVATION_DIR}" - ( - set -e -@@ -230,7 +231,27 @@ test_basic_usage() { - lxd activateifneeded --debug 2>&1 | grep -q -v "activating..." - lxc config set autostart boot.autostart true --force-local - lxd activateifneeded --debug 2>&1 | grep -q "Daemon has auto-started containers, activating..." -- lxc delete autostart --force-local -+ -+ lxc config unset autostart boot.autostart --force-local -+ lxd activateifneeded --debug 2>&1 | grep -q -v "activating..." -+ -+ lxc start autostart --force-local -+ PID=$(lxc info autostart --force-local | grep ^Pid | awk '{print $2}') -+ lxd shutdown -+ [ -d "/proc/${PID}" ] && false -+ -+ lxd activateifneeded --debug 2>&1 | grep -q "Daemon has auto-started containers, activating..." -+ -+ # shellcheck disable=SC2086 -+ lxd --logfile "${LXD_DIR}/lxd.log" ${DEBUG-} "$@" 2>&1 & -+ LXD_PID=$! -+ echo "${LXD_PID}" > "${LXD_DIR}/lxd.pid" -+ echo "${LXD_DIR}" >> "${TEST_DIR}/daemons" -+ lxd waitready --timeout=300 -+ -+ lxc list --force-local autostart | grep -q RUNNING -+ -+ lxc delete autostart --force --force-local - ) - # shellcheck disable=SC2031 - LXD_DIR=${LXD_DIR} diff -Nru lxd-2.6.2/debian/patches/0003-Only-mark-ready-once-containers-are-up.patch lxd-2.7/debian/patches/0003-Only-mark-ready-once-containers-are-up.patch --- lxd-2.6.2/debian/patches/0003-Only-mark-ready-once-containers-are-up.patch 2016-12-05 12:33:40.000000000 +0000 +++ lxd-2.7/debian/patches/0003-Only-mark-ready-once-containers-are-up.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -From e9073535ab9e6ec39b7e0f21e92ca09d1da4868d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?St=C3=A9phane=20Graber?= -Date: Mon, 5 Dec 2016 11:45:47 +0100 -Subject: Only mark ready once containers are up -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Stéphane Graber ---- - lxd/daemon.go | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/lxd/daemon.go b/lxd/daemon.go -index f0debc8..e68b89d 100644 ---- a/lxd/daemon.go -+++ b/lxd/daemon.go -@@ -1098,7 +1098,7 @@ func (d *Daemon) Ready() error { - }() - - /* Restore containers */ -- go containersRestart(d) -+ containersRestart(d) - - /* Re-balance in case things changed while LXD was down */ - deviceTaskBalance(d) diff -Nru lxd-2.6.2/debian/patches/series lxd-2.7/debian/patches/series --- lxd-2.6.2/debian/patches/series 2016-12-05 12:33:40.000000000 +0000 +++ lxd-2.7/debian/patches/series 2016-12-21 08:47:38.000000000 +0000 @@ -1,3 +1 @@ -0001-Fix-container-state-recording.patch -0002-tests-Test-lxd-shutdown.patch -0003-Only-mark-ready-once-containers-are-up.patch +0001-tests-Fix-shellcheck-being-confused-by-cd.patch diff -Nru lxd-2.6.2/debian/rules lxd-2.7/debian/rules --- lxd-2.6.2/debian/rules 2016-12-05 12:33:40.000000000 +0000 +++ lxd-2.7/debian/rules 2016-12-21 08:47:38.000000000 +0000 @@ -5,14 +5,14 @@ #export DH_VERBOSE=1 PKGDIR=debian/tmp -VERSION=$(shell grep "var Version" $(CURDIR)/shared/flex.go | cut -d'"' -f2) +VERSION=$(shell grep "var Version" $(CURDIR)/shared/version/flex.go | cut -d'"' -f2) DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) # temporary build path (see http://golang.org/doc/code.html#GOPATH) export DH_GOPKG := github.com/lxc/lxd export GOPATH := $(CURDIR)/obj-$(DEB_BUILD_GNU_TYPE) export USE_EMBEDDED := false -export USE_SHLIBS := false +export USE_SHLIBS := true ifeq ($(USE_SHLIBS), true) export DH_GOLANG_LINK_SHARED := 1 @@ -84,12 +84,17 @@ mv ${GOPATH}/lxd.tmp ${GOPATH}/src/github.com/lxc/lxd override_dh_install: + # Install lxc-to-lxd + cp scripts/lxc-to-lxd $(PKGDIR)/usr/bin/lxc-to-lxd + # Install the manpages mkdir -p $(PKGDIR)/usr/share/man/man1/ help2man $(PKGDIR)/usr/bin/fuidshift -n "uid/gid shifter" --no-info > $(PKGDIR)/usr/share/man/man1/fuidshift.1 help2man $(PKGDIR)/usr/bin/lxc -n "The container hypervisor - client" --no-info > $(PKGDIR)/usr/share/man/man1/lxc.1 + help2man $(PKGDIR)/usr/bin/lxc-to-lxd -n "Convert LXC containers to LXD" --no-info --version-string=$(VERSION) > $(PKGDIR)/usr/share/man/man1/lxc-to-lxd.1 help2man $(PKGDIR)/usr/bin/lxd -n "The container hypervisor - daemon" --no-info > $(PKGDIR)/usr/share/man/man1/lxd.1 + # Install bash completion scripts mkdir -p $(PKGDIR)/usr/share/bash-completion/completions/ cp config/bash/lxd-client $(PKGDIR)/usr/share/bash-completion/completions/lxc @@ -99,7 +104,7 @@ cp -RL ${GOPATH}/src/github.com/lxc/lxd $(PKGDIR)/usr/share/gocode/src/github.com/lxc/ rm -Rf $(PKGDIR)/usr/share/gocode/src/github.com/lxc/lxd/dist - # copy dnsmasq configuration + # Copy dnsmasq configuration mkdir -p $(PKGDIR)/etc/dnsmasq.d-available cp debian/lxd.dnsmasq $(PKGDIR)/etc/dnsmasq.d-available/lxd diff -Nru lxd-2.6.2/dist/src/github.com/gorilla/websocket/compression.go lxd-2.7/dist/src/github.com/gorilla/websocket/compression.go --- lxd-2.6.2/dist/src/github.com/gorilla/websocket/compression.go 2016-11-25 04:23:09.000000000 +0000 +++ lxd-2.7/dist/src/github.com/gorilla/websocket/compression.go 2016-12-21 00:52:47.000000000 +0000 @@ -1,4 +1,4 @@ -// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -9,6 +9,11 @@ "errors" "io" "strings" + "sync" +) + +var ( + flateWriterPool = sync.Pool{} ) func decompressNoContextTakeover(r io.Reader) io.Reader { @@ -17,13 +22,20 @@ "\x00\x00\xff\xff" + // Add final block to squelch unexpected EOF error from flate reader. "\x01\x00\x00\xff\xff" - return flate.NewReader(io.MultiReader(r, strings.NewReader(tail))) } func compressNoContextTakeover(w io.WriteCloser) (io.WriteCloser, error) { tw := &truncWriter{w: w} - fw, err := flate.NewWriter(tw, 3) + i := flateWriterPool.Get() + var fw *flate.Writer + var err error + if i == nil { + fw, err = flate.NewWriter(tw, 3) + } else { + fw = i.(*flate.Writer) + fw.Reset(tw) + } return &flateWrapper{fw: fw, tw: tw}, err } @@ -69,11 +81,19 @@ } func (w *flateWrapper) Write(p []byte) (int, error) { + if w.fw == nil { + return 0, errWriteClosed + } return w.fw.Write(p) } func (w *flateWrapper) Close() error { + if w.fw == nil { + return errWriteClosed + } err1 := w.fw.Flush() + flateWriterPool.Put(w.fw) + w.fw = nil if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { return errors.New("websocket: internal error, unexpected bytes at end of flate stream") } diff -Nru lxd-2.6.2/dist/src/github.com/gorilla/websocket/conn.go lxd-2.7/dist/src/github.com/gorilla/websocket/conn.go --- lxd-2.6.2/dist/src/github.com/gorilla/websocket/conn.go 2016-11-25 04:23:09.000000000 +0000 +++ lxd-2.7/dist/src/github.com/gorilla/websocket/conn.go 2016-12-21 00:52:47.000000000 +0000 @@ -218,6 +218,7 @@ return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) } +// The Conn type represents a WebSocket connection. type Conn struct { conn net.Conn isServer bool @@ -406,12 +407,7 @@ return err } -// NextWriter returns a writer for the next message to send. The writer's Close -// method flushes the complete message to the network. -// -// There can be at most one open writer on a connection. NextWriter closes the -// previous writer if the application has not already done so. -func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { +func (c *Conn) prepWrite(messageType int) error { // Close previous writer if not already closed by the application. It's // probably better to return an error in this situation, but we cannot // change this without breaking existing applications. @@ -421,13 +417,22 @@ } if !isControl(messageType) && !isData(messageType) { - return nil, errBadWriteOpCode + return errBadWriteOpCode } c.writeErrMu.Lock() err := c.writeErr c.writeErrMu.Unlock() - if err != nil { + return err +} + +// NextWriter returns a writer for the next message to send. The writer's Close +// method flushes the complete message to the network. +// +// There can be at most one open writer on a connection. NextWriter closes the +// previous writer if the application has not already done so. +func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { + if err := c.prepWrite(messageType); err != nil { return nil, err } @@ -652,16 +657,23 @@ // WriteMessage is a helper method for getting a writer using NextWriter, // writing the message and closing the writer. func (c *Conn) WriteMessage(messageType int, data []byte) error { - w, err := c.NextWriter(messageType) - if err != nil { - return err - } - if mw, ok := w.(*messageWriter); ok && c.isServer { - // Optimize write as a single frame. + + if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { + + // Fast path with no allocations and single frame. + + if err := c.prepWrite(messageType); err != nil { + return err + } + mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} n := copy(c.writeBuf[mw.pos:], data) mw.pos += n data = data[n:] - err = mw.flushFrame(true, data) + return mw.flushFrame(true, data) + } + + w, err := c.NextWriter(messageType) + if err != nil { return err } if _, err = w.Write(data); err != nil { diff -Nru lxd-2.6.2/dist/src/github.com/mattn/go-sqlite3/sqlite3.go lxd-2.7/dist/src/github.com/mattn/go-sqlite3/sqlite3.go --- lxd-2.6.2/dist/src/github.com/mattn/go-sqlite3/sqlite3.go 2016-11-25 04:23:33.000000000 +0000 +++ lxd-2.7/dist/src/github.com/mattn/go-sqlite3/sqlite3.go 2016-12-21 00:52:59.000000000 +0000 @@ -693,7 +693,7 @@ for i, v := range args { if v.Name != "" { - cname := C.CString(v.Name) + cname := C.CString(":" + v.Name) args[i].Ordinal = int(C.sqlite3_bind_parameter_index(s.s, cname)) C.free(unsafe.Pointer(cname)) } diff -Nru lxd-2.6.2/dist/src/github.com/mattn/go-sqlite3/sqlite3_go18.go lxd-2.7/dist/src/github.com/mattn/go-sqlite3/sqlite3_go18.go --- lxd-2.6.2/dist/src/github.com/mattn/go-sqlite3/sqlite3_go18.go 2016-11-25 04:23:33.000000000 +0000 +++ lxd-2.7/dist/src/github.com/mattn/go-sqlite3/sqlite3_go18.go 2016-12-21 00:52:59.000000000 +0000 @@ -45,8 +45,8 @@ return c.prepare(ctx, query) } -// BeginContext implement ConnBeginContext. -func (c *SQLiteConn) BeginContext(ctx context.Context) (driver.Tx, error) { +// BeginTx implement ConnBeginTx. +func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { return c.begin(ctx) } diff -Nru lxd-2.6.2/dist/src/github.com/mattn/go-sqlite3/sqlite3_go18_test.go lxd-2.7/dist/src/github.com/mattn/go-sqlite3/sqlite3_go18_test.go --- lxd-2.6.2/dist/src/github.com/mattn/go-sqlite3/sqlite3_go18_test.go 2016-11-25 04:23:33.000000000 +0000 +++ lxd-2.7/dist/src/github.com/mattn/go-sqlite3/sqlite3_go18_test.go 2016-12-21 00:52:59.000000000 +0000 @@ -29,12 +29,12 @@ t.Error("Failed to call db.Query:", err) } - _, err = db.Exec(`insert into foo(id, name, extra) values(:id, :name, :name)`, sql.Param(":name", "foo"), sql.Param(":id", 1)) + _, err = db.Exec(`insert into foo(id, name, extra) values(:id, :name, :name)`, sql.Named("name", "foo"), sql.Named("id", 1)) if err != nil { t.Error("Failed to call db.Exec:", err) } - row := db.QueryRow(`select id, extra from foo where id = :id and extra = :extra`, sql.Param(":id", 1), sql.Param(":extra", "foo")) + row := db.QueryRow(`select id, extra from foo where id = :id and extra = :extra`, sql.Named("id", 1), sql.Named("extra", "foo")) if row == nil { t.Error("Failed to call db.QueryRow") } diff -Nru lxd-2.6.2/dist/src/github.com/mattn/go-sqlite3/tool/upgrade.go lxd-2.7/dist/src/github.com/mattn/go-sqlite3/tool/upgrade.go --- lxd-2.6.2/dist/src/github.com/mattn/go-sqlite3/tool/upgrade.go 2016-11-25 04:23:33.000000000 +0000 +++ lxd-2.7/dist/src/github.com/mattn/go-sqlite3/tool/upgrade.go 2016-12-21 00:52:59.000000000 +0000 @@ -1,3 +1,5 @@ +// +build ignore + package main import ( diff -Nru lxd-2.6.2/dist/src/github.com/pborman/uuid/README.md lxd-2.7/dist/src/github.com/pborman/uuid/README.md --- lxd-2.6.2/dist/src/github.com/pborman/uuid/README.md 2016-11-25 04:23:36.000000000 +0000 +++ lxd-2.7/dist/src/github.com/pborman/uuid/README.md 2016-12-21 00:53:02.000000000 +0000 @@ -1,7 +1,7 @@ This project was automatically exported from code.google.com/p/go-uuid # uuid ![build status](https://travis-ci.org/pborman/uuid.svg?branch=master) -The uuid package generates and inspects UUIDs based on [RFC 412](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services. +The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services. ###### Install `go get github.com/pborman/uuid` diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/assert/assertion_forward.go lxd-2.7/dist/src/github.com/stretchr/testify/assert/assertion_forward.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/assert/assertion_forward.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/assert/assertion_forward.go 2016-12-21 00:53:06.000000000 +0000 @@ -43,6 +43,9 @@ // a.Equal(123, 123, "123 and 123 should be equal") // // Returns whether the assertion was successful (true) or not (false). +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { return Equal(a.t, expected, actual, msgAndArgs...) } @@ -262,6 +265,9 @@ // a.NotEqual(obj1, obj2, "two objects shouldn't be equal") // // Returns whether the assertion was successful (true) or not (false). +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { return NotEqual(a.t, expected, actual, msgAndArgs...) } diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/assert/assertions.go lxd-2.7/dist/src/github.com/stretchr/testify/assert/assertions.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/assert/assertions.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/assert/assertions.go 2016-12-21 00:53:06.000000000 +0000 @@ -153,7 +153,7 @@ parts := strings.Split(file, "/") file = parts[len(parts)-1] - return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line))) + return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line))) } @@ -170,22 +170,18 @@ return "" } -// Indents all lines of the message by appending a number of tabs to each line, in an output format compatible with Go's -// test printing (see inner comment for specifics) -func indentMessageLines(message string, tabs int) string { +// Aligns the provided message so that all lines after the first line start at the same location as the first line. +// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab). +// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the +// basis on which the alignment occurs). +func indentMessageLines(message string, longestLabelLen int) string { outBuf := new(bytes.Buffer) for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { + // no need to align first line because it starts at the correct location (after the label) if i != 0 { - outBuf.WriteRune('\n') - } - for ii := 0; ii < tabs; ii++ { - outBuf.WriteRune('\t') - // Bizarrely, all lines except the first need one fewer tabs prepended, so deliberately advance the counter - // by 1 prematurely. - if ii == 0 && i > 0 { - ii++ - } + // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab + outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen +1) + "\t") } outBuf.WriteString(scanner.Text()) } @@ -217,29 +213,49 @@ // Fail reports a failure through func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + content := []labeledContent{ + {"Error Trace", strings.Join(CallerInfo(), "\n\r\t\t\t")}, + {"Error", failureMessage}, + } message := messageFromMsgAndArgs(msgAndArgs...) - - errorTrace := strings.Join(CallerInfo(), "\n\r\t\t\t") if len(message) > 0 { - t.Errorf("\r%s\r\tError Trace:\t%s\n"+ - "\r\tError:%s\n"+ - "\r\tMessages:\t%s\n\r", - getWhitespaceString(), - errorTrace, - indentMessageLines(failureMessage, 2), - message) - } else { - t.Errorf("\r%s\r\tError Trace:\t%s\n"+ - "\r\tError:%s\n\r", - getWhitespaceString(), - errorTrace, - indentMessageLines(failureMessage, 2)) + content = append(content, labeledContent{"Messages", message}) } + t.Errorf("\r" + getWhitespaceString() + labeledOutput(content...)) + return false } +type labeledContent struct { + label string + content string +} + +// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: +// +// \r\t{{label}}:{{align_spaces}}\t{{content}}\n +// +// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. +// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this +// alignment is achieved, "\t{{content}}\n" is added for the output. +// +// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line. +func labeledOutput(content ...labeledContent) string { + longestLabel := 0 + for _, v := range content { + if len(v.label) > longestLabel { + longestLabel = len(v.label) + } + } + var output string + for _, v := range content { + output += "\r\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" + } + return output +} + // Implements asserts that an object is implemented by the specified interface. // // assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject") @@ -270,6 +286,9 @@ // assert.Equal(t, 123, 123, "123 and 123 should be equal") // // Returns whether the assertion was successful (true) or not (false). +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if !ObjectsAreEqual(expected, actual) { @@ -291,31 +310,15 @@ // with the type name, and the value will be enclosed in parenthesis similar // to a type conversion in the Go grammar. func formatUnequalValues(expected, actual interface{}) (e string, a string) { - aType := reflect.TypeOf(expected) - bType := reflect.TypeOf(actual) - - if aType != bType && isNumericType(aType) && isNumericType(bType) { - return fmt.Sprintf("%v(%#v)", aType, expected), - fmt.Sprintf("%v(%#v)", bType, actual) + if reflect.TypeOf(expected) != reflect.TypeOf(actual) { + return fmt.Sprintf("%T(%#v)", expected, expected), + fmt.Sprintf("%T(%#v)", actual, actual) } return fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual) } -func isNumericType(t reflect.Type) bool { - switch t.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return true - case reflect.Float32, reflect.Float64: - return true - } - - return false -} - // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // @@ -556,6 +559,9 @@ // assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") // // Returns whether the assertion was successful (true) or not (false). +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if ObjectsAreEqual(expected, actual) { diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/assert/assertions_test.go lxd-2.7/dist/src/github.com/stretchr/testify/assert/assertions_test.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/assert/assertions_test.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/assert/assertions_test.go 2016-12-21 00:53:06.000000000 +0000 @@ -1,12 +1,16 @@ package assert import ( + "bytes" "errors" + "fmt" "io" "math" "os" "reflect" "regexp" + "runtime" + "strings" "testing" "time" ) @@ -192,7 +196,67 @@ if !Equal(mockT, uint64(123), uint64(123)) { t.Error("Equal should return true") } + if !Equal(mockT, &struct{}{}, &struct{}{}) { + t.Error("Equal should return true (pointer equality is based on equality of underlying value)") + } +} +// bufferT implements TestingT. Its implementation of Errorf writes the output that would be produced by +// testing.T.Errorf to an internal bytes.Buffer. +type bufferT struct { + buf bytes.Buffer +} + +func (t *bufferT) Errorf(format string, args ...interface{}) { + // implementation of decorate is copied from testing.T + decorate := func(s string) string { + _, file, line, ok := runtime.Caller(3) // decorate + log + public function. + if ok { + // Truncate file name at last file name separator. + if index := strings.LastIndex(file, "/"); index >= 0 { + file = file[index+1:] + } else if index = strings.LastIndex(file, "\\"); index >= 0 { + file = file[index+1:] + } + } else { + file = "???" + line = 1 + } + buf := new(bytes.Buffer) + // Every line is indented at least one tab. + buf.WriteByte('\t') + fmt.Fprintf(buf, "%s:%d: ", file, line) + lines := strings.Split(s, "\n") + if l := len(lines); l > 1 && lines[l-1] == "" { + lines = lines[:l-1] + } + for i, line := range lines { + if i > 0 { + // Second and subsequent lines are indented an extra tab. + buf.WriteString("\n\t\t") + } + buf.WriteString(line) + } + buf.WriteByte('\n') + return buf.String() + } + t.buf.WriteString(decorate(fmt.Sprintf(format, args...))) +} + +func TestEqualFormatting(t *testing.T) { + for i, currCase := range []struct { + equalWant string + equalGot string + msgAndArgs []interface{} + want string + }{ + {equalWant:"want", equalGot: "got", want: "\tassertions.go:[0-9]+: \r \r\tError Trace:\t\n\t\t\r\tError: \tNot equal: \n\t\t\r\t \texpected: \"want\"\n\t\t\r\t \treceived: \"got\"\n"}, + {equalWant:"want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \r \r\tError Trace:\t\n\t\t\r\tError: \tNot equal: \n\t\t\r\t \texpected: \"want\"\n\t\t\r\t \treceived: \"got\"\n\t\t\r\tMessages: \thello, world!\n"}, + } { + mockT := &bufferT{} + Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) + Regexp(t, regexp.MustCompile(currCase.want), mockT.buf.String(), "Case %d", i) + } } func TestFormatUnequalValues(t *testing.T) { @@ -208,6 +272,10 @@ Equal(t, `int64(123)`, expected, "value should include type") Equal(t, `int32(123)`, actual, "value should include type") + expected, actual = formatUnequalValues(int64(123), nil) + Equal(t, `int64(123)`, expected, "value should include type") + Equal(t, `()`, actual, "value should include type") + type testStructType struct { Val string } @@ -343,6 +411,9 @@ if NotEqual(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { t.Error("NotEqual should return false") } + if NotEqual(mockT, &struct{}{}, &struct{}{}) { + t.Error("NotEqual should return false") + } } type A struct { diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/_codegen/main.go lxd-2.7/dist/src/github.com/stretchr/testify/_codegen/main.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/_codegen/main.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/_codegen/main.go 2016-12-21 00:53:06.000000000 +0000 @@ -1,5 +1,5 @@ // This program reads all assertion functions from the assert package and -// automatically generates the corersponding requires and forwarded assertions +// automatically generates the corresponding requires and forwarded assertions package main @@ -139,7 +139,7 @@ if !ok { continue } - // Check function signatuer has at least two arguments + // Check function signature has at least two arguments sig := fn.Type().(*types.Signature) if sig.Params().Len() < 2 { continue @@ -163,7 +163,7 @@ return importer, funcs, nil } -// parsePackageSource returns the types scope and the package documentation from the pa +// parsePackageSource returns the types scope and the package documentation from the package func parsePackageSource(pkg string) (*types.Scope, *doc.Package, error) { pd, err := build.Import(pkg, ".", 0) if err != nil { diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/mock/mock.go lxd-2.7/dist/src/github.com/stretchr/testify/mock/mock.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/mock/mock.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/mock/mock.go 2016-12-21 00:53:06.000000000 +0000 @@ -279,7 +279,7 @@ functionPath := runtime.FuncForPC(pc).Name() //Next four lines are required to use GCCGO function naming conventions. //For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock - //uses inteface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree + //uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree //With GCCGO we need to remove interface information starting from pN
. re := regexp.MustCompile("\\.pN\\d+_") if re.MatchString(functionPath) { diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/mock/mock_test.go lxd-2.7/dist/src/github.com/stretchr/testify/mock/mock_test.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/mock/mock_test.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/mock/mock_test.go 2016-12-21 00:53:06.000000000 +0000 @@ -1130,3 +1130,29 @@ assert.Equal(t, true, args.Bool(2)) } + +func Test_WaitUntil_Parallel(t *testing.T) { + + // make a test impl object + var mockedService *TestExampleImplementation = new(TestExampleImplementation) + + ch1 := make(chan time.Time) + ch2 := make(chan time.Time) + + mockedService.Mock.On("TheExampleMethod2", true).Return().WaitUntil(ch2).Run(func(args Arguments) { + ch1 <- time.Now() + }) + + mockedService.Mock.On("TheExampleMethod2", false).Return().WaitUntil(ch1) + + // Lock both goroutines on the .WaitUntil method + go func() { + mockedService.TheExampleMethod2(false) + }() + go func() { + mockedService.TheExampleMethod2(true) + }() + + // Allow the first call to execute, so the second one executes afterwards + ch2 <- time.Now() +} diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/require/require_forward.go lxd-2.7/dist/src/github.com/stretchr/testify/require/require_forward.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/require/require_forward.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/require/require_forward.go 2016-12-21 00:53:06.000000000 +0000 @@ -44,6 +44,9 @@ // a.Equal(123, 123, "123 and 123 should be equal") // // Returns whether the assertion was successful (true) or not (false). +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { Equal(a.t, expected, actual, msgAndArgs...) } @@ -263,6 +266,9 @@ // a.NotEqual(obj1, obj2, "two objects shouldn't be equal") // // Returns whether the assertion was successful (true) or not (false). +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { NotEqual(a.t, expected, actual, msgAndArgs...) } diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/require/require.go lxd-2.7/dist/src/github.com/stretchr/testify/require/require.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/require/require.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/require/require.go 2016-12-21 00:53:06.000000000 +0000 @@ -50,6 +50,9 @@ // assert.Equal(t, 123, 123, "123 and 123 should be equal") // // Returns whether the assertion was successful (true) or not (false). +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if !assert.Equal(t, expected, actual, msgAndArgs...) { t.FailNow() @@ -319,6 +322,9 @@ // assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") // // Returns whether the assertion was successful (true) or not (false). +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if !assert.NotEqual(t, expected, actual, msgAndArgs...) { t.FailNow() diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/suite/interfaces.go lxd-2.7/dist/src/github.com/stretchr/testify/suite/interfaces.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/suite/interfaces.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/suite/interfaces.go 2016-12-21 00:53:06.000000000 +0000 @@ -32,3 +32,15 @@ type TearDownTestSuite interface { TearDownTest() } + +// BeforeTest has a function to be executed right before the test +// starts and receives the suite and test names as input +type BeforeTest interface { + BeforeTest(suiteName, testName string) +} + +// AfterTest has a function to be executed right after the test +// finishes and receives the suite and test names as input +type AfterTest interface { + AfterTest(suiteName, testName string) +} diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/suite/suite.go lxd-2.7/dist/src/github.com/stretchr/testify/suite/suite.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/suite/suite.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/suite/suite.go 2016-12-21 00:53:06.000000000 +0000 @@ -86,7 +86,13 @@ if setupTestSuite, ok := suite.(SetupTestSuite); ok { setupTestSuite.SetupTest() } + if beforeTestSuite, ok := suite.(BeforeTest); ok { + beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) + } defer func() { + if afterTestSuite, ok := suite.(AfterTest); ok { + afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name) + } if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { tearDownTestSuite.TearDownTest() } diff -Nru lxd-2.6.2/dist/src/github.com/stretchr/testify/suite/suite_test.go lxd-2.7/dist/src/github.com/stretchr/testify/suite/suite_test.go --- lxd-2.6.2/dist/src/github.com/stretchr/testify/suite/suite_test.go 2016-11-25 04:23:41.000000000 +0000 +++ lxd-2.7/dist/src/github.com/stretchr/testify/suite/suite_test.go 2016-12-21 00:53:06.000000000 +0000 @@ -5,6 +5,7 @@ "io/ioutil" "os" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -58,6 +59,15 @@ TestOneRunCount int TestTwoRunCount int NonTestMethodRunCount int + + SuiteNameBefore []string + TestNameBefore []string + + SuiteNameAfter []string + TestNameAfter []string + + TimeBefore []time.Time + TimeAfter []time.Time } type SuiteSkipTester struct { @@ -75,6 +85,18 @@ suite.SetupSuiteRunCount++ } +func (suite *SuiteTester) BeforeTest(suiteName, testName string) { + suite.SuiteNameBefore = append(suite.SuiteNameBefore, suiteName) + suite.TestNameBefore = append(suite.TestNameBefore, testName) + suite.TimeBefore = append(suite.TimeBefore, time.Now()) +} + +func (suite *SuiteTester) AfterTest(suiteName, testName string) { + suite.SuiteNameAfter = append(suite.SuiteNameAfter, suiteName) + suite.TestNameAfter = append(suite.TestNameAfter, testName) + suite.TimeAfter = append(suite.TimeAfter, time.Now()) +} + func (suite *SuiteSkipTester) SetupSuite() { suite.SetupSuiteRunCount++ suite.T().Skip() @@ -145,6 +167,35 @@ assert.Equal(t, suiteTester.SetupSuiteRunCount, 1) assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1) + assert.Equal(t, len(suiteTester.SuiteNameAfter), 3) + assert.Equal(t, len(suiteTester.SuiteNameBefore), 3) + assert.Equal(t, len(suiteTester.TestNameAfter), 3) + assert.Equal(t, len(suiteTester.TestNameBefore), 3) + + assert.Contains(t, suiteTester.TestNameAfter, "TestOne") + assert.Contains(t, suiteTester.TestNameAfter, "TestTwo") + assert.Contains(t, suiteTester.TestNameAfter, "TestSkip") + + assert.Contains(t, suiteTester.TestNameBefore, "TestOne") + assert.Contains(t, suiteTester.TestNameBefore, "TestTwo") + assert.Contains(t, suiteTester.TestNameBefore, "TestSkip") + + for _, suiteName := range suiteTester.SuiteNameAfter { + assert.Equal(t, "SuiteTester", suiteName) + } + + for _, suiteName := range suiteTester.SuiteNameBefore { + assert.Equal(t, "SuiteTester", suiteName) + } + + for _, when := range suiteTester.TimeAfter { + assert.False(t, when.IsZero()) + } + + for _, when := range suiteTester.TimeBefore { + assert.False(t, when.IsZero()) + } + // There are three test methods (TestOne, TestTwo, and TestSkip), so // the SetupTest and TearDownTest methods (which should be run once for // each test) should have been run three times. diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/acme/autocert/autocert.go lxd-2.7/dist/src/golang.org/x/crypto/acme/autocert/autocert.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/acme/autocert/autocert.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/acme/autocert/autocert.go 2016-12-21 00:52:54.000000000 +0000 @@ -141,6 +141,12 @@ // If the Client's account key is already registered, Email is not used. Email string + // ForceRSA makes the Manager generate certificates with 2048-bit RSA keys. + // + // If false, a default is used. Currently the default + // is EC-based keys using the P-256 curve. + ForceRSA bool + clientMu sync.Mutex client *acme.Client // initialized by acmeClient method @@ -187,6 +193,7 @@ } // regular domain + name = strings.TrimSuffix(name, ".") // golang.org/issue/18114 cert, err := m.cert(name) if err == nil { return cert, nil @@ -384,11 +391,21 @@ if state, ok := m.state[domain]; ok { return state, nil } + // new locked state - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + var ( + err error + key crypto.Signer + ) + if m.ForceRSA { + key, err = rsa.GenerateKey(rand.Reader, 2048) + } else { + key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + } if err != nil { return nil, err } + state := &certState{ key: key, locked: true, diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/acme/autocert/autocert_test.go lxd-2.7/dist/src/golang.org/x/crypto/acme/autocert/autocert_test.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/acme/autocert/autocert_test.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/acme/autocert/autocert_test.go 2016-12-21 00:52:54.000000000 +0000 @@ -108,10 +108,41 @@ } func TestGetCertificate(t *testing.T) { - const domain = "example.org" man := &Manager{Prompt: AcceptTOS} defer man.stopRenew() + hello := &tls.ClientHelloInfo{ServerName: "example.org"} + testGetCertificate(t, man, "example.org", hello) +} + +func TestGetCertificate_trailingDot(t *testing.T) { + man := &Manager{Prompt: AcceptTOS} + defer man.stopRenew() + hello := &tls.ClientHelloInfo{ServerName: "example.org."} + testGetCertificate(t, man, "example.org", hello) +} + +func TestGetCertificate_ForceRSA(t *testing.T) { + man := &Manager{ + Prompt: AcceptTOS, + Cache: make(memCache), + ForceRSA: true, + } + defer man.stopRenew() + hello := &tls.ClientHelloInfo{ServerName: "example.org"} + testGetCertificate(t, man, "example.org", hello) + cert, err := man.cacheGet("example.org") + if err != nil { + t.Fatalf("man.cacheGet: %v", err) + } + if _, ok := cert.PrivateKey.(*rsa.PrivateKey); !ok { + t.Errorf("cert.PrivateKey is %T; want *rsa.PrivateKey", cert.PrivateKey) + } +} + +// tests man.GetCertificate flow using the provided hello argument. +// The domain argument is the expected domain name of a certificate request. +func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) { // echo token-02 | shasum -a 256 // then divide result in 2 parts separated by dot tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid" @@ -167,6 +198,9 @@ if err != nil { t.Fatalf("new-cert: CSR: %v", err) } + if csr.Subject.CommonName != domain { + t.Errorf("CommonName in CSR = %q; want %q", csr.Subject.CommonName, domain) + } der, err := dummyCert(csr.PublicKey, domain) if err != nil { t.Fatalf("new-cert: dummyCert: %v", err) @@ -202,7 +236,6 @@ var tlscert *tls.Certificate done := make(chan struct{}) go func() { - hello := &tls.ClientHelloInfo{ServerName: domain} tlscert, err = man.GetCertificate(hello) close(done) }() diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2b_amd64.go lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2b_amd64.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2b_amd64.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2b_amd64.go 2016-12-21 00:52:54.000000000 +0000 @@ -7,6 +7,7 @@ package blake2b var useAVX2 = false +var useAVX = false var useSSE4 = supportSSE4() //go:noescape diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2b_amd64.s lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2b_amd64.s --- lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2b_amd64.s 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2b_amd64.s 2016-12-21 00:52:54.000000000 +0000 @@ -20,7 +20,7 @@ DATA ·iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b DATA ·iv3<>+0x08(SB)/8, $0x5be0cd19137e2179 -GLOBL ·iv3<>(SB), (NOPTR+RODATA), $32 +GLOBL ·iv3<>(SB), (NOPTR+RODATA), $16 DATA ·c40<>+0x00(SB)/8, $0x0201000706050403 DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b @@ -30,16 +30,16 @@ DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a GLOBL ·c48<>(SB), (NOPTR+RODATA), $16 -#define SHUFFLE(v2, v3, v4, v5, v6, v7, t0, t1, t2) \ - MOVO v4, t0; \ +#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \ + MOVO v4, t1; \ MOVO v5, v4; \ - MOVO t0, v5; \ - MOVO v6, t0; \ + MOVO t1, v5; \ + MOVO v6, t1; \ PUNPCKLQDQ v6, t2; \ PUNPCKHQDQ v7, v6; \ PUNPCKHQDQ t2, v6; \ PUNPCKLQDQ v7, t2; \ - MOVO t0, v7; \ + MOVO t1, v7; \ MOVO v2, t1; \ PUNPCKHQDQ t2, v7; \ PUNPCKLQDQ v3, t2; \ @@ -47,16 +47,16 @@ PUNPCKLQDQ t1, t2; \ PUNPCKHQDQ t2, v3 -#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t0, t1, t2) \ - MOVO v4, t0; \ +#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \ + MOVO v4, t1; \ MOVO v5, v4; \ - MOVO t0, v5; \ - MOVO v2, t0; \ + MOVO t1, v5; \ + MOVO v2, t1; \ PUNPCKLQDQ v2, t2; \ PUNPCKHQDQ v3, v2; \ PUNPCKHQDQ t2, v2; \ PUNPCKLQDQ v3, t2; \ - MOVO t0, v3; \ + MOVO t1, v3; \ MOVO v6, t1; \ PUNPCKHQDQ t2, v3; \ PUNPCKLQDQ v7, t2; \ @@ -64,7 +64,7 @@ PUNPCKLQDQ t1, t2; \ PUNPCKHQDQ t2, v7 -#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, t1, t2, c40, c48) \ +#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \ PADDQ m0, v0; \ PADDQ m1, v1; \ PADDQ v2, v0; \ @@ -91,14 +91,14 @@ PADDQ v7, v5; \ PXOR v4, v2; \ PXOR v5, v3; \ - MOVOU v2, t2; \ - PADDQ v2, t2; \ + MOVOU v2, t0; \ + PADDQ v2, t0; \ PSRLQ $63, v2; \ - PXOR t2, v2; \ - MOVOU v3, t2; \ - PADDQ v3, t2; \ + PXOR t0, v2; \ + MOVOU v3, t0; \ + PADDQ v3, t0; \ PSRLQ $63, v3; \ - PXOR t2, v3 + PXOR t0, v3 #define LOAD_MSG(m0, m1, m2, m3, src, i0, i1, i2, i3, i4, i5, i6, i7) \ MOVQ i0*8(src), m0; \ @@ -111,7 +111,7 @@ PINSRQ $1, i7*8(src), m3 // func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) -TEXT ·hashBlocksSSE4(SB), 4, $32-48 // frame size = 16 + 16 byte alignment +TEXT ·hashBlocksSSE4(SB), 4, $288-48 // frame size = 272 + 16 byte alignment MOVQ h+0(FP), AX MOVQ c+8(FP), BX MOVQ flag+16(FP), CX @@ -131,6 +131,9 @@ MOVOU ·c40<>(SB), X13 MOVOU ·c48<>(SB), X14 + MOVOU 0(AX), X12 + MOVOU 16(AX), X15 + MOVQ 0(BX), R8 MOVQ 8(BX), R9 @@ -141,118 +144,126 @@ INCQ R9 noinc: - MOVQ R8, X15 - PINSRQ $1, R9, X15 + MOVQ R8, X8 + PINSRQ $1, R9, X8 - MOVOU 0(AX), X0 - MOVOU 16(AX), X1 + MOVO X12, X0 + MOVO X15, X1 MOVOU 32(AX), X2 MOVOU 48(AX), X3 MOVOU ·iv0<>(SB), X4 MOVOU ·iv1<>(SB), X5 MOVOU ·iv2<>(SB), X6 - PXOR X15, X6 + PXOR X8, X6 MOVO 0(SP), X7 LOAD_MSG(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + MOVO X8, 16(SP) + MOVO X9, 32(SP) + MOVO X10, 48(SP) + MOVO X11, 64(SP) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 8, 10, 12, 14, 9, 11, 13, 15) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + MOVO X8, 80(SP) + MOVO X9, 96(SP) + MOVO X10, 112(SP) + MOVO X11, 128(SP) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + MOVO X8, 144(SP) + MOVO X9, 160(SP) + MOVO X10, 176(SP) + MOVO X11, 192(SP) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 1, 0, 11, 5, 12, 2, 7, 3) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + MOVO X8, 208(SP) + MOVO X9, 224(SP) + MOVO X10, 240(SP) + MOVO X11, 256(SP) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 10, 3, 7, 9, 14, 6, 1, 4) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 2, 5, 4, 15, 6, 10, 0, 8) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 14, 11, 6, 3, 1, 12, 8, 13) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 4, 7, 15, 1, 13, 5, 14, 9) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 0, 6, 9, 8, 7, 3, 2, 11) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 5, 15, 8, 2, 0, 4, 6, 10) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 12, 13, 1, 10, 2, 7, 4, 5) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) LOAD_MSG(X8, X9, X10, X11, SI, 15, 9, 3, 13, 11, 14, 12, 0) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) - LOAD_MSG(X8, X9, X10, X11, SI, 8, 10, 12, 14, 9, 11, 13, 15) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 80(SP), 96(SP), 112(SP), 128(SP), X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 144(SP), 160(SP), 176(SP), 192(SP), X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 208(SP), 224(SP), 240(SP), 256(SP), X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9, X10) - LOAD_MSG(X8, X9, X10, X11, SI, 1, 0, 11, 5, 12, 2, 7, 3) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X8, X9, X12, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9, X10) - - MOVOU 0(AX), X8 - MOVOU 16(AX), X9 MOVOU 32(AX), X10 MOVOU 48(AX), X11 - PXOR X0, X8 - PXOR X1, X9 + PXOR X0, X12 + PXOR X1, X15 PXOR X2, X10 PXOR X3, X11 - PXOR X4, X8 - PXOR X5, X9 + PXOR X4, X12 + PXOR X5, X15 PXOR X6, X10 PXOR X7, X11 - MOVOU X8, 0(AX) - MOVOU X9, 16(AX) MOVOU X10, 32(AX) MOVOU X11, 48(AX) @@ -260,7 +271,11 @@ SUBQ $128, DI JNE loop - MOVOU X15, 0(BX) + MOVOU X12, 0(AX) + MOVOU X15, 16(AX) + + MOVQ R8, 0(BX) + MOVQ R9, 8(BX) MOVQ BP, SP RET @@ -269,7 +284,7 @@ TEXT ·supportSSE4(SB), 4, $0-1 MOVL $1, AX CPUID - SHRL $15, CX // Bit 15 indicates SSE4 support + SHRL $19, CX // Bit 19 indicates SSE4 support ANDL $1, CX // CX != 0 if support SSE4 MOVB CX, ret+0(FP) RET diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go 2016-12-21 00:52:54.000000000 +0000 @@ -7,23 +7,32 @@ package blake2b var useAVX2 = supportAVX2() +var useAVX = supportAVX() var useSSE4 = supportSSE4() //go:noescape func supportSSE4() bool //go:noescape +func supportAVX() bool + +//go:noescape func supportAVX2() bool //go:noescape func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) //go:noescape +func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) + +//go:noescape func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) { if useAVX2 { hashBlocksAVX2(h, c, flag, blocks) + } else if useAVX { + hashBlocksAVX(h, c, flag, blocks) } else if useSSE4 { hashBlocksSSE4(h, c, flag, blocks) } else { diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s --- lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s 2016-12-21 00:52:54.000000000 +0000 @@ -6,32 +6,56 @@ #include "textflag.h" +DATA ·AVX2_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908 +DATA ·AVX2_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b +DATA ·AVX2_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b +DATA ·AVX2_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1 +GLOBL ·AVX2_iv0<>(SB), (NOPTR+RODATA), $32 + +DATA ·AVX2_iv1<>+0x00(SB)/8, $0x510e527fade682d1 +DATA ·AVX2_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f +DATA ·AVX2_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b +DATA ·AVX2_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179 +GLOBL ·AVX2_iv1<>(SB), (NOPTR+RODATA), $32 + +DATA ·AVX2_c40<>+0x00(SB)/8, $0x0201000706050403 +DATA ·AVX2_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b +DATA ·AVX2_c40<>+0x10(SB)/8, $0x0201000706050403 +DATA ·AVX2_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b +GLOBL ·AVX2_c40<>(SB), (NOPTR+RODATA), $32 + +DATA ·AVX2_c48<>+0x00(SB)/8, $0x0100070605040302 +DATA ·AVX2_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a +DATA ·AVX2_c48<>+0x10(SB)/8, $0x0100070605040302 +DATA ·AVX2_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a +GLOBL ·AVX2_c48<>(SB), (NOPTR+RODATA), $32 + DATA ·AVX_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908 DATA ·AVX_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b -DATA ·AVX_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b -DATA ·AVX_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1 -GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $32 - -DATA ·AVX_iv1<>+0x00(SB)/8, $0x510e527fade682d1 -DATA ·AVX_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f -DATA ·AVX_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b -DATA ·AVX_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179 -GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $32 +GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $16 + +DATA ·AVX_iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b +DATA ·AVX_iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1 +GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $16 + +DATA ·AVX_iv2<>+0x00(SB)/8, $0x510e527fade682d1 +DATA ·AVX_iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f +GLOBL ·AVX_iv2<>(SB), (NOPTR+RODATA), $16 + +DATA ·AVX_iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b +DATA ·AVX_iv3<>+0x08(SB)/8, $0x5be0cd19137e2179 +GLOBL ·AVX_iv3<>(SB), (NOPTR+RODATA), $16 DATA ·AVX_c40<>+0x00(SB)/8, $0x0201000706050403 DATA ·AVX_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b -DATA ·AVX_c40<>+0x10(SB)/8, $0x0201000706050403 -DATA ·AVX_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b -GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $32 +GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $16 DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302 DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a -DATA ·AVX_c48<>+0x10(SB)/8, $0x0100070605040302 -DATA ·AVX_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a -GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $32 +GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16 // unfortunately the BYTE representation of VPERMQ must be used -#define ROUND(m0, m1, m2, m3, t, c40, c48) \ +#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \ VPADDQ m0, Y0, Y0; \ VPADDQ Y1, Y0, Y0; \ VPXOR Y0, Y3, Y3; \ @@ -72,7 +96,7 @@ BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93 \ // VPERMQ 0x93, Y1, Y1 // load msg into Y12, Y13, Y14, Y15 -#define LOAD_MSG(src, i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15) \ +#define LOAD_MSG_AVX2(src, i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15) \ MOVQ i0*8(src), X12; \ PINSRQ $1, i1*8(src), X12; \ MOVQ i2*8(src), X11; \ @@ -112,13 +136,13 @@ XORQ CX, CX MOVQ CX, 24(SP) - VMOVDQU ·AVX_c40<>(SB), Y4 - VMOVDQU ·AVX_c48<>(SB), Y5 + VMOVDQU ·AVX2_c40<>(SB), Y4 + VMOVDQU ·AVX2_c48<>(SB), Y5 VMOVDQU 0(AX), Y8 VMOVDQU 32(AX), Y9 - VMOVDQU ·AVX_iv0<>(SB), Y6 - VMOVDQU ·AVX_iv1<>(SB), Y7 + VMOVDQU ·AVX2_iv0<>(SB), Y6 + VMOVDQU ·AVX2_iv1<>(SB), Y7 MOVQ 0(BX), R8 MOVQ 8(BX), R9 @@ -135,41 +159,41 @@ noinc: VMOVDQA Y8, Y0 VMOVDQA Y9, Y1 - VMOVDQU Y6, Y2 + VMOVDQA Y6, Y2 VPXOR 0(SP), Y7, Y3 - LOAD_MSG(SI, 0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15) + LOAD_MSG_AVX2(SI, 0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15) VMOVDQA Y12, 32(SP) VMOVDQA Y13, 64(SP) VMOVDQA Y14, 96(SP) VMOVDQA Y15, 128(SP) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3) VMOVDQA Y12, 160(SP) VMOVDQA Y13, 192(SP) VMOVDQA Y14, 224(SP) VMOVDQA Y15, 256(SP) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG(SI, 10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0) - ROUND(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + LOAD_MSG_AVX2(SI, 10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0) + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - ROUND(32(SP), 64(SP), 96(SP), 128(SP), Y10, Y4, Y5) - ROUND(160(SP), 192(SP), 224(SP), 256(SP), Y10, Y4, Y5) + ROUND_AVX2(32(SP), 64(SP), 96(SP), 128(SP), Y10, Y4, Y5) + ROUND_AVX2(160(SP), 192(SP), 224(SP), 256(SP), Y10, Y4, Y5) VPXOR Y0, Y8, Y8 VPXOR Y1, Y9, Y9 @@ -189,8 +213,290 @@ MOVQ DX, SP RET +// unfortunately the BYTE representation of VPUNPCKLQDQ and VPUNPCKHQDQ must be used +#define VPUNPCKLQDQ_X8_X8_X10 BYTE $0xC4; BYTE $0x41; BYTE $0x39; BYTE $0x6C; BYTE $0xD0 +#define VPUNPCKHQDQ_X7_X10_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF2 +#define VPUNPCKLQDQ_X7_X7_X10 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xD7 +#define VPUNPCKHQDQ_X8_X10_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x39; BYTE $0x6D; BYTE $0xFA +#define VPUNPCKLQDQ_X3_X3_X10 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xD3 +#define VPUNPCKHQDQ_X2_X10_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD2 +#define VPUNPCKLQDQ_X9_X9_X10 BYTE $0xC4; BYTE $0x41; BYTE $0x31; BYTE $0x6C; BYTE $0xD1 +#define VPUNPCKHQDQ_X3_X10_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDA +#define VPUNPCKLQDQ_X2_X2_X10 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xD2 +#define VPUNPCKHQDQ_X3_X10_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD2 +#define VPUNPCKHQDQ_X8_X10_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x39; BYTE $0x6D; BYTE $0xDA +#define VPUNPCKHQDQ_X6_X10_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF2 +#define VPUNPCKHQDQ_X7_X10_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFA + +// shuffle X2 and X6 using the temp registers X8, X9, X10 +#define SHUFFLE_AVX() \ + VMOVDQA X4, X9; \ + VMOVDQA X5, X4; \ + VMOVDQA X9, X5; \ + VMOVDQA X6, X8; \ + VPUNPCKLQDQ_X8_X8_X10; \ + VPUNPCKHQDQ_X7_X10_X6; \ + VPUNPCKLQDQ_X7_X7_X10; \ + VPUNPCKHQDQ_X8_X10_X7; \ + VPUNPCKLQDQ_X3_X3_X10; \ + VMOVDQA X2, X9; \ + VPUNPCKHQDQ_X2_X10_X2; \ + VPUNPCKLQDQ_X9_X9_X10; \ + VPUNPCKHQDQ_X3_X10_X3; \ + +// inverse shuffle X2 and X6 using the temp registers X8, X9, X10 +#define SHUFFLE_AVX_INV() \ + VMOVDQA X4, X9; \ + VMOVDQA X5, X4; \ + VMOVDQA X9, X5; \ + VMOVDQA X2, X8; \ + VPUNPCKLQDQ_X2_X2_X10; \ + VPUNPCKHQDQ_X3_X10_X2; \ + VPUNPCKLQDQ_X3_X3_X10; \ + VPUNPCKHQDQ_X8_X10_X3; \ + VPUNPCKLQDQ_X7_X7_X10; \ + VMOVDQA X6, X9; \ + VPUNPCKHQDQ_X6_X10_X6; \ + VPUNPCKLQDQ_X9_X9_X10; \ + VPUNPCKHQDQ_X7_X10_X7; \ + +#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \ + VPADDQ m0, v0, v0; \ + VPADDQ v2, v0, v0; \ + VPADDQ m1, v1, v1; \ + VPADDQ v3, v1, v1; \ + VPXOR v0, v6, v6; \ + VPXOR v1, v7, v7; \ + VPSHUFD $-79, v6, v6; \ + VPSHUFD $-79, v7, v7; \ + VPADDQ v6, v4, v4; \ + VPADDQ v7, v5, v5; \ + VPXOR v4, v2, v2; \ + VPXOR v5, v3, v3; \ + VPSHUFB c40, v2, v2; \ + VPSHUFB c40, v3, v3; \ + VPADDQ m2, v0, v0; \ + VPADDQ v2, v0, v0; \ + VPADDQ m3, v1, v1; \ + VPADDQ v3, v1, v1; \ + VPXOR v0, v6, v6; \ + VPXOR v1, v7, v7; \ + VPSHUFB c48, v6, v6; \ + VPSHUFB c48, v7, v7; \ + VPADDQ v6, v4, v4; \ + VPADDQ v7, v5, v5; \ + VPXOR v4, v2, v2; \ + VPXOR v5, v3, v3; \ + VPADDQ v2, v2, t0; \ + VPSRLQ $63, v2, v2; \ + VPXOR t0, v2, v2; \ + VPADDQ v3, v3, t0; \ + VPSRLQ $63, v3, v3; \ + VPXOR t0, v3, v3 + +// unfortunately the BYTE representation of VPINSRQ must be used +#define VPINSRQ_1_R10_X8_X8 BYTE $0xC4; BYTE $0x43; BYTE $0xB9; BYTE $0x22; BYTE $0xC2; BYTE $0x01 +#define VPINSRQ_1_R11_X9_X9 BYTE $0xC4; BYTE $0x43; BYTE $0xB1; BYTE $0x22; BYTE $0xCB; BYTE $0x01 +#define VPINSRQ_1_R12_X10_X10 BYTE $0xC4; BYTE $0x43; BYTE $0xA9; BYTE $0x22; BYTE $0xD4; BYTE $0x01 +#define VPINSRQ_1_R13_X11_X11 BYTE $0xC4; BYTE $0x43; BYTE $0xA1; BYTE $0x22; BYTE $0xDD; BYTE $0x01 + +#define VPINSRQ_1_R9_X8_X8 BYTE $0xC4; BYTE $0x43; BYTE $0xB9; BYTE $0x22; BYTE $0xC1; BYTE $0x01 + +// load src into X8, X9, X10 and X11 using R10, R11, R12 and R13 for temp registers +#define LOAD_MSG_AVX(src, i0, i1, i2, i3, i4, i5, i6, i7) \ + MOVQ i0*8(src), X8; \ + MOVQ i1*8(src), R10; \ + MOVQ i2*8(src), X9; \ + MOVQ i3*8(src), R11; \ + MOVQ i4*8(src), X10; \ + MOVQ i5*8(src), R12; \ + MOVQ i6*8(src), X11; \ + MOVQ i7*8(src), R13; \ + VPINSRQ_1_R10_X8_X8; \ + VPINSRQ_1_R11_X9_X9; \ + VPINSRQ_1_R12_X10_X10; \ + VPINSRQ_1_R13_X11_X11 + +// func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) +TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment + MOVQ h+0(FP), AX + MOVQ c+8(FP), BX + MOVQ flag+16(FP), CX + MOVQ blocks_base+24(FP), SI + MOVQ blocks_len+32(FP), DI + + MOVQ SP, BP + MOVQ SP, R9 + ADDQ $15, R9 + ANDQ $~15, R9 + MOVQ R9, SP + + MOVOU ·AVX_c40<>(SB), X13 + MOVOU ·AVX_c48<>(SB), X14 + + VMOVDQU ·AVX_iv3<>(SB), X0 + VMOVDQA X0, 0(SP) + XORQ CX, 0(SP) // 0(SP) = ·AVX_iv3 ^ (CX || 0) + + VMOVDQU 0(AX), X12 + VMOVDQU 16(AX), X15 + VMOVDQU 32(AX), X2 + VMOVDQU 48(AX), X3 + + MOVQ 0(BX), R8 + MOVQ 8(BX), R9 + +loop: + ADDQ $128, R8 + CMPQ R8, $128 + JGE noinc + INCQ R9 + +noinc: + MOVQ R8, X8 + VPINSRQ_1_R9_X8_X8 + + VMOVDQA X12, X0 + VMOVDQA X15, X1 + VMOVDQU ·AVX_iv0<>(SB), X4 + VMOVDQU ·AVX_iv1<>(SB), X5 + VMOVDQU ·AVX_iv2<>(SB), X6 + + VPXOR X8, X6, X6 + VMOVDQA 0(SP), X7 + + LOAD_MSG_AVX(SI, 0, 2, 4, 6, 1, 3, 5, 7) + VMOVDQA X8, 16(SP) + VMOVDQA X9, 32(SP) + VMOVDQA X10, 48(SP) + VMOVDQA X11, 64(SP) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 8, 10, 12, 14, 9, 11, 13, 15) + VMOVDQA X8, 80(SP) + VMOVDQA X9, 96(SP) + VMOVDQA X10, 112(SP) + VMOVDQA X11, 128(SP) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 14, 4, 9, 13, 10, 8, 15, 6) + VMOVDQA X8, 144(SP) + VMOVDQA X9, 160(SP) + VMOVDQA X10, 176(SP) + VMOVDQA X11, 192(SP) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 1, 0, 11, 5, 12, 2, 7, 3) + VMOVDQA X8, 208(SP) + VMOVDQA X9, 224(SP) + VMOVDQA X10, 240(SP) + VMOVDQA X11, 256(SP) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 11, 12, 5, 15, 8, 0, 2, 13) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 10, 3, 7, 9, 14, 6, 1, 4) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 7, 3, 13, 11, 9, 1, 12, 14) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 2, 5, 4, 15, 6, 10, 0, 8) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 9, 5, 2, 10, 0, 7, 4, 15) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 14, 11, 6, 3, 1, 12, 8, 13) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 2, 6, 0, 8, 12, 10, 11, 3) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 4, 7, 15, 1, 13, 5, 14, 9) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 12, 1, 14, 4, 5, 15, 13, 10) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 0, 6, 9, 8, 7, 3, 2, 11) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 13, 7, 12, 3, 11, 14, 1, 9) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 5, 15, 8, 2, 0, 4, 6, 10) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 6, 14, 11, 0, 15, 9, 3, 8) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 12, 13, 1, 10, 2, 7, 4, 5) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + LOAD_MSG_AVX(SI, 10, 8, 7, 1, 2, 4, 6, 5) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX() + LOAD_MSG_AVX(SI, 15, 9, 3, 13, 11, 14, 12, 0) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_AVX_INV() + + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X11, X13, X14) + SHUFFLE_AVX() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 80(SP), 96(SP), 112(SP), 128(SP), X11, X13, X14) + SHUFFLE_AVX_INV() + + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 144(SP), 160(SP), 176(SP), 192(SP), X11, X13, X14) + SHUFFLE_AVX() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 208(SP), 224(SP), 240(SP), 256(SP), X11, X13, X14) + SHUFFLE_AVX_INV() + + VMOVDQU 32(AX), X10 + VMOVDQU 48(AX), X11 + VPXOR X0, X12, X12 + VPXOR X1, X15, X15 + VPXOR X2, X10, X10 + VPXOR X3, X11, X11 + VPXOR X4, X12, X12 + VPXOR X5, X15, X15 + VPXOR X6, X10, X2 + VPXOR X7, X11, X3 + VMOVDQU X2, 32(AX) + VMOVDQU X3, 48(AX) + + LEAQ 128(SI), SI + SUBQ $128, DI + JNE loop + + VMOVDQU X12, 0(AX) + VMOVDQU X15, 16(AX) + + MOVQ R8, 0(BX) + MOVQ R9, 8(BX) + + VZEROUPPER + + MOVQ BP, SP + RET + // func supportAVX2() bool TEXT ·supportAVX2(SB), 4, $0-1 MOVQ runtime·support_avx2(SB), AX MOVB AX, ret+0(FP) RET + +// func supportAVX() bool +TEXT ·supportAVX(SB), 4, $0-1 + MOVQ runtime·support_avx(SB), AX + MOVB AX, ret+0(FP) + RET diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2b_test.go lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2b_test.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/blake2b/blake2b_test.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/blake2b/blake2b_test.go 2016-12-21 00:52:54.000000000 +0000 @@ -21,15 +21,20 @@ } func TestHashes(t *testing.T) { - defer func(sse4, avx2 bool) { - useSSE4, useAVX2 = sse4, avx2 - }(useSSE4, useAVX2) + defer func(sse4, avx, avx2 bool) { + useSSE4, useAVX, useAVX2 = sse4, useAVX, avx2 + }(useSSE4, useAVX, useAVX2) if useAVX2 { t.Log("AVX2 version") testHashes(t) useAVX2 = false } + if useAVX { + t.Log("AVX version") + testHashes(t) + useAVX = false + } if useSSE4 { t.Log("SSE4 version") testHashes(t) diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/bn256/constants.go lxd-2.7/dist/src/golang.org/x/crypto/bn256/constants.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/bn256/constants.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/bn256/constants.go 2016-12-21 00:52:54.000000000 +0000 @@ -16,10 +16,10 @@ // u is the BN parameter that determines the prime: 1868033³. var u = bigFromBase10("6518589491078791937") -// p is a prime over which we form a basic field: 36u⁴+36u³+24u³+6u+1. +// p is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1. var p = bigFromBase10("65000549695646603732796438742359905742825358107623003571877145026864184071783") -// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u³+6u+1. +// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1. var Order = bigFromBase10("65000549695646603732796438742359905742570406053903786389881062969044166799969") // xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+3. diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s lxd-2.7/dist/src/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s --- lxd-2.6.2/dist/src/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s 2016-12-21 00:52:54.000000000 +0000 @@ -209,7 +209,7 @@ #define polyMul polyMulStage1; polyMulStage2; polyMulStage3; polyMulReduceStage #define polyMulAVX2 polyMulStage1_AVX2; polyMulStage2_AVX2; polyMulStage3_AVX2; polyMulReduceStage // ---------------------------------------------------------------------------- -TEXT polyHashADInternal(SB), NOSPLIT, $0 +TEXT polyHashADInternal<>(SB), NOSPLIT, $0 // adp points to beginning of additional data // itr2 holds ad length XORQ acc0, acc0 @@ -315,7 +315,7 @@ // Hash AAD MOVQ ad_len+80(FP), itr2 - CALL polyHashADInternal(SB) + CALL polyHashADInternal<>(SB) openSSEMainLoop: CMPQ inl, $256 @@ -476,7 +476,7 @@ // Hash MOVQ ad_len+80(FP), itr2 - CALL polyHashADInternal(SB) + CALL polyHashADInternal<>(SB) openSSE128Open: CMPQ inl, $16 @@ -822,7 +822,7 @@ // Hash AD + first 64 bytes MOVQ ad_len+80(FP), itr2 - CALL polyHashADInternal(SB) + CALL polyHashADInternal<>(SB) XORQ itr1, itr1 openAVX2InitialHash64: @@ -1014,7 +1014,7 @@ openAVX2ShortOpen: // Hash MOVQ ad_len+80(FP), itr2 - CALL polyHashADInternal(SB) + CALL polyHashADInternal<>(SB) openAVX2ShortOpenLoop: CMPQ inl, $32 @@ -1547,7 +1547,7 @@ // Hash AAD MOVQ ad_len+80(FP), itr2 - CALL polyHashADInternal(SB) + CALL polyHashADInternal<>(SB) MOVOU (0*16)(inp), A0; MOVOU (1*16)(inp), B0; MOVOU (2*16)(inp), C0; MOVOU (3*16)(inp), D0 PXOR A0, A1; PXOR B0, B1; PXOR C0, C1; PXOR D0, D1 @@ -1691,7 +1691,7 @@ MOVO D1, ctr0Store sealSSETail64LoopA: - // Perform ChaCha rounds, while hashing the prevsiosly encrpyted ciphertext + // Perform ChaCha rounds, while hashing the previously encrypted ciphertext polyAdd(0(oup)) polyMul LEAQ 16(oup), oup @@ -1725,7 +1725,7 @@ MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr1Store sealSSETail128LoopA: - // Perform ChaCha rounds, while hashing the prevsiosly encrpyted ciphertext + // Perform ChaCha rounds, while hashing the previously encrypted ciphertext polyAdd(0(oup)) polyMul LEAQ 16(oup), oup @@ -1771,7 +1771,7 @@ MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2; MOVO D2, ctr2Store sealSSETail192LoopA: - // Perform ChaCha rounds, while hashing the prevsiosly encrpyted ciphertext + // Perform ChaCha rounds, while hashing the previously encrypted ciphertext polyAdd(0(oup)) polyMul LEAQ 16(oup), oup @@ -1852,7 +1852,7 @@ // Hash MOVQ ad_len+80(FP), itr2 - CALL polyHashADInternal(SB) + CALL polyHashADInternal<>(SB) XORQ itr1, itr1 sealSSE128SealHash: @@ -2027,7 +2027,7 @@ // Hash AD MOVQ ad_len+80(FP), itr2 - CALL polyHashADInternal(SB) + CALL polyHashADInternal<>(SB) // Can store at least 320 bytes VPXOR (0*32)(inp), AA0, AA0 @@ -2290,7 +2290,7 @@ sealAVX2ShortSeal: // Hash aad MOVQ ad_len+80(FP), itr2 - CALL polyHashADInternal(SB) + CALL polyHashADInternal<>(SB) XORQ itr1, itr1 sealAVX2SealHash: diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/ocsp/ocsp.go lxd-2.7/dist/src/golang.org/x/crypto/ocsp/ocsp.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/ocsp/ocsp.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/ocsp/ocsp.go 2016-12-21 00:52:54.000000000 +0000 @@ -13,11 +13,14 @@ "crypto/elliptic" "crypto/rand" "crypto/rsa" - "crypto/sha1" + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "errors" + "fmt" "math/big" "strconv" "time" @@ -355,6 +358,11 @@ Signature []byte SignatureAlgorithm x509.SignatureAlgorithm + // IssuerHash is the hash used to compute the IssuerNameHash and IssuerKeyHash. + // Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384, and crypto.SHA512. + // If zero, the default is crypto.SHA1. + IssuerHash crypto.Hash + // Extensions contains raw X.509 extensions from the singleExtensions field // of the OCSP response. When parsing certificates, this can be used to // extract non-critical extensions that are not parsed by this package. When @@ -524,6 +532,16 @@ ret.SerialNumber = r.CertID.SerialNumber + for h, oid := range hashOIDs { + if r.CertID.HashAlgorithm.Algorithm.Equal(oid) { + ret.IssuerHash = h + break + } + } + if ret.IssuerHash == 0 { + return nil, ParseError("unsupported issuer hash algorithm") + } + switch { case bool(r.Good): ret.Status = Good @@ -606,11 +624,12 @@ // itself is provided alongside the OCSP response signature. // // The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields. -// (SHA-1 is used for the hash function; this is not configurable.) // // The template is used to populate the SerialNumber, RevocationStatus, RevokedAt, // RevocationReason, ThisUpdate, and NextUpdate fields. // +// If template.IssuerHash is not set, SHA1 will be used. +// // The ProducedAt date is automatically set to the current date, to the nearest minute. func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) { var publicKeyInfo struct { @@ -621,7 +640,18 @@ return nil, err } - h := sha1.New() + if template.IssuerHash == 0 { + template.IssuerHash = crypto.SHA1 + } + hashOID := getOIDFromHashAlgorithm(template.IssuerHash) + if hashOID == nil { + return nil, errors.New("unsupported issuer hash algorithm") + } + + if !template.IssuerHash.Available() { + return nil, fmt.Errorf("issuer hash algorithm %v not linked into binarya", template.IssuerHash) + } + h := template.IssuerHash.New() h.Write(publicKeyInfo.PublicKey.RightAlign()) issuerKeyHash := h.Sum(nil) @@ -632,7 +662,7 @@ innerResponse := singleResponse{ CertID: certID{ HashAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: hashOIDs[crypto.SHA1], + Algorithm: hashOID, Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */}, }, NameHash: issuerNameHash, diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/ocsp/ocsp_test.go lxd-2.7/dist/src/golang.org/x/crypto/ocsp/ocsp_test.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/ocsp/ocsp_test.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/ocsp/ocsp_test.go 2016-12-21 00:52:54.000000000 +0000 @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.7 + package ocsp import ( @@ -222,46 +224,76 @@ ExtraExtensions: extensions, } - responseBytes, err := CreateResponse(issuer, responder, template, responderPrivateKey) - if err != nil { - t.Fatal(err) - } - - resp, err := ParseResponse(responseBytes, nil) - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(resp.ThisUpdate, template.ThisUpdate) { - t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, template.ThisUpdate) - } - - if !reflect.DeepEqual(resp.NextUpdate, template.NextUpdate) { - t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, template.NextUpdate) - } - - if !reflect.DeepEqual(resp.RevokedAt, template.RevokedAt) { - t.Errorf("resp.RevokedAt: got %d, want %d", resp.RevokedAt, template.RevokedAt) - } - - if !reflect.DeepEqual(resp.Extensions, template.ExtraExtensions) { - t.Errorf("resp.Extensions: got %v, want %v", resp.Extensions, template.ExtraExtensions) - } - - if !resp.ProducedAt.Equal(producedAt) { - t.Errorf("resp.ProducedAt: got %d, want %d", resp.ProducedAt, producedAt) - } - - if resp.Status != template.Status { - t.Errorf("resp.Status: got %d, want %d", resp.Status, template.Status) - } - - if resp.SerialNumber.Cmp(template.SerialNumber) != 0 { - t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, template.SerialNumber) + template.IssuerHash = crypto.MD5 + _, err = CreateResponse(issuer, responder, template, responderPrivateKey) + if err == nil { + t.Fatal("CreateResponse didn't fail with non-valid template.IssuerHash value crypto.MD5") } - if resp.RevocationReason != template.RevocationReason { - t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, template.RevocationReason) + testCases := []struct { + name string + issuerHash crypto.Hash + }{ + {"Zero value", 0}, + {"crypto.SHA1", crypto.SHA1}, + {"crypto.SHA256", crypto.SHA256}, + {"crypto.SHA384", crypto.SHA384}, + {"crypto.SHA512", crypto.SHA512}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + template.IssuerHash = tc.issuerHash + responseBytes, err := CreateResponse(issuer, responder, template, responderPrivateKey) + if err != nil { + t.Fatalf("CreateResponse failed: %s", err) + } + + resp, err := ParseResponse(responseBytes, nil) + if err != nil { + t.Fatalf("ParseResponse failed: %s", err) + } + + if !reflect.DeepEqual(resp.ThisUpdate, template.ThisUpdate) { + t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, template.ThisUpdate) + } + + if !reflect.DeepEqual(resp.NextUpdate, template.NextUpdate) { + t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, template.NextUpdate) + } + + if !reflect.DeepEqual(resp.RevokedAt, template.RevokedAt) { + t.Errorf("resp.RevokedAt: got %d, want %d", resp.RevokedAt, template.RevokedAt) + } + + if !reflect.DeepEqual(resp.Extensions, template.ExtraExtensions) { + t.Errorf("resp.Extensions: got %v, want %v", resp.Extensions, template.ExtraExtensions) + } + + if !resp.ProducedAt.Equal(producedAt) { + t.Errorf("resp.ProducedAt: got %d, want %d", resp.ProducedAt, producedAt) + } + + if resp.Status != template.Status { + t.Errorf("resp.Status: got %d, want %d", resp.Status, template.Status) + } + + if resp.SerialNumber.Cmp(template.SerialNumber) != 0 { + t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, template.SerialNumber) + } + + if resp.RevocationReason != template.RevocationReason { + t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, template.RevocationReason) + } + + expectedHash := tc.issuerHash + if tc.issuerHash == 0 { + expectedHash = crypto.SHA1 + } + + if resp.IssuerHash != expectedHash { + t.Errorf("resp.IssuerHash: got %d, want %d", resp.IssuerHash, expectedHash) + } + }) } } diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/ssh/server.go lxd-2.7/dist/src/golang.org/x/crypto/ssh/server.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/ssh/server.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/ssh/server.go 2016-12-21 00:52:54.000000000 +0000 @@ -242,7 +242,7 @@ } if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { - if bytes.Equal(allowedIP, tcpAddr.IP) { + if allowedIP.Equal(tcpAddr.IP) { return nil } } else { diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/ssh/terminal/terminal.go lxd-2.7/dist/src/golang.org/x/crypto/ssh/terminal/terminal.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/ssh/terminal/terminal.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/ssh/terminal/terminal.go 2016-12-21 00:52:54.000000000 +0000 @@ -132,8 +132,11 @@ keyPasteEnd ) -var pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} -var pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} +var ( + crlf = []byte{'\r', '\n'} + pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} + pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} +) // bytesToKey tries to parse a key sequence from b. If successful, it returns // the key and the remainder of the input. Otherwise it returns utf8.RuneError. @@ -333,7 +336,7 @@ // So, if we are stopping at the end of a line, we // need to write a newline so that our cursor can be // advanced to the next line. - t.outBuf = append(t.outBuf, '\n') + t.outBuf = append(t.outBuf, '\r', '\n') } } @@ -593,6 +596,35 @@ } } +// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n. +func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) { + for len(buf) > 0 { + i := bytes.IndexByte(buf, '\n') + todo := len(buf) + if i >= 0 { + todo = i + } + + var nn int + nn, err = w.Write(buf[:todo]) + n += nn + if err != nil { + return n, err + } + buf = buf[todo:] + + if i >= 0 { + if _, err = w.Write(crlf); err != nil { + return n, err + } + n += 1 + buf = buf[1:] + } + } + + return n, nil +} + func (t *Terminal) Write(buf []byte) (n int, err error) { t.lock.Lock() defer t.lock.Unlock() @@ -600,7 +632,7 @@ if t.cursorX == 0 && t.cursorY == 0 { // This is the easy case: there's nothing on the screen that we // have to move out of the way. - return t.c.Write(buf) + return writeWithCRLF(t.c, buf) } // We have a prompt and possibly user input on the screen. We @@ -620,7 +652,7 @@ } t.outBuf = t.outBuf[:0] - if n, err = t.c.Write(buf); err != nil { + if n, err = writeWithCRLF(t.c, buf); err != nil { return } diff -Nru lxd-2.6.2/dist/src/golang.org/x/crypto/ssh/terminal/terminal_test.go lxd-2.7/dist/src/golang.org/x/crypto/ssh/terminal/terminal_test.go --- lxd-2.6.2/dist/src/golang.org/x/crypto/ssh/terminal/terminal_test.go 2016-11-25 04:23:23.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/crypto/ssh/terminal/terminal_test.go 2016-12-21 00:52:54.000000000 +0000 @@ -5,6 +5,7 @@ package terminal import ( + "bytes" "io" "os" "testing" @@ -289,3 +290,17 @@ t.Errorf("states do not match; was %v, expected %v", raw, st) } } + +func TestOutputNewlines(t *testing.T) { + // \n should be changed to \r\n in terminal output. + buf := new(bytes.Buffer) + term := NewTerminal(buf, ">") + + term.Write([]byte("1\n2\n")) + output := string(buf.Bytes()) + const expected = "1\r\n2\r\n" + + if output != expected { + t.Errorf("incorrect output: was %q, expected %q", output, expected) + } +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/context/context_test.go lxd-2.7/dist/src/golang.org/x/net/context/context_test.go --- lxd-2.6.2/dist/src/golang.org/x/net/context/context_test.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/context/context_test.go 2016-12-21 00:53:01.000000000 +0000 @@ -243,45 +243,51 @@ } func TestDeadline(t *testing.T) { - c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + t.Parallel() + const timeUnit = 500 * time.Millisecond + c, _ := WithDeadline(Background(), time.Now().Add(1*timeUnit)) if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { t.Errorf("c.String() = %q want prefix %q", got, prefix) } - testDeadline(c, 200*time.Millisecond, t) + testDeadline(c, 2*timeUnit, t) - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit)) o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) + testDeadline(o, 2*timeUnit, t) - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit)) o = otherContext{c} - c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) - testDeadline(c, 200*time.Millisecond, t) + c, _ = WithDeadline(o, time.Now().Add(3*timeUnit)) + testDeadline(c, 2*timeUnit, t) } func TestTimeout(t *testing.T) { - c, _ := WithTimeout(Background(), 100*time.Millisecond) + t.Parallel() + const timeUnit = 500 * time.Millisecond + c, _ := WithTimeout(Background(), 1*timeUnit) if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { t.Errorf("c.String() = %q want prefix %q", got, prefix) } - testDeadline(c, 200*time.Millisecond, t) + testDeadline(c, 2*timeUnit, t) - c, _ = WithTimeout(Background(), 100*time.Millisecond) + c, _ = WithTimeout(Background(), 1*timeUnit) o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) + testDeadline(o, 2*timeUnit, t) - c, _ = WithTimeout(Background(), 100*time.Millisecond) + c, _ = WithTimeout(Background(), 1*timeUnit) o = otherContext{c} - c, _ = WithTimeout(o, 300*time.Millisecond) - testDeadline(c, 200*time.Millisecond, t) + c, _ = WithTimeout(o, 3*timeUnit) + testDeadline(c, 2*timeUnit, t) } func TestCanceledTimeout(t *testing.T) { - c, _ := WithTimeout(Background(), 200*time.Millisecond) + t.Parallel() + const timeUnit = 500 * time.Millisecond + c, _ := WithTimeout(Background(), 2*timeUnit) o := otherContext{c} - c, cancel := WithTimeout(o, 400*time.Millisecond) + c, cancel := WithTimeout(o, 4*timeUnit) cancel() - time.Sleep(100 * time.Millisecond) // let cancelation propagate + time.Sleep(1 * timeUnit) // let cancelation propagate select { case <-c.Done(): default: diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/frame.go lxd-2.7/dist/src/golang.org/x/net/http2/frame.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/frame.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/frame.go 2016-12-21 00:53:01.000000000 +0000 @@ -317,10 +317,12 @@ // non-Continuation or Continuation on a different stream is // attempted to be written. - logReads bool + logReads, logWrites bool - debugFramer *Framer // only use for logging written writes - debugFramerBuf *bytes.Buffer + debugFramer *Framer // only use for logging written writes + debugFramerBuf *bytes.Buffer + debugReadLoggerf func(string, ...interface{}) + debugWriteLoggerf func(string, ...interface{}) } func (fr *Framer) maxHeaderListSize() uint32 { @@ -355,7 +357,7 @@ byte(length>>16), byte(length>>8), byte(length)) - if logFrameWrites { + if f.logWrites { f.logWrite() } @@ -378,10 +380,10 @@ f.debugFramerBuf.Write(f.wbuf) fr, err := f.debugFramer.ReadFrame() if err != nil { - log.Printf("http2: Framer %p: failed to decode just-written frame", f) + f.debugWriteLoggerf("http2: Framer %p: failed to decode just-written frame", f) return } - log.Printf("http2: Framer %p: wrote %v", f, summarizeFrame(fr)) + f.debugWriteLoggerf("http2: Framer %p: wrote %v", f, summarizeFrame(fr)) } func (f *Framer) writeByte(v byte) { f.wbuf = append(f.wbuf, v) } @@ -399,9 +401,12 @@ // NewFramer returns a Framer that writes frames to w and reads them from r. func NewFramer(w io.Writer, r io.Reader) *Framer { fr := &Framer{ - w: w, - r: r, - logReads: logFrameReads, + w: w, + r: r, + logReads: logFrameReads, + logWrites: logFrameWrites, + debugReadLoggerf: log.Printf, + debugWriteLoggerf: log.Printf, } fr.getReadBuf = func(size uint32) []byte { if cap(fr.readBuf) >= int(size) { @@ -483,7 +488,7 @@ return nil, err } if fr.logReads { - log.Printf("http2: Framer %p: read %v", fr, summarizeFrame(f)) + fr.debugReadLoggerf("http2: Framer %p: read %v", fr, summarizeFrame(f)) } if fh.Type == FrameHeaders && fr.ReadMetaHeaders != nil { return fr.readMetaFrame(f.(*HeadersFrame)) @@ -1419,8 +1424,8 @@ hdec.SetEmitEnabled(true) hdec.SetMaxStringLength(fr.maxHeaderStringLen()) hdec.SetEmitFunc(func(hf hpack.HeaderField) { - if VerboseLogs && logFrameReads { - log.Printf("http2: decoded hpack field %+v", hf) + if VerboseLogs && fr.logReads { + fr.debugReadLoggerf("http2: decoded hpack field %+v", hf) } if !httplex.ValidHeaderFieldValue(hf.Value) { invalid = headerFieldValueError(hf.Value) diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/go18.go lxd-2.7/dist/src/golang.org/x/net/http2/go18.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/go18.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/go18.go 2016-12-21 00:53:01.000000000 +0000 @@ -8,6 +8,7 @@ import ( "crypto/tls" + "io" "net/http" ) @@ -39,3 +40,11 @@ func shouldLogPanic(panicValue interface{}) bool { return panicValue != nil && panicValue != http.ErrAbortHandler } + +func reqGetBody(req *http.Request) func() (io.ReadCloser, error) { + return req.GetBody +} + +func reqBodyIsNoBody(body io.ReadCloser) bool { + return body == http.NoBody +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/not_go18.go lxd-2.7/dist/src/golang.org/x/net/http2/not_go18.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/not_go18.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/not_go18.go 2016-12-21 00:53:01.000000000 +0000 @@ -6,7 +6,10 @@ package http2 -import "net/http" +import ( + "io" + "net/http" +) func configureServer18(h1 *http.Server, h2 *Server) error { // No IdleTimeout to sync prior to Go 1.8. @@ -16,3 +19,9 @@ func shouldLogPanic(panicValue interface{}) bool { return panicValue != nil } + +func reqGetBody(req *http.Request) func() (io.ReadCloser, error) { + return nil +} + +func reqBodyIsNoBody(io.ReadCloser) bool { return false } diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/server.go lxd-2.7/dist/src/golang.org/x/net/http2/server.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/server.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/server.go 2016-12-21 00:53:01.000000000 +0000 @@ -423,6 +423,11 @@ return uint32(n + typicalHeaders*perFieldOverhead) } +func (sc *serverConn) curOpenStreams() uint32 { + sc.serveG.check() + return sc.curClientStreams + sc.curPushedStreams +} + // stream represents a stream. This is the minimal metadata needed by // the serve goroutine. Most of the actual stream state is owned by // the http.Handler's goroutine in the responseWriter. Because the @@ -448,8 +453,7 @@ numTrailerValues int64 weight uint8 state streamState - sentReset bool // only true once detached from streams map - gotReset bool // only true once detacted from streams map + resetQueued bool // RST_STREAM queued for write; set by sc.resetStream gotTrailerHeader bool // HEADER frame for trailers was seen wroteHeaders bool // whether we wrote headers (not status 100) reqBuf []byte // if non-nil, body pipe buffer to return later at EOF @@ -753,7 +757,7 @@ fn(loopNum) } - if sc.inGoAway && sc.curClientStreams == 0 && !sc.needToSendGoAway && !sc.writingFrame { + if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame { return } } @@ -869,8 +873,34 @@ func (sc *serverConn) writeFrame(wr FrameWriteRequest) { sc.serveG.check() + // If true, wr will not be written and wr.done will not be signaled. var ignoreWrite bool + // We are not allowed to write frames on closed streams. RFC 7540 Section + // 5.1.1 says: "An endpoint MUST NOT send frames other than PRIORITY on + // a closed stream." Our server never sends PRIORITY, so that exception + // does not apply. + // + // The serverConn might close an open stream while the stream's handler + // is still running. For example, the server might close a stream when it + // receives bad data from the client. If this happens, the handler might + // attempt to write a frame after the stream has been closed (since the + // handler hasn't yet been notified of the close). In this case, we simply + // ignore the frame. The handler will notice that the stream is closed when + // it waits for the frame to be written. + // + // As an exception to this rule, we allow sending RST_STREAM after close. + // This allows us to immediately reject new streams without tracking any + // state for those streams (except for the queued RST_STREAM frame). This + // may result in duplicate RST_STREAMs in some cases, but the client should + // ignore those. + if wr.StreamID() != 0 { + _, isReset := wr.write.(StreamError) + if state, _ := sc.state(wr.StreamID()); state == stateClosed && !isReset { + ignoreWrite = true + } + } + // Don't send a 100-continue response if we've already sent headers. // See golang.org/issue/14030. switch wr.write.(type) { @@ -878,6 +908,11 @@ wr.stream.wroteHeaders = true case write100ContinueHeadersFrame: if wr.stream.wroteHeaders { + // We do not need to notify wr.done because this frame is + // never written with wr.done != nil. + if wr.done != nil { + panic("wr.done != nil for write100ContinueHeadersFrame") + } ignoreWrite = true } } @@ -901,14 +936,15 @@ if st != nil { switch st.state { case stateHalfClosedLocal: - panic("internal error: attempt to send frame on half-closed-local stream") - case stateClosed: - if st.sentReset || st.gotReset { - // Skip this frame. - sc.scheduleFrameWrite() - return + switch wr.write.(type) { + case StreamError, handlerPanicRST, writeWindowUpdate: + // RFC 7540 Section 5.1 allows sending RST_STREAM, PRIORITY, and WINDOW_UPDATE + // in this state. (We never send PRIORITY from the server, so that is not checked.) + default: + panic(fmt.Sprintf("internal error: attempt to send frame on a half-closed-local stream: %v", wr)) } - panic(fmt.Sprintf("internal error: attempt to send a write %v on a closed stream", wr)) + case stateClosed: + panic(fmt.Sprintf("internal error: attempt to send frame on a closed stream: %v", wr)) } } if wpp, ok := wr.write.(*writePushPromise); ok { @@ -916,9 +952,7 @@ wpp.promisedID, err = wpp.allocatePromisedID() if err != nil { sc.writingFrameAsync = false - if wr.done != nil { - wr.done <- err - } + wr.replyToWriter(err) return } } @@ -951,25 +985,9 @@ sc.writingFrameAsync = false wr := res.wr - st := wr.stream - closeStream := endsStream(wr.write) - - if _, ok := wr.write.(handlerPanicRST); ok { - sc.closeStream(st, errHandlerPanicked) - } - - // Reply (if requested) to the blocked ServeHTTP goroutine. - if ch := wr.done; ch != nil { - select { - case ch <- res.err: - default: - panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wr.write)) - } - } - wr.write = nil // prevent use (assume it's tainted after wr.done send) - - if closeStream { + if writeEndsStream(wr.write) { + st := wr.stream if st == nil { panic("internal error: expecting non-nil stream") } @@ -982,15 +1000,29 @@ // reading data (see possible TODO at top of // this file), we go into closed state here // anyway, after telling the peer we're - // hanging up on them. - st.state = stateHalfClosedLocal // won't last long, but necessary for closeStream via resetStream - errCancel := streamError(st.id, ErrCodeCancel) - sc.resetStream(errCancel) + // hanging up on them. We'll transition to + // stateClosed after the RST_STREAM frame is + // written. + st.state = stateHalfClosedLocal + sc.resetStream(streamError(st.id, ErrCodeCancel)) case stateHalfClosedRemote: sc.closeStream(st, errHandlerComplete) } + } else { + switch v := wr.write.(type) { + case StreamError: + // st may be unknown if the RST_STREAM was generated to reject bad input. + if st, ok := sc.streams[v.StreamID]; ok { + sc.closeStream(st, v) + } + case handlerPanicRST: + sc.closeStream(wr.stream, errHandlerPanicked) + } } + // Reply (if requested) to unblock the ServeHTTP goroutine. + wr.replyToWriter(res.err) + sc.scheduleFrameWrite() } @@ -1087,8 +1119,7 @@ sc.serveG.check() sc.writeFrame(FrameWriteRequest{write: se}) if st, ok := sc.streams[se.StreamID]; ok { - st.sentReset = true - sc.closeStream(st, se) + st.resetQueued = true } } @@ -1252,7 +1283,6 @@ return ConnectionError(ErrCodeProtocol) } if st != nil { - st.gotReset = true st.cancelCtx() sc.closeStream(st, streamError(f.StreamID, f.ErrCode)) } @@ -1391,7 +1421,7 @@ // type PROTOCOL_ERROR." return ConnectionError(ErrCodeProtocol) } - if st == nil || state != stateOpen || st.gotTrailerHeader { + if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued { // This includes sending a RST_STREAM if the stream is // in stateHalfClosedLocal (which currently means that // the http.Handler returned, so it's done reading & @@ -1411,6 +1441,10 @@ sc.inflow.take(int32(f.Length)) sc.sendWindowUpdate(nil, int(f.Length)) // conn-level + if st != nil && st.resetQueued { + // Already have a stream error in flight. Don't send another. + return nil + } return streamError(id, ErrCodeStreamClosed) } if st.body == nil { @@ -1519,6 +1553,11 @@ // open, let it process its own HEADERS frame (trailers at this // point, if it's valid). if st := sc.streams[f.StreamID]; st != nil { + if st.resetQueued { + // We're sending RST_STREAM to close the stream, so don't bother + // processing this frame. + return nil + } return st.processTrailerHeaders(f) } @@ -1681,7 +1720,7 @@ } else { sc.curClientStreams++ } - if sc.curClientStreams+sc.curPushedStreams == 1 { + if sc.curOpenStreams() == 1 { sc.setConnState(http.StateActive) } @@ -2556,7 +2595,7 @@ scheme: msg.url.Scheme, authority: msg.url.Host, path: msg.url.RequestURI(), - header: msg.header, + header: cloneHeader(msg.header), // clone since handler runs concurrently with writing the PUSH_PROMISE }) if err != nil { // Should not happen, since we've already validated msg.url. diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/server_push_test.go lxd-2.7/dist/src/golang.org/x/net/http2/server_push_test.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/server_push_test.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/server_push_test.go 2016-12-21 00:53:01.000000000 +0000 @@ -244,6 +244,50 @@ } } +func TestServer_Push_SuccessNoRace(t *testing.T) { + // Regression test for issue #18326. Ensure the request handler can mutate + // pushed request headers without racing with the PUSH_PROMISE write. + errc := make(chan error, 2) + st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { + switch r.URL.RequestURI() { + case "/": + opt := &http.PushOptions{ + Header: http.Header{"User-Agent": {"testagent"}}, + } + if err := w.(http.Pusher).Push("/pushed", opt); err != nil { + errc <- fmt.Errorf("error pushing: %v", err) + return + } + w.WriteHeader(200) + errc <- nil + + case "/pushed": + // Update request header, ensure there is no race. + r.Header.Set("User-Agent", "newagent") + r.Header.Set("Cookie", "cookie") + w.WriteHeader(200) + errc <- nil + + default: + errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI()) + } + }) + + // Send one request, which should push one response. + st.greet() + getSlash(st) + for k := 0; k < 2; k++ { + select { + case <-time.After(2 * time.Second): + t.Errorf("timeout waiting for handler %d to finish", k) + case err := <-errc: + if err != nil { + t.Fatal(err) + } + } + } +} + func TestServer_Push_RejectRecursivePush(t *testing.T) { // Expect two requests, but might get three if there's a bug and the second push succeeds. errc := make(chan error, 3) diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/server_test.go lxd-2.7/dist/src/golang.org/x/net/http2/server_test.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/server_test.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/server_test.go 2016-12-21 00:53:01.000000000 +0000 @@ -45,13 +45,22 @@ t testing.TB ts *httptest.Server fr *Framer - logBuf *bytes.Buffer - logFilter []string // substrings to filter out - scMu sync.Mutex // guards sc + serverLogBuf bytes.Buffer // logger for httptest.Server + logFilter []string // substrings to filter out + scMu sync.Mutex // guards sc sc *serverConn hpackDec *hpack.Decoder decodedHeaders [][2]string + // If http2debug!=2, then we capture Frame debug logs that will be written + // to t.Log after a test fails. The read and write logs use separate locks + // and buffers so we don't accidentally introduce synchronization between + // the read and write goroutines, which may hide data races. + frameReadLogMu sync.Mutex + frameReadLogBuf bytes.Buffer + frameWriteLogMu sync.Mutex + frameWriteLogBuf bytes.Buffer + // writing headers: headerBuf bytes.Buffer hpackEnc *hpack.Encoder @@ -75,7 +84,6 @@ func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{}) *serverTester { resetHooks() - logBuf := new(bytes.Buffer) ts := httptest.NewUnstartedServer(handler) tlsConfig := &tls.Config{ @@ -110,9 +118,8 @@ ConfigureServer(ts.Config, h2server) st := &serverTester{ - t: t, - ts: ts, - logBuf: logBuf, + t: t, + ts: ts, } st.hpackEnc = hpack.NewEncoder(&st.headerBuf) st.hpackDec = hpack.NewDecoder(initialHeaderTableSize, st.onHeaderField) @@ -121,7 +128,7 @@ if quiet { ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0) } else { - ts.Config.ErrorLog = log.New(io.MultiWriter(stderrv(), twriter{t: t, st: st}, logBuf), "", log.LstdFlags) + ts.Config.ErrorLog = log.New(io.MultiWriter(stderrv(), twriter{t: t, st: st}, &st.serverLogBuf), "", log.LstdFlags) } ts.StartTLS() @@ -142,6 +149,22 @@ } st.cc = cc st.fr = NewFramer(cc, cc) + if !logFrameReads && !logFrameWrites { + st.fr.debugReadLoggerf = func(m string, v ...interface{}) { + m = time.Now().Format("2006-01-02 15:04:05.999999999 ") + strings.TrimPrefix(m, "http2: ") + "\n" + st.frameReadLogMu.Lock() + fmt.Fprintf(&st.frameReadLogBuf, m, v...) + st.frameReadLogMu.Unlock() + } + st.fr.debugWriteLoggerf = func(m string, v ...interface{}) { + m = time.Now().Format("2006-01-02 15:04:05.999999999 ") + strings.TrimPrefix(m, "http2: ") + "\n" + st.frameWriteLogMu.Lock() + fmt.Fprintf(&st.frameWriteLogBuf, m, v...) + st.frameWriteLogMu.Unlock() + } + st.fr.logReads = true + st.fr.logWrites = true + } } return st } @@ -201,6 +224,18 @@ func (st *serverTester) Close() { if st.t.Failed() { + st.frameReadLogMu.Lock() + if st.frameReadLogBuf.Len() > 0 { + st.t.Logf("Framer read log:\n%s", st.frameReadLogBuf.String()) + } + st.frameReadLogMu.Unlock() + + st.frameWriteLogMu.Lock() + if st.frameWriteLogBuf.Len() > 0 { + st.t.Logf("Framer write log:\n%s", st.frameWriteLogBuf.String()) + } + st.frameWriteLogMu.Unlock() + // If we failed already (and are likely in a Fatal, // unwindowing), force close the connection, so the // httptest.Server doesn't wait forever for the conn @@ -1100,10 +1135,10 @@ if gf.ErrCode != ErrCodeFrameSize { t.Errorf("GOAWAY err = %v; want %v", gf.ErrCode, ErrCodeFrameSize) } - if st.logBuf.Len() != 0 { + if st.serverLogBuf.Len() != 0 { // Previously we spun here for a bit until the GOAWAY disconnect // timer fired, logging while we fired. - t.Errorf("unexpected server output: %.500s\n", st.logBuf.Bytes()) + t.Errorf("unexpected server output: %.500s\n", st.serverLogBuf.Bytes()) } } @@ -1227,6 +1262,7 @@ inHandler <- true errc <- handler(w, r) }) + defer st.Close() st.greet() st.writeHeaders(HeadersFrameParam{ StreamID: 1, @@ -1244,7 +1280,6 @@ case <-time.After(5 * time.Second): t.Fatal("timeout waiting for Handler to return") } - st.Close() } func TestServer_RSTStream_Unblocks_Read(t *testing.T) { diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/transport.go lxd-2.7/dist/src/golang.org/x/net/http2/transport.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/transport.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/transport.go 2016-12-21 00:53:01.000000000 +0000 @@ -191,6 +191,7 @@ ID uint32 resc chan resAndError bufPipe pipe // buffered pipe with the flow-controlled response payload + startedWrite bool // started request body write; guarded by cc.mu requestedGzip bool on100 func() // optional code to run if get a 100 continue response @@ -314,6 +315,10 @@ if a, err := idna.ToASCII(host); err == nil { host = a } + // IPv6 address literal, without a port: + if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { + return host + ":" + port + } return net.JoinHostPort(host, port) } @@ -332,8 +337,10 @@ } traceGotConn(req, cc) res, err := cc.RoundTrip(req) - if shouldRetryRequest(req, err) { - continue + if err != nil { + if req, err = shouldRetryRequest(req, err); err == nil { + continue + } } if err != nil { t.vlogf("RoundTrip failure: %v", err) @@ -355,12 +362,41 @@ var ( errClientConnClosed = errors.New("http2: client conn is closed") errClientConnUnusable = errors.New("http2: client conn not usable") + + errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") + errClientConnGotGoAwayAfterSomeReqBody = errors.New("http2: Transport received Server's graceful shutdown GOAWAY; some request body already written") ) -func shouldRetryRequest(req *http.Request, err error) bool { - // TODO: retry GET requests (no bodies) more aggressively, if shutdown - // before response. - return err == errClientConnUnusable +// shouldRetryRequest is called by RoundTrip when a request fails to get +// response headers. It is always called with a non-nil error. +// It returns either a request to retry (either the same request, or a +// modified clone), or an error if the request can't be replayed. +func shouldRetryRequest(req *http.Request, err error) (*http.Request, error) { + switch err { + default: + return nil, err + case errClientConnUnusable, errClientConnGotGoAway: + return req, nil + case errClientConnGotGoAwayAfterSomeReqBody: + // If the Body is nil (or http.NoBody), it's safe to reuse + // this request and its Body. + if req.Body == nil || reqBodyIsNoBody(req.Body) { + return req, nil + } + // Otherwise we depend on the Request having its GetBody + // func defined. + getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody + if getBody == nil { + return nil, errors.New("http2: Transport: peer server initiated graceful shutdown after some of Request.Body was written; define Request.GetBody to avoid this error") + } + body, err := getBody() + if err != nil { + return nil, err + } + newReq := *req + newReq.Body = body + return &newReq, nil + } } func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, error) { @@ -513,6 +549,15 @@ if old != nil && old.ErrCode != ErrCodeNo { cc.goAway.ErrCode = old.ErrCode } + last := f.LastStreamID + for streamID, cs := range cc.streams { + if streamID > last { + select { + case cs.resc <- resAndError{err: errClientConnGotGoAway}: + default: + } + } + } } func (cc *ClientConn) CanTakeNewRequest() bool { @@ -773,6 +818,13 @@ cs.abortRequestBodyWrite(errStopReqBodyWrite) } if re.err != nil { + if re.err == errClientConnGotGoAway { + cc.mu.Lock() + if cs.startedWrite { + re.err = errClientConnGotGoAwayAfterSomeReqBody + } + cc.mu.Unlock() + } cc.forgetStreamID(cs.ID) return nil, re.err } @@ -2013,6 +2065,9 @@ resc := make(chan error, 1) s.resc = resc s.fn = func() { + cs.cc.mu.Lock() + cs.startedWrite = true + cs.cc.mu.Unlock() resc <- cs.writeRequestBody(body, cs.req.Body) } s.delay = t.expectContinueTimeout() diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/transport_test.go lxd-2.7/dist/src/golang.org/x/net/http2/transport_test.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/transport_test.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/transport_test.go 2016-12-21 00:53:01.000000000 +0000 @@ -2073,10 +2073,11 @@ // https://golang.org/issue/15930 func TestTransportFlowControl(t *testing.T) { - const ( - total = 100 << 20 // 100MB - bufLen = 1 << 16 - ) + const bufLen = 64 << 10 + var total int64 = 100 << 20 // 100MB + if testing.Short() { + total = 10 << 20 + } var wrote int64 // updated atomically st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { @@ -2745,3 +2746,171 @@ t.Errorf("Got = %q; want %q", slurp, msg) } } + +func TestTransportRetryAfterGOAWAY(t *testing.T) { + var dialer struct { + sync.Mutex + count int + } + ct1 := make(chan *clientTester) + ct2 := make(chan *clientTester) + + ln := newLocalListener(t) + defer ln.Close() + + tr := &Transport{ + TLSClientConfig: tlsConfigInsecure, + } + tr.DialTLS = func(network, addr string, cfg *tls.Config) (net.Conn, error) { + dialer.Lock() + defer dialer.Unlock() + dialer.count++ + if dialer.count == 3 { + return nil, errors.New("unexpected number of dials") + } + cc, err := net.Dial("tcp", ln.Addr().String()) + if err != nil { + return nil, fmt.Errorf("dial error: %v", err) + } + sc, err := ln.Accept() + if err != nil { + return nil, fmt.Errorf("accept error: %v", err) + } + ct := &clientTester{ + t: t, + tr: tr, + cc: cc, + sc: sc, + fr: NewFramer(sc, sc), + } + switch dialer.count { + case 1: + ct1 <- ct + case 2: + ct2 <- ct + } + return cc, nil + } + + errs := make(chan error, 3) + done := make(chan struct{}) + defer close(done) + + // Client. + go func() { + req, _ := http.NewRequest("GET", "https://dummy.tld/", nil) + res, err := tr.RoundTrip(req) + if res != nil { + res.Body.Close() + if got := res.Header.Get("Foo"); got != "bar" { + err = fmt.Errorf("foo header = %q; want bar", got) + } + } + if err != nil { + err = fmt.Errorf("RoundTrip: %v", err) + } + errs <- err + }() + + connToClose := make(chan io.Closer, 2) + + // Server for the first request. + go func() { + var ct *clientTester + select { + case ct = <-ct1: + case <-done: + return + } + + connToClose <- ct.cc + ct.greet() + hf, err := ct.firstHeaders() + if err != nil { + errs <- fmt.Errorf("server1 failed reading HEADERS: %v", err) + return + } + t.Logf("server1 got %v", hf) + if err := ct.fr.WriteGoAway(0 /*max id*/, ErrCodeNo, nil); err != nil { + errs <- fmt.Errorf("server1 failed writing GOAWAY: %v", err) + return + } + errs <- nil + }() + + // Server for the second request. + go func() { + var ct *clientTester + select { + case ct = <-ct2: + case <-done: + return + } + + connToClose <- ct.cc + ct.greet() + hf, err := ct.firstHeaders() + if err != nil { + errs <- fmt.Errorf("server2 failed reading HEADERS: %v", err) + return + } + t.Logf("server2 got %v", hf) + + var buf bytes.Buffer + enc := hpack.NewEncoder(&buf) + enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"}) + enc.WriteField(hpack.HeaderField{Name: "foo", Value: "bar"}) + err = ct.fr.WriteHeaders(HeadersFrameParam{ + StreamID: hf.StreamID, + EndHeaders: true, + EndStream: false, + BlockFragment: buf.Bytes(), + }) + if err != nil { + errs <- fmt.Errorf("server2 failed writing response HEADERS: %v", err) + } else { + errs <- nil + } + }() + + for k := 0; k < 3; k++ { + select { + case err := <-errs: + if err != nil { + t.Error(err) + } + case <-time.After(1 * time.Second): + t.Errorf("timed out") + } + } + + for { + select { + case c := <-connToClose: + c.Close() + default: + return + } + } +} + +func TestAuthorityAddr(t *testing.T) { + tests := []struct { + scheme, authority string + want string + }{ + {"http", "foo.com", "foo.com:80"}, + {"https", "foo.com", "foo.com:443"}, + {"https", "foo.com:1234", "foo.com:1234"}, + {"https", "1.2.3.4:1234", "1.2.3.4:1234"}, + {"https", "1.2.3.4", "1.2.3.4:443"}, + {"https", "[::1]:1234", "[::1]:1234"}, + {"https", "[::1]", "[::1]:443"}, + } + for _, tt := range tests { + got := authorityAddr(tt.scheme, tt.authority) + if got != tt.want { + t.Errorf("authorityAddr(%q, %q) = %q; want %q", tt.scheme, tt.authority, got, tt.want) + } + } +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/write.go lxd-2.7/dist/src/golang.org/x/net/http2/write.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/write.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/write.go 2016-12-21 00:53:01.000000000 +0000 @@ -45,9 +45,10 @@ HeaderEncoder() (*hpack.Encoder, *bytes.Buffer) } -// endsStream reports whether the given frame writer w will locally -// close the stream. -func endsStream(w writeFramer) bool { +// writeEndsStream reports whether w writes a frame that will transition +// the stream to a half-closed local state. This returns false for RST_STREAM, +// which closes the entire stream (not just the local half). +func writeEndsStream(w writeFramer) bool { switch v := w.(type) { case *writeData: return v.endStream @@ -57,7 +58,7 @@ // This can only happen if the caller reuses w after it's // been intentionally nil'ed out to prevent use. Keep this // here to catch future refactoring breaking it. - panic("endsStream called on nil writeFramer") + panic("writeEndsStream called on nil writeFramer") } return false } diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/http2/writesched.go lxd-2.7/dist/src/golang.org/x/net/http2/writesched.go --- lxd-2.6.2/dist/src/golang.org/x/net/http2/writesched.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/http2/writesched.go 2016-12-21 00:53:01.000000000 +0000 @@ -160,6 +160,20 @@ return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", wr.StreamID(), wr.done != nil, des) } +// replyToWriter sends err to wr.done and panics if the send must block +// This does nothing if wr.done is nil. +func (wr *FrameWriteRequest) replyToWriter(err error) { + if wr.done == nil { + return + } + select { + case wr.done <- err: + default: + panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wr.write)) + } + wr.write = nil // prevent use (assume it's tainted after wr.done send) +} + // writeQueue is used by implementations of WriteScheduler. type writeQueue struct { s []FrameWriteRequest diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/icmp/message.go lxd-2.7/dist/src/golang.org/x/net/icmp/message.go --- lxd-2.6.2/dist/src/golang.org/x/net/icmp/message.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/icmp/message.go 2016-12-21 00:53:01.000000000 +0000 @@ -24,6 +24,8 @@ "golang.org/x/net/ipv6" ) +// BUG(mikio): This package is not implemented on NaCl and Plan 9. + var ( errMessageTooShort = errors.New("message too short") errHeaderTooShort = errors.New("header too short") diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/doc.go lxd-2.7/dist/src/golang.org/x/net/ipv4/doc.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/doc.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/doc.go 2016-12-21 00:53:01.000000000 +0000 @@ -240,3 +240,5 @@ // In the fallback case, ExcludeSourceSpecificGroup and // IncludeSourceSpecificGroup may return an error. package ipv4 // import "golang.org/x/net/ipv4" + +// BUG(mikio): This package is not implemented on NaCl and Plan 9. diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/endpoint.go lxd-2.7/dist/src/golang.org/x/net/ipv4/endpoint.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/endpoint.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/endpoint.go 2016-12-21 00:53:01.000000000 +0000 @@ -12,6 +12,11 @@ "golang.org/x/net/internal/netreflect" ) +// BUG(mikio): On Windows, the JoinSourceSpecificGroup, +// LeaveSourceSpecificGroup, ExcludeSourceSpecificGroup and +// IncludeSourceSpecificGroup methods of PacketConn and RawConn are +// not implemented. + // A Conn represents a network endpoint that uses the IPv4 transport. // It is used to control basic IP-level socket options such as TOS and // TTL. diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/mocktransponder_test.go lxd-2.7/dist/src/golang.org/x/net/ipv4/mocktransponder_test.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/mocktransponder_test.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/mocktransponder_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ipv4_test - -import ( - "net" - "testing" -) - -func acceptor(t *testing.T, ln net.Listener, done chan<- bool) { - defer func() { done <- true }() - - c, err := ln.Accept() - if err != nil { - t.Error(err) - return - } - c.Close() -} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/packet.go lxd-2.7/dist/src/golang.org/x/net/ipv4/packet.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/packet.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/packet.go 2016-12-21 00:53:01.000000000 +0000 @@ -9,6 +9,9 @@ "syscall" ) +// BUG(mikio): On Windows, the ReadFrom and WriteTo methods of RawConn +// are not implemented. + // A packetHandler represents the IPv4 datagram handler. type packetHandler struct { c *net.IPConn diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/payload.go lxd-2.7/dist/src/golang.org/x/net/ipv4/payload.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/payload.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/payload.go 2016-12-21 00:53:01.000000000 +0000 @@ -6,6 +6,9 @@ import "net" +// BUG(mikio): On Windows, the ControlMessage for ReadFrom and WriteTo +// methods of PacketConn is not implemented. + // A payloadHandler represents the IPv4 datagram payload handler. type payloadHandler struct { net.PacketConn diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/sys_windows.go lxd-2.7/dist/src/golang.org/x/net/ipv4/sys_windows.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/sys_windows.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/sys_windows.go 2016-12-21 00:53:01.000000000 +0000 @@ -51,6 +51,7 @@ ssoMulticastTTL: {sysIP_MULTICAST_TTL, ssoTypeInt}, ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface}, ssoMulticastLoopback: {sysIP_MULTICAST_LOOP, ssoTypeInt}, + ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt}, ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq}, ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq}, } diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/unicastsockopt_test.go lxd-2.7/dist/src/golang.org/x/net/ipv4/unicastsockopt_test.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/unicastsockopt_test.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/unicastsockopt_test.go 2016-12-21 00:53:01.000000000 +0000 @@ -16,7 +16,7 @@ func TestConnUnicastSocketOptions(t *testing.T) { switch runtime.GOOS { - case "nacl", "plan9": + case "nacl", "plan9", "windows": t.Skipf("not supported on %s", runtime.GOOS) } ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) @@ -30,8 +30,15 @@ } defer ln.Close() - done := make(chan bool) - go acceptor(t, ln, done) + errc := make(chan error, 1) + go func() { + c, err := ln.Accept() + if err != nil { + errc <- err + return + } + errc <- c.Close() + }() c, err := net.Dial("tcp4", ln.Addr().String()) if err != nil { @@ -41,7 +48,9 @@ testUnicastSocketOptions(t, ipv4.NewConn(c)) - <-done + if err := <-errc; err != nil { + t.Errorf("server: %v", err) + } } var packetConnUnicastSocketOptionTests = []struct { @@ -53,7 +62,7 @@ func TestPacketConnUnicastSocketOptions(t *testing.T) { switch runtime.GOOS { - case "nacl", "plan9": + case "nacl", "plan9", "windows": t.Skipf("not supported on %s", runtime.GOOS) } ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) @@ -79,7 +88,7 @@ func TestRawConnUnicastSocketOptions(t *testing.T) { switch runtime.GOOS { - case "nacl", "plan9": + case "nacl", "plan9", "windows": t.Skipf("not supported on %s", runtime.GOOS) } if m, ok := nettest.SupportsRawIPSocket(); !ok { diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/zsys_linux_mips.go lxd-2.7/dist/src/golang.org/x/net/ipv4/zsys_linux_mips.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/zsys_linux_mips.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/zsys_linux_mips.go 2016-12-21 00:53:01.000000000 +0000 @@ -0,0 +1,146 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPFilter = 0x4 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv4/zsys_linux_mipsle.go lxd-2.7/dist/src/golang.org/x/net/ipv4/zsys_linux_mipsle.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv4/zsys_linux_mipsle.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv4/zsys_linux_mipsle.go 2016-12-21 00:53:01.000000000 +0000 @@ -0,0 +1,146 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPFilter = 0x4 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv6/dgramopt_posix.go lxd-2.7/dist/src/golang.org/x/net/ipv6/dgramopt_posix.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv6/dgramopt_posix.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv6/dgramopt_posix.go 2016-12-21 00:53:01.000000000 +0000 @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd windows solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows package ipv6 diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv6/doc.go lxd-2.7/dist/src/golang.org/x/net/ipv6/doc.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv6/doc.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv6/doc.go 2016-12-21 00:53:01.000000000 +0000 @@ -239,3 +239,5 @@ // In the fallback case, ExcludeSourceSpecificGroup and // IncludeSourceSpecificGroup may return an error. package ipv6 // import "golang.org/x/net/ipv6" + +// BUG(mikio): This package is not implemented on NaCl and Plan 9. diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv6/endpoint.go lxd-2.7/dist/src/golang.org/x/net/ipv6/endpoint.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv6/endpoint.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv6/endpoint.go 2016-12-21 00:53:01.000000000 +0000 @@ -12,6 +12,11 @@ "golang.org/x/net/internal/netreflect" ) +// BUG(mikio): On Windows, the JoinSourceSpecificGroup, +// LeaveSourceSpecificGroup, ExcludeSourceSpecificGroup and +// IncludeSourceSpecificGroup methods of PacketConn are not +// implemented. + // A Conn represents a network endpoint that uses IPv6 transport. // It allows to set basic IP-level socket options such as traffic // class and hop limit. diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv6/payload.go lxd-2.7/dist/src/golang.org/x/net/ipv6/payload.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv6/payload.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv6/payload.go 2016-12-21 00:53:01.000000000 +0000 @@ -6,6 +6,9 @@ import "net" +// BUG(mikio): On Windows, the ControlMessage for ReadFrom and WriteTo +// methods of PacketConn is not implemented. + // A payloadHandler represents the IPv6 datagram payload handler. type payloadHandler struct { net.PacketConn diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv6/unicastsockopt_test.go lxd-2.7/dist/src/golang.org/x/net/ipv6/unicastsockopt_test.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv6/unicastsockopt_test.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv6/unicastsockopt_test.go 2016-12-21 00:53:01.000000000 +0000 @@ -29,8 +29,15 @@ } defer ln.Close() - done := make(chan bool) - go acceptor(t, ln, done) + errc := make(chan error, 1) + go func() { + c, err := ln.Accept() + if err != nil { + errc <- err + return + } + errc <- c.Close() + }() c, err := net.Dial("tcp6", ln.Addr().String()) if err != nil { @@ -40,7 +47,9 @@ testUnicastSocketOptions(t, ipv6.NewConn(c)) - <-done + if err := <-errc; err != nil { + t.Errorf("server: %v", err) + } } var packetConnUnicastSocketOptionTests = []struct { diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv6/zsys_linux_mips.go lxd-2.7/dist/src/golang.org/x/net/ipv6/zsys_linux_mips.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv6/zsys_linux_mips.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv6/zsys_linux_mips.go 2016-12-21 00:53:01.000000000 +0000 @@ -0,0 +1,168 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/ipv6/zsys_linux_mipsle.go lxd-2.7/dist/src/golang.org/x/net/ipv6/zsys_linux_mipsle.go --- lxd-2.6.2/dist/src/golang.org/x/net/ipv6/zsys_linux_mipsle.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/ipv6/zsys_linux_mipsle.go 2016-12-21 00:53:01.000000000 +0000 @@ -0,0 +1,168 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/nettest/conntest.go lxd-2.7/dist/src/golang.org/x/net/nettest/conntest.go --- lxd-2.6.2/dist/src/golang.org/x/net/nettest/conntest.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/nettest/conntest.go 2016-12-21 00:53:01.000000000 +0000 @@ -0,0 +1,451 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package nettest provides utilities for network testing. +package nettest + +import ( + "bytes" + "encoding/binary" + "io" + "io/ioutil" + "math/rand" + "net" + "sync" + "testing" + "time" +) + +var ( + aLongTimeAgo = time.Unix(233431200, 0) + neverTimeout = time.Time{} +) + +// MakePipe creates a connection between two endpoints and returns the pair +// as c1 and c2, such that anything written to c1 is read by c2 and vice-versa. +// The stop function closes all resources, including c1, c2, and the underlying +// net.Listener (if there is one), and should not be nil. +type MakePipe func() (c1, c2 net.Conn, stop func(), err error) + +// TestConn tests that a net.Conn implementation properly satisfies the interface. +// The tests should not produce any false positives, but may experience +// false negatives. Thus, some issues may only be detected when the test is +// run multiple times. For maximal effectiveness, run the tests under the +// race detector. +func TestConn(t *testing.T, mp MakePipe) { + testConn(t, mp) +} + +type connTester func(t *testing.T, c1, c2 net.Conn) + +func timeoutWrapper(t *testing.T, mp MakePipe, f connTester) { + c1, c2, stop, err := mp() + if err != nil { + t.Fatalf("unable to make pipe: %v", err) + } + var once sync.Once + defer once.Do(func() { stop() }) + timer := time.AfterFunc(time.Minute, func() { + once.Do(func() { + t.Error("test timed out; terminating pipe") + stop() + }) + }) + defer timer.Stop() + f(t, c1, c2) +} + +// testBasicIO tests that the data sent on c1 is properly received on c2. +func testBasicIO(t *testing.T, c1, c2 net.Conn) { + want := make([]byte, 1<<20) + rand.New(rand.NewSource(0)).Read(want) + + dataCh := make(chan []byte) + go func() { + rd := bytes.NewReader(want) + if err := chunkedCopy(c1, rd); err != nil { + t.Errorf("unexpected c1.Write error: %v", err) + } + if err := c1.Close(); err != nil { + t.Errorf("unexpected c1.Close error: %v", err) + } + }() + + go func() { + wr := new(bytes.Buffer) + if err := chunkedCopy(wr, c2); err != nil { + t.Errorf("unexpected c2.Read error: %v", err) + } + if err := c2.Close(); err != nil { + t.Errorf("unexpected c2.Close error: %v", err) + } + dataCh <- wr.Bytes() + }() + + if got := <-dataCh; !bytes.Equal(got, want) { + t.Errorf("transmitted data differs") + } +} + +// testPingPong tests that the two endpoints can synchronously send data to +// each other in a typical request-response pattern. +func testPingPong(t *testing.T, c1, c2 net.Conn) { + var wg sync.WaitGroup + defer wg.Wait() + + pingPonger := func(c net.Conn) { + defer wg.Done() + buf := make([]byte, 8) + var prev uint64 + for { + if _, err := io.ReadFull(c, buf); err != nil { + if err == io.EOF { + break + } + t.Errorf("unexpected Read error: %v", err) + } + + v := binary.LittleEndian.Uint64(buf) + binary.LittleEndian.PutUint64(buf, v+1) + if prev != 0 && prev+2 != v { + t.Errorf("mismatching value: got %d, want %d", v, prev+2) + } + prev = v + if v == 1000 { + break + } + + if _, err := c.Write(buf); err != nil { + t.Errorf("unexpected Write error: %v", err) + break + } + } + if err := c.Close(); err != nil { + t.Errorf("unexpected Close error: %v", err) + } + } + + wg.Add(2) + go pingPonger(c1) + go pingPonger(c2) + + // Start off the chain reaction. + if _, err := c1.Write(make([]byte, 8)); err != nil { + t.Errorf("unexpected c1.Write error: %v", err) + } +} + +// testRacyRead tests that it is safe to mutate the input Read buffer +// immediately after cancelation has occurred. +func testRacyRead(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, rand.New(rand.NewSource(0))) + + var wg sync.WaitGroup + defer wg.Wait() + + c1.SetReadDeadline(time.Now().Add(time.Millisecond)) + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + b1 := make([]byte, 1024) + b2 := make([]byte, 1024) + for j := 0; j < 100; j++ { + _, err := c1.Read(b1) + copy(b1, b2) // Mutate b1 to trigger potential race + if err != nil { + checkForTimeoutError(t, err) + c1.SetReadDeadline(time.Now().Add(time.Millisecond)) + } + } + }() + } +} + +// testRacyWrite tests that it is safe to mutate the input Write buffer +// immediately after cancelation has occurred. +func testRacyWrite(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(ioutil.Discard, c2) + + var wg sync.WaitGroup + defer wg.Wait() + + c1.SetWriteDeadline(time.Now().Add(time.Millisecond)) + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + b1 := make([]byte, 1024) + b2 := make([]byte, 1024) + for j := 0; j < 100; j++ { + _, err := c1.Write(b1) + copy(b1, b2) // Mutate b1 to trigger potential race + if err != nil { + checkForTimeoutError(t, err) + c1.SetWriteDeadline(time.Now().Add(time.Millisecond)) + } + } + }() + } +} + +// testReadTimeout tests that Read timeouts do not affect Write. +func testReadTimeout(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(ioutil.Discard, c2) + + c1.SetReadDeadline(aLongTimeAgo) + _, err := c1.Read(make([]byte, 1024)) + checkForTimeoutError(t, err) + if _, err := c1.Write(make([]byte, 1024)); err != nil { + t.Errorf("unexpected Write error: %v", err) + } +} + +// testWriteTimeout tests that Write timeouts do not affect Read. +func testWriteTimeout(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, rand.New(rand.NewSource(0))) + + c1.SetWriteDeadline(aLongTimeAgo) + _, err := c1.Write(make([]byte, 1024)) + checkForTimeoutError(t, err) + if _, err := c1.Read(make([]byte, 1024)); err != nil { + t.Errorf("unexpected Read error: %v", err) + } +} + +// testPastTimeout tests that a deadline set in the past immediately times out +// Read and Write requests. +func testPastTimeout(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, c2) + + testRoundtrip(t, c1) + + c1.SetDeadline(aLongTimeAgo) + n, err := c1.Write(make([]byte, 1024)) + if n != 0 { + t.Errorf("unexpected Write count: got %d, want 0", n) + } + checkForTimeoutError(t, err) + n, err = c1.Read(make([]byte, 1024)) + if n != 0 { + t.Errorf("unexpected Read count: got %d, want 0", n) + } + checkForTimeoutError(t, err) + + testRoundtrip(t, c1) +} + +// testPresentTimeout tests that a deadline set while there are pending +// Read and Write operations immediately times out those operations. +func testPresentTimeout(t *testing.T, c1, c2 net.Conn) { + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(3) + + deadlineSet := make(chan bool, 1) + go func() { + defer wg.Done() + time.Sleep(100 * time.Millisecond) + deadlineSet <- true + c1.SetReadDeadline(aLongTimeAgo) + c1.SetWriteDeadline(aLongTimeAgo) + }() + go func() { + defer wg.Done() + n, err := c1.Read(make([]byte, 1024)) + if n != 0 { + t.Errorf("unexpected Read count: got %d, want 0", n) + } + checkForTimeoutError(t, err) + if len(deadlineSet) == 0 { + t.Error("Read timed out before deadline is set") + } + }() + go func() { + defer wg.Done() + var err error + for err == nil { + _, err = c1.Write(make([]byte, 1024)) + } + checkForTimeoutError(t, err) + if len(deadlineSet) == 0 { + t.Error("Write timed out before deadline is set") + } + }() +} + +// testFutureTimeout tests that a future deadline will eventually time out +// Read and Write operations. +func testFutureTimeout(t *testing.T, c1, c2 net.Conn) { + var wg sync.WaitGroup + wg.Add(2) + + c1.SetDeadline(time.Now().Add(100 * time.Millisecond)) + go func() { + defer wg.Done() + _, err := c1.Read(make([]byte, 1024)) + checkForTimeoutError(t, err) + }() + go func() { + defer wg.Done() + var err error + for err == nil { + _, err = c1.Write(make([]byte, 1024)) + } + checkForTimeoutError(t, err) + }() + wg.Wait() + + go chunkedCopy(c2, c2) + resyncConn(t, c1) + testRoundtrip(t, c1) +} + +// testCloseTimeout tests that calling Close immediately times out pending +// Read and Write operations. +func testCloseTimeout(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, c2) + + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(3) + + // Test for cancelation upon connection closure. + c1.SetDeadline(neverTimeout) + go func() { + defer wg.Done() + time.Sleep(100 * time.Millisecond) + c1.Close() + }() + go func() { + defer wg.Done() + var err error + buf := make([]byte, 1024) + for err == nil { + _, err = c1.Read(buf) + } + }() + go func() { + defer wg.Done() + var err error + buf := make([]byte, 1024) + for err == nil { + _, err = c1.Write(buf) + } + }() +} + +// testConcurrentMethods tests that the methods of net.Conn can safely +// be called concurrently. +func testConcurrentMethods(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, c2) + + // The results of the calls may be nonsensical, but this should + // not trigger a race detector warning. + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(7) + go func() { + defer wg.Done() + c1.Read(make([]byte, 1024)) + }() + go func() { + defer wg.Done() + c1.Write(make([]byte, 1024)) + }() + go func() { + defer wg.Done() + c1.SetDeadline(time.Now().Add(10 * time.Millisecond)) + }() + go func() { + defer wg.Done() + c1.SetReadDeadline(aLongTimeAgo) + }() + go func() { + defer wg.Done() + c1.SetWriteDeadline(aLongTimeAgo) + }() + go func() { + defer wg.Done() + c1.LocalAddr() + }() + go func() { + defer wg.Done() + c1.RemoteAddr() + }() + } + wg.Wait() // At worst, the deadline is set 10ms into the future + + resyncConn(t, c1) + testRoundtrip(t, c1) +} + +// checkForTimeoutError checks that the error satisfies the Error interface +// and that Timeout returns true. +func checkForTimeoutError(t *testing.T, err error) { + if nerr, ok := err.(net.Error); ok { + if !nerr.Timeout() { + t.Errorf("err.Timeout() = false, want true") + } + } else { + t.Errorf("got %T, want net.Error", err) + } +} + +// testRoundtrip writes something into c and reads it back. +// It assumes that everything written into c is echoed back to itself. +func testRoundtrip(t *testing.T, c net.Conn) { + if err := c.SetDeadline(neverTimeout); err != nil { + t.Errorf("roundtrip SetDeadline error: %v", err) + } + + const s = "Hello, world!" + buf := []byte(s) + if _, err := c.Write(buf); err != nil { + t.Errorf("roundtrip Write error: %v", err) + } + if _, err := io.ReadFull(c, buf); err != nil { + t.Errorf("roundtrip Read error: %v", err) + } + if string(buf) != s { + t.Errorf("roundtrip data mismatch: got %q, want %q", buf, s) + } +} + +// resyncConn resynchronizes the connection into a sane state. +// It assumes that everything written into c is echoed back to itself. +// It assumes that 0xff is not currently on the wire or in the read buffer. +func resyncConn(t *testing.T, c net.Conn) { + c.SetDeadline(neverTimeout) + errCh := make(chan error) + go func() { + _, err := c.Write([]byte{0xff}) + errCh <- err + }() + buf := make([]byte, 1024) + for { + n, err := c.Read(buf) + if n > 0 && bytes.IndexByte(buf[:n], 0xff) == n-1 { + break + } + if err != nil { + t.Errorf("unexpected Read error: %v", err) + } + } + if err := <-errCh; err != nil { + t.Errorf("unexpected Write error: %v", err) + } +} + +// chunkedCopy copies from r to w in fixed-width chunks to avoid +// causing a Write that exceeds the maximum packet size for packet-based +// connections like "unixpacket". +// We assume that the maximum packet size is at least 1024. +func chunkedCopy(w io.Writer, r io.Reader) error { + b := make([]byte, 1024) + _, err := io.CopyBuffer(struct{ io.Writer }{w}, struct{ io.Reader }{r}, b) + return err +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/nettest/conntest_go16.go lxd-2.7/dist/src/golang.org/x/net/nettest/conntest_go16.go --- lxd-2.6.2/dist/src/golang.org/x/net/nettest/conntest_go16.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/nettest/conntest_go16.go 2016-12-21 00:53:01.000000000 +0000 @@ -0,0 +1,24 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.7 + +package nettest + +import "testing" + +func testConn(t *testing.T, mp MakePipe) { + // Avoid using subtests on Go 1.6 and below. + timeoutWrapper(t, mp, testBasicIO) + timeoutWrapper(t, mp, testPingPong) + timeoutWrapper(t, mp, testRacyRead) + timeoutWrapper(t, mp, testRacyWrite) + timeoutWrapper(t, mp, testReadTimeout) + timeoutWrapper(t, mp, testWriteTimeout) + timeoutWrapper(t, mp, testPastTimeout) + timeoutWrapper(t, mp, testPresentTimeout) + timeoutWrapper(t, mp, testFutureTimeout) + timeoutWrapper(t, mp, testCloseTimeout) + timeoutWrapper(t, mp, testConcurrentMethods) +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/nettest/conntest_go17.go lxd-2.7/dist/src/golang.org/x/net/nettest/conntest_go17.go --- lxd-2.6.2/dist/src/golang.org/x/net/nettest/conntest_go17.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/nettest/conntest_go17.go 2016-12-21 00:53:01.000000000 +0000 @@ -0,0 +1,24 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.7 + +package nettest + +import "testing" + +func testConn(t *testing.T, mp MakePipe) { + // Use subtests on Go 1.7 and above since it is better organized. + t.Run("BasicIO", func(t *testing.T) { timeoutWrapper(t, mp, testBasicIO) }) + t.Run("PingPong", func(t *testing.T) { timeoutWrapper(t, mp, testPingPong) }) + t.Run("RacyRead", func(t *testing.T) { timeoutWrapper(t, mp, testRacyRead) }) + t.Run("RacyWrite", func(t *testing.T) { timeoutWrapper(t, mp, testRacyWrite) }) + t.Run("ReadTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testReadTimeout) }) + t.Run("WriteTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testWriteTimeout) }) + t.Run("PastTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testPastTimeout) }) + t.Run("PresentTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testPresentTimeout) }) + t.Run("FutureTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testFutureTimeout) }) + t.Run("CloseTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testCloseTimeout) }) + t.Run("ConcurrentMethods", func(t *testing.T) { timeoutWrapper(t, mp, testConcurrentMethods) }) +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/nettest/conntest_test.go lxd-2.7/dist/src/golang.org/x/net/nettest/conntest_test.go --- lxd-2.6.2/dist/src/golang.org/x/net/nettest/conntest_test.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/nettest/conntest_test.go 2016-12-21 00:53:01.000000000 +0000 @@ -0,0 +1,126 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.8 + +package nettest + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "runtime" + "testing" +) + +// testUnixAddr uses ioutil.TempFile to get a name that is unique. +// It also uses /tmp directory in case it is prohibited to create UNIX +// sockets in TMPDIR. +func testUnixAddr() string { + f, err := ioutil.TempFile("", "go-nettest") + if err != nil { + panic(err) + } + addr := f.Name() + f.Close() + os.Remove(addr) + return addr +} + +// testableNetwork reports whether network is testable on the current +// platform configuration. +// This is based on logic from standard library's net/platform_test.go. +func testableNetwork(network string) bool { + switch network { + case "unix": + switch runtime.GOOS { + case "android", "nacl", "plan9", "windows": + return false + } + if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { + return false + } + case "unixpacket": + switch runtime.GOOS { + case "android", "darwin", "nacl", "plan9", "windows", "freebsd": + return false + } + } + return true +} + +func newLocalListener(network string) (net.Listener, error) { + switch network { + case "tcp": + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + ln, err = net.Listen("tcp6", "[::1]:0") + } + return ln, err + case "unix", "unixpacket": + return net.Listen(network, testUnixAddr()) + } + return nil, fmt.Errorf("%s is not supported", network) +} + +func TestTestConn(t *testing.T) { + tests := []struct{ name, network string }{ + {"TCP", "tcp"}, + {"UnixPipe", "unix"}, + {"UnixPacketPipe", "unixpacket"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if !testableNetwork(tt.network) { + t.Skipf("not supported on %s", runtime.GOOS) + } + + mp := func() (c1, c2 net.Conn, stop func(), err error) { + ln, err := newLocalListener(tt.network) + if err != nil { + return nil, nil, nil, err + } + + // Start a connection between two endpoints. + var err1, err2 error + done := make(chan bool) + go func() { + c2, err2 = ln.Accept() + close(done) + }() + c1, err1 = net.Dial(ln.Addr().Network(), ln.Addr().String()) + <-done + + stop = func() { + if err1 == nil { + c1.Close() + } + if err2 == nil { + c2.Close() + } + ln.Close() + switch tt.network { + case "unix", "unixpacket": + os.Remove(ln.Addr().String()) + } + } + + switch { + case err1 != nil: + stop() + return nil, nil, nil, err1 + case err2 != nil: + stop() + return nil, nil, nil, err2 + default: + return c1, c2, stop, nil + } + } + + TestConn(t, mp) + }) + } +} diff -Nru lxd-2.6.2/dist/src/golang.org/x/net/websocket/websocket.go lxd-2.7/dist/src/golang.org/x/net/websocket/websocket.go --- lxd-2.6.2/dist/src/golang.org/x/net/websocket/websocket.go 2016-11-25 04:23:35.000000000 +0000 +++ lxd-2.7/dist/src/golang.org/x/net/websocket/websocket.go 2016-12-21 00:53:01.000000000 +0000 @@ -4,6 +4,12 @@ // Package websocket implements a client and server for the WebSocket protocol // as specified in RFC 6455. +// +// This package currently lacks some features found in an alternative +// and more actively maintained WebSocket package: +// +// https://godoc.org/github.com/gorilla/websocket +// package websocket // import "golang.org/x/net/websocket" import ( diff -Nru lxd-2.6.2/dist/src/gopkg.in/lxc/go-lxc.v2/container.go lxd-2.7/dist/src/gopkg.in/lxc/go-lxc.v2/container.go --- lxd-2.6.2/dist/src/gopkg.in/lxc/go-lxc.v2/container.go 2016-11-25 04:23:39.000000000 +0000 +++ lxd-2.7/dist/src/gopkg.in/lxc/go-lxc.v2/container.go 2016-12-21 00:53:05.000000000 +0000 @@ -1556,13 +1556,12 @@ return err } - if (cmd != MIGRATE_RESTORE) { + if cmd != MIGRATE_RESTORE { if err := c.makeSure(isRunning); err != nil { return err } } - cdirectory := C.CString(opts.Directory) defer C.free(unsafe.Pointer(cdirectory)) diff -Nru lxd-2.6.2/dist/src/gopkg.in/lxc/go-lxc.v2/lxc_test.go lxd-2.7/dist/src/gopkg.in/lxc/go-lxc.v2/lxc_test.go --- lxd-2.6.2/dist/src/gopkg.in/lxc/go-lxc.v2/lxc_test.go 2016-11-25 04:23:39.000000000 +0000 +++ lxd-2.7/dist/src/gopkg.in/lxc/go-lxc.v2/lxc_test.go 2016-12-21 00:53:05.000000000 +0000 @@ -14,6 +14,7 @@ "strconv" "strings" "sync" + "syscall" "testing" "time" ) @@ -974,12 +975,13 @@ t.FailNow() } - argsThree = []string{"/bin/sh", "-c", "exit 0"} + argsThree = []string{"/bin/sh", "-c", "exit 1"} pid, err = c.RunCommandNoWait(argsThree, DefaultAttachOptions) if err != nil { t.Errorf(err.Error()) t.FailNow() } + proc, err = os.FindProcess(pid) if err != nil { t.Errorf(err.Error()) @@ -991,6 +993,7 @@ t.Errorf(err.Error()) t.FailNow() } + if procState.Success() { t.Errorf("Expected failure") t.FailNow() @@ -1102,6 +1105,23 @@ } func TestCommandWithArch(t *testing.T) { + uname := syscall.Utsname{} + if err := syscall.Uname(&uname); err != nil { + t.Errorf(err.Error()) + } + + arch := "" + for _, c := range uname.Machine { + if c == 0 { + break + } + arch += string(byte(c)) + } + + if arch != "x86_64" && arch != "i686" { + t.Skip("skipping architecture test, not on x86") + } + c, err := NewContainer(ContainerName) if err != nil { t.Errorf(err.Error()) @@ -1222,7 +1242,9 @@ } func TestIPv6Addresses(t *testing.T) { - t.Skip("skipping test") + if !unprivileged() { + t.Skip("skipping test in privileged mode.") + } c, err := NewContainer(ContainerName) if err != nil { diff -Nru lxd-2.6.2/dist/src/gopkg.in/tomb.v2/context16.go lxd-2.7/dist/src/gopkg.in/tomb.v2/context16.go --- lxd-2.6.2/dist/src/gopkg.in/tomb.v2/context16.go 2016-11-25 04:23:40.000000000 +0000 +++ lxd-2.7/dist/src/gopkg.in/tomb.v2/context16.go 2016-12-21 00:53:06.000000000 +0000 @@ -42,10 +42,8 @@ if parent == nil { if t.parent == nil { t.parent = context.Background() - child, cancel := context.WithCancel(t.parent.(context.Context)) - t.addChild(t.parent.(context.Context), child, cancel) } - return t.child[t.parent].context.(context.Context) + parent = t.parent.(context.Context) } if child, ok := t.child[parent]; ok { diff -Nru lxd-2.6.2/dist/src/gopkg.in/tomb.v2/context16_test.go lxd-2.7/dist/src/gopkg.in/tomb.v2/context16_test.go --- lxd-2.6.2/dist/src/gopkg.in/tomb.v2/context16_test.go 2016-11-25 04:23:40.000000000 +0000 +++ lxd-2.7/dist/src/gopkg.in/tomb.v2/context16_test.go 2016-12-21 00:53:06.000000000 +0000 @@ -3,10 +3,11 @@ package tomb_test import ( - "context" "testing" "time" + "golang.org/x/net/context" + "gopkg.in/tomb.v2" ) @@ -97,4 +98,80 @@ case <-time.After(5 * time.Second): t.Fatalf("Child context should be born canceled") } + + childnil := tb.Context(nil) + select { + case <-childnil.Done(): + default: + t.Fatalf("Child context should be born canceled") + } +} + +func TestContextNoParent(t *testing.T) { + var tb tomb.Tomb + + parent2, cancel2 := context.WithCancel(context.WithValue(context.Background(), "parent", "parent2")) + child2 := tb.Context(parent2) + + if tb.Context(parent2) != child2 { + t.Fatalf("Context returned different context for same parent") + } + if child2.Value("parent") != "parent2" { + t.Fatalf("Child context didn't inherit its parent's properties") + } + select { + case <-child2.Done(): + t.Fatalf("Tomb's child context was born dead") + default: + } + + cancel2() + + select { + case <-child2.Done(): + default: + t.Fatalf("Tomb's child context didn't die after parent was canceled") + } + if !tb.Alive() { + t.Fatalf("Canceling unrelated parent context killed tomb") + } + + parent3 := context.WithValue(context.Background(), "parent", "parent3") + child3 := tb.Context(parent3) + + if child3.Value("parent") != "parent3" { + t.Fatalf("Child context didn't inherit its parent's properties") + } + select { + case <-child3.Done(): + t.Fatalf("Tomb's child context was born dead") + default: + } + + tb.Kill(nil) + + if tb.Context(parent3) == child3 { + t.Fatalf("Tomb is dead and shouldn't be tracking children anymore") + } + select { + case <-child3.Done(): + default: + t.Fatalf("Child context didn't die after tomb's death") + } + + parent4 := context.WithValue(context.Background(), "parent", "parent4") + child4 := tb.Context(parent4) + + select { + case <-child4.Done(): + default: + t.Fatalf("Child context should be born canceled") + } + + childnil := tb.Context(nil) + select { + case <-childnil.Done(): + default: + t.Fatalf("Child context should be born canceled") + } } diff -Nru lxd-2.6.2/dist/src/gopkg.in/tomb.v2/context.go lxd-2.7/dist/src/gopkg.in/tomb.v2/context.go --- lxd-2.6.2/dist/src/gopkg.in/tomb.v2/context.go 2016-11-25 04:23:40.000000000 +0000 +++ lxd-2.7/dist/src/gopkg.in/tomb.v2/context.go 2016-12-21 00:53:06.000000000 +0000 @@ -42,10 +42,8 @@ if parent == nil { if t.parent == nil { t.parent = context.Background() - child, cancel := context.WithCancel(t.parent.(context.Context)) - t.addChild(t.parent.(context.Context), child, cancel) } - return t.child[t.parent].context.(context.Context) + parent = t.parent.(context.Context) } if child, ok := t.child[parent]; ok { diff -Nru lxd-2.6.2/dist/src/gopkg.in/tomb.v2/context_test.go lxd-2.7/dist/src/gopkg.in/tomb.v2/context_test.go --- lxd-2.6.2/dist/src/gopkg.in/tomb.v2/context_test.go 2016-11-25 04:23:40.000000000 +0000 +++ lxd-2.7/dist/src/gopkg.in/tomb.v2/context_test.go 2016-12-21 00:53:06.000000000 +0000 @@ -97,4 +97,80 @@ default: t.Fatalf("Child context should be born canceled") } + + childnil := tb.Context(nil) + select { + case <-childnil.Done(): + default: + t.Fatalf("Child context should be born canceled") + } +} + +func TestContextNoParent(t *testing.T) { + var tb tomb.Tomb + + parent2, cancel2 := context.WithCancel(context.WithValue(context.Background(), "parent", "parent2")) + child2 := tb.Context(parent2) + + if tb.Context(parent2) != child2 { + t.Fatalf("Context returned different context for same parent") + } + if child2.Value("parent") != "parent2" { + t.Fatalf("Child context didn't inherit its parent's properties") + } + select { + case <-child2.Done(): + t.Fatalf("Tomb's child context was born dead") + default: + } + + cancel2() + + select { + case <-child2.Done(): + default: + t.Fatalf("Tomb's child context didn't die after parent was canceled") + } + if !tb.Alive() { + t.Fatalf("Canceling unrelated parent context killed tomb") + } + + parent3 := context.WithValue(context.Background(), "parent", "parent3") + child3 := tb.Context(parent3) + + if child3.Value("parent") != "parent3" { + t.Fatalf("Child context didn't inherit its parent's properties") + } + select { + case <-child3.Done(): + t.Fatalf("Tomb's child context was born dead") + default: + } + + tb.Kill(nil) + + if tb.Context(parent3) == child3 { + t.Fatalf("Tomb is dead and shouldn't be tracking children anymore") + } + select { + case <-child3.Done(): + default: + t.Fatalf("Child context didn't die after tomb's death") + } + + parent4 := context.WithValue(context.Background(), "parent", "parent4") + child4 := tb.Context(parent4) + + select { + case <-child4.Done(): + default: + t.Fatalf("Child context should be born canceled") + } + + childnil := tb.Context(nil) + select { + case <-childnil.Done(): + default: + t.Fatalf("Child context should be born canceled") + } } diff -Nru lxd-2.6.2/doc/api-extensions.md lxd-2.7/doc/api-extensions.md --- lxd-2.6.2/doc/api-extensions.md 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/doc/api-extensions.md 2016-12-21 00:52:23.000000000 +0000 @@ -164,3 +164,15 @@ ## id\_map Enables setting the `security.idmap.isolated` and `security.idmap.isolated`, `security.idmap.size`, and `raw.id_map` fields. + +## network\_firewall\_filtering +Add two new keys, "ipv4.firewall" and "ipv6.firewall" which if set to +false will turn off the generation of iptables FORWARDING rules. NAT +rules will still be added so long as the matching "ipv4.nat" or +"ipv6.nat" key is set to true. + +Rules necessary for dnsmasq to work (DHCP/DNS) will always be applied if +dnsmasq is enabled on the bridge. + +## network\_routes +Introduces "ipv4.routes" and "ipv6.routes" which allow routing additional subnets to a LXD bridge. diff -Nru lxd-2.6.2/doc/configuration.md lxd-2.7/doc/configuration.md --- lxd-2.6.2/doc/configuration.md 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/doc/configuration.md 2016-12-21 00:52:23.000000000 +0000 @@ -355,12 +355,16 @@ 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.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 ipv4.routing | boolean | ipv4 address | true | Whether to route traffic in and out of the bridge 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.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 +ipv6.routes | string | ipv6 address | - | Comma separated list of additional IPv6 CIDR subnets to route to the bridge ipv6.routing | boolean | ipv6 address | true | Whether to route traffic in and out of the bridge dns.domain | string | - | lxd | Domain to advertise to DHCP clients and use for DNS resolution dns.mode | string | - | managed | DNS registration mode ("none" for no DNS record, "managed" for LXD generated static records or "dynamic" for client generated records) diff -Nru lxd-2.6.2/.gitignore lxd-2.7/.gitignore --- lxd-2.6.2/.gitignore 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -*.swp -po/*.mo -po/*.po~ -lxd-*.tar.gz -.vagrant -test/deps/devlxd-client -*~ -tags - -# For Atom ctags -.tags -.tags1 diff -Nru lxd-2.6.2/lxc/action.go lxd-2.7/lxc/action.go --- lxd-2.6.2/lxc/action.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/action.go 2016-12-21 00:52:23.000000000 +0000 @@ -31,19 +31,19 @@ } return fmt.Sprintf(i18n.G( - `Changes state of one or more containers to %s. + `Change state of one or more containers to %s. -lxc %s [...]%s`), c.name, c.name, c.additionalHelp) +lxc %s [:] [[:]...]%s`), c.name, c.name, c.additionalHelp) } func (c *actionCmd) flags() { if c.hasTimeout { - gnuflag.IntVar(&c.timeout, "timeout", -1, i18n.G("Time to wait for the container before killing it.")) - gnuflag.BoolVar(&c.force, "f", false, i18n.G("Force the container to shutdown.")) - gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the container to shutdown.")) + gnuflag.IntVar(&c.timeout, "timeout", -1, i18n.G("Time to wait for the container before killing it")) + gnuflag.BoolVar(&c.force, "f", false, i18n.G("Force the container to shutdown")) + gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the container to shutdown")) } - gnuflag.BoolVar(&c.stateful, "stateful", false, i18n.G("Store the container state (only for stop).")) - gnuflag.BoolVar(&c.stateless, "stateless", false, i18n.G("Ignore the container state (only for start).")) + gnuflag.BoolVar(&c.stateful, "stateful", false, i18n.G("Store the container state (only for stop)")) + gnuflag.BoolVar(&c.stateless, "stateless", false, i18n.G("Ignore the container state (only for start)")) } func (c *actionCmd) run(config *lxd.Config, args []string) error { diff -Nru lxd-2.6.2/lxc/config.go lxd-2.7/lxc/config.go --- lxd-2.6.2/lxc/config.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/config.go 2016-12-21 00:52:23.000000000 +0000 @@ -29,7 +29,7 @@ } func (c *configCmd) flags() { - gnuflag.BoolVar(&c.expanded, "expanded", false, i18n.G("Whether to show the expanded configuration")) + gnuflag.BoolVar(&c.expanded, "expanded", false, i18n.G("Show the expanded configuration")) } func (c *configCmd) configEditHelp() string { @@ -57,33 +57,33 @@ return i18n.G( `Manage configuration. -lxc config device add <[remote:]container> [key=value]... Add a device to a container. -lxc config device get <[remote:]container> Get a device property. -lxc config device set <[remote:]container> Set a device property. -lxc config device unset <[remote:]container> Unset a device property. -lxc config device list <[remote:]container> List devices for container. -lxc config device show <[remote:]container> Show full device details for container. -lxc config device remove <[remote:]container> Remove device from container. - -lxc config get [remote:][container] Get container or server configuration key. -lxc config set [remote:][container] Set container or server configuration key. -lxc config unset [remote:][container] Unset container or server configuration key. -lxc config show [remote:][container] [--expanded] Show container or server configuration. -lxc config edit [remote:][container] Edit container or server configuration in external editor. +lxc config device add [:] [key=value...] Add a device to a container. +lxc config device get [:] Get a device property. +lxc config device set [:] Set a device property. +lxc config device unset [:] Unset a device property. +lxc config device list [:] List devices for container. +lxc config device show [:] Show full device details for container. +lxc config device remove [:] Remove device from container. + +lxc config get [:][container] Get container or server configuration key. +lxc config set [:][container] Set container or server configuration key. +lxc config unset [:][container] Unset container or server configuration key. +lxc config show [:][container] [--expanded] Show container or server configuration. +lxc config edit [:][container] Edit container or server configuration in external editor. Edit configuration, either by launching external editor or reading STDIN. Example: lxc config edit # launch editor - cat config.yaml | lxc config edit # read from config.yaml + cat config.yaml | lxc config edit # read from config.yaml -lxc config trust list [remote] List all trusted certs. -lxc config trust add [remote] Add certfile.crt to trusted hosts. -lxc config trust remove [remote] [hostname|fingerprint] Remove the cert from trusted hosts. +lxc config trust list [:] List all trusted certs. +lxc config trust add [:] Add certfile.crt to trusted hosts. +lxc config trust remove [:] [hostname|fingerprint] Remove the cert from trusted hosts. Examples: To mount host's /share/c1 onto /opt in the container: - lxc config device add [remote:]container1 disk source=/share/c1 path=opt + lxc config device add [:]container1 disk source=/share/c1 path=opt To set an lxc config value: - lxc config set [remote:] raw.lxc 'lxc.aa_allow_incomplete = 1' + lxc config set [:] raw.lxc 'lxc.aa_allow_incomplete = 1' To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the default): lxc config set core.https_address [::]:8443 diff -Nru lxd-2.6.2/lxc/copy.go lxd-2.7/lxc/copy.go --- lxd-2.6.2/lxc/copy.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/copy.go 2016-12-21 00:52:23.000000000 +0000 @@ -22,9 +22,9 @@ func (c *copyCmd) usage() string { return i18n.G( - `Copy containers within or in between lxd instances. + `Copy containers within or in between LXD instances. -lxc copy [remote:] [[remote:]] [--ephemeral|e] [--profile|-p ...] [--config|-c ...]`) +lxc copy [:][/] [[:]] [--ephemeral|e] [--profile|-p ...] [--config|-c ...]`) } func (c *copyCmd) flags() { diff -Nru lxd-2.6.2/lxc/delete.go lxd-2.7/lxc/delete.go --- lxd-2.6.2/lxc/delete.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/delete.go 2016-12-21 00:52:23.000000000 +0000 @@ -25,16 +25,16 @@ return i18n.G( `Delete containers or snapshots. -lxc delete [remote:][/] [remote:][[/]...] +lxc delete [:][/] [[:][/]...] Destroy containers or snapshots with any attached data (configuration, snapshots, ...).`) } func (c *deleteCmd) flags() { - gnuflag.BoolVar(&c.force, "f", false, i18n.G("Force the removal of stopped containers.")) - gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the removal of stopped containers.")) - gnuflag.BoolVar(&c.interactive, "i", false, i18n.G("Require user confirmation.")) - gnuflag.BoolVar(&c.interactive, "interactive", false, i18n.G("Require user confirmation.")) + gnuflag.BoolVar(&c.force, "f", false, i18n.G("Force the removal of stopped containers")) + gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the removal of stopped containers")) + gnuflag.BoolVar(&c.interactive, "i", false, i18n.G("Require user confirmation")) + gnuflag.BoolVar(&c.interactive, "interactive", false, i18n.G("Require user confirmation")) } func (c *deleteCmd) promptDelete(name string) error { diff -Nru lxd-2.6.2/lxc/exec.go lxd-2.7/lxc/exec.go --- lxd-2.6.2/lxc/exec.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/exec.go 2016-12-21 00:52:23.000000000 +0000 @@ -45,13 +45,13 @@ return i18n.G( `Execute the specified command in a container. -lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... [--] +lxc exec [:] [--mode=auto|interactive|non-interactive] [--env KEY=VALUE...] [--] Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored).`) } func (c *execCmd) flags() { - gnuflag.Var(&c.envArgs, "env", i18n.G("An environment variable of the form HOME=/home/foo")) + gnuflag.Var(&c.envArgs, "env", i18n.G("Environment variable to set (e.g. HOME=/home/foo)")) gnuflag.StringVar(&c.modeFlag, "mode", "auto", i18n.G("Override the terminal mode (auto, interactive or non-interactive)")) } diff -Nru lxd-2.6.2/lxc/file.go lxd-2.7/lxc/file.go --- lxd-2.6.2/lxc/file.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/file.go 2016-12-21 00:52:23.000000000 +0000 @@ -6,6 +6,7 @@ "io/ioutil" "os" "path" + "path/filepath" "strconv" "strings" "syscall" @@ -33,22 +34,20 @@ func (c *fileCmd) usage() string { return i18n.G( - `Manage files on a container. + `Manage files in a container. -lxc file pull [-r|--recursive] [...] -lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--mode=MODE] [...] -lxc file edit +lxc file pull [-r|--recursive] [:] [[:]...] +lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--mode=MODE] [...] [:] +lxc file edit [:]/ in the case of pull, in the case of push and in the case of edit are / Examples: - To push /etc/hosts into the container foo: - lxc file push /etc/hosts foo/etc/hosts + lxc file push /etc/hosts foo/etc/hosts To pull /etc/hosts from the container: - lxc file pull foo/etc/hosts . -`) + lxc file pull foo/etc/hosts .`) } func (c *fileCmd) flags() { @@ -79,7 +78,10 @@ // re-add leading / that got stripped by the SplitN targetPath := "/" + pathSpec[1] // clean various /./, /../, /////, etc. that users add (#2557) - targetPath = path.Clean(targetPath) + targetPath, err := filepath.Abs(targetPath) + if err != nil { + return fmt.Errorf(i18n.G("Could not sanitize path %s"), targetPath) + } // normalization may reveal that path is still a dir, e.g. /. if strings.HasSuffix(targetPath, "/") { targetIsDir = true diff -Nru lxd-2.6.2/lxc/finger.go lxd-2.7/lxc/finger.go --- lxd-2.6.2/lxc/finger.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/finger.go 2016-12-21 00:52:23.000000000 +0000 @@ -15,7 +15,7 @@ return i18n.G( `Check if the LXD instance is up. -lxc finger `) +lxc finger [:]`) } func (c *fingerCmd) flags() {} diff -Nru lxd-2.6.2/lxc/help.go lxd-2.7/lxc/help.go --- lxd-2.6.2/lxc/help.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/help.go 2016-12-21 00:52:23.000000000 +0000 @@ -23,9 +23,9 @@ func (c *helpCmd) usage() string { return i18n.G( - `Presents details on how to use LXD. + `Help page for the LXD client. -lxd help [--all]`) +lxc help [--all]`) } func (c *helpCmd) flags() { @@ -45,7 +45,7 @@ return nil } - fmt.Println(i18n.G("Usage: lxc [subcommand] [options]")) + fmt.Println(i18n.G("Usage: lxc [options]")) fmt.Println(i18n.G("Available commands:")) var names []string for name := range commands { @@ -61,14 +61,14 @@ if !c.showAll { fmt.Println() fmt.Println(i18n.G("Options:")) - fmt.Println(" --all " + i18n.G("Print less common commands.")) - fmt.Println(" --debug " + i18n.G("Print debug information.")) - fmt.Println(" --verbose " + i18n.G("Print verbose information.")) - fmt.Println(" --version " + i18n.G("Show client version.")) + fmt.Println(" --all " + i18n.G("Print less common commands")) + fmt.Println(" --debug " + i18n.G("Print debug information")) + fmt.Println(" --verbose " + i18n.G("Print verbose information")) + fmt.Println(" --version " + i18n.G("Show client version")) fmt.Println() fmt.Println(i18n.G("Environment:")) - fmt.Println(" LXD_CONF " + i18n.G("Path to an alternate client configuration directory.")) - fmt.Println(" LXD_DIR " + i18n.G("Path to an alternate server directory.")) + fmt.Println(" LXD_CONF " + i18n.G("Path to an alternate client configuration directory")) + fmt.Println(" LXD_DIR " + i18n.G("Path to an alternate server directory")) } return nil } diff -Nru lxd-2.6.2/lxc/image.go lxd-2.7/lxc/image.go --- lxd-2.6.2/lxc/image.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/image.go 2016-12-21 00:52:23.000000000 +0000 @@ -110,19 +110,19 @@ hash or alias name (if one is set). -lxc image import [rootfs tarball|URL] [remote:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--alias=ALIAS].. [prop=value] +lxc image import [|] [:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--alias=ALIAS...] [prop=value] Import an image tarball (or tarballs) into the LXD image store. -lxc image copy [remote:] : [--alias=ALIAS].. [--copy-aliases] [--public] [--auto-update] +lxc image copy [:] : [--alias=ALIAS...] [--copy-aliases] [--public] [--auto-update] Copy an image from one LXD daemon to another over the network. The auto-update flag instructs the server to keep this image up to date. It requires the source to be an alias and for it to be public. -lxc image delete [remote:] [remote:][...] +lxc image delete [:] [[:]...] Delete one or more images from the LXD image store. -lxc image export [remote:] [target] +lxc image export [:] [target] Export an image from the LXD image store into a distributable tarball. The output target is optional and defaults to the working directory. @@ -134,31 +134,30 @@ the appropriate extension will be appended to the provided file name based on the algorithm used to compress the image. -lxc image info [remote:] +lxc image info [:] Print everything LXD knows about a given image. -lxc image list [remote:] [filter] [--format table|json] +lxc image list [:] [filter] [--format table|json] List images in the LXD image store. Filters may be of the = form for property based filtering, or part of the image hash or part of the image alias name. -lxc image show [remote:] +lxc image show [:] Yaml output of the user modifiable properties of an image. -lxc image edit [remote:] +lxc image edit [:] Edit image, either by launching external editor or reading STDIN. Example: lxc image edit # launch editor cat image.yaml | lxc image edit # read from image.yaml -lxc image alias create [remote:] +lxc image alias create [:] Create a new alias for an existing image. -lxc image alias delete [remote:] +lxc image alias delete [:] Delete an alias. -lxc image alias list [remote:] [filter] - List the aliases. Filters may be part of the image hash or part of the image alias name. -`) +lxc image alias list [:] [filter] + List the aliases. Filters may be part of the image hash or part of the image alias name.`) } func (c *imageCmd) flags() { diff -Nru lxd-2.6.2/lxc/info.go lxd-2.7/lxc/info.go --- lxd-2.6.2/lxc/info.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/info.go 2016-12-21 00:52:23.000000000 +0000 @@ -26,10 +26,10 @@ `List information on LXD servers and containers. For a container: - lxc info [:]container [--show-log] + lxc info [] [--show-log] For a server: - lxc info [:]`) + lxc info []`) } func (c *infoCmd) flags() { diff -Nru lxd-2.6.2/lxc/init.go lxd-2.7/lxc/init.go --- lxd-2.6.2/lxc/init.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/init.go 2016-12-21 00:52:23.000000000 +0000 @@ -74,7 +74,7 @@ return i18n.G( `Initialize a container from a particular image. -lxc init [remote:] [remote:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ] +lxc init [:] [:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ] Initializes a container using the specified image and name. @@ -82,7 +82,7 @@ Specifying "-p" with no argument will result in no profile. Example: -lxc init ubuntu u1`) + lxc init ubuntu:16.04 u1`) } func (c *initCmd) is_ephem(s string) bool { diff -Nru lxd-2.6.2/lxc/launch.go lxd-2.7/lxc/launch.go --- lxd-2.6.2/lxc/launch.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/launch.go 2016-12-21 00:52:23.000000000 +0000 @@ -6,8 +6,8 @@ "github.com/lxc/lxd" "github.com/lxc/lxd/shared" - "github.com/lxc/lxd/shared/gnuflag" "github.com/lxc/lxd/shared/i18n" + "github.com/lxc/lxd/shared/version" ) type launchCmd struct { @@ -22,7 +22,7 @@ return i18n.G( `Launch a container from a particular image. -lxc launch [remote:] [remote:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ] +lxc launch [:] [:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ] Launches a container using the specified image and name. @@ -30,21 +30,12 @@ Specifying "-p" with no argument will result in no profile. Example: -lxc launch ubuntu:16.04 u1`) + lxc launch ubuntu:16.04 u1`) } func (c *launchCmd) flags() { c.init = initCmd{} - - c.init.massage_args() - gnuflag.Var(&c.init.confArgs, "config", i18n.G("Config key/value to apply to the new container")) - gnuflag.Var(&c.init.confArgs, "c", i18n.G("Config key/value to apply to the new container")) - gnuflag.Var(&c.init.profArgs, "profile", i18n.G("Profile to apply to the new container")) - gnuflag.Var(&c.init.profArgs, "p", i18n.G("Profile to apply to the new container")) - gnuflag.BoolVar(&c.init.ephem, "ephemeral", false, i18n.G("Ephemeral container")) - gnuflag.BoolVar(&c.init.ephem, "e", false, i18n.G("Ephemeral container")) - gnuflag.StringVar(&c.init.network, "network", "", i18n.G("Network name")) - gnuflag.StringVar(&c.init.network, "n", "", i18n.G("Network name")) + c.init.flags() } func (c *launchCmd) run(config *lxd.Config, args []string) error { @@ -117,9 +108,9 @@ return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server")) } - var version string + var restVersion string toScan := strings.Replace(containers[0], "/", " ", -1) - count, err := fmt.Sscanf(toScan, " %s containers %s", &version, &name) + count, err := fmt.Sscanf(toScan, " %s containers %s", &restVersion, &name) if err != nil { return err } @@ -128,7 +119,7 @@ return fmt.Errorf(i18n.G("bad number of things scanned from image, container or snapshot")) } - if version != shared.APIVersion { + if restVersion != version.APIVersion { return fmt.Errorf(i18n.G("got bad version")) } } diff -Nru lxd-2.6.2/lxc/list.go lxd-2.7/lxc/list.go --- lxd-2.6.2/lxc/list.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/list.go 2016-12-21 00:52:23.000000000 +0000 @@ -66,9 +66,9 @@ func (c *listCmd) usage() string { return i18n.G( - `Lists the available resources. + `Lists the containers. -lxc list [resource] [filters] [--format table|json] [-c columns] [--fast] +lxc list [:] [filters] [--format table|json] [-c ] [--fast] The filters are: * A single keyword like "web" which will list any container with a name starting by "web". @@ -113,8 +113,8 @@ Default column layout: ns46tS Fast column layout: nsacPt -Example: lxc list -c n,volatile.base_image:"BASE IMAGE":0,s46,volatile.eth0.hwaddr:MAC -`) +Example: + lxc list -c n,volatile.base_image:"BASE IMAGE":0,s46,volatile.eth0.hwaddr:MAC`) } func (c *listCmd) flags() { diff -Nru lxd-2.6.2/lxc/main.go lxd-2.7/lxc/main.go --- lxd-2.6.2/lxc/main.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/main.go 2016-12-21 00:52:23.000000000 +0000 @@ -37,10 +37,10 @@ } func run() error { - verbose := gnuflag.Bool("verbose", false, i18n.G("Enables verbose mode.")) - debug := gnuflag.Bool("debug", false, i18n.G("Enables debug mode.")) - forceLocal := gnuflag.Bool("force-local", false, i18n.G("Force using the local unix socket.")) - noAlias := gnuflag.Bool("no-alias", false, i18n.G("Ignore aliases when determining what command to run.")) + verbose := gnuflag.Bool("verbose", false, i18n.G("Enable verbose mode")) + debug := gnuflag.Bool("debug", false, i18n.G("Enable debug mode")) + forceLocal := gnuflag.Bool("force-local", false, i18n.G("Force using the local unix socket")) + noAlias := gnuflag.Bool("no-alias", false, i18n.G("Ignore aliases when determining what command to run")) configDir := "$HOME/.config/lxc" if os.Getenv("LXD_CONF") != "" { diff -Nru lxd-2.6.2/lxc/manpage.go lxd-2.7/lxc/manpage.go --- lxd-2.6.2/lxc/manpage.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/manpage.go 2016-12-21 00:52:23.000000000 +0000 @@ -16,7 +16,7 @@ func (c *manpageCmd) usage() string { return i18n.G( - `Prints all the subcommands help.`) + `Print all the subcommands help.`) } func (c *manpageCmd) flags() { diff -Nru lxd-2.6.2/lxc/monitor.go lxd-2.7/lxc/monitor.go --- lxd-2.6.2/lxc/monitor.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/monitor.go 2016-12-21 00:52:23.000000000 +0000 @@ -41,7 +41,7 @@ return i18n.G( `Monitor activity on the LXD server. -lxc monitor [remote:] [--type=TYPE...] +lxc monitor [:] [--type=TYPE...] Connects to the monitoring interface of the specified LXD server. @@ -49,7 +49,7 @@ Specific types to listen to can be specified with --type. Example: -lxc monitor --type=logging`) + lxc monitor --type=logging`) } func (c *monitorCmd) flags() { diff -Nru lxd-2.6.2/lxc/move.go lxd-2.7/lxc/move.go --- lxd-2.6.2/lxc/move.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/move.go 2016-12-21 00:52:23.000000000 +0000 @@ -16,12 +16,14 @@ return i18n.G( `Move containers within or in between lxd instances. -lxc move [remote:] [remote:] +lxc move [:] [:][] Move a container between two hosts, renaming it if destination name differs. lxc move Rename a local container. -`) + +lxc move / / + Rename a snapshot.`) } func (c *moveCmd) flags() {} diff -Nru lxd-2.6.2/lxc/network.go lxd-2.7/lxc/network.go --- lxd-2.6.2/lxc/network.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/network.go 2016-12-21 00:52:23.000000000 +0000 @@ -48,24 +48,23 @@ return i18n.G( `Manage networks. -lxc network list List available networks. -lxc network show Show details of a network. -lxc network create [key=value]... Create a network. -lxc network get Get network configuration. -lxc network set Set network configuration. -lxc network unset Unset network configuration. -lxc network delete Delete a network. -lxc network edit +lxc network list [:] List available networks. +lxc network show [:] Show details of a network. +lxc network create [:] [key=value...] Create a network. +lxc network get [:] Get network configuration. +lxc network set [:] Set network configuration. +lxc network unset [:] Unset network configuration. +lxc network delete [:] Delete a network. +lxc network edit [:] Edit network, either by launching external editor or reading STDIN. Example: lxc network edit # launch editor cat network.yaml | lxc network edit # read from network.yaml -lxc network attach [device name] -lxc network attach-profile [device name] +lxc network attach [:] [device name] +lxc network attach-profile [:] [device name] -lxc network detach [device name] -lxc network detach-profile [device name] -`) +lxc network detach [:] [device name] +lxc network detach-profile [:] [device name]`) } func (c *networkCmd) flags() {} diff -Nru lxd-2.6.2/lxc/profile.go lxd-2.7/lxc/profile.go --- lxd-2.6.2/lxc/profile.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/profile.go 2016-12-21 00:52:23.000000000 +0000 @@ -48,20 +48,20 @@ return i18n.G( `Manage configuration profiles. -lxc profile list [filters] List available profiles. -lxc profile show Show details of a profile. -lxc profile create Create a profile. -lxc profile copy Copy the profile to the specified remote. -lxc profile get Get profile configuration. -lxc profile set Set profile configuration. -lxc profile unset Unset profile configuration. -lxc profile delete Delete a profile. -lxc profile edit +lxc profile list [:] List available profiles. +lxc profile show [:] Show details of a profile. +lxc profile create [:] Create a profile. +lxc profile copy [:] [:] Copy the profile. +lxc profile get [:] Get profile configuration. +lxc profile set [:] Set profile configuration. +lxc profile unset [:] Unset profile configuration. +lxc profile delete [:] Delete a profile. +lxc profile edit [:] Edit profile, either by launching external editor or reading STDIN. Example: lxc profile edit # launch editor cat profile.yaml | lxc profile edit # read from profile.yaml -lxc profile assign +lxc profile assign [:] Assign a comma-separated list of profiles to a container, in order. All profiles passed in this call (and only those) will be applied to the specified container, i.e. it sets the list of profiles exactly to @@ -71,19 +71,18 @@ lxc profile assign foo default # Only default is active lxc profile assign '' # no profiles are applied anymore lxc profile assign bar,default # Apply default second now -lxc profile add # add a profile to a container -lxc profile remove # remove the profile from a container +lxc profile add [:] # add a profile to a container +lxc profile remove [:] # remove the profile from a container Devices: -lxc profile device list List devices in the given profile. -lxc profile device show Show full device details in the given profile. -lxc profile device remove Remove a device from a profile. -lxc profile device get <[remote:]profile> Get a device property. -lxc profile device set <[remote:]profile> Set a device property. -lxc profile device unset <[remote:]profile> Unset a device property. -lxc profile device add [key=value]... - Add a profile device, such as a disk or a nic, to the containers - using the specified profile.`) +lxc profile device list [:] List devices in the given profile. +lxc profile device show [:] Show full device details in the given profile. +lxc profile device remove [:] Remove a device from a profile. +lxc profile device get [:] Get a device property. +lxc profile device set [:] Set a device property. +lxc profile device unset [:] Unset a device property. +lxc profile device add [:] [key=value...] + Add a profile device, such as a disk or a nic, to the containers using the specified profile.`) } func (c *profileCmd) flags() {} diff -Nru lxd-2.6.2/lxc/publish.go lxd-2.7/lxc/publish.go --- lxd-2.6.2/lxc/publish.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/publish.go 2016-12-21 00:52:23.000000000 +0000 @@ -26,7 +26,7 @@ return i18n.G( `Publish containers as images. -lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-value]...`) +lxc publish [:][/] [:] [--alias=ALIAS...] [prop-key=prop-value...]`) } func (c *publishCmd) flags() { diff -Nru lxd-2.6.2/lxc/remote.go lxd-2.7/lxc/remote.go --- lxd-2.6.2/lxc/remote.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/remote.go 2016-12-21 00:52:23.000000000 +0000 @@ -1,7 +1,6 @@ package main import ( - "crypto/sha256" "crypto/x509" "encoding/pem" "fmt" @@ -38,13 +37,13 @@ return i18n.G( `Manage remote LXD servers. -lxc remote add [] [--accept-certificate] [--password=PASSWORD] - [--public] [--protocol=PROTOCOL] Add the remote at . -lxc remote remove Remove the remote . +lxc remote add [] [--accept-certificate] [--password=PASSWORD] + [--public] [--protocol=PROTOCOL] Add the remote at . +lxc remote remove Remove the remote . lxc remote list List all remotes. -lxc remote rename Rename remote to . -lxc remote set-url Update 's url to . -lxc remote set-default Set the default remote. +lxc remote rename Rename remote to . +lxc remote set-url Update 's url to . +lxc remote set-default Set the default remote. lxc remote get-default Print the default remote.`) } @@ -213,7 +212,7 @@ if certificate != nil { if !acceptCert { - digest := sha256.Sum256(certificate.Raw) + digest := shared.CertFingerprint(certificate) fmt.Printf(i18n.G("Certificate fingerprint: %x")+"\n", digest) fmt.Printf(i18n.G("ok (y/n)?") + " ") diff -Nru lxd-2.6.2/lxc/restore.go lxd-2.7/lxc/restore.go --- lxd-2.6.2/lxc/restore.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/restore.go 2016-12-21 00:52:23.000000000 +0000 @@ -19,16 +19,19 @@ func (c *restoreCmd) usage() string { return i18n.G( - `Set the current state of a container back to a snapshot. + `Restore a container's state to a previous snapshot. -lxc restore [remote:] [--stateful] +lxc restore [:] [--stateful] Restores a container from a snapshot (optionally with running state, see snapshot help for details). -For example: -lxc snapshot u1 snap0 # create the snapshot -lxc restore u1 snap0 # restore the snapshot`) +Examples: +Create the snapshot: + lxc snapshot u1 snap0 + +Restore the snapshot: + lxc restore u1 snap0`) } func (c *restoreCmd) flags() { diff -Nru lxd-2.6.2/lxc/snapshot.go lxd-2.7/lxc/snapshot.go --- lxd-2.6.2/lxc/snapshot.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/snapshot.go 2016-12-21 00:52:23.000000000 +0000 @@ -21,7 +21,7 @@ return i18n.G( `Create a read-only snapshot of a container. -lxc snapshot [remote:] [--stateful] +lxc snapshot [:] [--stateful] Creates a snapshot of the container (optionally with the container's memory state). When --stateful is used, LXD attempts to checkpoint the container's @@ -31,7 +31,7 @@ successfully). Example: -lxc snapshot u1 snap0`) + lxc snapshot u1 snap0`) } func (c *snapshotCmd) flags() { diff -Nru lxd-2.6.2/lxc/version.go lxd-2.7/lxc/version.go --- lxd-2.6.2/lxc/version.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxc/version.go 2016-12-21 00:52:23.000000000 +0000 @@ -4,8 +4,8 @@ "fmt" "github.com/lxc/lxd" - "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/i18n" + "github.com/lxc/lxd/shared/version" ) type versionCmd struct{} @@ -28,6 +28,6 @@ if len(args) > 0 { return errArgs } - fmt.Println(shared.Version) + fmt.Println(version.Version) return nil } diff -Nru lxd-2.6.2/lxd/api_1.0.go lxd-2.7/lxd/api_1.0.go --- lxd-2.6.2/lxd/api_1.0.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/api_1.0.go 2016-12-21 00:52:23.000000000 +0000 @@ -11,6 +11,8 @@ "gopkg.in/lxc/go-lxc.v2" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" + "github.com/lxc/lxd/shared/version" ) var api10 = []Command{ @@ -79,10 +81,12 @@ "container_image_properties", "migration_progress", "id_map", + "network_firewall_filtering", + "network_routes", }, "api_status": "stable", - "api_version": shared.APIVersion, + "api_version": version.APIVersion, } if d.isTrustedClient(r) { @@ -137,7 +141,7 @@ architectures := []string{} for _, architecture := range d.architectures { - architectureName, err := shared.ArchitectureName(architecture) + architectureName, err := osarch.ArchitectureName(architecture) if err != nil { return InternalError(err) } @@ -157,7 +161,7 @@ "storage_version": d.Storage.GetStorageTypeVersion(), "server": "lxd", "server_pid": os.Getpid(), - "server_version": shared.Version} + "server_version": version.Version} body["environment"] = env body["public"] = false diff -Nru lxd-2.6.2/lxd/api_internal.go lxd-2.7/lxd/api_internal.go --- lxd-2.6.2/lxd/api_internal.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/api_internal.go 2016-12-21 00:52:23.000000000 +0000 @@ -2,12 +2,16 @@ import ( "fmt" + "io/ioutil" "net/http" "strconv" + "strings" "github.com/gorilla/mux" + "gopkg.in/yaml.v2" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" log "gopkg.in/inconshreveable/log15.v2" ) @@ -17,6 +21,7 @@ internalShutdownCmd, internalContainerOnStartCmd, internalContainerOnStopCmd, + internalContainersCmd, } func internalReady(d *Daemon, r *http.Request) Response { @@ -95,3 +100,101 @@ var internalReadyCmd = Command{name: "ready", put: internalReady, get: internalWaitReady} var internalContainerOnStartCmd = Command{name: "containers/{id}/onstart", get: internalContainerOnStart} var internalContainerOnStopCmd = Command{name: "containers/{id}/onstop", get: internalContainerOnStop} + +func slurpBackupFile(path string) (*backupFile, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + sf := backupFile{} + + if err := yaml.Unmarshal(data, &sf); err != nil { + return nil, err + } + + return &sf, nil +} + +func internalImport(d *Daemon, r *http.Request) Response { + name := r.FormValue("target") + if name == "" { + return BadRequest(fmt.Errorf("target is required")) + } + + path := containerPath(name, false) + err := d.Storage.ContainerStart(name, path) + if err != nil { + return SmartError(err) + } + + defer d.Storage.ContainerStop(name, path) + + sf, err := slurpBackupFile(shared.VarPath("containers", name, "backup.yaml")) + if err != nil { + return SmartError(err) + } + + baseImage := sf.Container.Config["volatile.base_image"] + for k := range sf.Container.Config { + if strings.HasPrefix(k, "volatile") { + delete(sf.Container.Config, k) + } + } + + arch, err := osarch.ArchitectureId(sf.Container.Architecture) + if err != nil { + return SmartError(err) + } + _, err = containerCreateInternal(d, containerArgs{ + Architecture: arch, + BaseImage: baseImage, + Config: sf.Container.Config, + CreationDate: sf.Container.CreationDate, + LastUsedDate: sf.Container.LastUsedDate, + Ctype: cTypeRegular, + Devices: sf.Container.Devices, + Ephemeral: sf.Container.Ephemeral, + Name: sf.Container.Name, + Profiles: sf.Container.Profiles, + Stateful: sf.Container.Stateful, + }) + if err != nil { + return SmartError(err) + } + + for _, snap := range sf.Snapshots { + baseImage := snap.Config["volatile.base_image"] + for k := range snap.Config { + if strings.HasPrefix(k, "volatile") { + delete(snap.Config, k) + } + } + + arch, err := osarch.ArchitectureId(snap.Architecture) + if err != nil { + return SmartError(err) + } + + _, err = containerCreateInternal(d, containerArgs{ + Architecture: arch, + BaseImage: baseImage, + Config: snap.Config, + CreationDate: snap.CreationDate, + LastUsedDate: snap.LastUsedDate, + Ctype: cTypeSnapshot, + Devices: snap.Devices, + Ephemeral: snap.Ephemeral, + Name: snap.Name, + Profiles: snap.Profiles, + Stateful: snap.Stateful, + }) + if err != nil { + return SmartError(err) + } + } + + return EmptySyncResponse +} + +var internalContainersCmd = Command{name: "containers", post: internalImport} diff -Nru lxd-2.6.2/lxd/certificates.go lxd-2.7/lxd/certificates.go --- lxd-2.6.2/lxd/certificates.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/certificates.go 2016-12-21 00:52:23.000000000 +0000 @@ -1,7 +1,6 @@ package main import ( - "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" @@ -13,12 +12,9 @@ "github.com/gorilla/mux" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" ) -func certGenerateFingerprint(cert *x509.Certificate) string { - return fmt.Sprintf("%x", sha256.Sum256(cert.Raw)) -} - func certificatesGet(d *Daemon, r *http.Request) Response { recursion := d.isRecursionRequest(r) @@ -45,7 +41,7 @@ body := []string{} for _, cert := range d.clientCerts { - fingerprint := fmt.Sprintf("/%s/certificates/%s", shared.APIVersion, certGenerateFingerprint(&cert)) + fingerprint := fmt.Sprintf("/%s/certificates/%s", version.APIVersion, shared.CertFingerprint(&cert)) body = append(body, fingerprint) } @@ -86,7 +82,7 @@ func saveCert(d *Daemon, host string, cert *x509.Certificate) error { baseCert := new(dbCertInfo) - baseCert.Fingerprint = certGenerateFingerprint(cert) + baseCert.Fingerprint = shared.CertFingerprint(cert) baseCert.Type = 1 baseCert.Name = host baseCert.Certificate = string( @@ -142,9 +138,9 @@ return BadRequest(fmt.Errorf("Can't use TLS data on non-TLS link")) } - fingerprint := certGenerateFingerprint(cert) + fingerprint := shared.CertFingerprint(cert) for _, existingCert := range d.clientCerts { - if fingerprint == certGenerateFingerprint(&existingCert) { + if fingerprint == shared.CertFingerprint(&existingCert) { return BadRequest(fmt.Errorf("Certificate already in trust store")) } } @@ -156,7 +152,7 @@ d.clientCerts = append(d.clientCerts, *cert) - return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/certificates/%s", shared.APIVersion, fingerprint)) + return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/certificates/%s", version.APIVersion, fingerprint)) } var certificatesCmd = Command{name: "certificates", untrustedPost: true, get: certificatesGet, post: certificatesPost} diff -Nru lxd-2.6.2/lxd/container_exec.go lxd-2.7/lxd/container_exec.go --- lxd-2.6.2/lxd/container_exec.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/container_exec.go 2016-12-21 00:52:23.000000000 +0000 @@ -16,6 +16,7 @@ "github.com/gorilla/websocket" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" log "gopkg.in/inconshreveable/log15.v2" ) @@ -140,13 +141,14 @@ } controlExit := make(chan bool) - receivePid := make(chan int) + attachedChildIsBorn := make(chan int) + attachedChildIsDead := make(chan bool, 1) var wgEOF sync.WaitGroup if s.interactive { wgEOF.Add(1) go func() { - receivedPid := <-receivePid + attachedChildPid := <-attachedChildIsBorn select { case <-s.controlConnected: break @@ -198,21 +200,23 @@ continue } } else if command.Command == "signal" { - if err := syscall.Kill(receivedPid, command.Signal); err != nil { - shared.LogDebugf("Failed forwarding signal '%s' to PID %d.", command.Signal, receivedPid) + if err := syscall.Kill(attachedChildPid, command.Signal); err != nil { + shared.LogDebugf("Failed forwarding signal '%s' to PID %d.", command.Signal, attachedChildPid) continue } - shared.LogDebugf("Forwarded signal '%s' to PID %d.", command.Signal, receivedPid) + shared.LogDebugf("Forwarded signal '%s' to PID %d.", command.Signal, attachedChildPid) } } }() + go func() { - readDone, writeDone := shared.WebsocketMirror(s.conns[0], ptys[0], ptys[0]) + readDone, writeDone := shared.WebsocketExecMirror(s.conns[0], ptys[0], ptys[0], attachedChildIsDead, int(ptys[0].Fd())) <-readDone <-writeDone s.conns[0].Close() wgEOF.Done() }() + } else { wgEOF.Add(len(ttys) - 1) for i := 0; i < len(ttys); i++ { @@ -242,6 +246,8 @@ s.conns[-1].Close() } + attachedChildIsDead <- true + wgEOF.Wait() for _, pty := range ptys { @@ -263,7 +269,7 @@ } if s.interactive { - receivePid <- attachedPid + attachedChildIsBorn <- attachedPid } proc, err := os.FindProcess(pid) @@ -411,8 +417,8 @@ // Update metadata with the right URLs metadata["return"] = cmdResult metadata["output"] = shared.Jmap{ - "1": fmt.Sprintf("/%s/containers/%s/logs/%s", shared.APIVersion, c.Name(), filepath.Base(stdout.Name())), - "2": fmt.Sprintf("/%s/containers/%s/logs/%s", shared.APIVersion, c.Name(), filepath.Base(stderr.Name())), + "1": fmt.Sprintf("/%s/containers/%s/logs/%s", version.APIVersion, c.Name(), filepath.Base(stdout.Name())), + "2": fmt.Sprintf("/%s/containers/%s/logs/%s", version.APIVersion, c.Name(), filepath.Base(stderr.Name())), } } else { cmdResult, _, cmdErr = c.Exec(post.Command, env, nil, nil, nil, true) diff -Nru lxd-2.6.2/lxd/container_file.go lxd-2.7/lxd/container_file.go --- lxd-2.6.2/lxd/container_file.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/container_file.go 2016-12-21 00:52:23.000000000 +0000 @@ -52,6 +52,7 @@ // Pull the file from the container uid, gid, mode, type_, dirEnts, err := c.FilePull(path, temp.Name()) if err != nil { + os.Remove(temp.Name()) return SmartError(err) } @@ -71,8 +72,10 @@ return FileResponse(r, files, headers, true) } else if type_ == "directory" { + os.Remove(temp.Name()) return SyncResponseHeaders(true, dirEnts, headers) } else { + os.Remove(temp.Name()) return InternalError(fmt.Errorf("bad file type %s", type_)) } } diff -Nru lxd-2.6.2/lxd/container.go lxd-2.7/lxd/container.go --- lxd-2.6.2/lxd/container.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/container.go 2016-12-21 00:52:23.000000000 +0000 @@ -10,6 +10,7 @@ "gopkg.in/lxc/go-lxc.v2" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" ) // Helper functions @@ -48,9 +49,9 @@ } if key == "security.syscalls.blacklist_compat" { for _, arch := range d.architectures { - if arch == shared.ARCH_64BIT_INTEL_X86 || - arch == shared.ARCH_64BIT_ARMV8_LITTLE_ENDIAN || - arch == shared.ARCH_64BIT_POWERPC_BIG_ENDIAN { + if arch == osarch.ARCH_64BIT_INTEL_X86 || + arch == osarch.ARCH_64BIT_ARMV8_LITTLE_ENDIAN || + arch == osarch.ARCH_64BIT_POWERPC_BIG_ENDIAN { return nil } } @@ -566,6 +567,12 @@ return nil, err } + err = writeBackupFile(sourceContainer) + if err != nil { + c.Delete() + return nil, err + } + // Once we're done, remove the state directory if args.Stateful { os.RemoveAll(sourceContainer.StatePath()) @@ -617,7 +624,7 @@ } // Validate architecture - _, err = shared.ArchitectureName(args.Architecture) + _, err = osarch.ArchitectureName(args.Architecture) if err != nil { return nil, err } @@ -634,22 +641,22 @@ } } - path := containerPath(args.Name, args.Ctype == cTypeSnapshot) - if shared.PathExists(path) { - if shared.IsSnapshot(args.Name) { - return nil, fmt.Errorf("Snapshot '%s' already exists", args.Name) + // Create the container entry + id, err := dbContainerCreate(d.db, args) + if err != nil { + if err == DbErrAlreadyDefined { + thing := "Container" + if shared.IsSnapshot(args.Name) { + thing = "Snapshot" + } + return nil, fmt.Errorf("%s '%s' already exists", thing, args.Name) } - return nil, fmt.Errorf("The container already exists") + return nil, err } // Wipe any existing log for this container name os.RemoveAll(shared.LogPath(args.Name)) - // Create the container entry - id, err := dbContainerCreate(d.db, args) - if err != nil { - return nil, err - } args.Id = id // Read the timestamp from the database @@ -660,7 +667,12 @@ args.CreationDate = dbArgs.CreationDate args.LastUsedDate = dbArgs.LastUsedDate - return containerLXCCreate(d, args) + c, err := containerLXCCreate(d, args) + if err != nil { + return nil, err + } + + return c, nil } func containerConfigureInternal(c container) error { @@ -683,6 +695,11 @@ break } + err := writeBackupFile(c) + if err != nil { + return err + } + return nil } diff -Nru lxd-2.6.2/lxd/container_logs.go lxd-2.7/lxd/container_logs.go --- lxd-2.6.2/lxd/container_logs.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/container_logs.go 2016-12-21 00:52:23.000000000 +0000 @@ -10,6 +10,7 @@ "github.com/gorilla/mux" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" ) func containerLogsGet(d *Daemon, r *http.Request) Response { @@ -38,7 +39,7 @@ continue } - result = append(result, fmt.Sprintf("/%s/containers/%s/logs/%s", shared.APIVersion, name, f.Name())) + result = append(result, fmt.Sprintf("/%s/containers/%s/logs/%s", version.APIVersion, name, f.Name())) } return SyncResponse(true, result) diff -Nru lxd-2.6.2/lxd/container_lxc.go lxd-2.7/lxd/container_lxc.go --- lxd-2.6.2/lxd/container_lxc.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/container_lxc.go 2016-12-21 00:52:23.000000000 +0000 @@ -25,6 +25,7 @@ "gopkg.in/yaml.v2" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" log "gopkg.in/inconshreveable/log15.v2" ) @@ -137,6 +138,14 @@ return fmt.Errorf("Setting lxc.logfile is not allowed") } + if key == "lxc.syslog" { + return fmt.Errorf("Setting lxc.syslog is not allowed") + } + + if key == "lxc.ephemeral" { + return fmt.Errorf("Setting lxc.ephemeral is not allowed") + } + if strings.HasPrefix(key, "lxc.network.") { fields := strings.Split(key, ".") if len(fields) == 4 && shared.StringInSlice(fields[3], []string{"ipv4", "ipv6"}) { @@ -154,6 +163,20 @@ return nil } +func lxcStatusCode(state lxc.State) shared.StatusCode { + return map[int]shared.StatusCode{ + 1: shared.Stopped, + 2: shared.Starting, + 3: shared.Running, + 4: shared.Stopping, + 5: shared.Aborting, + 6: shared.Freezing, + 7: shared.Frozen, + 8: shared.Thawed, + 9: shared.Error, + }[int(state)] +} + // Loader functions func containerLXCCreate(d *Daemon, args containerArgs) (container, error) { // Create the container struct @@ -172,6 +195,11 @@ localDevices: args.Devices, } + ctxMap := log.Ctx{"name": c.name, + "ephemeral": c.ephemeral} + + shared.LogInfo("Creating container", ctxMap) + // No need to detect storage here, its a new container. c.storage = d.Storage @@ -179,6 +207,7 @@ err := c.init() if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } @@ -215,6 +244,7 @@ err = c.Update(updateArgs, false) if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } } @@ -223,12 +253,14 @@ err = containerValidConfig(d, c.expandedConfig, false, true) if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } err = containerValidDevices(c.expandedDevices, false, true) if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } @@ -246,6 +278,7 @@ if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } } @@ -255,6 +288,7 @@ idmapBytes, err := json.Marshal(idmap.Idmap) if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } jsonIdmap = string(idmapBytes) @@ -265,12 +299,14 @@ err = c.ConfigKeySet("volatile.idmap.next", jsonIdmap) if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } err = c.ConfigKeySet("volatile.idmap.base", fmt.Sprintf("%v", base)) if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } @@ -279,6 +315,7 @@ err = c.ConfigKeySet("volatile.last_state.idmap", jsonIdmap) if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } } @@ -287,11 +324,14 @@ err = c.init() if err != nil { c.Delete() + shared.LogError("Failed creating container", ctxMap) return nil, err } // Update lease files - networkUpdateStatic(d) + networkUpdateStatic(d, "") + + shared.LogInfo("Created container", ctxMap) return c, nil } @@ -845,9 +885,9 @@ } // Setup architecture - personality, err := shared.ArchitecturePersonality(c.architecture) + personality, err := osarch.ArchitecturePersonality(c.architecture) if err != nil { - personality, err = shared.ArchitecturePersonality(c.daemon.architectures[0]) + personality, err = osarch.ArchitecturePersonality(c.daemon.architectures[0]) if err != nil { return err } @@ -1552,6 +1592,7 @@ var usbs []usbDevice var gpus []gpuDevice var nvidiaDevices []nvidiaGpuDevices + diskDevices := map[string]shared.Device{} // Create the devices for _, k := range c.expandedDevices.DeviceNames() { @@ -1637,12 +1678,8 @@ } } } else if m["type"] == "disk" { - // Disk device if m["path"] != "/" { - _, err := c.createDiskDevice(k, m) - if err != nil { - return "", err - } + diskDevices[k] = m } } else if m["type"] == "nic" { if m["nictype"] == "bridged" && shared.IsTrue(m["security.mac_filtering"]) { @@ -1685,6 +1722,14 @@ } } + err = c.addDiskDevices(diskDevices, func(name string, d shared.Device) error { + _, err := c.createDiskDevice(name, d) + return err + }) + if err != nil { + return "", err + } + // Create any missing directory err = os.MkdirAll(c.LogPath(), 0700) if err != nil { @@ -1764,6 +1809,23 @@ return "", err } + // Update the backup.yaml file (as storage is guaranteed to be mountable now) + err = c.StorageStart() + if err != nil { + return "", err + } + + err = writeBackupFile(c) + if err != nil { + c.StorageStop() + return "", err + } + + err = c.StorageStop() + if err != nil { + return "", err + } + // Update time container was last started err = dbContainerLastUsedUpdate(c.daemon.db, c.id, time.Now().UTC()) if err != nil { @@ -2333,7 +2395,7 @@ } // Ignore err as the arch string on error is correct (unknown) - architectureName, _ := shared.ArchitectureName(c.architecture) + architectureName, _ := osarch.ArchitectureName(c.architecture) // Prepare the ETag etag := []interface{}{c.architecture, c.localConfig, c.localDevices, c.ephemeral, c.profiles} @@ -2358,7 +2420,7 @@ if err != nil { return nil, nil, err } - statusCode := shared.FromLXCState(int(cState)) + statusCode := lxcStatusCode(cState) return &shared.ContainerInfo{ Architecture: architectureName, @@ -2389,7 +2451,7 @@ if err != nil { return nil, err } - statusCode := shared.FromLXCState(int(cState)) + statusCode := lxcStatusCode(cState) status := shared.ContainerState{ Status: statusCode.String(), StatusCode: statusCode, @@ -2486,6 +2548,14 @@ return err } + // The old backup file may be out of date (e.g. it doesn't have all the + // current snapshots of the container listed); let's write a new one to + // be safe. + err = writeBackupFile(c) + if err != nil { + return err + } + // If the container wasn't running but was stateful, should we restore // it as running? if shared.PathExists(c.StatePath()) { @@ -2575,7 +2645,7 @@ } // Update lease files - networkUpdateStatic(c.daemon) + networkUpdateStatic(c.daemon, "") shared.LogInfo("Deleted container", ctxMap) @@ -2713,6 +2783,76 @@ return c.Update(args, false) } +type backupFile struct { + Container *shared.ContainerInfo `yaml:"container"` + Snapshots []*shared.SnapshotInfo `yaml:"snapshots"` +} + +func writeBackupFile(c container) error { + /* we only write backup files out for actual containers */ + if c.IsSnapshot() { + return nil + } + + /* immediately return if the container directory doesn't exist yet */ + if !shared.PathExists(c.Path()) { + return os.ErrNotExist + } + + /* deal with the container occasionaly not being monuted */ + if !shared.PathExists(c.RootfsPath()) { + shared.LogWarn("Unable to update backup.yaml at this time.", log.Ctx{"name": c.Name()}) + return nil + } + + ci, _, err := c.Render() + if err != nil { + return err + } + + snapshots, err := c.Snapshots() + if err != nil { + return err + } + + var sis []*shared.SnapshotInfo + + for _, s := range snapshots { + si, _, err := s.Render() + if err != nil { + return err + } + + sis = append(sis, si.(*shared.SnapshotInfo)) + } + + data, err := yaml.Marshal(&backupFile{ + Container: ci.(*shared.ContainerInfo), + Snapshots: sis, + }) + if err != nil { + return err + } + + f, err := os.Create(shared.VarPath("containers", c.Name(), "backup.yaml")) + if err != nil { + return err + } + defer f.Close() + + err = f.Chmod(0400) + if err != nil { + return err + } + + err = shared.WriteAll(f, data) + if err != nil { + return err + } + + return nil +} + func (c *containerLXC) Update(args containerArgs, userRequested bool) error { // Set sane defaults for unset keys if args.Architecture == 0 { @@ -2757,7 +2897,7 @@ // Validate the new architecture if args.Architecture != 0 { - _, err = shared.ArchitectureName(args.Architecture) + _, err = osarch.ArchitectureName(args.Architecture) if err != nil { return fmt.Errorf("Invalid architecture id: %s", err) } @@ -3292,6 +3432,8 @@ } } + diskDevices := map[string]shared.Device{} + for k, m := range addDevices { if shared.StringInSlice(m["type"], []string{"unix-char", "unix-block"}) { err = c.insertUnixDevice(m) @@ -3299,10 +3441,7 @@ return err } } else if m["type"] == "disk" && m["path"] != "/" { - err = c.insertDiskDevice(k, m) - if err != nil { - return err - } + diskDevices[k] = m } else if m["type"] == "nic" { err = c.insertNetworkDevice(k, m) if err != nil { @@ -3377,6 +3516,11 @@ } } + err = c.addDiskDevices(diskDevices, c.insertDiskDevice) + if err != nil { + return err + } + updateDiskLimit := false for k, m := range updateDevices { if m["type"] == "disk" { @@ -3461,6 +3605,14 @@ return err } + /* we can call Update in some cases when the directory doesn't exist + * yet before container creation; this is okay, because at the end of + * container creation we write the backup file, so let's not worry about + * ENOENT. */ + if err := writeBackupFile(c); err != nil && !os.IsNotExist(err) { + return err + } + // Update network leases needsUpdate := false for _, m := range updateDevices { @@ -3471,7 +3623,7 @@ } if needsUpdate { - networkUpdateStatic(c.daemon) + networkUpdateStatic(c.daemon, "") } // Success, update the closure to mark that the changes should be kept. @@ -3557,13 +3709,13 @@ return err } - arch, _ = shared.ArchitectureName(parent.Architecture()) + arch, _ = osarch.ArchitectureName(parent.Architecture()) } else { - arch, _ = shared.ArchitectureName(c.architecture) + arch, _ = osarch.ArchitectureName(c.architecture) } if arch == "" { - arch, err = shared.ArchitectureName(c.daemon.architectures[0]) + arch, err = osarch.ArchitectureName(c.daemon.architectures[0]) if err != nil { shared.LogError("Failed exporting container", ctxMap) return err @@ -3981,9 +4133,9 @@ } // Figure out the architecture - arch, err := shared.ArchitectureName(c.architecture) + arch, err := osarch.ArchitectureName(c.architecture) if err != nil { - arch, err = shared.ArchitectureName(c.daemon.architectures[0]) + arch, err = osarch.ArchitectureName(c.daemon.architectures[0]) if err != nil { return err } @@ -4067,9 +4219,10 @@ if err != nil { return fmt.Errorf( - "Error calling 'lxd forkcheckfile %s %d': err='%v'", - path, + "Error calling 'lxd forkcheckfile %s %d %s': err='%v'", + c.RootfsPath(), c.InitPID(), + path, err) } @@ -4108,7 +4261,6 @@ mode := -1 type_ := "unknown" var dirEnts []string - var errStr string // Process forkgetfile response @@ -4128,6 +4280,7 @@ if errno == "2" { return -1, -1, 0, "", nil, os.ErrNotExist } + return -1, -1, 0, "", nil, fmt.Errorf(errStr) } @@ -4178,9 +4331,10 @@ if err != nil { return -1, -1, 0, "", nil, fmt.Errorf( - "Error calling 'lxd forkgetfile %s %d %s': err='%v'", - dstpath, + "Error calling 'lxd forkgetfile %s %d %s %s': err='%v'", + c.RootfsPath(), c.InitPID(), + dstpath, srcpath, err) } @@ -4203,6 +4357,7 @@ func (c *containerLXC) FilePush(srcpath string, dstpath string, uid int, gid int, mode int) error { var rootUid = 0 var rootGid = 0 + var errStr string // Map uid and gid if needed if !c.IsRunning() { @@ -4249,26 +4404,41 @@ } } - // Process forkputfile response - if string(out) != "" { - if strings.HasPrefix(string(out), "error:") { - return fmt.Errorf(strings.TrimPrefix(strings.TrimSuffix(string(out), "\n"), "error: ")) + // Process forkgetfile response + 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("forkgetfile: %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) } } if err != nil { return fmt.Errorf( - "Error calling 'lxd forkputfile %s %d %s %d %d %d': err='%v'", - srcpath, + "Error calling 'lxd forkputfile %s %d %s %s %d %d %d %d %d %d': err='%v'", + c.RootfsPath(), c.InitPID(), + srcpath, dstpath, uid, gid, mode, + rootUid, + rootGid, + int(os.FileMode(0640)&os.ModePerm), err) } @@ -4314,9 +4484,10 @@ if err != nil { return fmt.Errorf( - "Error calling 'lxd forkremovefile %s %d': err='%v'", - path, + "Error calling 'lxd forkremovefile %s %d %s': err='%v'", + c.RootfsPath(), c.InitPID(), + path, err) } @@ -4638,7 +4809,7 @@ return c.storage.ContainerSnapshotStart(c) } - return c.storage.ContainerStart(c) + return c.storage.ContainerStart(c.Name(), c.Path()) } func (c *containerLXC) StorageStop() error { @@ -4646,7 +4817,7 @@ return c.storage.ContainerSnapshotStop(c) } - return c.storage.ContainerStop(c) + return c.storage.ContainerStop(c.Name(), c.Path()) } // Mount handling @@ -4917,8 +5088,10 @@ } dType := "" - if m["type"] != "" { - dType = m["type"] + if m["type"] == "unix-char" { + dType = "c" + } else if m["type"] == "unix-block" { + dType = "b" } if dType == "" || dMajor < 0 || dMinor < 0 { @@ -5229,6 +5402,26 @@ } } + updateKey := func(key string, value string) error { + tx, err := dbBegin(c.daemon.db) + if err != nil { + return err + } + + err = dbContainerConfigInsert(tx, c.id, map[string]string{key: value}) + if err != nil { + tx.Rollback() + return err + } + + err = txCommit(tx) + if err != nil { + return err + } + + return nil + } + // Fill in the MAC address if m["nictype"] != "physical" && m["hwaddr"] == "" { configKey := fmt.Sprintf("volatile.%s.hwaddr", name) @@ -5240,24 +5433,20 @@ return nil, err } - c.localConfig[configKey] = volatileHwaddr - c.expandedConfig[configKey] = volatileHwaddr - // Update the database - tx, err := dbBegin(c.daemon.db) + err = updateKey(configKey, volatileHwaddr) if err != nil { - return nil, err - } - - err = dbContainerConfigInsert(tx, c.id, map[string]string{configKey: volatileHwaddr}) - if err != nil { - tx.Rollback() - return nil, err - } + // Check if something else filled it in behind our back + value, err1 := dbContainerConfigGet(c.daemon.db, c.id, configKey) + if err1 != nil || value == "" { + return nil, err + } - err = txCommit(tx) - if err != nil { - return nil, err + c.localConfig[configKey] = value + c.expandedConfig[configKey] = value + } else { + c.localConfig[configKey] = volatileHwaddr + c.expandedConfig[configKey] = volatileHwaddr } } newDevice["hwaddr"] = volatileHwaddr @@ -5274,24 +5463,20 @@ return nil, err } - c.localConfig[configKey] = volatileName - c.expandedConfig[configKey] = volatileName - // Update the database - tx, err := dbBegin(c.daemon.db) + err = updateKey(configKey, volatileName) if err != nil { - return nil, err - } - - err = dbContainerConfigInsert(tx, c.id, map[string]string{configKey: volatileName}) - if err != nil { - tx.Rollback() - return nil, err - } + // Check if something else filled it in behind our back + value, err1 := dbContainerConfigGet(c.daemon.db, c.id, configKey) + if err1 != nil || value == "" { + return nil, err + } - err = txCommit(tx) - if err != nil { - return nil, err + c.localConfig[configKey] = value + c.expandedConfig[configKey] = value + } else { + c.localConfig[configKey] = volatileName + c.expandedConfig[configKey] = volatileName } } newDevice["name"] = volatileName @@ -5544,6 +5729,38 @@ } return nil +} + +type byPath []shared.Device + +func (a byPath) Len() int { + return len(a) +} + +func (a byPath) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a byPath) Less(i, j int) bool { + return a[i]["path"] < a[j]["path"] +} + +func (c *containerLXC) addDiskDevices(devices map[string]shared.Device, handler func(string, shared.Device) error) error { + ordered := byPath{} + + for _, d := range devices { + ordered = append(ordered, d) + } + + sort.Sort(ordered) + for _, d := range ordered { + err := handler(d["path"], d) + if err != nil { + return err + } + } + + return nil } func (c *containerLXC) removeDiskDevice(name string, m shared.Device) error { diff -Nru lxd-2.6.2/lxd/container_patch.go lxd-2.7/lxd/container_patch.go --- lxd-2.6.2/lxd/container_patch.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/container_patch.go 2016-12-21 00:52:23.000000000 +0000 @@ -8,7 +8,9 @@ "net/http" "github.com/gorilla/mux" + "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" ) func containerPatch(d *Daemon, r *http.Request) Response { @@ -54,7 +56,7 @@ if err != nil { architecture = c.Architecture() } else { - architecture, err = shared.ArchitectureId(req.Architecture) + architecture, err = osarch.ArchitectureId(req.Architecture) if err != nil { architecture = 0 } diff -Nru lxd-2.6.2/lxd/container_put.go lxd-2.7/lxd/container_put.go --- lxd-2.6.2/lxd/container_put.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/container_put.go 2016-12-21 00:52:23.000000000 +0000 @@ -7,7 +7,9 @@ "net/http" "github.com/gorilla/mux" + "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" log "gopkg.in/inconshreveable/log15.v2" ) @@ -45,7 +47,7 @@ return BadRequest(err) } - architecture, err := shared.ArchitectureId(configRaw.Architecture) + architecture, err := osarch.ArchitectureId(configRaw.Architecture) if err != nil { architecture = 0 } diff -Nru lxd-2.6.2/lxd/containers_get.go lxd-2.7/lxd/containers_get.go --- lxd-2.6.2/lxd/containers_get.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/containers_get.go 2016-12-21 00:52:23.000000000 +0000 @@ -6,6 +6,7 @@ "time" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" ) func containersGet(d *Daemon, r *http.Request) Response { @@ -42,7 +43,7 @@ for _, container := range result { if !recursion { - url := fmt.Sprintf("/%s/containers/%s", shared.APIVersion, container) + url := fmt.Sprintf("/%s/containers/%s", version.APIVersion, container) resultString = append(resultString, url) } else { c, err := doContainerGet(d, container) diff -Nru lxd-2.6.2/lxd/containers.go lxd-2.7/lxd/containers.go --- lxd-2.6.2/lxd/containers.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/containers.go 2016-12-21 00:52:23.000000000 +0000 @@ -1,18 +1,11 @@ package main import ( - "encoding/json" - "fmt" - "os" "sort" "strconv" - "strings" "sync" - "syscall" "time" - "gopkg.in/lxc/go-lxc.v2" - "github.com/lxc/lxd/shared" log "gopkg.in/inconshreveable/log15.v2" @@ -128,19 +121,6 @@ } } - // Reset the recorded state (to ensure it's up to date) - _, err = dbExec(d.db, "DELETE FROM containers_config WHERE key='volatile.last_state.power'") - if err != nil { - return err - } - - for _, c := range containers { - err = c.ConfigKeySet("volatile.last_state.power", c.State()) - if err != nil { - return err - } - } - return nil } @@ -167,10 +147,7 @@ } // Record the current state - err = c.ConfigKeySet("volatile.last_state.power", c.State()) - if err != nil { - return err - } + lastState := c.State() // Stop the container if c.IsRunning() { @@ -188,8 +165,12 @@ go func() { c.Shutdown(time.Second * time.Duration(timeoutSeconds)) c.Stop(false) + c.ConfigKeySet("volatile.last_state.power", lastState) + wg.Done() }() + } else { + c.ConfigKeySet("volatile.last_state.power", lastState) } } wg.Wait() @@ -225,173 +206,3 @@ return nil } - -/* - * This is called by lxd when called as "lxd forkstart " - * 'forkstart' is used instead of just 'start' in the hopes that people - * do not accidentally type 'lxd start' instead of 'lxc start' - */ -func startContainer(args []string) error { - if len(args) != 4 { - return fmt.Errorf("Bad arguments: %q", args) - } - - name := args[1] - lxcpath := args[2] - configPath := args[3] - - c, err := lxc.NewContainer(name, lxcpath) - if err != nil { - return fmt.Errorf("Error initializing container for start: %q", err) - } - - err = c.LoadConfigFile(configPath) - if err != nil { - return fmt.Errorf("Error opening startup config file: %q", err) - } - - /* due to https://github.com/golang/go/issues/13155 and the - * CollectOutput call we make for the forkstart process, we need to - * close our stdin/stdout/stderr here. Collecting some of the logs is - * better than collecting no logs, though. - */ - os.Stdin.Close() - os.Stderr.Close() - os.Stdout.Close() - - // Redirect stdout and stderr to a log file - logPath := shared.LogPath(name, "forkstart.log") - if shared.PathExists(logPath) { - os.Remove(logPath) - } - - logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) - if err == nil { - syscall.Dup3(int(logFile.Fd()), 1, 0) - syscall.Dup3(int(logFile.Fd()), 2, 0) - } - - return c.Start() -} - -/* - * This is called by lxd when called as "lxd forkexec " - */ -func execContainer(args []string) (int, error) { - if len(args) < 6 { - return -1, fmt.Errorf("Bad arguments: %q", args) - } - - name := args[1] - lxcpath := args[2] - configPath := args[3] - - c, err := lxc.NewContainer(name, lxcpath) - if err != nil { - return -1, fmt.Errorf("Error initializing container for start: %q", err) - } - - err = c.LoadConfigFile(configPath) - if err != nil { - return -1, fmt.Errorf("Error opening startup config file: %q", err) - } - - syscall.Dup3(int(os.Stdin.Fd()), 200, 0) - syscall.Dup3(int(os.Stdout.Fd()), 201, 0) - syscall.Dup3(int(os.Stderr.Fd()), 202, 0) - - syscall.Close(int(os.Stdin.Fd())) - syscall.Close(int(os.Stdout.Fd())) - syscall.Close(int(os.Stderr.Fd())) - - opts := lxc.DefaultAttachOptions - opts.ClearEnv = true - opts.StdinFd = 200 - opts.StdoutFd = 201 - opts.StderrFd = 202 - - logPath := shared.LogPath(name, "forkexec.log") - if shared.PathExists(logPath) { - os.Remove(logPath) - } - - logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) - if err == nil { - syscall.Dup3(int(logFile.Fd()), 1, 0) - syscall.Dup3(int(logFile.Fd()), 2, 0) - } - - env := []string{} - cmd := []string{} - - section := "" - for _, arg := range args[5:len(args)] { - // The "cmd" section must come last as it may contain a -- - if arg == "--" && section != "cmd" { - section = "" - continue - } - - if section == "" { - section = arg - continue - } - - if section == "env" { - fields := strings.SplitN(arg, "=", 2) - if len(fields) == 2 && fields[0] == "HOME" { - opts.Cwd = fields[1] - } - env = append(env, arg) - } else if section == "cmd" { - cmd = append(cmd, arg) - } else { - return -1, fmt.Errorf("Invalid exec section: %s", section) - } - } - - opts.Env = env - - status, err := c.RunCommandNoWait(cmd, opts) - if err != nil { - return -1, fmt.Errorf("Failed running command: %q", err) - } - // Send the PID of the executing process. - w := os.NewFile(uintptr(3), "attachedPid") - defer w.Close() - - err = json.NewEncoder(w).Encode(status) - if err != nil { - return -1, fmt.Errorf("Failed sending PID of executing command: %q", err) - } - - proc, err := os.FindProcess(status) - if err != nil { - return -1, fmt.Errorf("Failed finding process: %q", err) - } - - procState, err := proc.Wait() - if err != nil { - return -1, fmt.Errorf("Failed waiting on process %d: %q", status, err) - } - - if procState.Success() { - return 0, nil - } - - exCode, ok := procState.Sys().(syscall.WaitStatus) - if ok { - if exCode.Exited() { - return exCode.ExitStatus(), nil - } - // Backwards compatible behavior. Report success when we exited - // due to a signal. Otherwise this may break Jenkins, e.g. when - // lxc exec foo reboot receives SIGTERM and exCode.Exitstats() - // would report -1. - if exCode.Signaled() { - return 0, nil - } - } - - return -1, fmt.Errorf("Command failed") -} diff -Nru lxd-2.6.2/lxd/container_snapshot.go lxd-2.7/lxd/container_snapshot.go --- lxd-2.6.2/lxd/container_snapshot.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/container_snapshot.go 2016-12-21 00:52:23.000000000 +0000 @@ -10,6 +10,7 @@ "github.com/gorilla/mux" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" ) type containerSnapshotPostReq struct { @@ -41,7 +42,7 @@ for _, snap := range snaps { snapName := strings.SplitN(snap.Name(), shared.SnapshotDelimiter, 2)[1] if recursion == 0 { - url := fmt.Sprintf("/%s/containers/%s/snapshots/%s", shared.APIVersion, cname, snapName) + url := fmt.Sprintf("/%s/containers/%s/snapshots/%s", version.APIVersion, cname, snapName) resultString = append(resultString, url) } else { render, _, err := snap.Render() diff -Nru lxd-2.6.2/lxd/containers_post.go lxd-2.7/lxd/containers_post.go --- lxd-2.6.2/lxd/containers_post.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/containers_post.go 2016-12-21 00:52:23.000000000 +0000 @@ -10,7 +10,9 @@ "github.com/dustinkirkland/golang-petname" "github.com/gorilla/websocket" + "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" log "gopkg.in/inconshreveable/log15.v2" ) @@ -139,7 +141,7 @@ hash = imgInfo.Fingerprint - architecture, err := shared.ArchitectureId(imgInfo.Architecture) + architecture, err := osarch.ArchitectureId(imgInfo.Architecture) if err != nil { architecture = 0 } @@ -171,7 +173,7 @@ } func createFromNone(d *Daemon, req *containerPostReq) Response { - architecture, err := shared.ArchitectureId(req.Architecture) + architecture, err := osarch.ArchitectureId(req.Architecture) if err != nil { architecture = 0 } @@ -207,7 +209,7 @@ return NotImplemented } - architecture, err := shared.ArchitectureId(req.Architecture) + architecture, err := osarch.ArchitectureId(req.Architecture) if err != nil { architecture = 0 } diff -Nru lxd-2.6.2/lxd/daemon_config.go lxd-2.7/lxd/daemon_config.go --- lxd-2.6.2/lxd/daemon_config.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/daemon_config.go 2016-12-21 00:52:23.000000000 +0000 @@ -49,8 +49,17 @@ } func (k *daemonConfigKey) Validate(d *Daemon, value string) error { - // No need to validate when unsetting + // Handle unsetting if value == "" { + value = k.defaultValue + + if k.validator != nil { + err := k.validator(d, k.name(), value) + if err != nil { + return err + } + } + return nil } diff -Nru lxd-2.6.2/lxd/daemon.go lxd-2.7/lxd/daemon.go --- lxd-2.6.2/lxd/daemon.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/daemon.go 2016-12-21 00:52:23.000000000 +0000 @@ -32,6 +32,8 @@ "github.com/lxc/lxd" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/logging" + "github.com/lxc/lxd/shared/osarch" + "github.com/lxc/lxd/shared/version" log "gopkg.in/inconshreveable/log15.v2" ) @@ -107,10 +109,10 @@ patch func(d *Daemon, r *http.Request) Response } -func (d *Daemon) httpGetSync(url string, certificate string) (*lxd.Response, error) { +func (d *Daemon) httpClient(certificate string) (*http.Client, error) { var err error - var cert *x509.Certificate + if certificate != "" { certBlock, _ := pem.Decode([]byte(certificate)) if certBlock == nil { @@ -139,12 +141,23 @@ Transport: tr, } + return &myhttp, nil +} + +func (d *Daemon) httpGetSync(url string, certificate string) (*lxd.Response, error) { + var err error + req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } - req.Header.Set("User-Agent", shared.UserAgent) + req.Header.Set("User-Agent", version.UserAgent) + + myhttp, err := d.httpClient(certificate) + if err != nil { + return nil, err + } r, err := myhttp.Do(req) if err != nil { @@ -166,40 +179,17 @@ func (d *Daemon) httpGetFile(url string, certificate string) (*http.Response, error) { var err error - var cert *x509.Certificate - if certificate != "" { - certBlock, _ := pem.Decode([]byte(certificate)) - if certBlock == nil { - return nil, fmt.Errorf("Invalid certificate") - } - - cert, err = x509.ParseCertificate(certBlock.Bytes) - if err != nil { - return nil, err - } - } - - tlsConfig, err := shared.GetTLSConfig("", "", "", cert) + myhttp, err := d.httpClient(certificate) if err != nil { return nil, err } - tr := &http.Transport{ - TLSClientConfig: tlsConfig, - Dial: shared.RFC3493Dialer, - Proxy: d.proxy, - DisableKeepAlives: true, - } - myhttp := http.Client{ - Transport: tr, - } - req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } - req.Header.Set("User-Agent", shared.UserAgent) + req.Header.Set("User-Agent", version.UserAgent) raw, err := myhttp.Do(req) if err != nil { @@ -534,7 +524,7 @@ return fmt.Errorf("cannot listen on https socket: %v", err) } - d.tomb.Go(func() error { return http.Serve(tcpl, d.mux) }) + d.tomb.Go(func() error { return http.Serve(tcpl, &lxdHttpServer{d.mux, d}) }) d.TCPSocket = &Socket{Socket: tcpl, CloseOnExit: true} } @@ -576,13 +566,13 @@ /* Print welcome message */ if d.MockMode { - shared.LogInfo("LXD is starting in mock mode", + shared.LogInfo(fmt.Sprintf("LXD %s is starting in mock mode", version.Version), log.Ctx{"path": shared.VarPath("")}) } else if d.SetupMode { - shared.LogInfo("LXD is starting in setup mode", + shared.LogInfo(fmt.Sprintf("LXD %s is starting in setup mode", version.Version), log.Ctx{"path": shared.VarPath("")}) } else { - shared.LogInfo("LXD is starting in normal mode", + shared.LogInfo(fmt.Sprintf("LXD %s is starting in normal mode", version.Version), log.Ctx{"path": shared.VarPath("")}) } @@ -724,18 +714,18 @@ /* Get the list of supported architectures */ var architectures = []int{} - architectureName, err := shared.ArchitectureGetLocal() + architectureName, err := osarch.ArchitectureGetLocal() if err != nil { return err } - architecture, err := shared.ArchitectureId(architectureName) + architecture, err := osarch.ArchitectureId(architectureName) if err != nil { return err } architectures = append(architectures, architecture) - personalities, err := shared.ArchitecturePersonalities(architecture) + personalities, err := osarch.ArchitecturePersonalities(architecture) if err != nil { return err } @@ -748,6 +738,9 @@ d.lxcpath = shared.VarPath("containers") /* Make sure all our directories are available */ + if err := os.MkdirAll(shared.VarPath(), 0711); err != nil { + return err + } if err := os.MkdirAll(shared.CachePath(), 0700); err != nil { return err } @@ -878,11 +871,10 @@ } tlsConfig := &tls.Config{ - InsecureSkipVerify: true, - ClientAuth: tls.RequestClientCert, - Certificates: []tls.Certificate{cert}, - MinVersion: tls.VersionTLS12, - MaxVersion: tls.VersionTLS12, + ClientAuth: tls.RequestClientCert, + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS12, + MaxVersion: tls.VersionTLS12, CipherSuites: []uint16{ tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, @@ -1098,7 +1090,7 @@ }() /* Restore containers */ - go containersRestart(d) + containersRestart(d) /* Re-balance in case things changed while LXD was down */ deviceTaskBalance(d) diff -Nru lxd-2.6.2/lxd/daemon_images.go lxd-2.7/lxd/daemon_images.go --- lxd-2.6.2/lxd/daemon_images.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/daemon_images.go 2016-12-21 00:52:23.000000000 +0000 @@ -16,6 +16,9 @@ "gopkg.in/yaml.v2" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/ioprogress" + "github.com/lxc/lxd/shared/simplestreams" + "github.com/lxc/lxd/shared/version" log "gopkg.in/inconshreveable/log15.v2" ) @@ -25,7 +28,7 @@ Aliases shared.ImageAliases `yaml:"aliases"` Fingerprints []string `yaml:"fingerprints"` expiry time.Time - ss *shared.SimpleStreams + ss *simplestreams.SimpleStreams } var imageStreamCache = map[string]*imageStreamCacheEntry{} @@ -65,11 +68,12 @@ for url, entry := range imageStreamCache { if entry.ss == nil { - ss, err := shared.SimpleStreamsClient(url, d.proxy) + myhttp, err := d.httpClient("") if err != nil { return err } + ss := simplestreams.NewClient(url, *myhttp, version.UserAgent) entry.ss = ss } } @@ -81,7 +85,7 @@ // downloads the image from a remote server. func (d *Daemon) ImageDownload(op *operation, server string, protocol string, certificate string, secret string, alias string, forContainer bool, autoUpdate bool) (string, error) { var err error - var ss *shared.SimpleStreams + var ss *simplestreams.SimpleStreams var ctxMap log.Ctx if protocol == "" { @@ -97,11 +101,13 @@ if entry == nil || entry.expiry.Before(time.Now()) { refresh := func() (*imageStreamCacheEntry, error) { // Setup simplestreams client - ss, err = shared.SimpleStreamsClient(server, d.proxy) + myhttp, err := d.httpClient(certificate) if err != nil { return nil, err } + ss = simplestreams.NewClient(server, *myhttp, version.UserAgent) + // Get all aliases aliases, err := ss.ListAliases() if err != nil { @@ -271,9 +277,9 @@ if secret != "" { url = fmt.Sprintf( "%s/%s/images/%s?secret=%s", - server, shared.APIVersion, fp, secret) + server, version.APIVersion, fp, secret) } else { - url = fmt.Sprintf("%s/%s/images/%s", server, shared.APIVersion, fp) + url = fmt.Sprintf("%s/%s/images/%s", server, version.APIVersion, fp) } resp, err := d.httpGetSync(url, certificate) @@ -293,12 +299,12 @@ if secret != "" { exporturl = fmt.Sprintf( "%s/%s/images/%s/export?secret=%s", - server, shared.APIVersion, fp, secret) + server, version.APIVersion, fp, secret) } else { exporturl = fmt.Sprintf( "%s/%s/images/%s/export", - server, shared.APIVersion, fp) + server, version.APIVersion, fp) } } else if protocol == "simplestreams" { err := ss.Download(fp, "meta", destName, nil) @@ -359,9 +365,9 @@ ctype = "application/octet-stream" } - body := &shared.ProgressReader{ + body := &ioprogress.ProgressReader{ ReadCloser: raw.Body, - Tracker: &shared.ProgressTracker{ + Tracker: &ioprogress.ProgressTracker{ Length: raw.ContentLength, Handler: progress, }, diff -Nru lxd-2.6.2/lxd/db_containers.go lxd-2.7/lxd/db_containers.go --- lxd-2.6.2/lxd/db_containers.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/db_containers.go 2016-12-21 00:52:23.000000000 +0000 @@ -224,6 +224,15 @@ return nil } +func dbContainerConfigGet(db *sql.DB, id int, key string) (string, error) { + q := "SELECT value FROM containers_config WHERE container_id=? AND key=?" + value := "" + arg1 := []interface{}{id, key} + arg2 := []interface{}{&value} + err := dbQueryRowScan(db, q, arg1, arg2) + return value, err +} + func dbContainerConfigRemove(db *sql.DB, id int, name string) error { _, err := dbExec(db, "DELETE FROM containers_config WHERE key=? AND container_id=?", name, id) return err diff -Nru lxd-2.6.2/lxd/db_images.go lxd-2.7/lxd/db_images.go --- lxd-2.6.2/lxd/db_images.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/db_images.go 2016-12-21 00:52:23.000000000 +0000 @@ -8,6 +8,7 @@ _ "github.com/mattn/go-sqlite3" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" ) var dbImageSourceProtocol = map[int]string{ @@ -175,7 +176,7 @@ image.LastUsedDate = time.Time{} } - image.Architecture, _ = shared.ArchitectureName(arch) + image.Architecture, _ = osarch.ArchitectureName(arch) // The upload date is enforced by NOT NULL in the schema, so it can never be nil. image.UploadDate = *upload @@ -312,7 +313,7 @@ } func dbImageUpdate(db *sql.DB, id int, fname string, sz int64, public bool, autoUpdate bool, architecture string, creationDate time.Time, expiryDate time.Time, properties map[string]string) error { - arch, err := shared.ArchitectureId(architecture) + arch, err := osarch.ArchitectureId(architecture) if err != nil { arch = 0 } @@ -369,7 +370,7 @@ } func dbImageInsert(db *sql.DB, fp string, fname string, sz int64, public bool, autoUpdate bool, architecture string, creationDate time.Time, expiryDate time.Time, properties map[string]string) error { - arch, err := shared.ArchitectureId(architecture) + arch, err := osarch.ArchitectureId(architecture) if err != nil { arch = 0 } diff -Nru lxd-2.6.2/lxd/devlxd.go lxd-2.7/lxd/devlxd.go --- lxd-2.6.2/lxd/devlxd.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/devlxd.go 2016-12-21 00:52:23.000000000 +0000 @@ -16,6 +16,7 @@ "github.com/gorilla/mux" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" ) type devLxdResponse struct { @@ -74,7 +75,7 @@ return okResponse([]string{"/1.0"}, "json") }}, devLxdHandler{"/1.0", func(c container, r *http.Request) *devLxdResponse { - return okResponse(shared.Jmap{"api_version": shared.APIVersion}, "json") + return okResponse(shared.Jmap{"api_version": version.APIVersion}, "json") }}, configGet, configKeyGet, diff -Nru lxd-2.6.2/lxd/images.go lxd-2.7/lxd/images.go --- lxd-2.6.2/lxd/images.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/images.go 2016-12-21 00:52:23.000000000 +0000 @@ -24,6 +24,8 @@ "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/logging" + "github.com/lxc/lxd/shared/osarch" + "github.com/lxc/lxd/shared/version" log "gopkg.in/inconshreveable/log15.v2" ) @@ -319,7 +321,7 @@ return info, err } - info.Architecture, _ = shared.ArchitectureName(c.Architecture()) + info.Architecture, _ = osarch.ArchitectureName(c.Architecture()) info.Properties = req.Properties return info, nil @@ -375,22 +377,12 @@ return fmt.Errorf("Missing URL") } - // Resolve the image URL - tlsConfig, err := shared.GetTLSConfig("", "", "", nil) + myhttp, err := d.httpClient("") if err != nil { return err } - tr := &http.Transport{ - TLSClientConfig: tlsConfig, - Dial: shared.RFC3493Dialer, - Proxy: d.proxy, - } - - myhttp := http.Client{ - Transport: tr, - } - + // Resolve the image URL head, err := http.NewRequest("HEAD", req.Source["url"], nil) if err != nil { return err @@ -401,9 +393,9 @@ architecturesStr = append(architecturesStr, fmt.Sprintf("%d", arch)) } - head.Header.Set("User-Agent", shared.UserAgent) + head.Header.Set("User-Agent", version.UserAgent) head.Header.Set("LXD-Server-Architectures", strings.Join(architecturesStr, ", ")) - head.Header.Set("LXD-Server-Version", shared.Version) + head.Header.Set("LXD-Server-Version", version.Version) raw, err := myhttp.Do(head) if err != nil { @@ -814,7 +806,7 @@ return nil, fmt.Errorf("Could not parse %s: %v", metadataName, err) } - _, err = shared.ArchitectureId(metadata.Architecture) + _, err = osarch.ArchitectureId(metadata.Architecture) if err != nil { return nil, err } @@ -837,7 +829,7 @@ i := 0 for _, name := range results { if !recursion { - url := fmt.Sprintf("/%s/images/%s", shared.APIVersion, name) + url := fmt.Sprintf("/%s/images/%s", version.APIVersion, name) resultString[i] = url } else { image, response := doImageGet(d, name, public) @@ -1211,7 +1203,7 @@ return InternalError(err) } - return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/images/aliases/%s", shared.APIVersion, req.Name)) + return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/images/aliases/%s", version.APIVersion, req.Name)) } func aliasesGet(d *Daemon, r *http.Request) Response { @@ -1230,7 +1222,7 @@ for _, res := range results { name = res[0].(string) if !recursion { - url := fmt.Sprintf("/%s/images/aliases/%s", shared.APIVersion, name) + url := fmt.Sprintf("/%s/images/aliases/%s", version.APIVersion, name) responseStr = append(responseStr, url) } else { @@ -1387,7 +1379,7 @@ return SmartError(err) } - return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/images/aliases/%s", shared.APIVersion, req.Name)) + return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/images/aliases/%s", version.APIVersion, req.Name)) } func imageExport(d *Daemon, r *http.Request) Response { diff -Nru lxd-2.6.2/lxd/main_activateifneeded.go lxd-2.7/lxd/main_activateifneeded.go --- lxd-2.6.2/lxd/main_activateifneeded.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_activateifneeded.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,78 @@ +package main + +import ( + "sync" + + "github.com/lxc/lxd" + "github.com/lxc/lxd/shared" +) + +func cmdActivateIfNeeded() error { + // Don't start a full daemon, we just need DB access + d := &Daemon{ + imagesDownloading: map[string]chan bool{}, + imagesDownloadingLock: sync.RWMutex{}, + lxcpath: shared.VarPath("containers"), + } + + if !shared.PathExists(shared.VarPath("lxd.db")) { + shared.LogDebugf("No DB, so no need to start the daemon now.") + return nil + } + + err := initializeDbObject(d, shared.VarPath("lxd.db")) + if err != nil { + return err + } + + /* Load all config values from the database */ + err = daemonConfigInit(d.db) + if err != nil { + return err + } + + // Look for network socket + value := daemonConfig["core.https_address"].Get() + if value != "" { + shared.LogDebugf("Daemon has core.https_address set, activating...") + _, err := lxd.NewClient(&lxd.DefaultConfig, "local") + return err + } + + // Look for auto-started or previously started containers + d.IdmapSet, err = shared.DefaultIdmapSet() + if err != nil { + return err + } + + result, err := dbContainersList(d.db, cTypeRegular) + if err != nil { + return err + } + + for _, name := range result { + c, err := containerLoadByName(d, name) + if err != nil { + return err + } + + config := c.ExpandedConfig() + lastState := config["volatile.last_state.power"] + autoStart := config["boot.autostart"] + + if c.IsRunning() { + shared.LogDebugf("Daemon has running containers, activating...") + _, err := lxd.NewClient(&lxd.DefaultConfig, "local") + return err + } + + if lastState == "RUNNING" || lastState == "Running" || shared.IsTrue(autoStart) { + shared.LogDebugf("Daemon has auto-started containers, activating...") + _, err := lxd.NewClient(&lxd.DefaultConfig, "local") + return err + } + } + + shared.LogDebugf("No need to start the daemon now.") + return nil +} diff -Nru lxd-2.6.2/lxd/main_callhook.go lxd-2.7/lxd/main_callhook.go --- lxd-2.6.2/lxd/main_callhook.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_callhook.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,79 @@ +package main + +import ( + "fmt" + "net/http" + "os" + "time" + + "github.com/lxc/lxd" +) + +func cmdCallHook(args []string) error { + if len(args) < 4 { + return fmt.Errorf("Invalid arguments") + } + + path := args[1] + id := args[2] + state := args[3] + target := "" + + err := os.Setenv("LXD_DIR", path) + if err != nil { + return err + } + + c, err := lxd.NewClient(&lxd.DefaultConfig, "local") + if err != nil { + return err + } + + url := fmt.Sprintf("%s/internal/containers/%s/on%s", c.BaseURL, id, state) + + if state == "stop" { + target = os.Getenv("LXC_TARGET") + if target == "" { + target = "unknown" + } + url = fmt.Sprintf("%s?target=%s", url, target) + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + hook := make(chan error, 1) + go func() { + raw, err := c.Http.Do(req) + if err != nil { + hook <- err + return + } + + _, err = lxd.HoistResponse(raw, lxd.Sync) + if err != nil { + hook <- err + return + } + + hook <- nil + }() + + select { + case err := <-hook: + if err != nil { + return err + } + break + case <-time.After(30 * time.Second): + return fmt.Errorf("Hook didn't finish within 30s") + } + + if target == "reboot" { + return fmt.Errorf("Reboot must be handled by LXD.") + } + + return nil +} diff -Nru lxd-2.6.2/lxd/main_daemon.go lxd-2.7/lxd/main_daemon.go --- lxd-2.6.2/lxd/main_daemon.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_daemon.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,101 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "os/signal" + "runtime/pprof" + "sync" + "syscall" + "time" + + "github.com/lxc/lxd/shared" +) + +func cmdDaemon() error { + if *argCPUProfile != "" { + f, err := os.Create(*argCPUProfile) + if err != nil { + fmt.Printf("Error opening cpu profile file: %s\n", err) + return nil + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + if *argMemProfile != "" { + go memProfiler(*argMemProfile) + } + + neededPrograms := []string{"dnsmasq", "setfacl", "rsync", "tar", "unsquashfs", "xz"} + for _, p := range neededPrograms { + _, err := exec.LookPath(p) + if err != nil { + return err + } + } + + if *argPrintGoroutinesEvery > 0 { + go func() { + for { + time.Sleep(time.Duration(*argPrintGoroutinesEvery) * time.Second) + shared.PrintStack() + } + }() + } + + d := &Daemon{ + group: *argGroup, + SetupMode: shared.PathExists(shared.VarPath(".setup_mode"))} + err := d.Init() + if err != nil { + if d != nil && d.db != nil { + d.db.Close() + } + return err + } + + var ret error + var wg sync.WaitGroup + wg.Add(1) + + go func() { + ch := make(chan os.Signal) + signal.Notify(ch, syscall.SIGPWR) + sig := <-ch + + shared.LogInfof("Received '%s signal', shutting down containers.", sig) + + containersShutdown(d) + + ret = d.Stop() + wg.Done() + }() + + go func() { + <-d.shutdownChan + + shared.LogInfof("Asked to shutdown by API, shutting down containers.") + + containersShutdown(d) + + ret = d.Stop() + wg.Done() + }() + + go func() { + ch := make(chan os.Signal) + signal.Notify(ch, syscall.SIGINT) + signal.Notify(ch, syscall.SIGQUIT) + signal.Notify(ch, syscall.SIGTERM) + sig := <-ch + + shared.LogInfof("Received '%s signal', exiting.", sig) + ret = d.Stop() + wg.Done() + }() + + wg.Wait() + return ret +} diff -Nru lxd-2.6.2/lxd/main_forkexec.go lxd-2.7/lxd/main_forkexec.go --- lxd-2.6.2/lxd/main_forkexec.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_forkexec.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,135 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "strings" + "syscall" + + "gopkg.in/lxc/go-lxc.v2" + + "github.com/lxc/lxd/shared" +) + +/* + * This is called by lxd when called as "lxd forkexec " + */ +func cmdForkExec(args []string) (int, error) { + if len(args) < 6 { + return -1, fmt.Errorf("Bad arguments: %q", args) + } + + name := args[1] + lxcpath := args[2] + configPath := args[3] + + c, err := lxc.NewContainer(name, lxcpath) + if err != nil { + return -1, fmt.Errorf("Error initializing container for start: %q", err) + } + + err = c.LoadConfigFile(configPath) + if err != nil { + return -1, fmt.Errorf("Error opening startup config file: %q", err) + } + + syscall.Dup3(int(os.Stdin.Fd()), 200, 0) + syscall.Dup3(int(os.Stdout.Fd()), 201, 0) + syscall.Dup3(int(os.Stderr.Fd()), 202, 0) + + syscall.Close(int(os.Stdin.Fd())) + syscall.Close(int(os.Stdout.Fd())) + syscall.Close(int(os.Stderr.Fd())) + + opts := lxc.DefaultAttachOptions + opts.ClearEnv = true + opts.StdinFd = 200 + opts.StdoutFd = 201 + opts.StderrFd = 202 + + logPath := shared.LogPath(name, "forkexec.log") + if shared.PathExists(logPath) { + os.Remove(logPath) + } + + logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) + if err == nil { + syscall.Dup3(int(logFile.Fd()), 1, 0) + syscall.Dup3(int(logFile.Fd()), 2, 0) + } + + env := []string{} + cmd := []string{} + + section := "" + for _, arg := range args[5:len(args)] { + // The "cmd" section must come last as it may contain a -- + if arg == "--" && section != "cmd" { + section = "" + continue + } + + if section == "" { + section = arg + continue + } + + if section == "env" { + fields := strings.SplitN(arg, "=", 2) + if len(fields) == 2 && fields[0] == "HOME" { + opts.Cwd = fields[1] + } + env = append(env, arg) + } else if section == "cmd" { + cmd = append(cmd, arg) + } else { + return -1, fmt.Errorf("Invalid exec section: %s", section) + } + } + + opts.Env = env + + status, err := c.RunCommandNoWait(cmd, opts) + if err != nil { + return -1, fmt.Errorf("Failed running command: %q", err) + } + // Send the PID of the executing process. + w := os.NewFile(uintptr(3), "attachedPid") + defer w.Close() + + err = json.NewEncoder(w).Encode(status) + if err != nil { + return -1, fmt.Errorf("Failed sending PID of executing command: %q", err) + } + + proc, err := os.FindProcess(status) + if err != nil { + return -1, fmt.Errorf("Failed finding process: %q", err) + } + + procState, err := proc.Wait() + if err != nil { + return -1, fmt.Errorf("Failed waiting on process %d: %q", status, err) + } + + if procState.Success() { + return 0, nil + } + + exCode, ok := procState.Sys().(syscall.WaitStatus) + if ok { + if exCode.Exited() { + return exCode.ExitStatus(), nil + } + // Backwards compatible behavior. Report success when we exited + // due to a signal. Otherwise this may break Jenkins, e.g. when + // lxc exec foo reboot receives SIGTERM and exCode.Exitstats() + // would report -1. + if exCode.Signaled() { + return 0, nil + } + } + + return -1, fmt.Errorf("Command failed") +} diff -Nru lxd-2.6.2/lxd/main_forkgetnet.go lxd-2.7/lxd/main_forkgetnet.go --- lxd-2.6.2/lxd/main_forkgetnet.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_forkgetnet.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,146 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net" + "strconv" + "strings" + + "github.com/lxc/lxd/shared" +) + +func cmdForkGetNet() error { + networks := map[string]shared.ContainerStateNetwork{} + + interfaces, err := net.Interfaces() + if err != nil { + return err + } + + stats := map[string][]int64{} + + content, err := ioutil.ReadFile("/proc/net/dev") + if err == nil { + for _, line := range strings.Split(string(content), "\n") { + fields := strings.Fields(line) + + if len(fields) != 17 { + continue + } + + rxBytes, err := strconv.ParseInt(fields[1], 10, 64) + if err != nil { + continue + } + + rxPackets, err := strconv.ParseInt(fields[2], 10, 64) + if err != nil { + continue + } + + txBytes, err := strconv.ParseInt(fields[9], 10, 64) + if err != nil { + continue + } + + txPackets, err := strconv.ParseInt(fields[10], 10, 64) + if err != nil { + continue + } + + intName := strings.TrimSuffix(fields[0], ":") + stats[intName] = []int64{rxBytes, rxPackets, txBytes, txPackets} + } + } + + for _, netIf := range interfaces { + netState := "down" + netType := "unknown" + + if netIf.Flags&net.FlagBroadcast > 0 { + netType = "broadcast" + } + + if netIf.Flags&net.FlagPointToPoint > 0 { + netType = "point-to-point" + } + + if netIf.Flags&net.FlagLoopback > 0 { + netType = "loopback" + } + + if netIf.Flags&net.FlagUp > 0 { + netState = "up" + } + + network := shared.ContainerStateNetwork{ + Addresses: []shared.ContainerStateNetworkAddress{}, + Counters: shared.ContainerStateNetworkCounters{}, + Hwaddr: netIf.HardwareAddr.String(), + Mtu: netIf.MTU, + State: netState, + Type: netType, + } + + addrs, err := netIf.Addrs() + if err == nil { + for _, addr := range addrs { + fields := strings.SplitN(addr.String(), "/", 2) + if len(fields) != 2 { + continue + } + + family := "inet" + if strings.Contains(fields[0], ":") { + family = "inet6" + } + + scope := "global" + if strings.HasPrefix(fields[0], "127") { + scope = "local" + } + + if fields[0] == "::1" { + scope = "local" + } + + if strings.HasPrefix(fields[0], "169.254") { + scope = "link" + } + + if strings.HasPrefix(fields[0], "fe80:") { + scope = "link" + } + + address := shared.ContainerStateNetworkAddress{} + address.Family = family + address.Address = fields[0] + address.Netmask = fields[1] + address.Scope = scope + + network.Addresses = append(network.Addresses, address) + } + } + + counters, ok := stats[netIf.Name] + if ok { + network.Counters.BytesReceived = counters[0] + network.Counters.PacketsReceived = counters[1] + network.Counters.BytesSent = counters[2] + network.Counters.PacketsSent = counters[3] + } + + networks[netIf.Name] = network + } + + buf, err := json.Marshal(networks) + if err != nil { + return err + } + + fmt.Printf("%s\n", buf) + + return nil +} diff -Nru lxd-2.6.2/lxd/main_forkmigrate.go lxd-2.7/lxd/main_forkmigrate.go --- lxd-2.6.2/lxd/main_forkmigrate.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_forkmigrate.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "os" + "strconv" + + "gopkg.in/lxc/go-lxc.v2" +) + +/* + * Similar to forkstart, this is called when lxd is invoked as: + * + * lxd forkmigrate + * + * liblxc's restore() sets up the processes in such a way that the monitor ends + * up being a child of the process that calls it, in our case lxd. However, we + * really want the monitor to be daemonized, so we fork again. Additionally, we + * want to fork for the same reasons we do forkstart (i.e. reduced memory + * footprint when we fork tasks that will never free golang's memory, etc.) + */ +func cmdForkMigrate(args []string) error { + if len(args) != 6 { + return fmt.Errorf("Bad arguments %q", args) + } + + name := args[1] + lxcpath := args[2] + configPath := args[3] + imagesDir := args[4] + preservesInodes, err := strconv.ParseBool(args[5]) + + c, err := lxc.NewContainer(name, lxcpath) + if err != nil { + return err + } + + if err := c.LoadConfigFile(configPath); err != nil { + return err + } + + /* see https://github.com/golang/go/issues/13155, startContainer, and dc3a229 */ + os.Stdin.Close() + os.Stdout.Close() + os.Stderr.Close() + + return c.Migrate(lxc.MIGRATE_RESTORE, lxc.MigrateOptions{ + Directory: imagesDir, + Verbose: true, + PreservesInodes: preservesInodes, + }) +} diff -Nru lxd-2.6.2/lxd/main_forkstart.go lxd-2.7/lxd/main_forkstart.go --- lxd-2.6.2/lxd/main_forkstart.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_forkstart.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + "os" + "syscall" + + "gopkg.in/lxc/go-lxc.v2" + + "github.com/lxc/lxd/shared" +) + +/* + * This is called by lxd when called as "lxd forkstart " + * 'forkstart' is used instead of just 'start' in the hopes that people + * do not accidentally type 'lxd start' instead of 'lxc start' + */ +func cmdForkStart(args []string) error { + if len(args) != 4 { + return fmt.Errorf("Bad arguments: %q", args) + } + + name := args[1] + lxcpath := args[2] + configPath := args[3] + + c, err := lxc.NewContainer(name, lxcpath) + if err != nil { + return fmt.Errorf("Error initializing container for start: %q", err) + } + + err = c.LoadConfigFile(configPath) + if err != nil { + return fmt.Errorf("Error opening startup config file: %q", err) + } + + /* due to https://github.com/golang/go/issues/13155 and the + * CollectOutput call we make for the forkstart process, we need to + * close our stdin/stdout/stderr here. Collecting some of the logs is + * better than collecting no logs, though. + */ + os.Stdin.Close() + os.Stderr.Close() + os.Stdout.Close() + + // Redirect stdout and stderr to a log file + logPath := shared.LogPath(name, "forkstart.log") + if shared.PathExists(logPath) { + os.Remove(logPath) + } + + logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) + if err == nil { + syscall.Dup3(int(logFile.Fd()), 1, 0) + syscall.Dup3(int(logFile.Fd()), 2, 0) + } + + return c.Start() +} diff -Nru lxd-2.6.2/lxd/main.go lxd-2.7/lxd/main.go --- lxd-2.6.2/lxd/main.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/main.go 2016-12-21 00:52:23.000000000 +0000 @@ -1,29 +1,15 @@ package main import ( - "bufio" - "encoding/json" "fmt" - "io/ioutil" "math/rand" - "net" - "net/http" "os" - "os/exec" - "os/signal" - "runtime/pprof" - "strconv" - "strings" - "sync" - "syscall" "time" - "golang.org/x/crypto/ssh/terminal" - - "github.com/lxc/lxd" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/gnuflag" "github.com/lxc/lxd/shared/logging" + "github.com/lxc/lxd/shared/version" ) // Global arguments @@ -88,6 +74,8 @@ fmt.Printf(" Perform a clean shutdown of LXD and all running containers\n") fmt.Printf(" waitready [--timeout=15]\n") fmt.Printf(" Wait until LXD is ready to handle requests\n") + fmt.Printf(" import \n") + fmt.Printf(" Import a pre-existing container from storage\n") fmt.Printf("\n\nCommon options:\n") fmt.Printf(" --debug\n") @@ -181,7 +169,7 @@ // Deal with --version right here if *argVersion { - fmt.Println(shared.Version) + fmt.Println(version.Version) return nil } @@ -223,22 +211,24 @@ return cmdShutdown() case "waitready": return cmdWaitReady() + case "import": + return cmdImport(os.Args[1:]) // Internal commands case "forkgetnet": - return printnet() + return cmdForkGetNet() case "forkmigrate": - return MigrateContainer(os.Args[1:]) + return cmdForkMigrate(os.Args[1:]) case "forkstart": - return startContainer(os.Args[1:]) + return cmdForkStart(os.Args[1:]) case "forkexec": - ret, err := execContainer(os.Args[1:]) + ret, err := cmdForkExec(os.Args[1:]) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) } os.Exit(ret) case "netcat": - return Netcat(os.Args[1:]) + return cmdNetcat(os.Args[1:]) case "migratedumpsuccess": return cmdMigrateDumpSuccess(os.Args[1:]) } @@ -252,969 +242,3 @@ return cmdDaemon() } - -func cmdCallHook(args []string) error { - if len(args) < 4 { - return fmt.Errorf("Invalid arguments") - } - - path := args[1] - id := args[2] - state := args[3] - target := "" - - err := os.Setenv("LXD_DIR", path) - if err != nil { - return err - } - - c, err := lxd.NewClient(&lxd.DefaultConfig, "local") - if err != nil { - return err - } - - url := fmt.Sprintf("%s/internal/containers/%s/on%s", c.BaseURL, id, state) - - if state == "stop" { - target = os.Getenv("LXC_TARGET") - if target == "" { - target = "unknown" - } - url = fmt.Sprintf("%s?target=%s", url, target) - } - - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return err - } - - hook := make(chan error, 1) - go func() { - raw, err := c.Http.Do(req) - if err != nil { - hook <- err - return - } - - _, err = lxd.HoistResponse(raw, lxd.Sync) - if err != nil { - hook <- err - return - } - - hook <- nil - }() - - select { - case err := <-hook: - if err != nil { - return err - } - break - case <-time.After(30 * time.Second): - return fmt.Errorf("Hook didn't finish within 30s") - } - - if target == "reboot" { - return fmt.Errorf("Reboot must be handled by LXD.") - } - - return nil -} - -func cmdDaemon() error { - if *argCPUProfile != "" { - f, err := os.Create(*argCPUProfile) - if err != nil { - fmt.Printf("Error opening cpu profile file: %s\n", err) - return nil - } - pprof.StartCPUProfile(f) - defer pprof.StopCPUProfile() - } - - if *argMemProfile != "" { - go memProfiler(*argMemProfile) - } - - neededPrograms := []string{"dnsmasq", "setfacl", "rsync", "tar", "unsquashfs", "xz"} - for _, p := range neededPrograms { - _, err := exec.LookPath(p) - if err != nil { - return err - } - } - - if *argPrintGoroutinesEvery > 0 { - go func() { - for { - time.Sleep(time.Duration(*argPrintGoroutinesEvery) * time.Second) - shared.PrintStack() - } - }() - } - - d := &Daemon{ - group: *argGroup, - SetupMode: shared.PathExists(shared.VarPath(".setup_mode"))} - err := d.Init() - if err != nil { - if d != nil && d.db != nil { - d.db.Close() - } - return err - } - - var ret error - var wg sync.WaitGroup - wg.Add(1) - - go func() { - ch := make(chan os.Signal) - signal.Notify(ch, syscall.SIGPWR) - sig := <-ch - - shared.LogInfof("Received '%s signal', shutting down containers.", sig) - - containersShutdown(d) - - ret = d.Stop() - wg.Done() - }() - - go func() { - <-d.shutdownChan - - shared.LogInfof("Asked to shutdown by API, shutting down containers.") - - containersShutdown(d) - - ret = d.Stop() - wg.Done() - }() - - go func() { - ch := make(chan os.Signal) - signal.Notify(ch, syscall.SIGINT) - signal.Notify(ch, syscall.SIGQUIT) - signal.Notify(ch, syscall.SIGTERM) - sig := <-ch - - shared.LogInfof("Received '%s signal', exiting.", sig) - ret = d.Stop() - wg.Done() - }() - - wg.Wait() - return ret -} - -func cmdReady() error { - c, err := lxd.NewClient(&lxd.DefaultConfig, "local") - if err != nil { - return err - } - - req, err := http.NewRequest("PUT", c.BaseURL+"/internal/ready", nil) - if err != nil { - return err - } - - raw, err := c.Http.Do(req) - if err != nil { - return err - } - - _, err = lxd.HoistResponse(raw, lxd.Sync) - if err != nil { - return err - } - - return nil -} - -func cmdShutdown() error { - var timeout int - - if *argTimeout == -1 { - timeout = 60 - } else { - timeout = *argTimeout - } - - c, err := lxd.NewClient(&lxd.DefaultConfig, "local") - if err != nil { - return err - } - - req, err := http.NewRequest("PUT", c.BaseURL+"/internal/shutdown", nil) - if err != nil { - return err - } - - _, err = c.Http.Do(req) - if err != nil { - return err - } - - monitor := make(chan error, 1) - go func() { - monitor <- c.Monitor(nil, func(m interface{}) {}) - }() - - select { - case <-monitor: - break - case <-time.After(time.Second * time.Duration(timeout)): - return fmt.Errorf("LXD still running after %ds timeout.", timeout) - } - - return nil -} - -func cmdActivateIfNeeded() error { - // Don't start a full daemon, we just need DB access - d := &Daemon{ - imagesDownloading: map[string]chan bool{}, - imagesDownloadingLock: sync.RWMutex{}, - lxcpath: shared.VarPath("containers"), - } - - if !shared.PathExists(shared.VarPath("lxd.db")) { - shared.LogDebugf("No DB, so no need to start the daemon now.") - return nil - } - - err := initializeDbObject(d, shared.VarPath("lxd.db")) - if err != nil { - return err - } - - /* Load all config values from the database */ - err = daemonConfigInit(d.db) - if err != nil { - return err - } - - // Look for network socket - value := daemonConfig["core.https_address"].Get() - if value != "" { - shared.LogDebugf("Daemon has core.https_address set, activating...") - _, err := lxd.NewClient(&lxd.DefaultConfig, "local") - return err - } - - // Look for auto-started or previously started containers - d.IdmapSet, err = shared.DefaultIdmapSet() - if err != nil { - return err - } - - result, err := dbContainersList(d.db, cTypeRegular) - if err != nil { - return err - } - - for _, name := range result { - c, err := containerLoadByName(d, name) - if err != nil { - return err - } - - config := c.ExpandedConfig() - lastState := config["volatile.last_state.power"] - autoStart := config["boot.autostart"] - - if c.IsRunning() { - shared.LogDebugf("Daemon has running containers, activating...") - _, err := lxd.NewClient(&lxd.DefaultConfig, "local") - return err - } - - if lastState == "RUNNING" || lastState == "Running" || shared.IsTrue(autoStart) { - shared.LogDebugf("Daemon has auto-started containers, activating...") - _, err := lxd.NewClient(&lxd.DefaultConfig, "local") - return err - } - } - - shared.LogDebugf("No need to start the daemon now.") - return nil -} - -func cmdWaitReady() error { - var timeout int - - if *argTimeout == -1 { - timeout = 15 - } else { - timeout = *argTimeout - } - - finger := make(chan error, 1) - go func() { - for { - c, err := lxd.NewClient(&lxd.DefaultConfig, "local") - if err != nil { - time.Sleep(500 * time.Millisecond) - continue - } - - req, err := http.NewRequest("GET", c.BaseURL+"/internal/ready", nil) - if err != nil { - time.Sleep(500 * time.Millisecond) - continue - } - - raw, err := c.Http.Do(req) - if err != nil { - time.Sleep(500 * time.Millisecond) - continue - } - - _, err = lxd.HoistResponse(raw, lxd.Sync) - if err != nil { - time.Sleep(500 * time.Millisecond) - continue - } - - finger <- nil - return - } - }() - - select { - case <-finger: - break - case <-time.After(time.Second * time.Duration(timeout)): - return fmt.Errorf("LXD still not running after %ds timeout.", timeout) - } - - return nil -} - -func cmdInit() error { - var defaultPrivileged int // controls whether we set security.privileged=true - var storageBackend string // dir or zfs - var storageMode string // existing, loop or device - var storageLoopSize int64 // Size in GB - var storageDevice string // Path - var storagePool string // pool name - var networkAddress string // Address - var networkPort int64 // Port - var trustPassword string // Trust password - var imagesAutoUpdate bool // controls whether we set images.auto_update_interval to 0 - var bridgeName string // Bridge name - var bridgeIPv4 string // IPv4 address - var bridgeIPv4Nat bool // IPv4 address - var bridgeIPv6 string // IPv6 address - var bridgeIPv6Nat bool // IPv6 address - - // Detect userns - defaultPrivileged = -1 - runningInUserns = shared.RunningInUserNS() - imagesAutoUpdate = true - - // Only root should run this - if os.Geteuid() != 0 { - return fmt.Errorf("This must be run as root") - } - - backendsAvailable := []string{"dir"} - backendsSupported := []string{"dir", "zfs"} - - // Detect zfs - out, err := exec.LookPath("zfs") - if err == nil && len(out) != 0 && !runningInUserns { - _ = loadModule("zfs") - - err := shared.RunCommand("zpool", "list") - if err == nil { - backendsAvailable = append(backendsAvailable, "zfs") - } - } - - reader := bufio.NewReader(os.Stdin) - - askBool := func(question string, default_ string) bool { - for { - fmt.Printf(question) - input, _ := reader.ReadString('\n') - input = strings.TrimSuffix(input, "\n") - if input == "" { - input = default_ - } - if shared.StringInSlice(strings.ToLower(input), []string{"yes", "y"}) { - return true - } else if shared.StringInSlice(strings.ToLower(input), []string{"no", "n"}) { - return false - } - - fmt.Printf("Invalid input, try again.\n\n") - } - } - - askChoice := func(question string, choices []string, default_ string) string { - for { - fmt.Printf(question) - input, _ := reader.ReadString('\n') - input = strings.TrimSuffix(input, "\n") - if input == "" { - input = default_ - } - if shared.StringInSlice(input, choices) { - return input - } - - fmt.Printf("Invalid input, try again.\n\n") - } - } - - askInt := func(question string, min int64, max int64, default_ string) int64 { - for { - fmt.Printf(question) - input, _ := reader.ReadString('\n') - input = strings.TrimSuffix(input, "\n") - if input == "" { - input = default_ - } - intInput, err := strconv.ParseInt(input, 10, 64) - - if err == nil && (min == -1 || intInput >= min) && (max == -1 || intInput <= max) { - return intInput - } - - fmt.Printf("Invalid input, try again.\n\n") - } - } - - askString := func(question string, default_ string, validate func(string) error) string { - for { - fmt.Printf(question) - input, _ := reader.ReadString('\n') - input = strings.TrimSuffix(input, "\n") - if input == "" { - input = default_ - } - if validate != nil { - result := validate(input) - if result != nil { - fmt.Printf("Invalid input: %s\n\n", result) - continue - } - } - if len(input) != 0 { - return input - } - - fmt.Printf("Invalid input, try again.\n\n") - } - } - - askPassword := func(question string) string { - for { - fmt.Printf(question) - pwd, _ := terminal.ReadPassword(0) - fmt.Printf("\n") - inFirst := string(pwd) - inFirst = strings.TrimSuffix(inFirst, "\n") - - fmt.Printf("Again: ") - pwd, _ = terminal.ReadPassword(0) - fmt.Printf("\n") - inSecond := string(pwd) - inSecond = strings.TrimSuffix(inSecond, "\n") - - if inFirst == inSecond { - return inFirst - } - - fmt.Printf("Invalid input, try again.\n\n") - } - } - - // Confirm that LXD is online - c, err := lxd.NewClient(&lxd.DefaultConfig, "local") - if err != nil { - return fmt.Errorf("Unable to talk to LXD: %s", err) - } - - // Check that we have no containers or images in the store - containers, err := c.ListContainers() - if err != nil { - return fmt.Errorf("Unable to list the LXD containers: %s", err) - } - - images, err := c.ListImages() - if err != nil { - return fmt.Errorf("Unable to list the LXD images: %s", err) - } - - if len(containers) > 0 || len(images) > 0 { - return fmt.Errorf("You have existing containers or images. lxd init requires an empty LXD.") - } - - if *argAuto { - if *argStorageBackend == "" { - *argStorageBackend = "dir" - } - - // Do a bunch of sanity checks - if !shared.StringInSlice(*argStorageBackend, backendsSupported) { - return fmt.Errorf("The requested backend '%s' isn't supported by lxd init.", *argStorageBackend) - } - - if !shared.StringInSlice(*argStorageBackend, backendsAvailable) { - return fmt.Errorf("The requested backend '%s' isn't available on your system (missing tools).", *argStorageBackend) - } - - if *argStorageBackend == "dir" { - if *argStorageCreateLoop != -1 || *argStorageCreateDevice != "" || *argStoragePool != "" { - return fmt.Errorf("None of --storage-pool, --storage-create-device or --storage-create-loop may be used with the 'dir' backend.") - } - } - - if *argStorageBackend == "zfs" { - if *argStorageCreateLoop != -1 && *argStorageCreateDevice != "" { - return fmt.Errorf("Only one of --storage-create-device or --storage-create-loop can be specified with the 'zfs' backend.") - } - - if *argStoragePool == "" { - return fmt.Errorf("--storage-pool must be specified with the 'zfs' backend.") - } - } - - if *argNetworkAddress == "" { - if *argNetworkPort != -1 { - return fmt.Errorf("--network-port cannot be used without --network-address.") - } - if *argTrustPassword != "" { - return fmt.Errorf("--trust-password cannot be used without --network-address.") - } - } - - // Set the local variables - if *argStorageCreateDevice != "" { - storageMode = "device" - } else if *argStorageCreateLoop != -1 { - storageMode = "loop" - } else { - storageMode = "existing" - } - - storageBackend = *argStorageBackend - storageLoopSize = *argStorageCreateLoop - storageDevice = *argStorageCreateDevice - storagePool = *argStoragePool - networkAddress = *argNetworkAddress - networkPort = *argNetworkPort - trustPassword = *argTrustPassword - } else { - if *argStorageBackend != "" || *argStorageCreateDevice != "" || *argStorageCreateLoop != -1 || *argStoragePool != "" || *argNetworkAddress != "" || *argNetworkPort != -1 || *argTrustPassword != "" { - return fmt.Errorf("Init configuration is only valid with --auto") - } - - defaultStorage := "dir" - if shared.StringInSlice("zfs", backendsAvailable) { - defaultStorage = "zfs" - } - - storageBackend = askChoice(fmt.Sprintf("Name of the storage backend to use (dir or zfs) [default=%s]: ", defaultStorage), backendsSupported, defaultStorage) - - if !shared.StringInSlice(storageBackend, backendsSupported) { - return fmt.Errorf("The requested backend '%s' isn't supported by lxd init.", storageBackend) - } - - if !shared.StringInSlice(storageBackend, backendsAvailable) { - return fmt.Errorf("The requested backend '%s' isn't available on your system (missing tools).", storageBackend) - } - - if storageBackend == "zfs" { - if askBool("Create a new ZFS pool (yes/no) [default=yes]? ", "yes") { - storagePool = askString("Name of the new ZFS pool [default=lxd]: ", "lxd", nil) - if askBool("Would you like to use an existing block device (yes/no) [default=no]? ", "no") { - deviceExists := func(path string) error { - if !shared.IsBlockdevPath(path) { - return fmt.Errorf("'%s' is not a block device", path) - } - return nil - } - storageDevice = askString("Path to the existing block device: ", "", deviceExists) - storageMode = "device" - } else { - st := syscall.Statfs_t{} - err := syscall.Statfs(shared.VarPath(), &st) - if err != nil { - return fmt.Errorf("couldn't statfs %s: %s", shared.VarPath(), err) - } - - /* choose 15 GB < x < 100GB, where x is 20% of the disk size */ - def := uint64(st.Frsize) * st.Blocks / (1024 * 1024 * 1024) / 5 - if def > 100 { - def = 100 - } - if def < 15 { - def = 15 - } - - q := fmt.Sprintf("Size in GB of the new loop device (1GB minimum) [default=%d]: ", def) - storageLoopSize = askInt(q, 1, -1, fmt.Sprintf("%d", def)) - storageMode = "loop" - } - } else { - storagePool = askString("Name of the existing ZFS pool or dataset: ", "", nil) - storageMode = "existing" - } - } - - if runningInUserns { - fmt.Printf(` -We detected that you are running inside an unprivileged container. -This means that unless you manually configured your host otherwise, -you will not have enough uid and gid to allocate to your containers. - -LXD can re-use your container's own allocation to avoid the problem. -Doing so makes your nested containers slightly less safe as they could -in theory attack their parent container and gain more privileges than -they otherwise would. - -`) - if askBool("Would you like to have your containers share their parent's allocation (yes/no) [default=yes]? ", "yes") { - defaultPrivileged = 1 - } else { - defaultPrivileged = 0 - } - } - - if askBool("Would you like LXD to be available over the network (yes/no) [default=no]? ", "no") { - isIPAddress := func(s string) error { - if s != "all" && net.ParseIP(s) == nil { - return fmt.Errorf("'%s' is not an IP address", s) - } - return nil - } - - networkAddress = askString("Address to bind LXD to (not including port) [default=all]: ", "all", isIPAddress) - if networkAddress == "all" { - networkAddress = "::" - } - - if net.ParseIP(networkAddress).To4() == nil { - networkAddress = fmt.Sprintf("[%s]", networkAddress) - } - networkPort = askInt("Port to bind LXD to [default=8443]: ", 1, 65535, "8443") - trustPassword = askPassword("Trust password for new clients: ") - } - - if !askBool("Would you like stale cached images to be updated automatically (yes/no) [default=yes]? ", "yes") { - imagesAutoUpdate = false - } - - if askBool("Would you like to create a new network bridge (yes/no) [default=yes]? ", "yes") { - bridgeName = askString("What should the new bridge be called [default=lxdbr0]? ", "lxdbr0", networkValidName) - bridgeIPv4 = askString("What IPv4 subnet should be used (CIDR notation, “auto” or “none”) [default=auto]? ", "auto", func(value string) error { - if shared.StringInSlice(value, []string{"auto", "none"}) { - return nil - } - return networkValidAddressCIDRV4(value) - }) - - if !shared.StringInSlice(bridgeIPv4, []string{"auto", "none"}) { - bridgeIPv4Nat = askBool("Would you like LXD to NAT IPv4 traffic on your bridge? [default=yes]? ", "yes") - } - - bridgeIPv6 = askString("What IPv6 subnet should be used (CIDR notation, “auto” or “none”) [default=auto]? ", "auto", func(value string) error { - if shared.StringInSlice(value, []string{"auto", "none"}) { - return nil - } - return networkValidAddressCIDRV6(value) - }) - - if !shared.StringInSlice(bridgeIPv6, []string{"auto", "none"}) { - bridgeIPv6Nat = askBool("Would you like LXD to NAT IPv6 traffic on your bridge? [default=yes]? ", "yes") - } - } - } - - if !shared.StringInSlice(storageBackend, []string{"dir", "zfs"}) { - return fmt.Errorf("Invalid storage backend: %s", storageBackend) - } - - // Unset all storage keys, core.https_address and core.trust_password - for _, key := range []string{"storage.zfs_pool_name", "core.https_address", "core.trust_password"} { - _, err = c.SetServerConfig(key, "") - if err != nil { - return err - } - } - - // Destroy any existing loop device - for _, file := range []string{"zfs.img"} { - os.Remove(shared.VarPath(file)) - } - - if storageBackend == "zfs" { - if storageMode == "loop" { - storageDevice = shared.VarPath("zfs.img") - f, err := os.Create(storageDevice) - if err != nil { - return fmt.Errorf("Failed to open %s: %s", storageDevice, err) - } - - err = f.Chmod(0600) - if err != nil { - return fmt.Errorf("Failed to chmod %s: %s", storageDevice, err) - } - - err = f.Truncate(int64(storageLoopSize * 1024 * 1024 * 1024)) - if err != nil { - return fmt.Errorf("Failed to create sparse file %s: %s", storageDevice, err) - } - - err = f.Close() - if err != nil { - return fmt.Errorf("Failed to close %s: %s", storageDevice, err) - } - } - - if shared.StringInSlice(storageMode, []string{"loop", "device"}) { - output, err := exec.Command( - "zpool", - "create", storagePool, storageDevice, - "-f", "-m", "none", "-O", "compression=on").CombinedOutput() - if err != nil { - return fmt.Errorf("Failed to create the ZFS pool: %s", output) - } - } - - // Configure LXD to use the pool - _, err = c.SetServerConfig("storage.zfs_pool_name", storagePool) - if err != nil { - return err - } - } - - if defaultPrivileged == 0 { - err = c.SetProfileConfigItem("default", "security.privileged", "") - if err != nil { - return err - } - } else if defaultPrivileged == 1 { - err = c.SetProfileConfigItem("default", "security.privileged", "true") - if err != nil { - } - } - - if imagesAutoUpdate { - ss, err := c.ServerStatus() - if err != nil { - return err - } - if val, ok := ss.Config["images.auto_update_interval"]; ok && val == "0" { - _, err = c.SetServerConfig("images.auto_update_interval", "") - if err != nil { - return err - } - } - } else { - _, err = c.SetServerConfig("images.auto_update_interval", "0") - if err != nil { - return err - } - } - - if networkAddress != "" { - _, err = c.SetServerConfig("core.https_address", fmt.Sprintf("%s:%d", networkAddress, networkPort)) - if err != nil { - return err - } - - if trustPassword != "" { - _, err = c.SetServerConfig("core.trust_password", trustPassword) - if err != nil { - return err - } - } - } - - if bridgeName != "" { - bridgeConfig := map[string]string{} - bridgeConfig["ipv4.address"] = bridgeIPv4 - bridgeConfig["ipv6.address"] = bridgeIPv6 - - if bridgeIPv4Nat { - bridgeConfig["ipv4.nat"] = "true" - } - - if bridgeIPv6Nat { - bridgeConfig["ipv6.nat"] = "true" - } - - err = c.NetworkCreate(bridgeName, bridgeConfig) - if err != nil { - return err - } - - props := []string{"nictype=bridged", fmt.Sprintf("parent=%s", bridgeName)} - _, err = c.ProfileDeviceAdd("default", "eth0", "nic", props) - if err != nil { - return err - } - } - - fmt.Printf("LXD has been successfully configured.\n") - return nil -} - -func printnet() error { - networks := map[string]shared.ContainerStateNetwork{} - - interfaces, err := net.Interfaces() - if err != nil { - return err - } - - stats := map[string][]int64{} - - content, err := ioutil.ReadFile("/proc/net/dev") - if err == nil { - for _, line := range strings.Split(string(content), "\n") { - fields := strings.Fields(line) - - if len(fields) != 17 { - continue - } - - rxBytes, err := strconv.ParseInt(fields[1], 10, 64) - if err != nil { - continue - } - - rxPackets, err := strconv.ParseInt(fields[2], 10, 64) - if err != nil { - continue - } - - txBytes, err := strconv.ParseInt(fields[9], 10, 64) - if err != nil { - continue - } - - txPackets, err := strconv.ParseInt(fields[10], 10, 64) - if err != nil { - continue - } - - intName := strings.TrimSuffix(fields[0], ":") - stats[intName] = []int64{rxBytes, rxPackets, txBytes, txPackets} - } - } - - for _, netIf := range interfaces { - netState := "down" - netType := "unknown" - - if netIf.Flags&net.FlagBroadcast > 0 { - netType = "broadcast" - } - - if netIf.Flags&net.FlagPointToPoint > 0 { - netType = "point-to-point" - } - - if netIf.Flags&net.FlagLoopback > 0 { - netType = "loopback" - } - - if netIf.Flags&net.FlagUp > 0 { - netState = "up" - } - - network := shared.ContainerStateNetwork{ - Addresses: []shared.ContainerStateNetworkAddress{}, - Counters: shared.ContainerStateNetworkCounters{}, - Hwaddr: netIf.HardwareAddr.String(), - Mtu: netIf.MTU, - State: netState, - Type: netType, - } - - addrs, err := netIf.Addrs() - if err == nil { - for _, addr := range addrs { - fields := strings.SplitN(addr.String(), "/", 2) - if len(fields) != 2 { - continue - } - - family := "inet" - if strings.Contains(fields[0], ":") { - family = "inet6" - } - - scope := "global" - if strings.HasPrefix(fields[0], "127") { - scope = "local" - } - - if fields[0] == "::1" { - scope = "local" - } - - if strings.HasPrefix(fields[0], "169.254") { - scope = "link" - } - - if strings.HasPrefix(fields[0], "fe80:") { - scope = "link" - } - - address := shared.ContainerStateNetworkAddress{} - address.Family = family - address.Address = fields[0] - address.Netmask = fields[1] - address.Scope = scope - - network.Addresses = append(network.Addresses, address) - } - } - - counters, ok := stats[netIf.Name] - if ok { - network.Counters.BytesReceived = counters[0] - network.Counters.PacketsReceived = counters[1] - network.Counters.BytesSent = counters[2] - network.Counters.PacketsSent = counters[3] - } - - networks[netIf.Name] = network - } - - buf, err := json.Marshal(networks) - if err != nil { - return err - } - - fmt.Printf("%s\n", buf) - - return nil -} - -func cmdMigrateDumpSuccess(args []string) error { - if len(args) != 3 { - return fmt.Errorf("bad migrate dump success args %s", args) - } - - c, err := lxd.NewClient(&lxd.DefaultConfig, "local") - if err != nil { - return err - } - - conn, err := c.Websocket(args[1], args[2]) - if err != nil { - return err - } - conn.Close() - - return c.WaitForSuccess(args[1]) -} diff -Nru lxd-2.6.2/lxd/main_import.go lxd-2.7/lxd/main_import.go --- lxd-2.6.2/lxd/main_import.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_import.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "net/http" + + "github.com/lxc/lxd" +) + +func cmdImport(args []string) error { + name := args[1] + + c, err := lxd.NewClient(&lxd.DefaultConfig, "local") + if err != nil { + return err + } + + url := fmt.Sprintf("%s/internal/containers?target=%s", c.BaseURL, name) + + req, err := http.NewRequest("POST", url, nil) + if err != nil { + return err + } + + raw, err := c.Http.Do(req) + _, err = lxd.HoistResponse(raw, lxd.Sync) + if err != nil { + return err + } + + return nil +} diff -Nru lxd-2.6.2/lxd/main_init.go lxd-2.7/lxd/main_init.go --- lxd-2.6.2/lxd/main_init.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_init.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,490 @@ +package main + +import ( + "bufio" + "fmt" + "net" + "os" + "os/exec" + "strconv" + "strings" + "syscall" + + "golang.org/x/crypto/ssh/terminal" + + "github.com/lxc/lxd" + "github.com/lxc/lxd/shared" +) + +func cmdInit() error { + var defaultPrivileged int // controls whether we set security.privileged=true + var storageBackend string // dir or zfs + var storageMode string // existing, loop or device + var storageLoopSize int64 // Size in GB + var storageDevice string // Path + var storagePool string // pool name + var networkAddress string // Address + var networkPort int64 // Port + var trustPassword string // Trust password + var imagesAutoUpdate bool // controls whether we set images.auto_update_interval to 0 + var bridgeName string // Bridge name + var bridgeIPv4 string // IPv4 address + var bridgeIPv4Nat bool // IPv4 address + var bridgeIPv6 string // IPv6 address + var bridgeIPv6Nat bool // IPv6 address + + // Detect userns + defaultPrivileged = -1 + runningInUserns = shared.RunningInUserNS() + imagesAutoUpdate = true + + // Only root should run this + if os.Geteuid() != 0 { + return fmt.Errorf("This must be run as root") + } + + backendsAvailable := []string{"dir"} + backendsSupported := []string{"dir", "zfs"} + + // Detect zfs + out, err := exec.LookPath("zfs") + if err == nil && len(out) != 0 && !runningInUserns { + _ = loadModule("zfs") + + err := shared.RunCommand("zpool", "list") + if err == nil { + backendsAvailable = append(backendsAvailable, "zfs") + } + } + + reader := bufio.NewReader(os.Stdin) + + askBool := func(question string, default_ string) bool { + for { + fmt.Printf(question) + input, _ := reader.ReadString('\n') + input = strings.TrimSuffix(input, "\n") + if input == "" { + input = default_ + } + if shared.StringInSlice(strings.ToLower(input), []string{"yes", "y"}) { + return true + } else if shared.StringInSlice(strings.ToLower(input), []string{"no", "n"}) { + return false + } + + fmt.Printf("Invalid input, try again.\n\n") + } + } + + askChoice := func(question string, choices []string, default_ string) string { + for { + fmt.Printf(question) + input, _ := reader.ReadString('\n') + input = strings.TrimSuffix(input, "\n") + if input == "" { + input = default_ + } + if shared.StringInSlice(input, choices) { + return input + } + + fmt.Printf("Invalid input, try again.\n\n") + } + } + + askInt := func(question string, min int64, max int64, default_ string) int64 { + for { + fmt.Printf(question) + input, _ := reader.ReadString('\n') + input = strings.TrimSuffix(input, "\n") + if input == "" { + input = default_ + } + intInput, err := strconv.ParseInt(input, 10, 64) + + if err == nil && (min == -1 || intInput >= min) && (max == -1 || intInput <= max) { + return intInput + } + + fmt.Printf("Invalid input, try again.\n\n") + } + } + + askString := func(question string, default_ string, validate func(string) error) string { + for { + fmt.Printf(question) + input, _ := reader.ReadString('\n') + input = strings.TrimSuffix(input, "\n") + if input == "" { + input = default_ + } + if validate != nil { + result := validate(input) + if result != nil { + fmt.Printf("Invalid input: %s\n\n", result) + continue + } + } + if len(input) != 0 { + return input + } + + fmt.Printf("Invalid input, try again.\n\n") + } + } + + askPassword := func(question string) string { + for { + fmt.Printf(question) + pwd, _ := terminal.ReadPassword(0) + fmt.Printf("\n") + inFirst := string(pwd) + inFirst = strings.TrimSuffix(inFirst, "\n") + + fmt.Printf("Again: ") + pwd, _ = terminal.ReadPassword(0) + fmt.Printf("\n") + inSecond := string(pwd) + inSecond = strings.TrimSuffix(inSecond, "\n") + + if inFirst == inSecond { + return inFirst + } + + fmt.Printf("Invalid input, try again.\n\n") + } + } + + // Confirm that LXD is online + c, err := lxd.NewClient(&lxd.DefaultConfig, "local") + if err != nil { + return fmt.Errorf("Unable to talk to LXD: %s", err) + } + + // Check that we have no containers or images in the store + containers, err := c.ListContainers() + if err != nil { + return fmt.Errorf("Unable to list the LXD containers: %s", err) + } + + images, err := c.ListImages() + if err != nil { + return fmt.Errorf("Unable to list the LXD images: %s", err) + } + + if len(containers) > 0 || len(images) > 0 { + return fmt.Errorf("You have existing containers or images. lxd init requires an empty LXD.") + } + + if *argAuto { + if *argStorageBackend == "" { + *argStorageBackend = "dir" + } + + // Do a bunch of sanity checks + if !shared.StringInSlice(*argStorageBackend, backendsSupported) { + return fmt.Errorf("The requested backend '%s' isn't supported by lxd init.", *argStorageBackend) + } + + if !shared.StringInSlice(*argStorageBackend, backendsAvailable) { + return fmt.Errorf("The requested backend '%s' isn't available on your system (missing tools).", *argStorageBackend) + } + + if *argStorageBackend == "dir" { + if *argStorageCreateLoop != -1 || *argStorageCreateDevice != "" || *argStoragePool != "" { + return fmt.Errorf("None of --storage-pool, --storage-create-device or --storage-create-loop may be used with the 'dir' backend.") + } + } + + if *argStorageBackend == "zfs" { + if *argStorageCreateLoop != -1 && *argStorageCreateDevice != "" { + return fmt.Errorf("Only one of --storage-create-device or --storage-create-loop can be specified with the 'zfs' backend.") + } + + if *argStoragePool == "" { + return fmt.Errorf("--storage-pool must be specified with the 'zfs' backend.") + } + } + + if *argNetworkAddress == "" { + if *argNetworkPort != -1 { + return fmt.Errorf("--network-port cannot be used without --network-address.") + } + if *argTrustPassword != "" { + return fmt.Errorf("--trust-password cannot be used without --network-address.") + } + } + + // Set the local variables + if *argStorageCreateDevice != "" { + storageMode = "device" + } else if *argStorageCreateLoop != -1 { + storageMode = "loop" + } else { + storageMode = "existing" + } + + storageBackend = *argStorageBackend + storageLoopSize = *argStorageCreateLoop + storageDevice = *argStorageCreateDevice + storagePool = *argStoragePool + networkAddress = *argNetworkAddress + networkPort = *argNetworkPort + trustPassword = *argTrustPassword + } else { + if *argStorageBackend != "" || *argStorageCreateDevice != "" || *argStorageCreateLoop != -1 || *argStoragePool != "" || *argNetworkAddress != "" || *argNetworkPort != -1 || *argTrustPassword != "" { + return fmt.Errorf("Init configuration is only valid with --auto") + } + + defaultStorage := "dir" + if shared.StringInSlice("zfs", backendsAvailable) { + defaultStorage = "zfs" + } + + storageBackend = askChoice(fmt.Sprintf("Name of the storage backend to use (dir or zfs) [default=%s]: ", defaultStorage), backendsSupported, defaultStorage) + + if !shared.StringInSlice(storageBackend, backendsSupported) { + return fmt.Errorf("The requested backend '%s' isn't supported by lxd init.", storageBackend) + } + + if !shared.StringInSlice(storageBackend, backendsAvailable) { + return fmt.Errorf("The requested backend '%s' isn't available on your system (missing tools).", storageBackend) + } + + if storageBackend == "zfs" { + if askBool("Create a new ZFS pool (yes/no) [default=yes]? ", "yes") { + storagePool = askString("Name of the new ZFS pool [default=lxd]: ", "lxd", nil) + if askBool("Would you like to use an existing block device (yes/no) [default=no]? ", "no") { + deviceExists := func(path string) error { + if !shared.IsBlockdevPath(path) { + return fmt.Errorf("'%s' is not a block device", path) + } + return nil + } + storageDevice = askString("Path to the existing block device: ", "", deviceExists) + storageMode = "device" + } else { + st := syscall.Statfs_t{} + err := syscall.Statfs(shared.VarPath(), &st) + if err != nil { + return fmt.Errorf("couldn't statfs %s: %s", shared.VarPath(), err) + } + + /* choose 15 GB < x < 100GB, where x is 20% of the disk size */ + def := uint64(st.Frsize) * st.Blocks / (1024 * 1024 * 1024) / 5 + if def > 100 { + def = 100 + } + if def < 15 { + def = 15 + } + + q := fmt.Sprintf("Size in GB of the new loop device (1GB minimum) [default=%d]: ", def) + storageLoopSize = askInt(q, 1, -1, fmt.Sprintf("%d", def)) + storageMode = "loop" + } + } else { + storagePool = askString("Name of the existing ZFS pool or dataset: ", "", nil) + storageMode = "existing" + } + } + + if runningInUserns { + fmt.Printf(` +We detected that you are running inside an unprivileged container. +This means that unless you manually configured your host otherwise, +you will not have enough uid and gid to allocate to your containers. + +LXD can re-use your container's own allocation to avoid the problem. +Doing so makes your nested containers slightly less safe as they could +in theory attack their parent container and gain more privileges than +they otherwise would. + +`) + if askBool("Would you like to have your containers share their parent's allocation (yes/no) [default=yes]? ", "yes") { + defaultPrivileged = 1 + } else { + defaultPrivileged = 0 + } + } + + if askBool("Would you like LXD to be available over the network (yes/no) [default=no]? ", "no") { + isIPAddress := func(s string) error { + if s != "all" && net.ParseIP(s) == nil { + return fmt.Errorf("'%s' is not an IP address", s) + } + return nil + } + + networkAddress = askString("Address to bind LXD to (not including port) [default=all]: ", "all", isIPAddress) + if networkAddress == "all" { + networkAddress = "::" + } + + if net.ParseIP(networkAddress).To4() == nil { + networkAddress = fmt.Sprintf("[%s]", networkAddress) + } + networkPort = askInt("Port to bind LXD to [default=8443]: ", 1, 65535, "8443") + trustPassword = askPassword("Trust password for new clients: ") + } + + if !askBool("Would you like stale cached images to be updated automatically (yes/no) [default=yes]? ", "yes") { + imagesAutoUpdate = false + } + + if askBool("Would you like to create a new network bridge (yes/no) [default=yes]? ", "yes") { + bridgeName = askString("What should the new bridge be called [default=lxdbr0]? ", "lxdbr0", networkValidName) + bridgeIPv4 = askString("What IPv4 subnet should be used (CIDR notation, “auto” or “none”) [default=auto]? ", "auto", func(value string) error { + if shared.StringInSlice(value, []string{"auto", "none"}) { + return nil + } + return networkValidAddressCIDRV4(value) + }) + + if !shared.StringInSlice(bridgeIPv4, []string{"auto", "none"}) { + bridgeIPv4Nat = askBool("Would you like LXD to NAT IPv4 traffic on your bridge? [default=yes]? ", "yes") + } + + bridgeIPv6 = askString("What IPv6 subnet should be used (CIDR notation, “auto” or “none”) [default=auto]? ", "auto", func(value string) error { + if shared.StringInSlice(value, []string{"auto", "none"}) { + return nil + } + return networkValidAddressCIDRV6(value) + }) + + if !shared.StringInSlice(bridgeIPv6, []string{"auto", "none"}) { + bridgeIPv6Nat = askBool("Would you like LXD to NAT IPv6 traffic on your bridge? [default=yes]? ", "yes") + } + } + } + + if !shared.StringInSlice(storageBackend, []string{"dir", "zfs"}) { + return fmt.Errorf("Invalid storage backend: %s", storageBackend) + } + + // Unset all storage keys, core.https_address and core.trust_password + for _, key := range []string{"storage.zfs_pool_name", "core.https_address", "core.trust_password"} { + _, err = c.SetServerConfig(key, "") + if err != nil { + return err + } + } + + // Destroy any existing loop device + for _, file := range []string{"zfs.img"} { + os.Remove(shared.VarPath(file)) + } + + if storageBackend == "zfs" { + if storageMode == "loop" { + storageDevice = shared.VarPath("zfs.img") + f, err := os.Create(storageDevice) + if err != nil { + return fmt.Errorf("Failed to open %s: %s", storageDevice, err) + } + + err = f.Chmod(0600) + if err != nil { + return fmt.Errorf("Failed to chmod %s: %s", storageDevice, err) + } + + err = f.Truncate(int64(storageLoopSize * 1024 * 1024 * 1024)) + if err != nil { + return fmt.Errorf("Failed to create sparse file %s: %s", storageDevice, err) + } + + err = f.Close() + if err != nil { + return fmt.Errorf("Failed to close %s: %s", storageDevice, err) + } + } + + if shared.StringInSlice(storageMode, []string{"loop", "device"}) { + output, err := exec.Command( + "zpool", + "create", storagePool, storageDevice, + "-f", "-m", "none", "-O", "compression=on").CombinedOutput() + if err != nil { + return fmt.Errorf("Failed to create the ZFS pool: %s", output) + } + } + + // Configure LXD to use the pool + _, err = c.SetServerConfig("storage.zfs_pool_name", storagePool) + if err != nil { + return err + } + } + + if defaultPrivileged == 0 { + err = c.SetProfileConfigItem("default", "security.privileged", "") + if err != nil { + return err + } + } else if defaultPrivileged == 1 { + err = c.SetProfileConfigItem("default", "security.privileged", "true") + if err != nil { + } + } + + if imagesAutoUpdate { + ss, err := c.ServerStatus() + if err != nil { + return err + } + if val, ok := ss.Config["images.auto_update_interval"]; ok && val == "0" { + _, err = c.SetServerConfig("images.auto_update_interval", "") + if err != nil { + return err + } + } + } else { + _, err = c.SetServerConfig("images.auto_update_interval", "0") + if err != nil { + return err + } + } + + if networkAddress != "" { + _, err = c.SetServerConfig("core.https_address", fmt.Sprintf("%s:%d", networkAddress, networkPort)) + if err != nil { + return err + } + + if trustPassword != "" { + _, err = c.SetServerConfig("core.trust_password", trustPassword) + if err != nil { + return err + } + } + } + + if bridgeName != "" { + bridgeConfig := map[string]string{} + bridgeConfig["ipv4.address"] = bridgeIPv4 + bridgeConfig["ipv6.address"] = bridgeIPv6 + + if bridgeIPv4Nat { + bridgeConfig["ipv4.nat"] = "true" + } + + if bridgeIPv6Nat { + bridgeConfig["ipv6.nat"] = "true" + } + + err = c.NetworkCreate(bridgeName, bridgeConfig) + if err != nil { + return err + } + + props := []string{"nictype=bridged", fmt.Sprintf("parent=%s", bridgeName)} + _, err = c.ProfileDeviceAdd("default", "eth0", "nic", props) + if err != nil { + return err + } + } + + fmt.Printf("LXD has been successfully configured.\n") + return nil +} diff -Nru lxd-2.6.2/lxd/main_migratedumpsuccess.go lxd-2.7/lxd/main_migratedumpsuccess.go --- lxd-2.6.2/lxd/main_migratedumpsuccess.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_migratedumpsuccess.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + + "github.com/lxc/lxd" +) + +func cmdMigrateDumpSuccess(args []string) error { + if len(args) != 3 { + return fmt.Errorf("bad migrate dump success args %s", args) + } + + c, err := lxd.NewClient(&lxd.DefaultConfig, "local") + if err != nil { + return err + } + + conn, err := c.Websocket(args[1], args[2]) + if err != nil { + return err + } + conn.Close() + + return c.WaitForSuccess(args[1]) +} diff -Nru lxd-2.6.2/lxd/main_netcat.go lxd-2.7/lxd/main_netcat.go --- lxd-2.6.2/lxd/main_netcat.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_netcat.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,49 @@ +package main + +import ( + "fmt" + "io" + "net" + "os" + "sync" +) + +// Netcat is called with: +// +// lxd netcat /path/to/unix/socket +// +// and does unbuffered netcatting of to socket to stdin/stdout. Any arguments +// after the path to the unix socket are ignored, so that this can be passed +// directly to rsync as the sync command. +func cmdNetcat(args []string) error { + if len(args) < 2 { + return fmt.Errorf("Bad arguments %q", args) + } + + uAddr, err := net.ResolveUnixAddr("unix", args[1]) + if err != nil { + return err + } + + conn, err := net.DialUnix("unix", nil, uAddr) + if err != nil { + return err + } + + wg := sync.WaitGroup{} + wg.Add(1) + + go func() { + io.Copy(os.Stdout, conn) + conn.Close() + wg.Done() + }() + + go func() { + io.Copy(conn, os.Stdin) + }() + + wg.Wait() + + return nil +} diff -Nru lxd-2.6.2/lxd/main_nsexec.go lxd-2.7/lxd/main_nsexec.go --- lxd-2.6.2/lxd/main_nsexec.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_nsexec.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,683 @@ +/** + * This file is a bit funny. The goal here is to use setns() to manipulate + * files inside the container, so we don't have to reason about the paths to + * make sure they don't escape (we can simply rely on the kernel for + * correctness). Unfortunately, you can't setns() to a mount namespace with a + * multi-threaded program, which every golang binary is. However, by declaring + * our init as an initializer, we can capture process control before it is + * transferred to the golang runtime, so we can then setns() as we'd like + * before golang has a chance to set up any threads. So, we implement two new + * lxd fork* commands which are captured here, and take a file on the host fs + * and copy it into the container ns. + * + * An alternative to this would be to move this code into a separate binary, + * which of course has problems of its own when it comes to packaging (how do + * we find the binary, what do we do if someone does file push and it is + * missing, etc.). After some discussion, even though the embedded method is + * somewhat convoluted, it was preferred. + */ +package main + +/* +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This expects: +// ./lxd forkputfile /source/path /target/path +// or +// ./lxd forkgetfile /target/path /soruce/path +// i.e. 8 arguments, each which have a max length of PATH_MAX. +// Unfortunately, lseek() and fstat() both fail (EINVAL and 0 size) for +// procfs. Also, we can't mmap, because procfs doesn't support that, either. +// +#define CMDLINE_SIZE (8 * PATH_MAX) + +void error(char *msg) +{ + int old_errno = errno; + + if (old_errno == 0) { + fprintf(stderr, "%s\n", msg); + fprintf(stderr, "errno: 0\n"); + return; + } + + perror(msg); + fprintf(stderr, "errno: %d\n", old_errno); +} + +int mkdir_p(const char *dir, mode_t mode) +{ + const char *tmp = dir; + const char *orig = dir; + char *makeme; + + do { + dir = tmp + strspn(tmp, "/"); + tmp = dir + strcspn(dir, "/"); + makeme = strndup(orig, dir - orig); + if (*makeme) { + if (mkdir(makeme, mode) && errno != EEXIST) { + fprintf(stderr, "failed to create directory '%s': %s\n", makeme, strerror(errno)); + free(makeme); + return -1; + } + } + free(makeme); + } while(tmp != dir); + + return 0; +} + +int copy(int target, int source) +{ + ssize_t n; + char buf[1024]; + + if (ftruncate(target, 0) < 0) { + error("error: truncate"); + return -1; + } + + while ((n = read(source, buf, 1024)) > 0) { + if (write(target, buf, n) != n) { + error("error: write"); + return -1; + } + } + + if (n < 0) { + error("error: read"); + return -1; + } + + return 0; +} + +int dosetns(int pid, char *nstype) { + int mntns; + char buf[PATH_MAX]; + + sprintf(buf, "/proc/%d/ns/%s", pid, nstype); + mntns = open(buf, O_RDONLY); + if (mntns < 0) { + error("error: open mntns"); + return -1; + } + + if (setns(mntns, 0) < 0) { + error("error: setns"); + close(mntns); + return -1; + } + close(mntns); + + return 0; +} + +void attach_userns(int pid) { + char nspath[PATH_MAX]; + char userns_source[PATH_MAX]; + char userns_target[PATH_MAX]; + + sprintf(nspath, "/proc/%d/ns/user", pid); + if (access(nspath, F_OK) == 0) { + if (readlink("/proc/self/ns/user", userns_source, 18) < 0) { + fprintf(stderr, "Failed readlink of source namespace: %s\n", strerror(errno)); + _exit(1); + } + + if (readlink(nspath, userns_target, PATH_MAX) < 0) { + fprintf(stderr, "Failed readlink of target namespace: %s\n", strerror(errno)); + _exit(1); + } + + if (strncmp(userns_source, userns_target, PATH_MAX) != 0) { + if (dosetns(pid, "user") < 0) { + fprintf(stderr, "Failed setns to container user namespace: %s\n", strerror(errno)); + _exit(1); + } + + if (setgroups(0, NULL) < 0) { + fprintf(stderr, "Failed setgroups to container root groups: %s\n", strerror(errno)); + _exit(1); + } + + if (setgid(0) < 0) { + fprintf(stderr, "Failed setgid to container root group: %s\n", strerror(errno)); + _exit(1); + } + + if (setuid(0) < 0) { + fprintf(stderr, "Failed setuid to container root user: %s\n", strerror(errno)); + _exit(1); + } + + } + } +} + +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 host_fd = -1, container_fd = -1; + int ret = -1; + int container_open_flags; + struct stat st; + int exists = 1; + bool is_dir_manip = !strcmp(host, ""); + + if (!is_dir_manip) { + host_fd = open(host, O_RDWR); + if (host_fd < 0) { + error("error: open"); + return -1; + } + } + + if (pid > 0) { + attach_userns(pid); + + if (dosetns(pid, "mnt") < 0) { + error("error: setns"); + goto close_host; + } + } else { + if (chroot(rootfs) < 0) { + error("error: chroot"); + goto close_host; + } + + if (chdir("/") < 0) { + error("error: chdir"); + goto close_host; + } + } + + if (is_put && is_dir_manip) { + if (mode == -1) { + mode = defaultMode; + } + + if (uid == -1) { + uid = defaultUid; + } + + if (gid == -1) { + gid = defaultGid; + } + + if (mkdir(container, mode) < 0 && errno != EEXIST) { + error("error: mkdir"); + return -1; + } + + if (chown(container, uid, gid) < 0) { + error("error: chown"); + return -1; + } + + return 0; + } + + if (stat(container, &st) < 0) + exists = 0; + + container_open_flags = O_RDWR; + if (is_put) + container_open_flags |= O_CREAT; + + if (is_put && !is_dir_manip && exists && S_ISDIR(st.st_mode)) { + error("error: Path already exists as a directory"); + goto close_host; + } + + if (exists && S_ISDIR(st.st_mode)) + container_open_flags = O_DIRECTORY; + + umask(0); + container_fd = open(container, container_open_flags, 0); + if (container_fd < 0) { + error("error: open"); + goto close_host; + } + + if (is_put) { + if (!exists) { + if (mode == -1) { + mode = defaultMode; + } + + if (uid == -1) { + uid = defaultUid; + } + + if (gid == -1) { + gid = defaultGid; + } + } + + if (copy(container_fd, host_fd) < 0) { + error("error: copy"); + goto close_container; + } + + if (mode != -1 && fchmod(container_fd, mode) < 0) { + error("error: chmod"); + goto close_container; + } + + if (fchown(container_fd, uid, gid) < 0) { + error("error: chown"); + goto close_container; + } + ret = 0; + } else { + + if (fstat(container_fd, &st) < 0) { + error("error: stat"); + goto close_container; + } + + fprintf(stderr, "uid: %ld\n", (long)st.st_uid); + fprintf(stderr, "gid: %ld\n", (long)st.st_gid); + fprintf(stderr, "mode: %ld\n", (unsigned long)st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + if (S_ISDIR(st.st_mode)) { + DIR *fdir; + struct dirent *de; + + fdir = fdopendir(container_fd); + if (!fdir) { + error("error: fdopendir"); + goto close_container; + } + + fprintf(stderr, "type: directory\n"); + + while((de = readdir(fdir))) { + int len, i; + + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + fprintf(stderr, "entry: "); + + // swap \n to \0 since we split this output by line + for (i = 0, len = strlen(de->d_name); i < len; i++) { + if (*(de->d_name + i) == '\n') + putc(0, stderr); + else + putc(*(de->d_name + i), stderr); + } + fprintf(stderr, "\n"); + } + + // container_fd is dead now that we fopendir'd it + goto close_host; + } else { + fprintf(stderr, "type: file\n"); + ret = copy(host_fd, container_fd); + } + fprintf(stderr, "type: %s", S_ISDIR(st.st_mode) ? "directory" : "file"); + } + +close_container: + close(container_fd); +close_host: + close(host_fd); + return ret; +} + +#define ADVANCE_ARG_REQUIRED() \ + do { \ + while (*cur != 0) \ + cur++; \ + cur++; \ + if (size <= cur - buf) { \ + fprintf(stderr, "not enough arguments\n"); \ + _exit(1); \ + } \ + } while(0) + +void ensure_dir(char *dest) { + struct stat sb; + if (stat(dest, &sb) == 0) { + if ((sb.st_mode & S_IFMT) == S_IFDIR) + return; + if (unlink(dest) < 0) { + fprintf(stderr, "Failed to remove old %s: %s\n", dest, strerror(errno)); + _exit(1); + } + } + if (mkdir(dest, 0755) < 0) { + fprintf(stderr, "Failed to mkdir %s: %s\n", dest, strerror(errno)); + _exit(1); + } +} + +void ensure_file(char *dest) { + struct stat sb; + int fd; + + if (stat(dest, &sb) == 0) { + if ((sb.st_mode & S_IFMT) != S_IFDIR) + return; + if (rmdir(dest) < 0) { + fprintf(stderr, "Failed to remove old %s: %s\n", dest, strerror(errno)); + _exit(1); + } + } + + fd = creat(dest, 0755); + if (fd < 0) { + fprintf(stderr, "Failed to mkdir %s: %s\n", dest, strerror(errno)); + _exit(1); + } + close(fd); +} + +void create(char *src, char *dest) { + char *destdirname; + struct stat sb; + if (stat(src, &sb) < 0) { + fprintf(stderr, "source %s does not exist\n", src); + _exit(1); + } + + destdirname = strdup(dest); + destdirname = dirname(destdirname); + + if (mkdir_p(destdirname, 0755) < 0) { + fprintf(stderr, "failed to create path: %s\n", destdirname); + free(destdirname); + _exit(1); + } + + switch (sb.st_mode & S_IFMT) { + case S_IFDIR: + ensure_dir(dest); + return; + default: + ensure_file(dest); + return; + } + + free(destdirname); +} + +void forkmount(char *buf, char *cur, ssize_t size) { + char *src, *dest, *opts; + + ADVANCE_ARG_REQUIRED(); + int pid = atoi(cur); + + attach_userns(pid); + + if (dosetns(pid, "mnt") < 0) { + fprintf(stderr, "Failed setns to container mount namespace: %s\n", strerror(errno)); + _exit(1); + } + + ADVANCE_ARG_REQUIRED(); + src = cur; + + ADVANCE_ARG_REQUIRED(); + dest = cur; + + create(src, dest); + + if (access(src, F_OK) < 0) { + fprintf(stderr, "Mount source doesn't exist: %s\n", strerror(errno)); + _exit(1); + } + + if (access(dest, F_OK) < 0) { + fprintf(stderr, "Mount destination doesn't exist: %s\n", strerror(errno)); + _exit(1); + } + + // Here, we always move recursively, because we sometimes allow + // recursive mounts. If the mount has no kids then it doesn't matter, + // but if it does, we want to move those too. + if (mount(src, dest, "none", MS_MOVE | MS_REC, NULL) < 0) { + fprintf(stderr, "Failed mounting %s onto %s: %s\n", src, dest, strerror(errno)); + _exit(1); + } + + _exit(0); +} + +void forkumount(char *buf, char *cur, ssize_t size) { + ADVANCE_ARG_REQUIRED(); + int pid = atoi(cur); + + if (dosetns(pid, "mnt") < 0) { + fprintf(stderr, "Failed setns to container mount namespace: %s\n", strerror(errno)); + _exit(1); + } + + ADVANCE_ARG_REQUIRED(); + if (access(cur, F_OK) < 0) { + fprintf(stderr, "Mount path doesn't exist: %s\n", strerror(errno)); + _exit(1); + } + + if (umount2(cur, MNT_DETACH) < 0) { + fprintf(stderr, "Error unmounting %s: %s\n", cur, strerror(errno)); + _exit(1); + } + _exit(0); +} + +void forkdofile(char *buf, char *cur, bool is_put, ssize_t size) { + uid_t uid = 0; + gid_t gid = 0; + mode_t mode = 0; + uid_t defaultUid = 0; + gid_t defaultGid = 0; + mode_t defaultMode = 0; + char *command = cur, *rootfs = NULL, *source = NULL, *target = NULL; + pid_t pid; + + ADVANCE_ARG_REQUIRED(); + rootfs = cur; + + ADVANCE_ARG_REQUIRED(); + pid = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + source = cur; + + ADVANCE_ARG_REQUIRED(); + target = cur; + + if (is_put) { + ADVANCE_ARG_REQUIRED(); + uid = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + gid = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + mode = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + defaultUid = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + defaultGid = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + defaultMode = atoi(cur); + } + + _exit(manip_file_in_ns(rootfs, pid, source, target, is_put, uid, gid, mode, defaultUid, defaultGid, defaultMode)); +} + +void forkcheckfile(char *buf, char *cur, bool is_put, ssize_t size) { + char *command = cur, *rootfs = NULL, *path = NULL; + pid_t pid; + + ADVANCE_ARG_REQUIRED(); + rootfs = cur; + + ADVANCE_ARG_REQUIRED(); + pid = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + path = cur; + + if (pid > 0) { + attach_userns(pid); + + if (dosetns(pid, "mnt") < 0) { + error("error: setns"); + _exit(1); + } + } else { + if (chroot(rootfs) < 0) { + error("error: chroot"); + _exit(1); + } + + if (chdir("/") < 0) { + error("error: chdir"); + _exit(1); + } + } + + if (access(path, F_OK) < 0) { + fprintf(stderr, "Path doesn't exist: %s\n", strerror(errno)); + _exit(1); + } + + _exit(0); +} + +void forkremovefile(char *buf, char *cur, bool is_put, ssize_t size) { + char *command = cur, *rootfs = NULL, *path = NULL; + pid_t pid; + struct stat sb; + + ADVANCE_ARG_REQUIRED(); + rootfs = cur; + + ADVANCE_ARG_REQUIRED(); + pid = atoi(cur); + + ADVANCE_ARG_REQUIRED(); + path = cur; + + if (pid > 0) { + attach_userns(pid); + + if (dosetns(pid, "mnt") < 0) { + error("error: setns"); + _exit(1); + } + } else { + if (chroot(rootfs) < 0) { + error("error: chroot"); + _exit(1); + } + + if (chdir("/") < 0) { + error("error: chdir"); + _exit(1); + } + } + + if (stat(path, &sb) < 0) { + error("error: stat"); + _exit(1); + } + + if ((sb.st_mode & S_IFMT) == S_IFDIR) { + if (rmdir(path) < 0) { + fprintf(stderr, "Failed to remove %s: %s\n", path, strerror(errno)); + _exit(1); + } + } else { + if (unlink(path) < 0) { + fprintf(stderr, "Failed to remove %s: %s\n", path, strerror(errno)); + _exit(1); + } + } + + _exit(0); +} + +void forkgetnet(char *buf, char *cur, ssize_t size) { + ADVANCE_ARG_REQUIRED(); + int pid = atoi(cur); + + if (dosetns(pid, "net") < 0) { + fprintf(stderr, "Failed setns to container network namespace: %s\n", strerror(errno)); + _exit(1); + } + + // The rest happens in Go +} + +__attribute__((constructor)) void init(void) { + int cmdline; + char buf[CMDLINE_SIZE]; + ssize_t size; + char *cur; + + cmdline = open("/proc/self/cmdline", O_RDONLY); + if (cmdline < 0) { + error("error: open"); + _exit(232); + } + + memset(buf, 0, sizeof(buf)); + if ((size = read(cmdline, buf, sizeof(buf)-1)) < 0) { + close(cmdline); + error("error: read"); + _exit(232); + } + close(cmdline); + + cur = buf; + // skip argv[0] + while (*cur != 0) + cur++; + cur++; + if (size <= cur - buf) + return; + + if (strcmp(cur, "forkputfile") == 0) { + forkdofile(buf, cur, true, size); + } else if (strcmp(cur, "forkgetfile") == 0) { + forkdofile(buf, cur, false, size); + } else if (strcmp(cur, "forkcheckfile") == 0) { + forkcheckfile(buf, cur, false, size); + } else if (strcmp(cur, "forkremovefile") == 0) { + forkremovefile(buf, cur, false, size); + } else if (strcmp(cur, "forkmount") == 0) { + forkmount(buf, cur, size); + } else if (strcmp(cur, "forkumount") == 0) { + forkumount(buf, cur, size); + } else if (strcmp(cur, "forkgetnet") == 0) { + forkgetnet(buf, cur, size); + } +} +*/ +import "C" diff -Nru lxd-2.6.2/lxd/main_ready.go lxd-2.7/lxd/main_ready.go --- lxd-2.6.2/lxd/main_ready.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_ready.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,31 @@ +package main + +import ( + "net/http" + + "github.com/lxc/lxd" +) + +func cmdReady() error { + c, err := lxd.NewClient(&lxd.DefaultConfig, "local") + if err != nil { + return err + } + + req, err := http.NewRequest("PUT", c.BaseURL+"/internal/ready", nil) + if err != nil { + return err + } + + raw, err := c.Http.Do(req) + if err != nil { + return err + } + + _, err = lxd.HoistResponse(raw, lxd.Sync) + if err != nil { + return err + } + + return nil +} diff -Nru lxd-2.6.2/lxd/main_shutdown.go lxd-2.7/lxd/main_shutdown.go --- lxd-2.6.2/lxd/main_shutdown.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_shutdown.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "net/http" + "time" + + "github.com/lxc/lxd" +) + +func cmdShutdown() error { + var timeout int + + if *argTimeout == -1 { + timeout = 60 + } else { + timeout = *argTimeout + } + + c, err := lxd.NewClient(&lxd.DefaultConfig, "local") + if err != nil { + return err + } + + req, err := http.NewRequest("PUT", c.BaseURL+"/internal/shutdown", nil) + if err != nil { + return err + } + + _, err = c.Http.Do(req) + if err != nil { + return err + } + + monitor := make(chan error, 1) + go func() { + monitor <- c.Monitor(nil, func(m interface{}) {}) + }() + + select { + case <-monitor: + break + case <-time.After(time.Second * time.Duration(timeout)): + return fmt.Errorf("LXD still running after %ds timeout.", timeout) + } + + return nil +} diff -Nru lxd-2.6.2/lxd/main_waitready.go lxd-2.7/lxd/main_waitready.go --- lxd-2.6.2/lxd/main_waitready.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/lxd/main_waitready.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + "net/http" + "time" + + "github.com/lxc/lxd" +) + +func cmdWaitReady() error { + var timeout int + + if *argTimeout == -1 { + timeout = 15 + } else { + timeout = *argTimeout + } + + finger := make(chan error, 1) + go func() { + for { + c, err := lxd.NewClient(&lxd.DefaultConfig, "local") + if err != nil { + time.Sleep(500 * time.Millisecond) + continue + } + + req, err := http.NewRequest("GET", c.BaseURL+"/internal/ready", nil) + if err != nil { + time.Sleep(500 * time.Millisecond) + continue + } + + raw, err := c.Http.Do(req) + if err != nil { + time.Sleep(500 * time.Millisecond) + continue + } + + _, err = lxd.HoistResponse(raw, lxd.Sync) + if err != nil { + time.Sleep(500 * time.Millisecond) + continue + } + + finger <- nil + return + } + }() + + select { + case <-finger: + break + case <-time.After(time.Second * time.Duration(timeout)): + return fmt.Errorf("LXD still not running after %ds timeout.", timeout) + } + + return nil +} diff -Nru lxd-2.6.2/lxd/migrate.go lxd-2.7/lxd/migrate.go --- lxd-2.6.2/lxd/migrate.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/migrate.go 2016-12-21 00:52:23.000000000 +0000 @@ -12,7 +12,6 @@ "net/url" "os" "path/filepath" - "strconv" "strings" "sync" @@ -876,46 +875,3 @@ } } } - -/* - * Similar to forkstart, this is called when lxd is invoked as: - * - * lxd forkmigrate - * - * liblxc's restore() sets up the processes in such a way that the monitor ends - * up being a child of the process that calls it, in our case lxd. However, we - * really want the monitor to be daemonized, so we fork again. Additionally, we - * want to fork for the same reasons we do forkstart (i.e. reduced memory - * footprint when we fork tasks that will never free golang's memory, etc.) - */ -func MigrateContainer(args []string) error { - if len(args) != 6 { - return fmt.Errorf("Bad arguments %q", args) - } - - name := args[1] - lxcpath := args[2] - configPath := args[3] - imagesDir := args[4] - preservesInodes, err := strconv.ParseBool(args[5]) - - c, err := lxc.NewContainer(name, lxcpath) - if err != nil { - return err - } - - if err := c.LoadConfigFile(configPath); err != nil { - return err - } - - /* see https://github.com/golang/go/issues/13155, startContainer, and dc3a229 */ - os.Stdin.Close() - os.Stdout.Close() - os.Stderr.Close() - - return c.Migrate(lxc.MIGRATE_RESTORE, lxc.MigrateOptions{ - Directory: imagesDir, - Verbose: true, - PreservesInodes: preservesInodes, - }) -} diff -Nru lxd-2.6.2/lxd/networks_config.go lxd-2.7/lxd/networks_config.go --- lxd-2.6.2/lxd/networks_config.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/networks_config.go 2016-12-21 00:52:23.000000000 +0000 @@ -59,9 +59,11 @@ return networkValidAddressCIDRV4(value) }, + "ipv4.firewall": shared.IsBool, "ipv4.nat": shared.IsBool, "ipv4.dhcp": shared.IsBool, "ipv4.dhcp.ranges": shared.IsAny, + "ipv4.routes": shared.IsAny, "ipv4.routing": shared.IsBool, "ipv6.address": func(value string) error { @@ -71,10 +73,12 @@ return networkValidAddressCIDRV6(value) }, + "ipv6.firewall": shared.IsBool, "ipv6.nat": shared.IsBool, "ipv6.dhcp": shared.IsBool, "ipv6.dhcp.stateful": shared.IsBool, "ipv6.dhcp.ranges": shared.IsAny, + "ipv6.routes": shared.IsAny, "ipv6.routing": shared.IsBool, "dns.domain": shared.IsAny, diff -Nru lxd-2.6.2/lxd/networks.go lxd-2.7/lxd/networks.go --- lxd-2.6.2/lxd/networks.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/networks.go 2016-12-21 00:52:23.000000000 +0000 @@ -16,6 +16,7 @@ log "gopkg.in/inconshreveable/log15.v2" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" ) // API endpoints @@ -35,7 +36,7 @@ resultMap := []shared.NetworkConfig{} for _, iface := range ifs { if recursion == 0 { - resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, iface)) + resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", version.APIVersion, iface)) } else { net, err := doNetworkGet(d, iface) if err != nil { @@ -142,7 +143,7 @@ return InternalError(err) } - return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, req.Name)) + return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/networks/%s", version.APIVersion, req.Name)) } var networksCmd = Command{name: "networks", get: networksGet, post: networksPost} @@ -189,7 +190,7 @@ } if networkIsInUse(c, n.Name) { - n.UsedBy = append(n.UsedBy, fmt.Sprintf("/%s/containers/%s", shared.APIVersion, ct)) + n.UsedBy = append(n.UsedBy, fmt.Sprintf("/%s/containers/%s", version.APIVersion, ct)) } } @@ -284,7 +285,7 @@ return SmartError(err) } - return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, req.Name)) + return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/networks/%s", version.APIVersion, req.Name)) } func networkPut(d *Daemon, r *http.Request) Response { @@ -649,25 +650,27 @@ return err } - // Flush all IPv4 addresses + // Flush all IPv4 addresses and routes err = shared.RunCommand("ip", "-4", "addr", "flush", "dev", n.name, "scope", "global") if err != nil { return err } + err = shared.RunCommand("ip", "-4", "route", "flush", "dev", n.name, "proto", "static") + if err != nil { + return err + } + // Configure IPv4 firewall (includes fan) if n.config["bridge.mode"] == "fan" || !shared.StringInSlice(n.config["ipv4.address"], []string{"", "none"}) { // Setup basic iptables overrides rules := [][]string{ []string{"ipv4", n.name, "", "INPUT", "-i", n.name, "-p", "udp", "--dport", "67", "-j", "ACCEPT"}, - []string{"ipv4", n.name, "", "INPUT", "-i", n.name, "-p", "tcp", "--dport", "67", "-j", "ACCEPT"}, []string{"ipv4", n.name, "", "INPUT", "-i", n.name, "-p", "udp", "--dport", "53", "-j", "ACCEPT"}, []string{"ipv4", n.name, "", "INPUT", "-i", n.name, "-p", "tcp", "--dport", "53", "-j", "ACCEPT"}, []string{"ipv4", n.name, "", "OUTPUT", "-o", n.name, "-p", "udp", "--sport", "67", "-j", "ACCEPT"}, - []string{"ipv4", n.name, "", "OUTPUT", "-o", n.name, "-p", "tcp", "--sport", "67", "-j", "ACCEPT"}, []string{"ipv4", n.name, "", "OUTPUT", "-o", n.name, "-p", "udp", "--sport", "53", "-j", "ACCEPT"}, - []string{"ipv4", n.name, "", "OUTPUT", "-o", n.name, "-p", "tcp", "--sport", "53", "-j", "ACCEPT"}, - []string{"ipv4", n.name, "mangle", "POSTROUTING", "-o", n.name, "-p", "udp", "--dport", "68", "-j", "CHECKSUM", "--checksum-fill"}} + []string{"ipv4", n.name, "", "OUTPUT", "-o", n.name, "-p", "tcp", "--sport", "53", "-j", "ACCEPT"}} for _, rule := range rules { err = networkIptablesPrepend(rule[0], rule[1], rule[2], rule[3], rule[4:]...) @@ -676,6 +679,12 @@ } } + // Workaround for broken DHCP clients + err = networkIptablesPrepend("ipv4", n.name, "mangle", "POSTROUTING", "-o", n.name, "-p", "udp", "--dport", "68", "-j", "CHECKSUM", "--checksum-fill") + if err != nil { + return err + } + // Allow forwarding if n.config["bridge.mode"] == "fan" || n.config["ipv4.routing"] == "" || shared.IsTrue(n.config["ipv4.routing"]) { err = networkSysctl("ipv4/ip_forward", "1") @@ -683,24 +692,28 @@ return err } - err = networkIptablesPrepend("ipv4", n.name, "", "FORWARD", "-i", n.name, "-j", "ACCEPT") - if err != nil { - return err - } + if n.config["ipv4.firewall"] == "" || shared.IsTrue(n.config["ipv4.firewall"]) { + err = networkIptablesPrepend("ipv4", n.name, "", "FORWARD", "-i", n.name, "-j", "ACCEPT") + if err != nil { + return err + } - err = networkIptablesPrepend("ipv4", n.name, "", "FORWARD", "-o", n.name, "-j", "ACCEPT") - if err != nil { - return err + err = networkIptablesPrepend("ipv4", n.name, "", "FORWARD", "-o", n.name, "-j", "ACCEPT") + if err != nil { + return err + } } } else { - err = networkIptablesPrepend("ipv4", n.name, "", "FORWARD", "-i", n.name, "-j", "REJECT") - if err != nil { - return err - } + if n.config["ipv4.firewall"] == "" || shared.IsTrue(n.config["ipv4.firewall"]) { + err = networkIptablesPrepend("ipv4", n.name, "", "FORWARD", "-i", n.name, "-j", "REJECT") + if err != nil { + return err + } - err = networkIptablesPrepend("ipv4", n.name, "", "FORWARD", "-o", n.name, "-j", "REJECT") - if err != nil { - return err + err = networkIptablesPrepend("ipv4", n.name, "", "FORWARD", "-o", n.name, "-j", "REJECT") + if err != nil { + return err + } } } } @@ -749,6 +762,17 @@ return err } } + + // Add additional routes + if n.config["ipv4.routes"] != "" { + for _, route := range strings.Split(n.config["ipv4.routes"], ",") { + route = strings.TrimSpace(route) + err = shared.RunCommand("ip", "-4", "route", "add", "dev", n.name, route, "proto", "static") + if err != nil { + return err + } + } + } } // Remove any existing IPv6 iptables rules @@ -762,12 +786,17 @@ return err } - // Flush all IPv6 addresses + // Flush all IPv6 addresses and routes err = shared.RunCommand("ip", "-6", "addr", "flush", "dev", n.name, "scope", "global") if err != nil { return err } + err = shared.RunCommand("ip", "-6", "route", "flush", "dev", n.name, "proto", "static") + if err != nil { + return err + } + // Configure IPv6 if !shared.StringInSlice(n.config["ipv6.address"], []string{"", "none"}) { // Enable IPv6 for the subnet @@ -808,11 +837,9 @@ // Setup basic iptables overrides rules := [][]string{ []string{"ipv6", n.name, "", "INPUT", "-i", n.name, "-p", "udp", "--dport", "546", "-j", "ACCEPT"}, - []string{"ipv6", n.name, "", "INPUT", "-i", n.name, "-p", "tcp", "--dport", "546", "-j", "ACCEPT"}, []string{"ipv6", n.name, "", "INPUT", "-i", n.name, "-p", "udp", "--dport", "53", "-j", "ACCEPT"}, []string{"ipv6", n.name, "", "INPUT", "-i", n.name, "-p", "tcp", "--dport", "53", "-j", "ACCEPT"}, []string{"ipv6", n.name, "", "OUTPUT", "-o", n.name, "-p", "udp", "--sport", "546", "-j", "ACCEPT"}, - []string{"ipv6", n.name, "", "OUTPUT", "-o", n.name, "-p", "tcp", "--sport", "546", "-j", "ACCEPT"}, []string{"ipv6", n.name, "", "OUTPUT", "-o", n.name, "-p", "udp", "--sport", "53", "-j", "ACCEPT"}, []string{"ipv6", n.name, "", "OUTPUT", "-o", n.name, "-p", "tcp", "--sport", "53", "-j", "ACCEPT"}} @@ -852,24 +879,28 @@ } } - err = networkIptablesPrepend("ipv6", n.name, "", "FORWARD", "-i", n.name, "-j", "ACCEPT") - if err != nil { - return err - } + if n.config["ipv6.firewall"] == "" || shared.IsTrue(n.config["ipv6.firewall"]) { + err = networkIptablesPrepend("ipv6", n.name, "", "FORWARD", "-i", n.name, "-j", "ACCEPT") + if err != nil { + return err + } - err = networkIptablesPrepend("ipv6", n.name, "", "FORWARD", "-o", n.name, "-j", "ACCEPT") - if err != nil { - return err + err = networkIptablesPrepend("ipv6", n.name, "", "FORWARD", "-o", n.name, "-j", "ACCEPT") + if err != nil { + return err + } } } else { - err = networkIptablesPrepend("ipv6", n.name, "", "FORWARD", "-i", n.name, "-j", "REJECT") - if err != nil { - return err - } + if n.config["ipv6.firewall"] == "" || shared.IsTrue(n.config["ipv6.firewall"]) { + err = networkIptablesPrepend("ipv6", n.name, "", "FORWARD", "-i", n.name, "-j", "REJECT") + if err != nil { + return err + } - err = networkIptablesPrepend("ipv6", n.name, "", "FORWARD", "-o", n.name, "-j", "REJECT") - if err != nil { - return err + err = networkIptablesPrepend("ipv6", n.name, "", "FORWARD", "-o", n.name, "-j", "REJECT") + if err != nil { + return err + } } } @@ -886,6 +917,17 @@ return err } } + + // Add additional routes + if n.config["ipv6.routes"] != "" { + for _, route := range strings.Split(n.config["ipv6.routes"], ",") { + route = strings.TrimSpace(route) + err = shared.RunCommand("ip", "-6", "route", "add", "dev", n.name, route, "proto", "static") + if err != nil { + return err + } + } + } } // Configure the fan @@ -1114,7 +1156,7 @@ } // Update the static leases - err = networkUpdateStatic(n.daemon) + err = networkUpdateStatic(n.daemon, n.name) if err != nil { return err } diff -Nru lxd-2.6.2/lxd/networks_utils.go lxd-2.7/lxd/networks_utils.go --- lxd-2.6.2/lxd/networks_utils.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/networks_utils.go 2016-12-21 00:52:23.000000000 +0000 @@ -671,24 +671,29 @@ return nil } -func networkUpdateStatic(d *Daemon) error { +func networkUpdateStatic(d *Daemon, name string) error { // Get all the containers containers, err := dbContainersList(d.db, cTypeRegular) if err != nil { return err } - // Get all the networks - networks, err := dbNetworks(d.db) - if err != nil { - return err + networks := []string{} + if name == "" { + // Get all the networks + networks, err = dbNetworks(d.db) + if err != nil { + return err + } + } else { + networks = []string{name} } // Build a list of dhcp host entries entries := map[string][][]string{} - for _, name := range containers { + for _, cName := range containers { // Load the container - c, err := containerLoadByName(d, name) + c, err := containerLoadByName(d, cName) if err != nil { continue } @@ -712,7 +717,7 @@ entries[d["parent"]] = [][]string{} } - entries[d["parent"]] = append(entries[d["parent"]], []string{d["hwaddr"], name, d["ipv4.address"], d["ipv6.address"]}) + entries[d["parent"]] = append(entries[d["parent"]], []string{d["hwaddr"], cName, d["ipv4.address"], d["ipv6.address"]}) } } @@ -741,7 +746,7 @@ lines := []string{} for _, entry := range entries { hwaddr := entry[0] - name := entry[1] + cName := entry[1] ipv4Address := entry[2] ipv6Address := entry[3] @@ -756,7 +761,7 @@ } if config["dns.mode"] == "" || config["dns.mode"] == "managed" { - line += fmt.Sprintf(",%s", name) + line += fmt.Sprintf(",%s", cName) } if line == hwaddr { diff -Nru lxd-2.6.2/lxd/nsexec.go lxd-2.7/lxd/nsexec.go --- lxd-2.6.2/lxd/nsexec.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/nsexec.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,665 +0,0 @@ -/** - * This file is a bit funny. The goal here is to use setns() to manipulate - * files inside the container, so we don't have to reason about the paths to - * make sure they don't escape (we can simply rely on the kernel for - * correctness). Unfortunately, you can't setns() to a mount namespace with a - * multi-threaded program, which every golang binary is. However, by declaring - * our init as an initializer, we can capture process control before it is - * transferred to the golang runtime, so we can then setns() as we'd like - * before golang has a chance to set up any threads. So, we implement two new - * lxd fork* commands which are captured here, and take a file on the host fs - * and copy it into the container ns. - * - * An alternative to this would be to move this code into a separate binary, - * which of course has problems of its own when it comes to packaging (how do - * we find the binary, what do we do if someone does file push and it is - * missing, etc.). After some discussion, even though the embedded method is - * somewhat convoluted, it was preferred. - */ -package main - -/* -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// This expects: -// ./lxd forkputfile /source/path /target/path -// or -// ./lxd forkgetfile /target/path /soruce/path -// i.e. 8 arguments, each which have a max length of PATH_MAX. -// Unfortunately, lseek() and fstat() both fail (EINVAL and 0 size) for -// procfs. Also, we can't mmap, because procfs doesn't support that, either. -// -#define CMDLINE_SIZE (8 * PATH_MAX) - -void error(char *msg) -{ - int old_errno = errno; - - perror(msg); - fprintf(stderr, "errno: %d\n", old_errno); -} - -int mkdir_p(const char *dir, mode_t mode) -{ - const char *tmp = dir; - const char *orig = dir; - char *makeme; - - do { - dir = tmp + strspn(tmp, "/"); - tmp = dir + strcspn(dir, "/"); - makeme = strndup(orig, dir - orig); - if (*makeme) { - if (mkdir(makeme, mode) && errno != EEXIST) { - fprintf(stderr, "failed to create directory '%s': %s\n", makeme, strerror(errno)); - free(makeme); - return -1; - } - } - free(makeme); - } while(tmp != dir); - - return 0; -} - -int copy(int target, int source) -{ - ssize_t n; - char buf[1024]; - - if (ftruncate(target, 0) < 0) { - error("error: truncate"); - return -1; - } - - while ((n = read(source, buf, 1024)) > 0) { - if (write(target, buf, n) != n) { - error("error: write"); - return -1; - } - } - - if (n < 0) { - error("error: read"); - return -1; - } - - return 0; -} - -int dosetns(int pid, char *nstype) { - int mntns; - char buf[PATH_MAX]; - - sprintf(buf, "/proc/%d/ns/%s", pid, nstype); - mntns = open(buf, O_RDONLY); - if (mntns < 0) { - error("error: open mntns"); - return -1; - } - - if (setns(mntns, 0) < 0) { - error("error: setns"); - close(mntns); - return -1; - } - close(mntns); - - return 0; -} - -void attach_userns(int pid) { - char nspath[PATH_MAX]; - char userns_source[PATH_MAX]; - char userns_target[PATH_MAX]; - - sprintf(nspath, "/proc/%d/ns/user", pid); - if (access(nspath, F_OK) == 0) { - if (readlink("/proc/self/ns/user", userns_source, 18) < 0) { - fprintf(stderr, "Failed readlink of source namespace: %s\n", strerror(errno)); - _exit(1); - } - - if (readlink(nspath, userns_target, PATH_MAX) < 0) { - fprintf(stderr, "Failed readlink of target namespace: %s\n", strerror(errno)); - _exit(1); - } - - if (strncmp(userns_source, userns_target, PATH_MAX) != 0) { - if (dosetns(pid, "user") < 0) { - fprintf(stderr, "Failed setns to container user namespace: %s\n", strerror(errno)); - _exit(1); - } - - if (setuid(0) < 0) { - fprintf(stderr, "Failed setuid to container root user: %s\n", strerror(errno)); - _exit(1); - } - - if (setgid(0) < 0) { - fprintf(stderr, "Failed setgid to container root group: %s\n", strerror(errno)); - _exit(1); - } - } - } -} - -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 host_fd = -1, container_fd = -1; - int ret = -1; - int container_open_flags; - struct stat st; - int exists = 1; - bool is_dir_manip = !strcmp(host, ""); - - if (!is_dir_manip) { - host_fd = open(host, O_RDWR); - if (host_fd < 0) { - error("error: open"); - return -1; - } - } - - if (pid > 0) { - attach_userns(pid); - - if (dosetns(pid, "mnt") < 0) { - error("error: setns"); - goto close_host; - } - } else { - if (chroot(rootfs) < 0) { - error("error: chroot"); - goto close_host; - } - - if (chdir("/") < 0) { - error("error: chdir"); - goto close_host; - } - } - - if (is_put && is_dir_manip) { - if (mode == -1) { - mode = defaultMode; - } - - if (uid == -1) { - uid = defaultUid; - } - - if (gid == -1) { - gid = defaultGid; - } - - if (mkdir(container, mode) < 0 && errno != EEXIST) { - error("error: mkdir"); - return -1; - } - - if (chown(container, uid, gid) < 0) { - error("error: chown"); - return -1; - } - - return 0; - } - - if (stat(container, &st) < 0) - exists = 0; - - container_open_flags = O_RDWR; - if (is_put) - container_open_flags |= O_CREAT; - - if (exists && S_ISDIR(st.st_mode)) - container_open_flags = O_DIRECTORY; - - umask(0); - container_fd = open(container, container_open_flags, 0); - if (container_fd < 0) { - error("error: open"); - goto close_host; - } - - if (is_put) { - if (!exists) { - if (mode == -1) { - mode = defaultMode; - } - - if (uid == -1) { - uid = defaultUid; - } - - if (gid == -1) { - gid = defaultGid; - } - } - - if (copy(container_fd, host_fd) < 0) { - error("error: copy"); - goto close_container; - } - - if (mode != -1 && fchmod(container_fd, mode) < 0) { - error("error: chmod"); - goto close_container; - } - - if (fchown(container_fd, uid, gid) < 0) { - error("error: chown"); - goto close_container; - } - ret = 0; - } else { - - if (fstat(container_fd, &st) < 0) { - error("error: stat"); - goto close_container; - } - - fprintf(stderr, "uid: %ld\n", (long)st.st_uid); - fprintf(stderr, "gid: %ld\n", (long)st.st_gid); - fprintf(stderr, "mode: %ld\n", (unsigned long)st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); - if (S_ISDIR(st.st_mode)) { - DIR *fdir; - struct dirent *de; - - fdir = fdopendir(container_fd); - if (!fdir) { - error("error: fdopendir"); - goto close_container; - } - - fprintf(stderr, "type: directory\n"); - - while((de = readdir(fdir))) { - int len, i; - - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) - continue; - - fprintf(stderr, "entry: "); - - // swap \n to \0 since we split this output by line - for (i = 0, len = strlen(de->d_name); i < len; i++) { - if (*(de->d_name + i) == '\n') - putc(0, stderr); - else - putc(*(de->d_name + i), stderr); - } - fprintf(stderr, "\n"); - } - - // container_fd is dead now that we fopendir'd it - goto close_host; - } else { - fprintf(stderr, "type: file\n"); - ret = copy(host_fd, container_fd); - } - fprintf(stderr, "type: %s", S_ISDIR(st.st_mode) ? "directory" : "file"); - } - -close_container: - close(container_fd); -close_host: - close(host_fd); - return ret; -} - -#define ADVANCE_ARG_REQUIRED() \ - do { \ - while (*cur != 0) \ - cur++; \ - cur++; \ - if (size <= cur - buf) { \ - fprintf(stderr, "not enough arguments\n"); \ - _exit(1); \ - } \ - } while(0) - -void ensure_dir(char *dest) { - struct stat sb; - if (stat(dest, &sb) == 0) { - if ((sb.st_mode & S_IFMT) == S_IFDIR) - return; - if (unlink(dest) < 0) { - fprintf(stderr, "Failed to remove old %s: %s\n", dest, strerror(errno)); - _exit(1); - } - } - if (mkdir(dest, 0755) < 0) { - fprintf(stderr, "Failed to mkdir %s: %s\n", dest, strerror(errno)); - _exit(1); - } -} - -void ensure_file(char *dest) { - struct stat sb; - int fd; - - if (stat(dest, &sb) == 0) { - if ((sb.st_mode & S_IFMT) != S_IFDIR) - return; - if (rmdir(dest) < 0) { - fprintf(stderr, "Failed to remove old %s: %s\n", dest, strerror(errno)); - _exit(1); - } - } - - fd = creat(dest, 0755); - if (fd < 0) { - fprintf(stderr, "Failed to mkdir %s: %s\n", dest, strerror(errno)); - _exit(1); - } - close(fd); -} - -void create(char *src, char *dest) { - char *destdirname; - struct stat sb; - if (stat(src, &sb) < 0) { - fprintf(stderr, "source %s does not exist\n", src); - _exit(1); - } - - destdirname = strdup(dest); - destdirname = dirname(destdirname); - - if (mkdir_p(destdirname, 0755) < 0) { - fprintf(stderr, "failed to create path: %s\n", destdirname); - free(destdirname); - _exit(1); - } - - switch (sb.st_mode & S_IFMT) { - case S_IFDIR: - ensure_dir(dest); - return; - default: - ensure_file(dest); - return; - } - - free(destdirname); -} - -void forkmount(char *buf, char *cur, ssize_t size) { - char *src, *dest, *opts; - - ADVANCE_ARG_REQUIRED(); - int pid = atoi(cur); - - attach_userns(pid); - - if (dosetns(pid, "mnt") < 0) { - fprintf(stderr, "Failed setns to container mount namespace: %s\n", strerror(errno)); - _exit(1); - } - - ADVANCE_ARG_REQUIRED(); - src = cur; - - ADVANCE_ARG_REQUIRED(); - dest = cur; - - create(src, dest); - - if (access(src, F_OK) < 0) { - fprintf(stderr, "Mount source doesn't exist: %s\n", strerror(errno)); - _exit(1); - } - - if (access(dest, F_OK) < 0) { - fprintf(stderr, "Mount destination doesn't exist: %s\n", strerror(errno)); - _exit(1); - } - - // Here, we always move recursively, because we sometimes allow - // recursive mounts. If the mount has no kids then it doesn't matter, - // but if it does, we want to move those too. - if (mount(src, dest, "none", MS_MOVE | MS_REC, NULL) < 0) { - fprintf(stderr, "Failed mounting %s onto %s: %s\n", src, dest, strerror(errno)); - _exit(1); - } - - _exit(0); -} - -void forkumount(char *buf, char *cur, ssize_t size) { - ADVANCE_ARG_REQUIRED(); - int pid = atoi(cur); - - if (dosetns(pid, "mnt") < 0) { - fprintf(stderr, "Failed setns to container mount namespace: %s\n", strerror(errno)); - _exit(1); - } - - ADVANCE_ARG_REQUIRED(); - if (access(cur, F_OK) < 0) { - fprintf(stderr, "Mount path doesn't exist: %s\n", strerror(errno)); - _exit(1); - } - - if (umount2(cur, MNT_DETACH) < 0) { - fprintf(stderr, "Error unmounting %s: %s\n", cur, strerror(errno)); - _exit(1); - } - _exit(0); -} - -void forkdofile(char *buf, char *cur, bool is_put, ssize_t size) { - uid_t uid = 0; - gid_t gid = 0; - mode_t mode = 0; - uid_t defaultUid = 0; - gid_t defaultGid = 0; - mode_t defaultMode = 0; - char *command = cur, *rootfs = NULL, *source = NULL, *target = NULL; - pid_t pid; - - ADVANCE_ARG_REQUIRED(); - rootfs = cur; - - ADVANCE_ARG_REQUIRED(); - pid = atoi(cur); - - ADVANCE_ARG_REQUIRED(); - source = cur; - - ADVANCE_ARG_REQUIRED(); - target = cur; - - if (is_put) { - ADVANCE_ARG_REQUIRED(); - uid = atoi(cur); - - ADVANCE_ARG_REQUIRED(); - gid = atoi(cur); - - ADVANCE_ARG_REQUIRED(); - mode = atoi(cur); - - ADVANCE_ARG_REQUIRED(); - defaultUid = atoi(cur); - - ADVANCE_ARG_REQUIRED(); - defaultGid = atoi(cur); - - ADVANCE_ARG_REQUIRED(); - defaultMode = atoi(cur); - } - - _exit(manip_file_in_ns(rootfs, pid, source, target, is_put, uid, gid, mode, defaultUid, defaultGid, defaultMode)); -} - -void forkcheckfile(char *buf, char *cur, bool is_put, ssize_t size) { - char *command = cur, *rootfs = NULL, *path = NULL; - pid_t pid; - - ADVANCE_ARG_REQUIRED(); - rootfs = cur; - - ADVANCE_ARG_REQUIRED(); - pid = atoi(cur); - - ADVANCE_ARG_REQUIRED(); - path = cur; - - if (pid > 0) { - attach_userns(pid); - - if (dosetns(pid, "mnt") < 0) { - error("error: setns"); - _exit(1); - } - } else { - if (chroot(rootfs) < 0) { - error("error: chroot"); - _exit(1); - } - - if (chdir("/") < 0) { - error("error: chdir"); - _exit(1); - } - } - - if (access(path, F_OK) < 0) { - fprintf(stderr, "Path doesn't exist: %s\n", strerror(errno)); - _exit(1); - } - - _exit(0); -} - -void forkremovefile(char *buf, char *cur, bool is_put, ssize_t size) { - char *command = cur, *rootfs = NULL, *path = NULL; - pid_t pid; - struct stat sb; - - ADVANCE_ARG_REQUIRED(); - rootfs = cur; - - ADVANCE_ARG_REQUIRED(); - pid = atoi(cur); - - ADVANCE_ARG_REQUIRED(); - path = cur; - - if (pid > 0) { - attach_userns(pid); - - if (dosetns(pid, "mnt") < 0) { - error("error: setns"); - _exit(1); - } - } else { - if (chroot(rootfs) < 0) { - error("error: chroot"); - _exit(1); - } - - if (chdir("/") < 0) { - error("error: chdir"); - _exit(1); - } - } - - if (stat(path, &sb) < 0) { - error("error: stat"); - _exit(1); - } - - if ((sb.st_mode & S_IFMT) == S_IFDIR) { - if (rmdir(path) < 0) { - fprintf(stderr, "Failed to remove %s: %s\n", path, strerror(errno)); - _exit(1); - } - } else { - if (unlink(path) < 0) { - fprintf(stderr, "Failed to remove %s: %s\n", path, strerror(errno)); - _exit(1); - } - } - - _exit(0); -} - -void forkgetnet(char *buf, char *cur, ssize_t size) { - ADVANCE_ARG_REQUIRED(); - int pid = atoi(cur); - - if (dosetns(pid, "net") < 0) { - fprintf(stderr, "Failed setns to container network namespace: %s\n", strerror(errno)); - _exit(1); - } - - // The rest happens in Go -} - -__attribute__((constructor)) void init(void) { - int cmdline; - char buf[CMDLINE_SIZE]; - ssize_t size; - char *cur; - - cmdline = open("/proc/self/cmdline", O_RDONLY); - if (cmdline < 0) { - error("error: open"); - _exit(232); - } - - memset(buf, 0, sizeof(buf)); - if ((size = read(cmdline, buf, sizeof(buf)-1)) < 0) { - close(cmdline); - error("error: read"); - _exit(232); - } - close(cmdline); - - cur = buf; - // skip argv[0] - while (*cur != 0) - cur++; - cur++; - if (size <= cur - buf) - return; - - if (strcmp(cur, "forkputfile") == 0) { - forkdofile(buf, cur, true, size); - } else if (strcmp(cur, "forkgetfile") == 0) { - forkdofile(buf, cur, false, size); - } else if (strcmp(cur, "forkcheckfile") == 0) { - forkcheckfile(buf, cur, false, size); - } else if (strcmp(cur, "forkremovefile") == 0) { - forkremovefile(buf, cur, false, size); - } else if (strcmp(cur, "forkmount") == 0) { - forkmount(buf, cur, size); - } else if (strcmp(cur, "forkumount") == 0) { - forkumount(buf, cur, size); - } else if (strcmp(cur, "forkgetnet") == 0) { - forkgetnet(buf, cur, size); - } -} -*/ -import "C" diff -Nru lxd-2.6.2/lxd/operations.go lxd-2.7/lxd/operations.go --- lxd-2.6.2/lxd/operations.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/operations.go 2016-12-21 00:52:23.000000000 +0000 @@ -12,6 +12,7 @@ "github.com/pborman/uuid" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" ) var operationsLock sync.Mutex @@ -252,7 +253,7 @@ for key, value := range resources { var values []string for _, c := range value { - values = append(values, fmt.Sprintf("/%s/%s/%s", shared.APIVersion, key, c)) + values = append(values, fmt.Sprintf("/%s/%s/%s", version.APIVersion, key, c)) } tmpResources[key] = values } @@ -365,7 +366,7 @@ op.createdAt = time.Now() op.updatedAt = op.createdAt op.status = shared.Pending - op.url = fmt.Sprintf("/%s/operations/%s", shared.APIVersion, op.id) + op.url = fmt.Sprintf("/%s/operations/%s", version.APIVersion, op.id) op.resources = opResources op.chanDone = make(chan error) diff -Nru lxd-2.6.2/lxd/profiles.go lxd-2.7/lxd/profiles.go --- lxd-2.6.2/lxd/profiles.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/profiles.go 2016-12-21 00:52:23.000000000 +0000 @@ -13,6 +13,7 @@ _ "github.com/mattn/go-sqlite3" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" log "gopkg.in/inconshreveable/log15.v2" ) @@ -38,7 +39,7 @@ i := 0 for _, name := range results { if !recursion { - url := fmt.Sprintf("/%s/profiles/%s", shared.APIVersion, name) + url := fmt.Sprintf("/%s/profiles/%s", version.APIVersion, name) resultString[i] = url } else { profile, err := doProfileGet(d, name) @@ -99,7 +100,7 @@ fmt.Errorf("Error inserting %s into database: %s", req.Name, err)) } - return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/profiles/%s", shared.APIVersion, req.Name)) + return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/profiles/%s", version.APIVersion, req.Name)) } var profilesCmd = Command{ @@ -120,7 +121,7 @@ usedBy := []string{} for _, ct := range cts { - usedBy = append(usedBy, fmt.Sprintf("/%s/containers/%s", shared.APIVersion, ct)) + usedBy = append(usedBy, fmt.Sprintf("/%s/containers/%s", version.APIVersion, ct)) } profile.UsedBy = usedBy @@ -366,7 +367,7 @@ return InternalError(err) } - return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/profiles/%s", shared.APIVersion, req.Name)) + return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/profiles/%s", version.APIVersion, req.Name)) } // The handler for the delete operation. diff -Nru lxd-2.6.2/lxd/remote.go lxd-2.7/lxd/remote.go --- lxd-2.6.2/lxd/remote.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/remote.go 2016-12-21 00:52:23.000000000 +0000 @@ -5,12 +5,13 @@ "fmt" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/version" ) func remoteGetImageFingerprint(d *Daemon, server string, certificate string, alias string) (string, error) { url := fmt.Sprintf( "%s/%s/images/aliases/%s", - server, shared.APIVersion, alias) + server, version.APIVersion, alias) resp, err := d.httpGetSync(url, certificate) if err != nil { diff -Nru lxd-2.6.2/lxd/rsync.go lxd-2.7/lxd/rsync.go --- lxd-2.6.2/lxd/rsync.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/rsync.go 2016-12-21 00:52:23.000000000 +0000 @@ -7,7 +7,6 @@ "net" "os" "os/exec" - "sync" "github.com/gorilla/websocket" @@ -102,7 +101,7 @@ readPipe = readWrapper(dataSocket) } - readDone, writeDone := shared.WebsocketMirror(conn, dataSocket, readPipe) + readDone, writeDone := shared.WebsocketMirror(conn, dataSocket, readPipe, nil, nil) output, err := ioutil.ReadAll(stderr) if err != nil { @@ -157,7 +156,7 @@ writePipe = writeWrapper(stdin) } - readDone, writeDone := shared.WebsocketMirror(conn, writePipe, stdout) + readDone, writeDone := shared.WebsocketMirror(conn, writePipe, stdout, nil, nil) data, err2 := ioutil.ReadAll(stderr) if err2 != nil { shared.LogDebugf("error reading rsync stderr: %s", err2) @@ -174,43 +173,3 @@ return err } - -// Netcat is called with: -// -// lxd netcat /path/to/unix/socket -// -// and does unbuffered netcatting of to socket to stdin/stdout. Any arguments -// after the path to the unix socket are ignored, so that this can be passed -// directly to rsync as the sync command. -func Netcat(args []string) error { - if len(args) < 2 { - return fmt.Errorf("Bad arguments %q", args) - } - - uAddr, err := net.ResolveUnixAddr("unix", args[1]) - if err != nil { - return err - } - - conn, err := net.DialUnix("unix", nil, uAddr) - if err != nil { - return err - } - - wg := sync.WaitGroup{} - wg.Add(1) - - go func() { - io.Copy(os.Stdout, conn) - conn.Close() - wg.Done() - }() - - go func() { - io.Copy(conn, os.Stdin) - }() - - wg.Wait() - - return nil -} diff -Nru lxd-2.6.2/lxd/seccomp.go lxd-2.7/lxd/seccomp.go --- lxd-2.6.2/lxd/seccomp.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/seccomp.go 2016-12-21 00:52:23.000000000 +0000 @@ -7,14 +7,13 @@ "path" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" ) -const SECCOMP_HEADER = ` -2 +const SECCOMP_HEADER = `2 ` -const DEFAULT_SECCOMP_POLICY = ` -reject_force_umount # comment this to allow umount -f; not recommended +const DEFAULT_SECCOMP_POLICY = `reject_force_umount # comment this to allow umount -f; not recommended [all] kexec_load errno 38 open_by_handle_at errno 38 @@ -22,8 +21,7 @@ finit_module errno 38 delete_module errno 38 ` -const COMPAT_BLOCKING_POLICY = ` -[%s] +const COMPAT_BLOCKING_POLICY = `[%s] compat_sys_rt_sigaction errno 38 stub_x32_rt_sigreturn errno 38 compat_sys_ioctl errno 38 @@ -124,7 +122,7 @@ compat := config["security.syscalls.blacklist_compat"] if shared.IsTrue(compat) { - arch, err := shared.ArchitectureName(c.Architecture()) + arch, err := osarch.ArchitectureName(c.Architecture()) if err != nil { return "", err } diff -Nru lxd-2.6.2/lxd/storage_btrfs.go lxd-2.7/lxd/storage_btrfs.go --- lxd-2.6.2/lxd/storage_btrfs.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/storage_btrfs.go 2016-12-21 00:52:23.000000000 +0000 @@ -172,11 +172,11 @@ return container.TemplateApply("copy") } -func (s *storageBtrfs) ContainerStart(container container) error { +func (s *storageBtrfs) ContainerStart(name string, path string) error { return nil } -func (s *storageBtrfs) ContainerStop(container container) error { +func (s *storageBtrfs) ContainerStop(name string, path string) error { return nil } diff -Nru lxd-2.6.2/lxd/storage_dir.go lxd-2.7/lxd/storage_dir.go --- lxd-2.6.2/lxd/storage_dir.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/storage_dir.go 2016-12-21 00:52:23.000000000 +0000 @@ -123,11 +123,11 @@ return container.TemplateApply("copy") } -func (s *storageDir) ContainerStart(container container) error { +func (s *storageDir) ContainerStart(name string, path string) error { return nil } -func (s *storageDir) ContainerStop(container container) error { +func (s *storageDir) ContainerStop(name string, path string) error { return nil } diff -Nru lxd-2.6.2/lxd/storage.go lxd-2.7/lxd/storage.go --- lxd-2.6.2/lxd/storage.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/storage.go 2016-12-21 00:52:23.000000000 +0000 @@ -14,6 +14,7 @@ "github.com/gorilla/websocket" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/ioprogress" "github.com/lxc/lxd/shared/logging" log "gopkg.in/inconshreveable/log15.v2" @@ -149,8 +150,8 @@ ContainerCanRestore(container container, sourceContainer container) error ContainerDelete(container container) error ContainerCopy(container container, sourceContainer container) error - ContainerStart(container container) error - ContainerStop(container container) error + ContainerStart(name string, path string) error + ContainerStop(name string, path string) error ContainerRename(container container, newName string) error ContainerRestore(container container, sourceContainer container) error ContainerSetQuota(container container, size int64) error @@ -431,14 +432,14 @@ return lw.w.ContainerCopy(container, sourceContainer) } -func (lw *storageLogWrapper) ContainerStart(container container) error { - lw.log.Debug("ContainerStart", log.Ctx{"container": container.Name()}) - return lw.w.ContainerStart(container) +func (lw *storageLogWrapper) ContainerStart(name string, path string) error { + lw.log.Debug("ContainerStart", log.Ctx{"container": name}) + return lw.w.ContainerStart(name, path) } -func (lw *storageLogWrapper) ContainerStop(container container) error { - lw.log.Debug("ContainerStop", log.Ctx{"container": container.Name()}) - return lw.w.ContainerStop(container) +func (lw *storageLogWrapper) ContainerStop(name string, path string) error { + lw.log.Debug("ContainerStop", log.Ctx{"container": name}) + return lw.w.ContainerStop(name, path) } func (lw *storageLogWrapper) ContainerRename( @@ -833,9 +834,9 @@ progressWrapperRender(op, key, description, progressInt, speedInt) } - readPipe := &shared.ProgressReader{ + readPipe := &ioprogress.ProgressReader{ ReadCloser: reader, - Tracker: &shared.ProgressTracker{ + Tracker: &ioprogress.ProgressTracker{ Handler: progress, }, } @@ -854,9 +855,9 @@ progressWrapperRender(op, key, description, progressInt, speedInt) } - writePipe := &shared.ProgressWriter{ + writePipe := &ioprogress.ProgressWriter{ WriteCloser: writer, - Tracker: &shared.ProgressTracker{ + Tracker: &ioprogress.ProgressTracker{ Handler: progress, }, } diff -Nru lxd-2.6.2/lxd/storage_lvm.go lxd-2.7/lxd/storage_lvm.go --- lxd-2.6.2/lxd/storage_lvm.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/storage_lvm.go 2016-12-21 00:52:23.000000000 +0000 @@ -404,7 +404,7 @@ return err } - if err := s.ContainerStart(container); err != nil { + if err := s.ContainerStart(container.Name(), container.Path()); err != nil { s.log.Error("Error starting/mounting container", log.Ctx{"err": err, "container": container.Name()}) s.ContainerDelete(container) return err @@ -419,36 +419,36 @@ return fmt.Errorf("rsync failed: %s", string(output)) } - if err := s.ContainerStop(container); err != nil { + if err := s.ContainerStop(container.Name(), container.Path()); err != nil { return err } } return container.TemplateApply("copy") } -func (s *storageLvm) ContainerStart(container container) error { - lvName := containerNameToLVName(container.Name()) +func (s *storageLvm) ContainerStart(name string, path string) error { + lvName := containerNameToLVName(name) lvpath := fmt.Sprintf("/dev/%s/%s", s.vgName, lvName) fstype := daemonConfig["storage.lvm_fstype"].Get() mountOptions := daemonConfig["storage.lvm_mount_options"].Get() - err := tryMount(lvpath, container.Path(), fstype, 0, mountOptions) + err := tryMount(lvpath, path, fstype, 0, mountOptions) if err != nil { return fmt.Errorf( "Error mounting snapshot LV path='%s': %v", - container.Path(), + path, err) } return nil } -func (s *storageLvm) ContainerStop(container container) error { - err := tryUnmount(container.Path(), 0) +func (s *storageLvm) ContainerStop(name string, path string) error { + err := tryUnmount(path, 0) if err != nil { return fmt.Errorf( "failed to unmount container path '%s'.\nError: %v", - container.Path(), + path, err) } @@ -690,7 +690,7 @@ } func (s *storageLvm) ContainerSnapshotStop(container container) error { - err := s.ContainerStop(container) + err := s.ContainerStop(container.Name(), container.Path()) if err != nil { return err } diff -Nru lxd-2.6.2/lxd/storage_test.go lxd-2.7/lxd/storage_test.go --- lxd-2.6.2/lxd/storage_test.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/storage_test.go 2016-12-21 00:52:23.000000000 +0000 @@ -61,11 +61,11 @@ return nil } -func (s *storageMock) ContainerStart(container container) error { +func (s *storageMock) ContainerStart(name string, path string) error { return nil } -func (s *storageMock) ContainerStop(container container) error { +func (s *storageMock) ContainerStop(name string, path string) error { return nil } diff -Nru lxd-2.6.2/lxd/storage_zfs.go lxd-2.7/lxd/storage_zfs.go --- lxd-2.6.2/lxd/storage_zfs.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/lxd/storage_zfs.go 2016-12-21 00:52:23.000000000 +0000 @@ -92,8 +92,8 @@ } // Things we don't need to care about -func (s *storageZfs) ContainerStart(container container) error { - fs := fmt.Sprintf("containers/%s", container.Name()) +func (s *storageZfs) ContainerStart(name string, path string) error { + fs := fmt.Sprintf("containers/%s", name) // Just in case the container filesystem got unmounted if !shared.IsMountPoint(shared.VarPath(fs)) { @@ -103,7 +103,7 @@ return nil } -func (s *storageZfs) ContainerStop(container container) error { +func (s *storageZfs) ContainerStop(name string, path string) error { return nil } @@ -1217,17 +1217,17 @@ if err != nil { return fmt.Errorf("Invalid ZFS pool: %v", err) } - } - // Confirm that the new pool is empty - s.zfsPool = value - subvols, err := s.zfsListSubvolumes("") - if err != nil { - return err - } + // Confirm that the new pool is empty + s.zfsPool = value + subvols, err := s.zfsListSubvolumes("") + if err != nil { + return err + } - if len(subvols) > 0 { - return fmt.Errorf("Provided ZFS pool (or dataset) isn't empty") + if len(subvols) > 0 { + return fmt.Errorf("Provided ZFS pool (or dataset) isn't empty") + } } // Confirm the old pool isn't in use anymore diff -Nru lxd-2.6.2/Makefile lxd-2.7/Makefile --- lxd-2.6.2/Makefile 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/Makefile 2016-12-21 00:52:23.000000000 +0000 @@ -7,7 +7,7 @@ # dist is primarily for use when packaging; for development we still manage # dependencies via `go get` explicitly. # TODO: use git describe for versioning -VERSION=$(shell grep "var Version" shared/flex.go | cut -d'"' -f2) +VERSION=$(shell grep "var Version" shared/version/flex.go | cut -d'"' -f2) ARCHIVE=lxd-$(VERSION).tar .PHONY: default @@ -56,20 +56,29 @@ .PHONY: dist dist: + # Cleanup + rm -Rf $(ARCHIVE).gz + + # Create build dir $(eval TMP := $(shell mktemp -d)) - rm -Rf lxd-$(VERSION) $(ARCHIVE) $(ARCHIVE).gz - mkdir -p lxd-$(VERSION)/ - -GOPATH=$(TMP) go get -t -v -d ./... - -GOPATH=$(TMP) go get -t -v -d ./... - -GOPATH=$(TMP) go get -t -v -d ./... - GOPATH=$(TMP) go get -t -v -d ./... - rm -rf $(TMP)/src/github.com/lxc/lxd - ln -s ../../../.. $(TMP)/src/github.com/lxc/lxd - mv $(TMP)/ lxd-$(VERSION)/dist - git archive --prefix=lxd-$(VERSION)/ --output=$(ARCHIVE) HEAD - tar -uf $(ARCHIVE) --exclude-vcs lxd-$(VERSION)/ - gzip -9 $(ARCHIVE) - rm -Rf lxd-$(VERSION) $(ARCHIVE) + git archive --prefix=lxd-$(VERSION)/ HEAD | tar -x -C $(TMP) + mkdir -p $(TMP)/dist/src/github.com/lxc + ln -s ../../../../lxd-$(VERSION) $(TMP)/dist/src/github.com/lxc/lxd + + # Download dependencies + -cd $(TMP)/lxd-$(VERSION) && GOPATH=$(TMP)/dist go get -t -v -d ./... + -cd $(TMP)/lxd-$(VERSION) && GOPATH=$(TMP)/dist go get -t -v -d ./... + -cd $(TMP)/lxd-$(VERSION) && GOPATH=$(TMP)/dist go get -t -v -d ./... + cd $(TMP)/lxd-$(VERSION) && GOPATH=$(TMP)/dist go get -t -v -d ./... + + # Assemble tarball + rm $(TMP)/dist/src/github.com/lxc/lxd + ln -s ../../../../ $(TMP)/dist/src/github.com/lxc/lxd + mv $(TMP)/dist $(TMP)/lxd-$(VERSION)/ + tar --exclude-vcs -C $(TMP) -zcf $(ARCHIVE).gz lxd-$(VERSION)/ + + # Cleanup + rm -Rf $(TMP) .PHONY: i18n update-po update-pot build-mo static-analysis i18n: update-pot diff -Nru lxd-2.6.2/po/de.po lxd-2.7/po/de.po --- lxd-2.6.2/po/de.po 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/po/de.po 2016-12-21 00:52:23.000000000 +0000 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: LXD\n" "Report-Msgid-Bugs-To: lxc-devel@lists.linuxcontainers.org\n" -"POT-Creation-Date: 2016-11-04 21:31-0400\n" +"POT-Creation-Date: 2016-12-15 13:20-0500\n" "PO-Revision-Date: 2015-06-13 06:10+0200\n" "Last-Translator: Felix Engelmann \n" "Language-Team: \n" @@ -165,7 +165,7 @@ "###\n" "### Der Name wird zwar angezeigt, lässt sich jedoch nicht ändern.\n" -#: lxc/image.go:617 +#: lxc/image.go:614 #, c-format msgid "%s (%d more)" msgstr "" @@ -175,15 +175,15 @@ msgid "'/' not allowed in snapshot name" msgstr "'/' ist kein gültiges Zeichen im Namen eines Sicherungspunktes\n" -#: lxc/profile.go:254 +#: lxc/profile.go:253 msgid "(none)" msgstr "" -#: lxc/image.go:638 lxc/image.go:680 +#: lxc/image.go:635 lxc/image.go:677 msgid "ALIAS" msgstr "" -#: lxc/image.go:642 +#: lxc/image.go:639 msgid "ARCH" msgstr "" @@ -191,30 +191,26 @@ msgid "ARCHITECTURE" msgstr "" -#: lxc/remote.go:52 +#: lxc/remote.go:51 msgid "Accept certificate" msgstr "Akzeptiere Zertifikat" -#: lxc/remote.go:268 +#: lxc/remote.go:267 #, c-format msgid "Admin password for %s: " msgstr "Administrator Passwort für %s: " -#: lxc/image.go:365 +#: lxc/image.go:362 #, fuzzy msgid "Aliases:" msgstr "Aliasse:\n" -#: lxc/exec.go:54 -msgid "An environment variable of the form HOME=/home/foo" -msgstr "" - -#: lxc/image.go:348 lxc/info.go:93 +#: lxc/image.go:345 lxc/info.go:93 #, fuzzy, c-format msgid "Architecture: %s" msgstr "Architektur: %s\n" -#: lxc/image.go:369 +#: lxc/image.go:366 #, c-format msgid "Auto update: %s" msgstr "" @@ -253,11 +249,11 @@ msgid "Can't unset key '%s', it's not currently set." msgstr "" -#: lxc/network.go:386 lxc/profile.go:420 +#: lxc/network.go:385 lxc/profile.go:419 msgid "Cannot provide container name to list" msgstr "" -#: lxc/remote.go:218 +#: lxc/remote.go:217 #, fuzzy, c-format msgid "Certificate fingerprint: %x" msgstr "Fingerabdruck des Zertifikats: % x\n" @@ -265,15 +261,26 @@ #: lxc/action.go:33 #, fuzzy, c-format msgid "" -"Changes state of one or more containers to %s.\n" +"Change state of one or more containers to %s.\n" "\n" -"lxc %s [...]%s" +"lxc %s [:] [[:]...]%s" msgstr "" "Ändert den Laufzustand eines Containers in %s.\n" "\n" "lxd %s \n" -#: lxc/remote.go:291 +#: lxc/finger.go:15 +#, fuzzy +msgid "" +"Check if the LXD instance is up.\n" +"\n" +"lxc finger [:]" +msgstr "" +"Fingert die LXD Instanz zum überprüfen ob diese funktionsfähig ist.\n" +"\n" +"lxc finger \n" + +#: lxc/remote.go:290 msgid "Client certificate stored at server: " msgstr "Gespeichertes Nutzerzertifikat auf dem Server: " @@ -287,8 +294,8 @@ msgid "Config key/value to apply to the new container" msgstr "kann nicht zum selben Container Namen kopieren" -#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:734 lxc/network.go:342 -#: lxc/profile.go:218 +#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:731 lxc/network.go:341 +#: lxc/profile.go:217 #, fuzzy, c-format msgid "Config parsing error: %s" msgstr "YAML Analyse Fehler %v\n" @@ -301,7 +308,7 @@ msgid "Container name is mandatory" msgstr "" -#: lxc/copy.go:140 lxc/copy.go:241 lxc/init.go:227 +#: lxc/copy.go:140 lxc/copy.go:241 lxc/init.go:229 #, c-format msgid "Container name is: %s" msgstr "" @@ -311,36 +318,41 @@ msgid "Container published with fingerprint: %s" msgstr "Abbild mit Fingerabdruck %s importiert\n" -#: lxc/image.go:166 +#: lxc/image.go:165 msgid "Copy aliases from source" msgstr "Kopiere Aliasse von der Quelle" #: lxc/copy.go:24 #, fuzzy msgid "" -"Copy containers within or in between lxd instances.\n" +"Copy containers within or in between LXD instances.\n" "\n" -"lxc copy [remote:] [[remote:]] [--" +"lxc copy [:][/] [[:]] [--" "ephemeral|e] [--profile|-p ...] [--config|-c ...]" msgstr "" "Kopiert Container innerhalb einer oder zwischen lxd Instanzen\n" "\n" "lxc copy \n" -#: lxc/image.go:280 +#: lxc/image.go:278 #, c-format msgid "Copying the image: %s" msgstr "" -#: lxc/remote.go:233 +#: lxc/remote.go:232 msgid "Could not create server cert dir" msgstr "Kann Verzeichnis für Zertifikate auf dem Server nicht erstellen" +#: lxc/file.go:83 +#, c-format +msgid "Could not sanitize path %s" +msgstr "" + #: lxc/snapshot.go:21 msgid "" "Create a read-only snapshot of a container.\n" "\n" -"lxc snapshot [remote:] [--stateful]\n" +"lxc snapshot [:] [--stateful]\n" "\n" "Creates a snapshot of the container (optionally with the container's memory\n" "state). When --stateful is used, LXD attempts to checkpoint the container's\n" @@ -353,19 +365,19 @@ "successfully).\n" "\n" "Example:\n" -"lxc snapshot u1 snap0" +" lxc snapshot u1 snap0" msgstr "" -#: lxc/file.go:60 lxc/file.go:61 +#: lxc/file.go:59 lxc/file.go:60 msgid "Create any directories necessary" msgstr "" -#: lxc/image.go:353 lxc/info.go:95 +#: lxc/image.go:350 lxc/info.go:95 #, c-format msgid "Created: %s" msgstr "" -#: lxc/init.go:180 lxc/launch.go:134 +#: lxc/init.go:180 lxc/launch.go:135 #, c-format msgid "Creating %s" msgstr "" @@ -375,7 +387,7 @@ msgid "Creating the container" msgstr "kann nicht zum selben Container Namen kopieren" -#: lxc/image.go:641 lxc/image.go:682 +#: lxc/image.go:638 lxc/image.go:679 msgid "DESCRIPTION" msgstr "" @@ -386,9 +398,9 @@ #: lxc/delete.go:25 #, fuzzy msgid "" -"Delete containers or container snapshots.\n" +"Delete containers or snapshots.\n" "\n" -"lxc delete [remote:][/] [remote:][[/" +"lxc delete [:][/] [[:][/" "]...]\n" "\n" "Destroy containers or snapshots with any attached data (configuration, " @@ -418,13 +430,19 @@ msgstr "" #: lxc/main.go:41 -msgid "Enables debug mode." +#, fuzzy +msgid "Enable debug mode" msgstr "Aktiviert Debug Modus" #: lxc/main.go:40 -msgid "Enables verbose mode." +#, fuzzy +msgid "Enable verbose mode" msgstr "Aktiviert ausführliche Ausgabe" +#: lxc/exec.go:54 +msgid "Environment variable to set (e.g. HOME=/home/foo)" +msgstr "" + #: lxc/help.go:69 msgid "Environment:" msgstr "" @@ -443,8 +461,8 @@ msgid "" "Execute the specified command in a container.\n" "\n" -"lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env " -"EDITOR=/usr/bin/vim]... [--] \n" +"lxc exec [:] [--mode=auto|interactive|non-interactive] [--" +"env KEY=VALUE...] [--] \n" "\n" "Mode defaults to non-interactive, interactive mode is selected if both stdin " "AND stdout are terminals (stderr is ignored)." @@ -453,16 +471,16 @@ "\n" "lxc exec [--env EDITOR=/usr/bin/vim]... \n" -#: lxc/image.go:357 +#: lxc/image.go:354 #, c-format msgid "Expires: %s" msgstr "" -#: lxc/image.go:359 +#: lxc/image.go:356 msgid "Expires: never" msgstr "" -#: lxc/config.go:272 lxc/image.go:639 lxc/image.go:681 +#: lxc/config.go:272 lxc/image.go:636 lxc/image.go:678 msgid "FINGERPRINT" msgstr "" @@ -470,43 +488,44 @@ msgid "Fast mode (same as --columns=nsacPt" msgstr "" -#: lxc/image.go:346 +#: lxc/image.go:343 #, fuzzy, c-format msgid "Fingerprint: %s" msgstr "Fingerabdruck: %s\n" -#: lxc/finger.go:15 -#, fuzzy -msgid "" -"Fingers the LXD instance to check if it is up and working.\n" -"\n" -"lxc finger " -msgstr "" -"Fingert die LXD Instanz zum überprüfen ob diese funktionsfähig ist.\n" -"\n" -"lxc finger \n" - #: lxc/action.go:42 lxc/action.go:43 -msgid "Force the container to shutdown." +#, fuzzy +msgid "Force the container to shutdown" msgstr "Herunterfahren des Containers erzwingen." #: lxc/delete.go:34 lxc/delete.go:35 -msgid "Force the removal of stopped containers." +msgid "Force the removal of stopped containers" msgstr "" #: lxc/main.go:42 -msgid "Force using the local unix socket." +msgid "Force using the local unix socket" msgstr "" -#: lxc/image.go:169 lxc/list.go:123 +#: lxc/image.go:168 lxc/list.go:123 msgid "Format" msgstr "" -#: lxc/remote.go:66 +#: lxc/remote.go:65 #, fuzzy msgid "Generating a client certificate. This may take a minute..." msgstr "Generiere Nutzerzertifikat. Dies kann wenige Minuten dauern...\n" +#: lxc/help.go:25 +#, fuzzy +msgid "" +"Help page for the LXD client.\n" +"\n" +"lxc help [--all]" +msgstr "" +"Zeigt Details über die Benutzung von LXD an.\n" +"\n" +"lxd help [—all]\n" + #: lxc/list.go:423 msgid "IPV4" msgstr "" @@ -525,24 +544,24 @@ msgstr "" #: lxc/main.go:43 -msgid "Ignore aliases when determining what command to run." +msgid "Ignore aliases when determining what command to run" msgstr "" #: lxc/action.go:46 #, fuzzy -msgid "Ignore the container state (only for start)." +msgid "Ignore the container state (only for start)" msgstr "Herunterfahren des Containers erzwingen." -#: lxc/image.go:285 +#: lxc/image.go:281 msgid "Image copied successfully!" msgstr "" -#: lxc/image.go:442 +#: lxc/image.go:421 lxc/image.go:433 #, fuzzy, c-format msgid "Image imported with fingerprint: %s" msgstr "Abbild mit Fingerabdruck %s importiert\n" -#: lxc/image.go:429 +#: lxc/image.go:418 #, c-format msgid "Importing the image: %s" msgstr "" @@ -552,8 +571,8 @@ msgid "" "Initialize a container from a particular image.\n" "\n" -"lxc init [remote:] [remote:][] [--ephemeral|-e] [--profile|-p " -"...] [--config|-c ...] [--network|-n ]\n" +"lxc init [:] [:][] [--ephemeral|-e] [--profile|-" +"p ...] [--config|-c ...] [--network|-n ]\n" "\n" "Initializes a container using the specified image and name.\n" "\n" @@ -561,7 +580,7 @@ "Specifying \"-p\" with no argument will result in no profile.\n" "\n" "Example:\n" -"lxc init ubuntu u1" +" lxc init ubuntu u1" msgstr "" "Starte Container von gegebenem Abbild.\n" "\n" @@ -575,7 +594,7 @@ "Beispiel:\n" "lxc launch ubuntu u1\n" -#: lxc/remote.go:136 +#: lxc/remote.go:135 #, c-format msgid "Invalid URL scheme \"%s\" in \"%s\"" msgstr "" @@ -589,12 +608,12 @@ msgid "Invalid configuration key" msgstr "" -#: lxc/file.go:247 +#: lxc/file.go:249 #, c-format msgid "Invalid source %s" msgstr "Ungültige Quelle %s" -#: lxc/file.go:73 +#: lxc/file.go:72 #, c-format msgid "Invalid target %s" msgstr "Ungültiges Ziel %s" @@ -603,7 +622,7 @@ msgid "Ips:" msgstr "" -#: lxc/image.go:167 +#: lxc/image.go:166 msgid "Keep the image up to date after initial copy" msgstr "" @@ -620,8 +639,9 @@ msgid "" "Launch a container from a particular image.\n" "\n" -"lxc launch [remote:] [remote:][] [--ephemeral|-e] [--profile|-p " -"...] [--config|-c ...] [--network|-n ]\n" +"lxc launch [:] [:][] [--ephemeral|-e] [--" +"profile|-p ...] [--config|-c ...] [--network|-n " +"]\n" "\n" "Launches a container using the specified image and name.\n" "\n" @@ -629,7 +649,7 @@ "Specifying \"-p\" with no argument will result in no profile.\n" "\n" "Example:\n" -"lxc launch ubuntu:16.04 u1" +" lxc launch ubuntu:16.04 u1" msgstr "" "Starte Container von gegebenem Abbild.\n" "\n" @@ -649,10 +669,10 @@ "List information on LXD servers and containers.\n" "\n" "For a container:\n" -" lxc info [:]container [--show-log]\n" +" lxc info [] [--show-log]\n" "\n" "For a server:\n" -" lxc info [:]" +" lxc info []" msgstr "" "Listet Informationen über Container.\n" "\n" @@ -664,9 +684,10 @@ #: lxc/list.go:68 #, fuzzy msgid "" -"Lists the available resources.\n" +"Lists the containers.\n" "\n" -"lxc list [resource] [filters] [--format table|json] [-c columns] [--fast]\n" +"lxc list [:] [filters] [--format table|json] [-c ] [--" +"fast]\n" "\n" "The filters are:\n" "* A single keyword like \"web\" which will list any container with a name " @@ -715,8 +736,9 @@ "Default column layout: ns46tS\n" "Fast column layout: nsacPt\n" "\n" -"Example: lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile." -"eth0.hwaddr:MAC\n" +"Example:\n" +" lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile.eth0." +"hwaddr:MAC" msgstr "" "Listet vorhandene Ressourcen.\n" "\n" @@ -737,11 +759,11 @@ msgid "Log:" msgstr "" -#: lxc/network.go:424 +#: lxc/network.go:423 msgid "MANAGED" msgstr "" -#: lxc/image.go:165 +#: lxc/image.go:164 msgid "Make image public" msgstr "Veröffentliche Abbild" @@ -755,22 +777,29 @@ msgid "" "Manage configuration profiles.\n" "\n" -"lxc profile list [filters] List available profiles.\n" -"lxc profile show Show details of a profile.\n" -"lxc profile create Create a profile.\n" -"lxc profile copy Copy the profile to the " -"specified remote.\n" -"lxc profile get Get profile configuration.\n" -"lxc profile set Set profile configuration.\n" -"lxc profile unset Unset profile configuration.\n" -"lxc profile delete Delete a profile.\n" -"lxc profile edit \n" +"lxc profile list [:] List " +"available profiles.\n" +"lxc profile show [:] Show details " +"of a profile.\n" +"lxc profile create [:] Create a " +"profile.\n" +"lxc profile copy [:] [:] Copy the " +"profile.\n" +"lxc profile get [:] Get profile " +"configuration.\n" +"lxc profile set [:] Set profile " +"configuration.\n" +"lxc profile unset [:] Unset " +"profile configuration.\n" +"lxc profile delete [:] Delete a " +"profile.\n" +"lxc profile edit [:]\n" " Edit profile, either by launching external editor or reading STDIN.\n" " Example: lxc profile edit # launch editor\n" " cat profile.yaml | lxc profile edit # read from " "profile.yaml\n" "\n" -"lxc profile assign \n" +"lxc profile assign [:] \n" " Assign a comma-separated list of profiles to a container, in order.\n" " All profiles passed in this call (and only those) will be applied\n" " to the specified container, i.e. it sets the list of profiles exactly " @@ -782,27 +811,27 @@ " lxc profile assign foo default # Only default is active\n" " lxc profile assign '' # no profiles are applied anymore\n" " lxc profile assign bar,default # Apply default second now\n" -"lxc profile add # add a profile to a container\n" -"lxc profile remove # remove the profile from a " +"lxc profile add [:] # add a profile to a " "container\n" +"lxc profile remove [:] # remove the profile " +"from a container\n" "\n" "Devices:\n" -"lxc profile device list List " -"devices in the given profile.\n" -"lxc profile device show Show " -"full device details in the given profile.\n" -"lxc profile device remove Remove a " -"device from a profile.\n" -"lxc profile device get <[remote:]profile> Get a " -"device property.\n" -"lxc profile device set <[remote:]profile> Set a " -"device property.\n" -"lxc profile device unset <[remote:]profile> Unset a " -"device property.\n" -"lxc profile device add " -"[key=value]...\n" -" Add a profile device, such as a disk or a nic, to the containers\n" -" using the specified profile." +"lxc profile device list [:] " +"List devices in the given profile.\n" +"lxc profile device show [:] " +"Show full device details in the given profile.\n" +"lxc profile device remove [:] " +"Remove a device from a profile.\n" +"lxc profile device get [:] " +"Get a device property.\n" +"lxc profile device set [:] " +"Set a device property.\n" +"lxc profile device unset [:] " +"Unset a device property.\n" +"lxc profile device add [:] [key=value...]\n" +" Add a profile device, such as a disk or a nic, to the containers using " +"the specified profile." msgstr "" "Verwalte Konfigurationsprofile.\n" "\n" @@ -843,51 +872,58 @@ msgid "" "Manage configuration.\n" "\n" -"lxc config device add <[remote:]container> [key=value]... " -"Add a device to a container.\n" -"lxc config device get <[remote:]container> " -"Get a device property.\n" -"lxc config device set <[remote:]container> " -"Set a device property.\n" -"lxc config device unset <[remote:]container> " -"Unset a device property.\n" -"lxc config device list <[remote:]container> " -"List devices for container.\n" -"lxc config device show <[remote:]container> " -"Show full device details for container.\n" -"lxc config device remove <[remote:]container> " -"Remove device from container.\n" -"\n" -"lxc config get [remote:][container] " -"Get container or server configuration key.\n" -"lxc config set [remote:][container] " -"Set container or server configuration key.\n" -"lxc config unset [remote:][container] " -"Unset container or server configuration key.\n" -"lxc config show [remote:][container] [--expanded] " -"Show container or server configuration.\n" -"lxc config edit [remote:][container] " -"Edit container or server configuration in external editor.\n" +"lxc config device add [:] " +"[key=value...] Add a device to a container.\n" +"lxc config device get [:] " +" Get a device property.\n" +"lxc config device set [:] " +" Set a device property.\n" +"lxc config device unset [:] " +" Unset a device property.\n" +"lxc config device list " +"[:] List devices for " +"container.\n" +"lxc config device show " +"[:] Show full device " +"details for container.\n" +"lxc config device remove [:] " +" Remove device from container.\n" +"\n" +"lxc config get [:][container] " +" Get container or server " +"configuration key.\n" +"lxc config set [:][container] " +" Set container or server configuration " +"key.\n" +"lxc config unset [:][container] " +" Unset container or server " +"configuration key.\n" +"lxc config show [:][container] [--" +"expanded] Show container or server configuration.\n" +"lxc config edit [:]" +"[container] Edit container or server " +"configuration in external editor.\n" " Edit configuration, either by launching external editor or reading " "STDIN.\n" " Example: lxc config edit # launch editor\n" -" cat config.yaml | lxc config edit # read from config." -"yaml\n" +" cat config.yaml | lxc config edit # read from " +"config.yaml\n" "\n" -"lxc config trust list [remote] " -"List all trusted certs.\n" -"lxc config trust add [remote] " -"Add certfile.crt to trusted hosts.\n" -"lxc config trust remove [remote] [hostname|fingerprint] " -"Remove the cert from trusted hosts.\n" +"lxc config trust list " +"[:] List all trusted " +"certs.\n" +"lxc config trust add [:] Add certfile.crt to trusted hosts.\n" +"lxc config trust remove [:] [hostname|" +"fingerprint] Remove the cert from trusted hosts.\n" "\n" "Examples:\n" "To mount host's /share/c1 onto /opt in the container:\n" -" lxc config device add [remote:]container1 disk source=/" +" lxc config device add [:]container1 disk source=/" "share/c1 path=opt\n" "\n" "To set an lxc config value:\n" -" lxc config set [remote:] raw.lxc 'lxc.aa_allow_incomplete = " +" lxc config set [:] raw.lxc 'lxc.aa_allow_incomplete = " "1'\n" "\n" "To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the " @@ -930,26 +966,26 @@ "Um das Server Passwort zur authentifizierung zu setzen:\n" "\tlxc config set core.trust_password blah\n" -#: lxc/file.go:35 +#: lxc/file.go:36 #, fuzzy msgid "" -"Manage files on a container.\n" +"Manage files in a container.\n" "\n" -"lxc file pull [-r|--recursive] [...] \n" +"lxc file pull [-r|--recursive] [:] " +"[[:]...] \n" "lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--" -"mode=MODE] [...] \n" -"lxc file edit \n" +"mode=MODE] [...] [:]\n" +"lxc file edit [:]/\n" "\n" " in the case of pull, in the case of push and in the " "case of edit are /\n" "\n" "Examples:\n" -"\n" "To push /etc/hosts into the container foo:\n" -" lxc file push /etc/hosts foo/etc/hosts\n" +" lxc file push /etc/hosts foo/etc/hosts\n" "\n" "To pull /etc/hosts from the container:\n" -" lxc file pull foo/etc/hosts .\n" +" lxc file pull foo/etc/hosts ." msgstr "" "Verwaltet Dateien in einem Container.\n" "\n" @@ -964,44 +1000,49 @@ msgid "" "Manage networks.\n" "\n" -"lxc network list List available networks.\n" -"lxc network show Show details of a network.\n" -"lxc network create [key=value]... Create a network.\n" -"lxc network get Get network configuration.\n" -"lxc network set Set network configuration.\n" -"lxc network unset Unset network configuration.\n" -"lxc network delete Delete a network.\n" -"lxc network edit \n" +"lxc network list [:] List available " +"networks.\n" +"lxc network show [:] Show details of a " +"network.\n" +"lxc network create [:] [key=value...] Create a network.\n" +"lxc network get [:] Get network " +"configuration.\n" +"lxc network set [:] Set network " +"configuration.\n" +"lxc network unset [:] Unset network " +"configuration.\n" +"lxc network delete [:] Delete a network.\n" +"lxc network edit [:]\n" " Edit network, either by launching external editor or reading STDIN.\n" " Example: lxc network edit # launch editor\n" " cat network.yaml | lxc network edit # read from " "network.yaml\n" "\n" -"lxc network attach [device name]\n" -"lxc network attach-profile [device name]\n" +"lxc network attach [:] [device name]\n" +"lxc network attach-profile [:] [device name]\n" "\n" -"lxc network detach [device name]\n" -"lxc network detach-profile [device name]\n" +"lxc network detach [:] [device name]\n" +"lxc network detach-profile [:] [device name]" msgstr "" -#: lxc/remote.go:38 +#: lxc/remote.go:37 #, fuzzy msgid "" "Manage remote LXD servers.\n" "\n" -"lxc remote add [] [--accept-certificate] [--" +"lxc remote add [] [--accept-certificate] [--" "password=PASSWORD]\n" -" [--public] [--protocol=PROTOCOL] " +" [--public] [--protocol=PROTOCOL] " "Add the remote at .\n" -"lxc remote remove " +"lxc remote remove " "Remove the remote .\n" "lxc remote list " "List all remotes.\n" -"lxc remote rename " +"lxc remote rename " "Rename remote to .\n" -"lxc remote set-url " +"lxc remote set-url " "Update 's url to .\n" -"lxc remote set-default " +"lxc remote set-default " "Set the default remote.\n" "lxc remote get-default " "Print the default remote." @@ -1042,22 +1083,22 @@ "hash or alias name (if one is set).\n" "\n" "\n" -"lxc image import [rootfs tarball|URL] [remote:] [--public] [--" -"created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--" -"alias=ALIAS].. [prop=value]\n" +"lxc image import [|] [:] [--public] " +"[--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] " +"[--alias=ALIAS...] [prop=value]\n" " Import an image tarball (or tarballs) into the LXD image store.\n" "\n" -"lxc image copy [remote:] : [--alias=ALIAS].. [--copy-aliases] " -"[--public] [--auto-update]\n" +"lxc image copy [:] : [--alias=ALIAS...] [--copy-" +"aliases] [--public] [--auto-update]\n" " Copy an image from one LXD daemon to another over the network.\n" "\n" " The auto-update flag instructs the server to keep this image up to\n" " date. It requires the source to be an alias and for it to be public.\n" "\n" -"lxc image delete [remote:] [remote:][...]\n" +"lxc image delete [:] [[:]...]\n" " Delete one or more images from the LXD image store.\n" "\n" -"lxc image export [remote:] [target]\n" +"lxc image export [:] [target]\n" " Export an image from the LXD image store into a distributable tarball.\n" "\n" " The output target is optional and defaults to the working directory.\n" @@ -1069,31 +1110,31 @@ " the appropriate extension will be appended to the provided file name\n" " based on the algorithm used to compress the image. \n" "\n" -"lxc image info [remote:]\n" +"lxc image info [:]\n" " Print everything LXD knows about a given image.\n" "\n" -"lxc image list [remote:] [filter] [--format table|json]\n" +"lxc image list [:] [filter] [--format table|json]\n" " List images in the LXD image store. Filters may be of the\n" " = form for property based filtering, or part of the image\n" " hash or part of the image alias name.\n" "\n" -"lxc image show [remote:]\n" +"lxc image show [:]\n" " Yaml output of the user modifiable properties of an image.\n" "\n" -"lxc image edit [remote:]\n" +"lxc image edit [:]\n" " Edit image, either by launching external editor or reading STDIN.\n" " Example: lxc image edit # launch editor\n" " cat image.yaml | lxc image edit # read from image.yaml\n" "\n" -"lxc image alias create [remote:] \n" +"lxc image alias create [:] \n" " Create a new alias for an existing image.\n" "\n" -"lxc image alias delete [remote:]\n" +"lxc image alias delete [:]\n" " Delete an alias.\n" "\n" -"lxc image alias list [remote:] [filter]\n" +"lxc image alias list [:] [filter]\n" " List the aliases. Filters may be part of the image hash or part of the " -"image alias name.\n" +"image alias name." msgstr "" #: lxc/info.go:161 @@ -1112,7 +1153,7 @@ msgid "" "Monitor activity on the LXD server.\n" "\n" -"lxc monitor [remote:] [--type=TYPE...]\n" +"lxc monitor [:] [--type=TYPE...]\n" "\n" "Connects to the monitoring interface of the specified LXD server.\n" "\n" @@ -1120,14 +1161,14 @@ "Specific types to listen to can be specified with --type.\n" "\n" "Example:\n" -"lxc monitor --type=logging" +" lxc monitor --type=logging" msgstr "" -#: lxc/network.go:216 lxc/network.go:265 +#: lxc/network.go:215 lxc/network.go:264 msgid "More than one device matches, specify the device name." msgstr "" -#: lxc/file.go:235 +#: lxc/file.go:237 msgid "More than one file to download, but target is not a directory" msgstr "" "Mehr als eine Datei herunterzuladen, aber das Ziel ist kein Verzeichnis" @@ -1137,12 +1178,15 @@ msgid "" "Move containers within or in between lxd instances.\n" "\n" -"lxc move [remote:] [remote:]\n" +"lxc move [:] [:][]\n" " Move a container between two hosts, renaming it if destination name " "differs.\n" "\n" "lxc move \n" " Rename a local container.\n" +"\n" +"lxc move / /\n" +" Rename a snapshot." msgstr "" "Verschiebt Container innerhalb einer oder zwischen lxd Instanzen\n" "\n" @@ -1153,11 +1197,11 @@ msgid "Must supply container name for: " msgstr "der Name des Ursprung Containers muss angegeben werden" -#: lxc/list.go:428 lxc/network.go:422 lxc/profile.go:447 lxc/remote.go:381 +#: lxc/list.go:428 lxc/network.go:421 lxc/profile.go:446 lxc/remote.go:380 msgid "NAME" msgstr "" -#: lxc/network.go:408 lxc/remote.go:355 lxc/remote.go:360 +#: lxc/network.go:407 lxc/remote.go:354 lxc/remote.go:359 msgid "NO" msgstr "" @@ -1166,12 +1210,12 @@ msgid "Name: %s" msgstr "" -#: lxc/network.go:190 +#: lxc/network.go:189 #, fuzzy, c-format msgid "Network %s created" msgstr "Profil %s erstellt\n" -#: lxc/network.go:293 +#: lxc/network.go:292 #, fuzzy, c-format msgid "Network %s deleted" msgstr "Profil %s gelöscht\n" @@ -1180,7 +1224,7 @@ msgid "Network name" msgstr "" -#: lxc/image.go:168 lxc/publish.go:34 +#: lxc/image.go:167 lxc/publish.go:34 msgid "New alias to define at target" msgstr "" @@ -1189,7 +1233,7 @@ msgid "No certificate provided to add" msgstr "Kein Zertifikat zum hinzufügen bereitgestellt" -#: lxc/network.go:225 lxc/network.go:274 +#: lxc/network.go:224 lxc/network.go:273 #, fuzzy msgid "No device found for this network" msgstr "Kein Zertifikat für diese Verbindung" @@ -1198,11 +1242,11 @@ msgid "No fingerprint specified." msgstr "Kein Fingerabdruck angegeben." -#: lxc/remote.go:121 +#: lxc/remote.go:120 msgid "Only https URLs are supported for simplestreams" msgstr "" -#: lxc/image.go:434 +#: lxc/image.go:424 msgid "Only https:// is supported for remote image import." msgstr "" @@ -1210,7 +1254,7 @@ msgid "Options:" msgstr "" -#: lxc/image.go:538 +#: lxc/image.go:535 #, c-format msgid "Output is in %s" msgstr "" @@ -1231,11 +1275,11 @@ msgid "PROFILES" msgstr "" -#: lxc/remote.go:383 +#: lxc/remote.go:382 msgid "PROTOCOL" msgstr "" -#: lxc/image.go:640 lxc/remote.go:384 +#: lxc/image.go:637 lxc/remote.go:383 msgid "PUBLIC" msgstr "" @@ -1249,12 +1293,12 @@ #: lxc/help.go:70 #, fuzzy -msgid "Path to an alternate client configuration directory." +msgid "Path to an alternate client configuration directory" msgstr "Alternatives config Verzeichnis." #: lxc/help.go:71 #, fuzzy -msgid "Path to an alternate server directory." +msgid "Path to an alternate server directory" msgstr "Alternatives config Verzeichnis." #: lxc/main.go:31 @@ -1266,39 +1310,28 @@ msgid "Pid: %d" msgstr "" -#: lxc/help.go:25 -#, fuzzy -msgid "" -"Presents details on how to use LXD.\n" -"\n" -"lxd help [--all]" -msgstr "" -"Zeigt Details über die Benutzung von LXD an.\n" -"\n" -"lxd help [—all]\n" - -#: lxc/network.go:343 lxc/profile.go:219 +#: lxc/network.go:342 lxc/profile.go:218 msgid "Press enter to open the editor again" msgstr "" -#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:735 +#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:732 msgid "Press enter to start the editor again" msgstr "" +#: lxc/manpage.go:18 +msgid "Print all the subcommands help." +msgstr "" + #: lxc/help.go:65 -msgid "Print debug information." +msgid "Print debug information" msgstr "" #: lxc/help.go:64 -msgid "Print less common commands." +msgid "Print less common commands" msgstr "" #: lxc/help.go:66 -msgid "Print verbose information." -msgstr "" - -#: lxc/manpage.go:18 -msgid "Prints all the subcommands help." +msgid "Print verbose information" msgstr "" #: lxc/version.go:18 @@ -1317,22 +1350,22 @@ msgid "Processes: %d" msgstr "Profil %s erstellt\n" -#: lxc/profile.go:275 +#: lxc/profile.go:274 #, fuzzy, c-format msgid "Profile %s added to %s" msgstr "Profil %s wurde auf %s angewandt\n" -#: lxc/profile.go:170 +#: lxc/profile.go:169 #, fuzzy, c-format msgid "Profile %s created" msgstr "Profil %s erstellt\n" -#: lxc/profile.go:240 +#: lxc/profile.go:239 #, fuzzy, c-format msgid "Profile %s deleted" msgstr "Profil %s gelöscht\n" -#: lxc/profile.go:306 +#: lxc/profile.go:305 #, fuzzy, c-format msgid "Profile %s removed from %s" msgstr "Gerät %s wurde von %s entfernt\n" @@ -1343,7 +1376,7 @@ msgid "Profile to apply to the new container" msgstr "kann nicht zum selben Container Namen kopieren" -#: lxc/profile.go:256 +#: lxc/profile.go:255 #, fuzzy, c-format msgid "Profiles %s applied to %s" msgstr "Profil %s wurde auf %s angewandt\n" @@ -1353,16 +1386,16 @@ msgid "Profiles: %s" msgstr "Profil %s erstellt\n" -#: lxc/image.go:361 +#: lxc/image.go:358 #, fuzzy msgid "Properties:" msgstr "Eigenschaften:\n" -#: lxc/remote.go:55 +#: lxc/remote.go:54 msgid "Public image server" msgstr "" -#: lxc/image.go:349 +#: lxc/image.go:346 #, fuzzy, c-format msgid "Public: %s" msgstr "Öffentlich: %s\n" @@ -1371,15 +1404,15 @@ msgid "" "Publish containers as images.\n" "\n" -"lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-" -"value]..." +"lxc publish [:][/] [:] [--" +"alias=ALIAS...] [prop-key=prop-value...]" msgstr "" -#: lxc/file.go:58 lxc/file.go:59 +#: lxc/file.go:57 lxc/file.go:58 msgid "Recursively push or pull files" msgstr "" -#: lxc/remote.go:53 +#: lxc/remote.go:52 msgid "Remote admin password" msgstr "Entferntes Administrator Passwort" @@ -1394,19 +1427,36 @@ msgstr "" #: lxc/delete.go:36 lxc/delete.go:37 -msgid "Require user confirmation." +msgid "Require user confirmation" msgstr "" #: lxc/info.go:127 msgid "Resources:" msgstr "" -#: lxc/init.go:267 +#: lxc/restore.go:21 +msgid "" +"Restore a container's state to a previous snapshot.\n" +"\n" +"lxc restore [:] [--stateful]\n" +"\n" +"Restores a container from a snapshot (optionally with running state, see\n" +"snapshot help for details).\n" +"\n" +"Examples:\n" +"Create the snapshot:\n" +" lxc snapshot u1 snap0\n" +"\n" +"Restore the snapshot:\n" +" lxc restore u1 snap0" +msgstr "" + +#: lxc/init.go:239 #, c-format msgid "Retrieving image: %s" msgstr "" -#: lxc/image.go:643 +#: lxc/image.go:640 msgid "SIZE" msgstr "" @@ -1418,46 +1468,32 @@ msgid "STATE" msgstr "" -#: lxc/remote.go:385 +#: lxc/remote.go:384 msgid "STATIC" msgstr "" -#: lxc/remote.go:226 +#: lxc/remote.go:225 msgid "Server certificate NACKed by user" msgstr "Server Zertifikat vom Benutzer nicht akzeptiert" -#: lxc/remote.go:288 +#: lxc/remote.go:287 msgid "Server doesn't trust us after adding our cert" msgstr "" "Der Server vertraut uns nicht nachdem er unser Zertifikat hinzugefügt hat" -#: lxc/remote.go:54 +#: lxc/remote.go:53 msgid "Server protocol (lxd or simplestreams)" msgstr "" -#: lxc/restore.go:21 -msgid "" -"Set the current state of a resource back to a snapshot.\n" -"\n" -"lxc restore [remote:] [--stateful]\n" -"\n" -"Restores a container from a snapshot (optionally with running state, see\n" -"snapshot help for details).\n" -"\n" -"For example:\n" -"lxc snapshot u1 snap0 # create the snapshot\n" -"lxc restore u1 snap0 # restore the snapshot" -msgstr "" - -#: lxc/file.go:56 +#: lxc/file.go:55 msgid "Set the file's gid on push" msgstr "Setzt die gid der Datei beim Übertragen" -#: lxc/file.go:57 +#: lxc/file.go:56 msgid "Set the file's perms on push" msgstr "Setzt die Dateiberechtigungen beim Übertragen" -#: lxc/file.go:55 +#: lxc/file.go:54 msgid "Set the file's uid on push" msgstr "Setzt die uid der Datei beim Übertragen" @@ -1466,14 +1502,18 @@ msgstr "Zeigt alle Befehle (nicht nur die interessanten)" #: lxc/help.go:67 -msgid "Show client version." +msgid "Show client version" msgstr "" #: lxc/info.go:36 msgid "Show the container's last 100 log lines?" msgstr "Zeige die letzten 100 Zeilen Protokoll des Containers?" -#: lxc/image.go:347 +#: lxc/config.go:32 +msgid "Show the expanded configuration" +msgstr "" + +#: lxc/image.go:344 #, fuzzy, c-format msgid "Size: %.2fMB" msgstr "Größe: %.2vMB\n" @@ -1482,11 +1522,11 @@ msgid "Snapshots:" msgstr "" -#: lxc/image.go:371 +#: lxc/image.go:368 msgid "Source:" msgstr "" -#: lxc/launch.go:142 +#: lxc/launch.go:144 #, c-format msgid "Starting %s" msgstr "" @@ -1506,7 +1546,7 @@ #: lxc/action.go:45 #, fuzzy -msgid "Store the container state (only for stop)." +msgid "Store the container state (only for stop)" msgstr "Herunterfahren des Containers erzwingen." #: lxc/info.go:169 @@ -1517,7 +1557,7 @@ msgid "Swap (peak)" msgstr "" -#: lxc/list.go:433 lxc/network.go:423 +#: lxc/list.go:433 lxc/network.go:422 msgid "TYPE" msgstr "" @@ -1531,8 +1571,8 @@ "restarted." msgstr "" -#: lxc/init.go:313 -msgid "The container you are starting doesn’t have any network attached to it." +#: lxc/init.go:312 +msgid "The container you are starting doesn't have any network attached to it." msgstr "" #: lxc/config.go:675 lxc/config.go:687 lxc/config.go:720 lxc/config.go:738 @@ -1541,7 +1581,7 @@ msgid "The device doesn't exist" msgstr "entfernte Instanz %s existiert nicht" -#: lxc/init.go:297 +#: lxc/init.go:296 #, c-format msgid "The local image '%s' couldn't be found, trying '%s:' instead." msgstr "" @@ -1550,12 +1590,12 @@ msgid "The opposite of `lxc pause` is `lxc start`." msgstr "" -#: lxc/network.go:230 lxc/network.go:279 +#: lxc/network.go:229 lxc/network.go:278 #, fuzzy msgid "The specified device doesn't exist" msgstr "entfernte Instanz %s existiert nicht" -#: lxc/network.go:234 lxc/network.go:283 +#: lxc/network.go:233 lxc/network.go:282 #, fuzzy msgid "The specified device doesn't match the network" msgstr "entfernte Instanz %s existiert nicht" @@ -1565,19 +1605,20 @@ msgstr "" #: lxc/action.go:41 -msgid "Time to wait for the container before killing it." +#, fuzzy +msgid "Time to wait for the container before killing it" msgstr "Wartezeit bevor der Container gestoppt wird." -#: lxc/image.go:350 +#: lxc/image.go:347 #, fuzzy msgid "Timestamps:" msgstr "Zeitstempel:\n" -#: lxc/init.go:315 +#: lxc/init.go:314 msgid "To attach a network to a container, use: lxc network attach" msgstr "" -#: lxc/init.go:314 +#: lxc/init.go:313 msgid "To create a new network, use: lxc network create" msgstr "" @@ -1585,12 +1626,12 @@ msgid "To start your first container, try: lxc launch ubuntu:16.04" msgstr "" -#: lxc/image.go:421 +#: lxc/image.go:426 #, c-format -msgid "Transferring image: %d%%" +msgid "Transferring image: %s" msgstr "" -#: lxc/action.go:99 lxc/launch.go:155 +#: lxc/action.go:99 lxc/launch.go:157 #, c-format msgid "Try `lxc info --show-log %s` for more info" msgstr "" @@ -1603,23 +1644,23 @@ msgid "Type: persistent" msgstr "" -#: lxc/image.go:644 +#: lxc/image.go:641 msgid "UPLOAD DATE" msgstr "" -#: lxc/remote.go:382 +#: lxc/remote.go:381 msgid "URL" msgstr "" -#: lxc/network.go:425 lxc/profile.go:448 +#: lxc/network.go:424 lxc/profile.go:447 msgid "USED BY" msgstr "" -#: lxc/remote.go:96 +#: lxc/remote.go:95 msgid "Unable to read remote TLS certificate" msgstr "" -#: lxc/image.go:355 +#: lxc/image.go:352 #, c-format msgid "Uploaded: %s" msgstr "" @@ -1635,7 +1676,7 @@ #: lxc/help.go:48 #, fuzzy -msgid "Usage: lxc [subcommand] [options]" +msgid "Usage: lxc [options]" msgstr "" "Benutzung: lxc [Unterbefehl] [Optionen]\n" "Verfügbare Befehle:\n" @@ -1644,7 +1685,7 @@ msgid "User aborted delete operation." msgstr "" -#: lxc/restore.go:35 +#: lxc/restore.go:38 msgid "" "Whether or not to restore the container's running state from snapshot (if " "available)" @@ -1656,11 +1697,7 @@ msgid "Whether or not to snapshot the container's running state" msgstr "Zustand des laufenden Containers sichern oder nicht" -#: lxc/config.go:32 -msgid "Whether to show the expanded configuration" -msgstr "" - -#: lxc/network.go:410 lxc/remote.go:357 lxc/remote.go:362 +#: lxc/network.go:409 lxc/remote.go:356 lxc/remote.go:361 msgid "YES" msgstr "" @@ -1668,7 +1705,7 @@ msgid "`lxc config profile` is deprecated, please use `lxc profile`" msgstr "" -#: lxc/launch.go:127 +#: lxc/launch.go:128 msgid "bad number of things scanned from image, container or snapshot" msgstr "" "Falsche Anzahl an Objekten im Abbild, Container oder Sicherungspunkt gelesen." @@ -1681,32 +1718,32 @@ msgid "can't copy to the same container name" msgstr "kann nicht zum selben Container Namen kopieren" -#: lxc/file.go:270 +#: lxc/file.go:272 msgid "can't pull a directory without --recursive" msgstr "" -#: lxc/remote.go:345 +#: lxc/remote.go:344 msgid "can't remove the default remote" msgstr "" -#: lxc/file.go:117 +#: lxc/file.go:119 msgid "can't supply uid/gid/mode in recursive mode" msgstr "" -#: lxc/remote.go:371 +#: lxc/remote.go:370 msgid "default" msgstr "" #: lxc/copy.go:131 lxc/copy.go:136 lxc/copy.go:232 lxc/copy.go:237 -#: lxc/init.go:217 lxc/init.go:222 lxc/launch.go:111 lxc/launch.go:116 +#: lxc/init.go:219 lxc/init.go:224 lxc/launch.go:112 lxc/launch.go:117 msgid "didn't get any affected image, container or snapshot from server" msgstr "" -#: lxc/image.go:341 +#: lxc/image.go:338 msgid "disabled" msgstr "" -#: lxc/image.go:343 +#: lxc/image.go:340 msgid "enabled" msgstr "" @@ -1720,11 +1757,11 @@ msgid "error: unknown command: %s" msgstr "Fehler: unbekannter Befehl: %s\n" -#: lxc/launch.go:131 +#: lxc/launch.go:132 msgid "got bad version" msgstr "Versionskonflikt" -#: lxc/image.go:336 lxc/image.go:620 +#: lxc/image.go:333 lxc/image.go:617 msgid "no" msgstr "" @@ -1732,7 +1769,7 @@ msgid "not all the profiles from the source exist on the target" msgstr "nicht alle Profile der Quelle sind am Ziel vorhanden." -#: lxc/remote.go:219 +#: lxc/remote.go:218 #, fuzzy msgid "ok (y/n)?" msgstr "OK (y/n)? " @@ -1742,26 +1779,26 @@ msgid "processing aliases failed %s\n" msgstr "" -#: lxc/file.go:311 +#: lxc/file.go:313 msgid "recursive edit doesn't make sense :(" msgstr "" -#: lxc/remote.go:407 +#: lxc/remote.go:406 #, c-format msgid "remote %s already exists" msgstr "entfernte Instanz %s existiert bereits" -#: lxc/remote.go:337 lxc/remote.go:399 lxc/remote.go:434 lxc/remote.go:450 +#: lxc/remote.go:336 lxc/remote.go:398 lxc/remote.go:433 lxc/remote.go:449 #, c-format msgid "remote %s doesn't exist" msgstr "entfernte Instanz %s existiert nicht" -#: lxc/remote.go:320 +#: lxc/remote.go:319 #, c-format msgid "remote %s exists as <%s>" msgstr "entfernte Instanz %s existiert als <%s>" -#: lxc/remote.go:341 lxc/remote.go:403 lxc/remote.go:438 +#: lxc/remote.go:340 lxc/remote.go:402 lxc/remote.go:437 #, c-format msgid "remote %s is static and cannot be modified" msgstr "" @@ -1787,7 +1824,7 @@ msgid "wrong number of subcommand arguments" msgstr "falsche Anzahl an Parametern für Unterbefehl" -#: lxc/delete.go:45 lxc/image.go:338 lxc/image.go:624 +#: lxc/delete.go:45 lxc/image.go:335 lxc/image.go:621 msgid "yes" msgstr "" diff -Nru lxd-2.6.2/po/fr.po lxd-2.7/po/fr.po --- lxd-2.6.2/po/fr.po 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/po/fr.po 2016-12-21 00:52:23.000000000 +0000 @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: LXD\n" "Report-Msgid-Bugs-To: lxc-devel@lists.linuxcontainers.org\n" -"POT-Creation-Date: 2016-11-04 21:31-0400\n" -"PO-Revision-Date: 2015-02-26 02:05-0600\n" +"POT-Creation-Date: 2016-12-15 13:20-0500\n" +"PO-Revision-Date: 2016-12-16 14:08+0100\n" "Last-Translator: Stéphane Graber \n" "Language: \n" @@ -18,19 +18,19 @@ #: lxc/info.go:154 msgid " CPU usage:" -msgstr "" +msgstr " CPU utilisé :" #: lxc/info.go:143 msgid " Disk usage:" -msgstr "" +msgstr " Disque utilisé :" #: lxc/info.go:177 msgid " Memory usage:" -msgstr "" +msgstr " Mémoire utilisée :" #: lxc/info.go:194 msgid " Network usage:" -msgstr "" +msgstr " Réseau utilisé :" #: lxc/config.go:36 msgid "" @@ -52,6 +52,23 @@ "###\n" "### Note that the name is shown but cannot be changed" msgstr "" +"### Ceci est une réprésentation yaml de la configuration.\n" +"### Toute ligne commençant par un '# sera ignorée.\n" +"###\n" +"### Une exemple de configuration ressemble à :\n" +"### name: container1\n" +"### profiles:\n" +"### - default\n" +"### config:\n" +"### volatile.eth0.hwaddr: 00:16:3e:e9:f8:7f\n" +"### devices:\n" +"### homedir:\n" +"### path: /extra\n" +"### source: /home/user\n" +"### type: disk\n" +"### ephemeral: false\n" +"###\n" +"### Notez que le nom est affiché mais ne peut être modifié" #: lxc/image.go:85 msgid "" @@ -62,6 +79,12 @@ "### An example would be:\n" "### description: My custom image" msgstr "" +"### Ceci est une représentation yaml des propriétés de l'image.\n" +"### Toute ligne commençant par un '# sera ignorée.\n" +"###\n" +"### Caque propriété est représentée par une ligne unique :\n" +"### Un exemple serait :\n" +"### description: Mon image personnalisée" #: lxc/network.go:28 msgid "" @@ -82,6 +105,23 @@ "###\n" "### Note that only the configuration can be changed." msgstr "" +"### Ceci est une représentation yaml du réseau.\n" +"### Toute ligne commençant par un '# sera ignorée.\n" +"###\n" +"### Un réseau est constitué d'un ensemble d'éléments de configuration.\n" +"### A network consists of a set of configuration item<\n" +"###\n" +"### Un exemple reseemblerait à :\n" +"### name: lxdbr0\n" +"### config:\n" +"### ipv4.address: 10.62.42.1/24\n" +"### ipv4.nat: true\n" +"### ipv6.address: fd00:56ad:9f7a:9800::1/64\n" +"### ipv6.nat: true\n" +"### managed: true\n" +"### type: bridge\n" +"###\n" +"### Notez que seule la configuration peut être modifiée." #: lxc/profile.go:27 msgid "" @@ -103,26 +143,41 @@ "###\n" "### Note that the name is shown but cannot be changed" msgstr "" +"### Ceci est une représentation yaml du profil.\n" +"### Toute ligne commençant par un '# sera ignorée.\n" +"###\n" +"### Un profil est constitué d'un ensemble d'éléments de configuration.\n" +"###\n" +"### Un exemple resemblerait à :\n" +"### name: onenic\n" +"### config:\n" +"### raw.lxc: lxc.aa_profile=unconfined\n" +"### devices:\n" +"### eth0:\n" +"### nictype: bridged\n" +"### parent: lxdbr0\n" +"### type: nic\n" +"###\n" +"### Notez que le nom est affiché mais ne peut être modifié" -#: lxc/image.go:617 +#: lxc/image.go:614 #, c-format msgid "%s (%d more)" -msgstr "" +msgstr "%s (%d de plus)" #: lxc/snapshot.go:61 -#, fuzzy msgid "'/' not allowed in snapshot name" -msgstr "'/' n'est pas autorisé dans le nom d'un instantané (snapshot)\n" +msgstr "'/' n'est pas autorisé dans le nom d'un instantané" -#: lxc/profile.go:254 +#: lxc/profile.go:253 msgid "(none)" -msgstr "" +msgstr "(aucun)" -#: lxc/image.go:638 lxc/image.go:680 +#: lxc/image.go:635 lxc/image.go:677 msgid "ALIAS" msgstr "" -#: lxc/image.go:642 +#: lxc/image.go:639 msgid "ARCH" msgstr "" @@ -130,44 +185,40 @@ msgid "ARCHITECTURE" msgstr "" -#: lxc/remote.go:52 +#: lxc/remote.go:51 msgid "Accept certificate" -msgstr "" +msgstr "Accepter le certificat" -#: lxc/remote.go:268 +#: lxc/remote.go:267 #, c-format msgid "Admin password for %s: " -msgstr "Mot de passe administrateur pour %s: " +msgstr "Mot de passe administrateur pour %s : " -#: lxc/image.go:365 +#: lxc/image.go:362 msgid "Aliases:" -msgstr "" +msgstr "Alias :" -#: lxc/exec.go:54 -msgid "An environment variable of the form HOME=/home/foo" -msgstr "" - -#: lxc/image.go:348 lxc/info.go:93 +#: lxc/image.go:345 lxc/info.go:93 #, c-format msgid "Architecture: %s" -msgstr "" +msgstr "Architecture : %s" -#: lxc/image.go:369 +#: lxc/image.go:366 #, c-format msgid "Auto update: %s" -msgstr "" +msgstr "Mise à jour auto. : %s" #: lxc/help.go:49 msgid "Available commands:" -msgstr "" +msgstr "Commandes disponibles :" #: lxc/info.go:186 msgid "Bytes received" -msgstr "" +msgstr "Octets reçus" #: lxc/info.go:187 msgid "Bytes sent" -msgstr "" +msgstr "Octets émis" #: lxc/config.go:273 msgid "COMMON NAME" @@ -175,102 +226,125 @@ #: lxc/info.go:150 msgid "CPU usage (in seconds)" -msgstr "" +msgstr "CPU utilisé (en secondes)" #: lxc/list.go:426 msgid "CREATED AT" -msgstr "" +msgstr "CRÉÉ À" #: lxc/config.go:113 #, c-format msgid "Can't read from stdin: %s" -msgstr "" +msgstr "Impossible de lire depuis stdin : %s" #: lxc/config.go:126 lxc/config.go:159 lxc/config.go:181 #, c-format msgid "Can't unset key '%s', it's not currently set." msgstr "" +"Impossible de désaffecter la clé '%s', elle n'est pas définie actuellement." -#: lxc/network.go:386 lxc/profile.go:420 +#: lxc/network.go:385 lxc/profile.go:419 msgid "Cannot provide container name to list" -msgstr "" +msgstr "Impossible de fournir le nom du conteneur à lister" -#: lxc/remote.go:218 -#, fuzzy, c-format +#: lxc/remote.go:217 +#, c-format msgid "Certificate fingerprint: %x" -msgstr "Empreinte du certificat: % x\n" +msgstr "Empreinte du certificat : %x" #: lxc/action.go:33 -#, fuzzy, c-format +#, c-format +msgid "" +"Change state of one or more containers to %s.\n" +"\n" +"lxc %s [:] [[:]...]%s" +msgstr "" +"Change l'état d'un ou plusieurs conteneurs à %s.\n" +"\n" +"lxc %s [:] [[:]...]%s" + +#: lxc/finger.go:15 msgid "" -"Changes state of one or more containers to %s.\n" +"Check if the LXD instance is up.\n" "\n" -"lxc %s [...]%s" -msgstr "Change l'état du conteneur à %s.\n" +"lxc finger [:]" +msgstr "" +"Contacte LXD pour vérifier qu'il est actif et fonctionel.\n" +"\n" +"lxc finger [:]" -#: lxc/remote.go:291 +#: lxc/remote.go:290 msgid "Client certificate stored at server: " -msgstr "Certificat client enregistré avec le serveur: " +msgstr "Certificat client enregistré sur le serveur : " #: lxc/list.go:121 lxc/list.go:122 msgid "Columns" -msgstr "" +msgstr "Colonnes" #: lxc/copy.go:31 lxc/copy.go:32 lxc/init.go:135 lxc/init.go:136 #: lxc/launch.go:40 lxc/launch.go:41 msgid "Config key/value to apply to the new container" -msgstr "" +msgstr "Clé/valeur de configuration à appliquer au nouveau conteneur" -#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:734 lxc/network.go:342 -#: lxc/profile.go:218 -#, fuzzy, c-format +#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:731 lxc/network.go:341 +#: lxc/profile.go:217 +#, c-format msgid "Config parsing error: %s" -msgstr "erreur: %v\n" +msgstr "Erreur lors de la lecture de la configuration : %s" #: lxc/main.go:29 msgid "Connection refused; is LXD running?" -msgstr "" +msgstr "Connexion refusée ; LXD est-il actif ?" #: lxc/publish.go:61 msgid "Container name is mandatory" -msgstr "" +msgstr "Le nom du conteneur est obligatoire" -#: lxc/copy.go:140 lxc/copy.go:241 lxc/init.go:227 +#: lxc/copy.go:140 lxc/copy.go:241 lxc/init.go:229 #, c-format msgid "Container name is: %s" -msgstr "" +msgstr "Le nom du conteneur est : %s" #: lxc/publish.go:151 lxc/publish.go:166 -#, fuzzy, c-format +#, c-format msgid "Container published with fingerprint: %s" -msgstr "Empreinte du certificat: % x\n" +msgstr "Conteneur publié avec l'empreinte : %s" -#: lxc/image.go:166 +#: lxc/image.go:165 msgid "Copy aliases from source" -msgstr "" +msgstr "Copier les alias depuis la source" #: lxc/copy.go:24 msgid "" -"Copy containers within or in between lxd instances.\n" +"Copy containers within or in between LXD instances.\n" "\n" -"lxc copy [remote:] [[remote:]] [--" +"lxc copy [:][/] [[:]] [--" "ephemeral|e] [--profile|-p ...] [--config|-c ...]" msgstr "" +"Copier les conteneurs dans ou depuis les instances LXD.\n" +"\n" +"lxc copy [:][/] [[:]] [--" +"ephemeral|e] [--profile|-p ...] [--config|-c ...]" -#: lxc/image.go:280 +#: lxc/image.go:278 #, c-format msgid "Copying the image: %s" -msgstr "" +msgstr "Copier l'image : %s" -#: lxc/remote.go:233 +#: lxc/remote.go:232 msgid "Could not create server cert dir" -msgstr "Le dossier de stockage des certificats serveurs n'a pas pû être créé" +msgstr "Impossible de créer le dossier de stockage des certificats serveurs" + +#: lxc/file.go:83 +#, c-format +msgid "Could not sanitize path %s" +msgstr "" #: lxc/snapshot.go:21 msgid "" "Create a read-only snapshot of a container.\n" "\n" -"lxc snapshot [remote:] [--stateful]\n" +"lxc snapshot [:] [--stateful]\n" "\n" "Creates a snapshot of the container (optionally with the container's memory\n" "state). When --stateful is used, LXD attempts to checkpoint the container's\n" @@ -283,147 +357,181 @@ "successfully).\n" "\n" "Example:\n" -"lxc snapshot u1 snap0" +" lxc snapshot u1 snap0" msgstr "" +"Créer un instantané en lecture seule d'un conteneur.\n" +"\n" +"lxc snapshot [:] [--stateful]\n" +"\n" +"Crée un instantanté du conteneur (y compris — de manière optionnelle —\n" +"l'état de la mémoire du conteneur). Si --stateful est utilisé, LXD\n" +"tente de créer un point d'arrêt de l'état d'exécution du conteneur,\n" +"incluant l'état de la mémoire, les connexions TCP, etc. afin qu'il\n" +"puisse être restauré (via lxc restore) ultérieurement (bien que\n" +"certaines choses, les connexions TCP ayant expirées après que la\n" +"fenêtre de temps se soit écoulée par exemple, ne puissent être\n" +"restaurées avec succès).\n" +"\n" +"Exemple :\n" +" lxc snapshot u1 snap0" -#: lxc/file.go:60 lxc/file.go:61 +#: lxc/file.go:59 lxc/file.go:60 msgid "Create any directories necessary" -msgstr "" +msgstr "Créer tout répertoire nécessaire" -#: lxc/image.go:353 lxc/info.go:95 +#: lxc/image.go:350 lxc/info.go:95 #, c-format msgid "Created: %s" -msgstr "" +msgstr "Créé : %s" -#: lxc/init.go:180 lxc/launch.go:134 +#: lxc/init.go:180 lxc/launch.go:135 #, c-format msgid "Creating %s" -msgstr "" +msgstr "Création de %s" #: lxc/init.go:178 msgid "Creating the container" -msgstr "" +msgstr "Création du conteneur" -#: lxc/image.go:641 lxc/image.go:682 +#: lxc/image.go:638 lxc/image.go:679 msgid "DESCRIPTION" msgstr "" #: lxc/publish.go:37 msgid "Define a compression algorithm: for image or none" -msgstr "" +msgstr "Définir un algorithme de compression : pour image ou aucun" #: lxc/delete.go:25 msgid "" -"Delete containers or container snapshots.\n" +"Delete containers or snapshots.\n" "\n" -"lxc delete [remote:][/] [remote:][[/" +"lxc delete [:][/] [[:][/" "]...]\n" "\n" "Destroy containers or snapshots with any attached data (configuration, " "snapshots, ...)." msgstr "" +"Supprimer des conteneurs ou des instantanés de conteneur.\n" +"\n" +"lxc delete [:][/] [:][[/" +"]...]\n" +"\n" +"Détruit les conteneurs ou les instantanés ainsi que toute donnée\n" +"associée (configuration, instantanés, …)." #: lxc/config.go:647 #, c-format msgid "Device %s added to %s" -msgstr "" +msgstr "Périphérique %s ajouté à %s" #: lxc/config.go:834 #, c-format msgid "Device %s removed from %s" -msgstr "" +msgstr "Périphérique %s retiré de %s" #: lxc/list.go:570 msgid "EPHEMERAL" -msgstr "" +msgstr "ÉPHÉMÈRE" #: lxc/config.go:275 msgid "EXPIRY DATE" -msgstr "" +msgstr "DATE D'EXPIRATION" #: lxc/main.go:41 -msgid "Enables debug mode." -msgstr "Active le mode de déboguage." +msgid "Enable debug mode" +msgstr "Active le mode de débogage" #: lxc/main.go:40 -msgid "Enables verbose mode." -msgstr "Active le mode verbeux." +msgid "Enable verbose mode" +msgstr "Active le mode verbeux" + +#: lxc/exec.go:54 +msgid "Environment variable to set (e.g. HOME=/home/foo)" +msgstr "Une variable d'environnement (de la forme HOME=/home/foo)" #: lxc/help.go:69 msgid "Environment:" -msgstr "" +msgstr "Environnement :" #: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:139 lxc/init.go:140 #: lxc/launch.go:44 lxc/launch.go:45 msgid "Ephemeral container" -msgstr "" +msgstr "Conteneur éphémère" #: lxc/monitor.go:56 msgid "Event type to listen for" -msgstr "" +msgstr "Type d'évènements à surveiller" #: lxc/exec.go:45 -#, fuzzy msgid "" "Execute the specified command in a container.\n" "\n" -"lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env " -"EDITOR=/usr/bin/vim]... [--] \n" +"lxc exec [:] [--mode=auto|interactive|non-interactive] [--" +"env KEY=VALUE...] [--] \n" "\n" "Mode defaults to non-interactive, interactive mode is selected if both stdin " "AND stdout are terminals (stderr is ignored)." -msgstr "Exécute la commande spécifiée dans un conteneur.\n" +msgstr "" +"Exécute la commande spécifiée dans un conteneur.\n" +"\n" +"lxc exec [:] [--mode=auto|interactive|non-interactive] [--" +"env KEY=VALUE...] [--] \n" +"\n" +"Le mode est non-interactif par défaut, le mode interactif est\n" +"sélectionné si à la fois stdin et stdout sont des terminaux (stderr\n" +"est ignoré)." -#: lxc/image.go:357 +#: lxc/image.go:354 #, c-format msgid "Expires: %s" -msgstr "" +msgstr "Expire : %s" -#: lxc/image.go:359 +#: lxc/image.go:356 msgid "Expires: never" -msgstr "" +msgstr "N'expire jamais" -#: lxc/config.go:272 lxc/image.go:639 lxc/image.go:681 +#: lxc/config.go:272 lxc/image.go:636 lxc/image.go:678 msgid "FINGERPRINT" -msgstr "" +msgstr "EMPREINTE" #: lxc/list.go:124 msgid "Fast mode (same as --columns=nsacPt" -msgstr "" +msgstr "Mode rapide (identique à --columns=nsacPt" -#: lxc/image.go:346 -#, fuzzy, c-format +#: lxc/image.go:343 +#, c-format msgid "Fingerprint: %s" -msgstr "Empreinte du certificat: % x\n" - -#: lxc/finger.go:15 -#, fuzzy -msgid "" -"Fingers the LXD instance to check if it is up and working.\n" -"\n" -"lxc finger " -msgstr "Contacte LXD pour voir s'il est fonctionel.\n" +msgstr "Empreinte : %s" #: lxc/action.go:42 lxc/action.go:43 -msgid "Force the container to shutdown." -msgstr "Force l'arrêt du conteneur." +msgid "Force the container to shutdown" +msgstr "Force le conteneur à s'arrêter" #: lxc/delete.go:34 lxc/delete.go:35 -msgid "Force the removal of stopped containers." -msgstr "" +msgid "Force the removal of stopped containers" +msgstr "Force la suppression des conteneurs arrêtés" #: lxc/main.go:42 -msgid "Force using the local unix socket." -msgstr "" +msgid "Force using the local unix socket" +msgstr "Force l'utilisation de la socket unix locale" -#: lxc/image.go:169 lxc/list.go:123 +#: lxc/image.go:168 lxc/list.go:123 msgid "Format" msgstr "" -#: lxc/remote.go:66 -#, fuzzy +#: lxc/remote.go:65 msgid "Generating a client certificate. This may take a minute..." -msgstr "Géneration d'un certificat client. Ceci peut prendre une minute...\n" +msgstr "Génération d'un certificat client. Ceci peut prendre une minute…" + +#: lxc/help.go:25 +msgid "" +"Help page for the LXD client.\n" +"\n" +"lxc help [--all]" +msgstr "" +"Explique comment utiliser LXD.\n" +"\n" +"lxc help [--all]" #: lxc/list.go:423 msgid "IPV4" @@ -435,42 +543,43 @@ #: lxc/config.go:274 msgid "ISSUE DATE" -msgstr "" +msgstr "DATE D'ÉMISSION" #: lxc/main.go:136 msgid "" "If this is your first time using LXD, you should also run: sudo lxd init" msgstr "" +"Si c'est la première fois que vous lancez LXD, vous devriez aussi lancer : " +"sudo lxd init" #: lxc/main.go:43 -msgid "Ignore aliases when determining what command to run." -msgstr "" +msgid "Ignore aliases when determining what command to run" +msgstr "Ignorer les alias pour déterminer la commande à exécuter" #: lxc/action.go:46 -#, fuzzy -msgid "Ignore the container state (only for start)." -msgstr "Force l'arrêt du conteneur." +msgid "Ignore the container state (only for start)" +msgstr "Ignorer l'état du conteneur (seulement pour start)" -#: lxc/image.go:285 +#: lxc/image.go:281 msgid "Image copied successfully!" -msgstr "" +msgstr "Image copiée avec succès !" -#: lxc/image.go:442 -#, fuzzy, c-format +#: lxc/image.go:421 lxc/image.go:433 +#, c-format msgid "Image imported with fingerprint: %s" -msgstr "Empreinte du certificat: % x\n" +msgstr "Image importée avec l'empreinte : %s" -#: lxc/image.go:429 +#: lxc/image.go:418 #, c-format msgid "Importing the image: %s" -msgstr "" +msgstr "Importation de l'image : %s" #: lxc/init.go:74 msgid "" "Initialize a container from a particular image.\n" "\n" -"lxc init [remote:] [remote:][] [--ephemeral|-e] [--profile|-p " -"...] [--config|-c ...] [--network|-n ]\n" +"lxc init [:] [:][] [--ephemeral|-e] [--profile|-" +"p ...] [--config|-c ...] [--network|-n ]\n" "\n" "Initializes a container using the specified image and name.\n" "\n" @@ -478,56 +587,67 @@ "Specifying \"-p\" with no argument will result in no profile.\n" "\n" "Example:\n" -"lxc init ubuntu u1" +" lxc init ubuntu u1" msgstr "" +"Initialiser un conteneur avec une image particulière.\n" +"\n" +"lxc init [:] [:][] [--ephemeral|-e] [--profile|-" +"p ...] [--config|-c ...] [--network|-n ]\n" +"\n" +"Initialise un conteneur en utilisant l'image et le nom spécifiés.\n" +"\n" +"Ne pas spécifier -p résultera en l'utilisation du profil par défaut.\n" +"Spécifier \\-p\\ sans argument résultera en aucun profil.\n" +"\n" +"Exemple :\n" +" lxc init ubuntu u1" -#: lxc/remote.go:136 +#: lxc/remote.go:135 #, c-format msgid "Invalid URL scheme \"%s\" in \"%s\"" -msgstr "" +msgstr "Schème d'URL invalide \"%s\" in \"%s\"" #: lxc/config.go:253 -#, fuzzy msgid "Invalid certificate" -msgstr "Gérer la configuration.\n" +msgstr "Certificat invalide" #: lxc/init.go:30 lxc/init.go:35 -#, fuzzy msgid "Invalid configuration key" -msgstr "Gérer la configuration.\n" +msgstr "Clé de configuration invalide" -#: lxc/file.go:247 +#: lxc/file.go:249 #, c-format msgid "Invalid source %s" msgstr "Source invalide %s" -#: lxc/file.go:73 +#: lxc/file.go:72 #, c-format msgid "Invalid target %s" -msgstr "Destination invalide %s" +msgstr "Cible invalide %s" #: lxc/info.go:124 msgid "Ips:" -msgstr "" +msgstr "Ips :" -#: lxc/image.go:167 +#: lxc/image.go:166 msgid "Keep the image up to date after initial copy" -msgstr "" +msgstr "Garder l'image à jour après la copie initiale" #: lxc/list.go:427 msgid "LAST USED AT" -msgstr "" +msgstr "DERNIÈRE UTILISATION À" #: lxc/main.go:27 msgid "LXD socket not found; is LXD installed and running?" -msgstr "" +msgstr "Socket LXD introuvable ; LXD est-il installé et actif ?" #: lxc/launch.go:22 msgid "" "Launch a container from a particular image.\n" "\n" -"lxc launch [remote:] [remote:][] [--ephemeral|-e] [--profile|-p " -"...] [--config|-c ...] [--network|-n ]\n" +"lxc launch [:] [:][] [--ephemeral|-e] [--" +"profile|-p ...] [--config|-c ...] [--network|-n " +"]\n" "\n" "Launches a container using the specified image and name.\n" "\n" @@ -535,25 +655,46 @@ "Specifying \"-p\" with no argument will result in no profile.\n" "\n" "Example:\n" -"lxc launch ubuntu:16.04 u1" +" lxc launch ubuntu:16.04 u1" msgstr "" +"Lancer le conteneur depuis une image particulière.\n" +"\n" +"lxc launch [:] [:][] [--ephemeral|-e] [--" +"profile|-p ...] [--config|-c ...] [--network|-n " +"]\n" +"\n" +"Lance un conteneur en utilisant l'image et le nom spécifiés.\n" +"\n" +"Ne pas spécifier -p résultera en l'utilisation du profil par défaut.\n" +"Spécifier \\\\-p\\\\ sans argument résultera en aucun profil.\n" +"\n" +"Exemple :\n" +" lxc launch ubuntu:16.04 u1" #: lxc/info.go:25 msgid "" "List information on LXD servers and containers.\n" "\n" "For a container:\n" -" lxc info [:]container [--show-log]\n" +" lxc info [] [--show-log]\n" "\n" "For a server:\n" -" lxc info [:]" +" lxc info []" msgstr "" +"Lister les informations concernant les serveurs LXD et les conteneurs.\n" +"\n" +"Pour un conteneur :\n" +" lxc info [:] [--show-log]\n" +"\n" +"Pour un serveur :\n" +" lxc info [:]" #: lxc/list.go:68 msgid "" -"Lists the available resources.\n" +"Lists the containers.\n" "\n" -"lxc list [resource] [filters] [--format table|json] [-c columns] [--fast]\n" +"lxc list [:] [filters] [--format table|json] [-c ] [--" +"fast]\n" "\n" "The filters are:\n" "* A single keyword like \"web\" which will list any container with a name " @@ -602,46 +743,113 @@ "Default column layout: ns46tS\n" "Fast column layout: nsacPt\n" "\n" -"Example: lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile." -"eth0.hwaddr:MAC\n" +"Example:\n" +" lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile.eth0." +"hwaddr:MAC" msgstr "" +"Lister les ressources disponibles.\n" +"\n" +"lxc list [:] [filters] [--format table|json] [-c ] [--" +"fast]\n" +"\n" +"Les filtres sont :\n" +"* Un mot-clé unique comme \\web\\ qui listera tous les conteneurs dont\n" +"le nom commence par \\web\\.\n" +"* Une expression rationnelle sur le nom du conteneur (ex : *web.*01$)\n" +"* Une paire de clé/valeur faisant référence à un élément de\n" +"configuration. Pour ceux-ci, l'espace de nom peut être abrégé au plus\n" +"petit identifiant non ambigu :\n" +" * \\user.blah=abc\\ listera tous les conteneurs dont la propriété\n" +" utilisateur \\blah\\ est positionnée à \\abc\\.\n" +" * \\u.blah=abc\\ fera la même chose\n" +" * \\security.privileged=1\\ listera tous les conteneurs privilégiés\n" +" * \\s.privileged=1\\ fera la même chose\n" +"* Une expression rationelle correspondant à un élément de\n" +"configuration ou à sa valeur (ex : volatile.eth0.hwaddr=00:16:3e:.*)\n" +"\n" +"L'option -c prend une liste d'arguments séparés par des virgules\n" +"contrôlant les attributs des conteneurs à afficher au format tableau.\n" +"Les arguments pour les colonnes sont soient des raccourcis par\n" +"caractère prédéfinis (voir ci-dessous) ou des clés (étendues) de\n" +"configuration. Les virgules entre les caractères courts sont\n" +"optionnelles.\n" +"\n" +"Raccourcis par caractère prédéfinis :\n" +"* 4 - adresse IPv4\n" +"* 6 - adresse IPv6\n" +"* a - architecture\n" +"* c - date de création\n" +"* l - date de dernière utilisation\n" +"* n - nom\n" +"* p - pid du processus init du conteneur\n" +"* P - profils\n" +"* s - état\n" +"* S - nombre d'instantanés\n" +"* t - type (persistant ou éphémère)\n" +"\n" +"Syntaxe des clés de configuration : clé[:nom][:largeurMax]\n" +"* clé - La clé (étendue) de configuration à afficher\n" +"* nom - Nom à afficher dans l'entête de colonne, c'est la clé\n" +" par défaut si rien n'est spécifié ou vide (pour\n" +" permettre la définition de largeurMax sans nom\n" +" personnalisé, ex : user.key::0)\n" +"* largeurMax - Largeur max de la colonne (les résultats plus longs\n" +" seront tronqués).\n" +" -1 == pas de limite\n" +" 0 == largeur de l'entête de colonne\n" +" >0 == largeur maximale en caractères\n" +" La valeur par défaut est -1 (pas de limite)\n" +"\n" +"Colonnes par défaut : ns46tS\n" +"Colonnes en mode rapide : nsacPt\n" +"\n" +"Exemple :\n" +" lxc list -c n,volatile.base_image:\\BASE IMAGE\\:0,s46,volatile.eth0." +"hwaddr:MAC" #: lxc/info.go:239 msgid "Log:" -msgstr "" +msgstr "Journal : " -#: lxc/network.go:424 +#: lxc/network.go:423 msgid "MANAGED" -msgstr "" +msgstr "GÉRÉ" -#: lxc/image.go:165 +#: lxc/image.go:164 msgid "Make image public" -msgstr "" +msgstr "Rendre l'image publique" #: lxc/publish.go:33 msgid "Make the image public" -msgstr "" +msgstr "Rendre l'image publique" #: lxc/profile.go:48 msgid "" "Manage configuration profiles.\n" "\n" -"lxc profile list [filters] List available profiles.\n" -"lxc profile show Show details of a profile.\n" -"lxc profile create Create a profile.\n" -"lxc profile copy Copy the profile to the " -"specified remote.\n" -"lxc profile get Get profile configuration.\n" -"lxc profile set Set profile configuration.\n" -"lxc profile unset Unset profile configuration.\n" -"lxc profile delete Delete a profile.\n" -"lxc profile edit \n" +"lxc profile list [:] List " +"available profiles.\n" +"lxc profile show [:] Show details " +"of a profile.\n" +"lxc profile create [:] Create a " +"profile.\n" +"lxc profile copy [:] [:] Copy the " +"profile.\n" +"lxc profile get [:] Get profile " +"configuration.\n" +"lxc profile set [:] Set profile " +"configuration.\n" +"lxc profile unset [:] Unset " +"profile configuration.\n" +"lxc profile delete [:] Delete a " +"profile.\n" +"lxc profile edit [:]\n" " Edit profile, either by launching external editor or reading STDIN.\n" " Example: lxc profile edit # launch editor\n" " cat profile.yaml | lxc profile edit # read from " "profile.yaml\n" "\n" -"lxc profile assign \n" +"lxc profile assign [:] \n" " Assign a comma-separated list of profiles to a container, in order.\n" " All profiles passed in this call (and only those) will be applied\n" " to the specified container, i.e. it sets the list of profiles exactly " @@ -653,78 +861,143 @@ " lxc profile assign foo default # Only default is active\n" " lxc profile assign '' # no profiles are applied anymore\n" " lxc profile assign bar,default # Apply default second now\n" -"lxc profile add # add a profile to a container\n" -"lxc profile remove # remove the profile from a " +"lxc profile add [:] # add a profile to a " "container\n" +"lxc profile remove [:] # remove the profile " +"from a container\n" "\n" "Devices:\n" -"lxc profile device list List " -"devices in the given profile.\n" -"lxc profile device show Show " -"full device details in the given profile.\n" -"lxc profile device remove Remove a " -"device from a profile.\n" -"lxc profile device get <[remote:]profile> Get a " -"device property.\n" -"lxc profile device set <[remote:]profile> Set a " -"device property.\n" -"lxc profile device unset <[remote:]profile> Unset a " -"device property.\n" -"lxc profile device add " -"[key=value]...\n" -" Add a profile device, such as a disk or a nic, to the containers\n" -" using the specified profile." -msgstr "" +"lxc profile device list [:] " +"List devices in the given profile.\n" +"lxc profile device show [:] " +"Show full device details in the given profile.\n" +"lxc profile device remove [:] " +"Remove a device from a profile.\n" +"lxc profile device get [:] " +"Get a device property.\n" +"lxc profile device set [:] " +"Set a device property.\n" +"lxc profile device unset [:] " +"Unset a device property.\n" +"lxc profile device add [:] [key=value...]\n" +" Add a profile device, such as a disk or a nic, to the containers using " +"the specified profile." +msgstr "" +"Gérer les profils de configuration.\n" +"\n" +"lxc profile list [:] Lister les " +"profils disponibles.\n" +"lxc profile show [:] Afficher les " +"détails d'un profil.\n" +"lxc profile create [:] Créer un " +"profil.\n" +"lxc profile copy [:] [:] Copie le " +"profil.\n" +"lxc profile get [:] Obtenir une " +"clé de la configuration du profil.\n" +"lxc profile set [:] Positionner " +"une clé de la configuration du profil.\n" +"lxc profile unset [:] Désaffecter " +"une clé de la configuration du profil.\n" +"lxc profile delete [:] Supprimer un " +"profil.\n" +"lxc profile edit [:]\n" +" Editer le profil, soit en lançant un éditeur externe ou en lisant " +"STDIN.\n" +" Exemple : lxc profile edit # lancer l'éditeur\n" +" cat profile.yaml | lxc profile edit # lire depuis " +"profile.yaml\n" +"\n" +"lxc profile assign [:] \n" +" Assigner une liste de profils séparés par des virgules à un\n" +" conteneur, dans l'ordre spécifié. Tous les profils fournis dans\n" +" cet appel (et seulement ceux-ci) seront appliqués au conteneur\n" +" indiqué, i.e. cela positionne exactement la liste de profils à\n" +" ceux spécifiés dans la commande. Pour ajouter ou supprimer un\n" +" profil en particulier d'un conteneur, utiliser {add|remove}\n" +" ci-dessous.\n" +" Exemple : lxc profile assign foo default,bar # Applique default et bar\n" +" lxc profile assign foo default # Seul default est actif\n" +" lxc profile assign '' # Plus aucun profil n'est appliqué\n" +" lxc profile assign bar,default # Applique default en second\n" +"lxc profile add [:] # Ajouter un profil à un " +"conteneur\n" +"lxc profile remove [:] # Supprimer le profil " +"d'un conteneur\n" +"\n" +"Périphériques :\n" +"lxc profile device list [:] Lister " +"les périphériques d'un profil particulier.\n" +"lxc profile device show [:] Afficher " +"le détail des périphériques d'un profil donné.\n" +"lxc profile device remove [:] Supprimer " +"un périphérique d'un profil.\n" +"lxc profile device get [:] Obtenir " +"la propriété d'un périphérique.\n" +"lxc profile device set [:] " +"Positionner la propriété d'un périphérique.\n" +"lxc profile device unset [:] " +"Désaffecter la propriété d'un périphérique.\n" +"lxc profile device add [:] [key=value]...\n" +" Ajouter un périphérique de profil, comme un disque ou une\n" +" interface réseau, aux conteneurs utilisant le profil spécifié." #: lxc/config.go:57 msgid "" "Manage configuration.\n" "\n" -"lxc config device add <[remote:]container> [key=value]... " -"Add a device to a container.\n" -"lxc config device get <[remote:]container> " -"Get a device property.\n" -"lxc config device set <[remote:]container> " -"Set a device property.\n" -"lxc config device unset <[remote:]container> " -"Unset a device property.\n" -"lxc config device list <[remote:]container> " -"List devices for container.\n" -"lxc config device show <[remote:]container> " -"Show full device details for container.\n" -"lxc config device remove <[remote:]container> " -"Remove device from container.\n" -"\n" -"lxc config get [remote:][container] " -"Get container or server configuration key.\n" -"lxc config set [remote:][container] " -"Set container or server configuration key.\n" -"lxc config unset [remote:][container] " -"Unset container or server configuration key.\n" -"lxc config show [remote:][container] [--expanded] " -"Show container or server configuration.\n" -"lxc config edit [remote:][container] " -"Edit container or server configuration in external editor.\n" +"lxc config device add [:] " +"[key=value...] Add a device to a container.\n" +"lxc config device get [:] " +" Get a device property.\n" +"lxc config device set [:] " +" Set a device property.\n" +"lxc config device unset [:] " +" Unset a device property.\n" +"lxc config device list " +"[:] List devices for " +"container.\n" +"lxc config device show " +"[:] Show full device " +"details for container.\n" +"lxc config device remove [:] " +" Remove device from container.\n" +"\n" +"lxc config get [:][container] " +" Get container or server " +"configuration key.\n" +"lxc config set [:][container] " +" Set container or server configuration " +"key.\n" +"lxc config unset [:][container] " +" Unset container or server " +"configuration key.\n" +"lxc config show [:][container] [--" +"expanded] Show container or server configuration.\n" +"lxc config edit [:]" +"[container] Edit container or server " +"configuration in external editor.\n" " Edit configuration, either by launching external editor or reading " "STDIN.\n" " Example: lxc config edit # launch editor\n" -" cat config.yaml | lxc config edit # read from config." -"yaml\n" +" cat config.yaml | lxc config edit # read from " +"config.yaml\n" "\n" -"lxc config trust list [remote] " -"List all trusted certs.\n" -"lxc config trust add [remote] " -"Add certfile.crt to trusted hosts.\n" -"lxc config trust remove [remote] [hostname|fingerprint] " -"Remove the cert from trusted hosts.\n" +"lxc config trust list " +"[:] List all trusted " +"certs.\n" +"lxc config trust add [:] Add certfile.crt to trusted hosts.\n" +"lxc config trust remove [:] [hostname|" +"fingerprint] Remove the cert from trusted hosts.\n" "\n" "Examples:\n" "To mount host's /share/c1 onto /opt in the container:\n" -" lxc config device add [remote:]container1 disk source=/" +" lxc config device add [:]container1 disk source=/" "share/c1 path=opt\n" "\n" "To set an lxc config value:\n" -" lxc config set [remote:] raw.lxc 'lxc.aa_allow_incomplete = " +" lxc config set [:] raw.lxc 'lxc.aa_allow_incomplete = " "1'\n" "\n" "To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the " @@ -734,73 +1007,197 @@ "To set the server trust password:\n" " lxc config set core.trust_password blah" msgstr "" +"Gérer la configuration.\n" +"\n" +"lxc config device add [:] [key=value]... " +"Ajouter un périphérique à un conteneur.\n" +"lxc config device get [:] " +"Obtenir la propriété d'un périphérique.\n" +"lxc config device set [:] " +"Positionner la propriété d'un périphérique.\n" +"lxc config device unset [:] " +"Désaffecter la propriété d'un périphérique.\n" +"lxc config device list [:] " +"Lister les périphériques d'un conteneur.\n" +"lxc config device show [:] " +"Afficher les détails des périphériques d'un conteneur.\n" +"lxc config device remove [:] " +"Supprimer les périphériques d'un conteneur.\n" +"\n" +"lxc config get [:] " +"Obtenir une clé de configuration d'un conteneur ou d'un serveur.\n" +"lxc config set [:] " +"Positionner une clé de configuration d'un conteneur ou d'un serveur.\n" +"lxc config unset [:] " +"Désaffecter une clé de configuration d'un conteneur ou d'un serveur.\n" +"lxc config show [:] [--expanded] " +"Afficher la configuration d'un conteneur ou d'un serveur.\n" +"lxc config edit [:] " +"Éditer la configuration d'un conteneur ou d'un serveur dans un éditeur " +"externe.\n" +" Éditer la configuration, soit en lançant un éditeur externe ou en lisant " +"STDIN.\n" +" Exemple : lxc config edit # nace l'éditeur\n" +" cat config.yaml | lxc config edit # lit depuis config." +"yaml\n" +"\n" +"lxc config trust list [:] " +"Lister tous les certificats de confiance.\n" +"lxc config trust add [:] " +"Ajouter certfile.crt aux hôtes de confiance.\n" +"lxc config trust remove [:] [hostname|fingerprint] " +"Supprimer le certificat des hôtes de confiance.\n" +"\n" +"Exemples :\n" +"Pour monter le répertoire /share/c1 sur le répertoire /opt du conteneur :\n" +" lxc config device add [remote:]container1 disk source=/" +"share/c1 path=opt\n" +"\n" +"Pour positionner une clé de configuration dans la configuration lxc :\n" +" lxc config set [:] raw.lxc 'lxc.aa_allow_incomplete = " +"1'\n" +"\n" +"Pour écouter sur le port 8443 en IPv4 et IPv6 (vous pouvez omettre 8443 " +"c'est la valeur par défaut) :\n" +" lxc config set core.https_address [::]:8443\n" +"\n" +"Pour positionner le mot de passe de confiance du serveur :\n" +" lxc config set core.trust_password blah" -#: lxc/file.go:35 +#: lxc/file.go:36 msgid "" -"Manage files on a container.\n" +"Manage files in a container.\n" "\n" -"lxc file pull [-r|--recursive] [...] \n" +"lxc file pull [-r|--recursive] [:] " +"[[:]...] \n" "lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--" -"mode=MODE] [...] \n" -"lxc file edit \n" +"mode=MODE] [...] [:]\n" +"lxc file edit [:]/\n" "\n" " in the case of pull, in the case of push and in the " "case of edit are /\n" "\n" "Examples:\n" -"\n" "To push /etc/hosts into the container foo:\n" -" lxc file push /etc/hosts foo/etc/hosts\n" +" lxc file push /etc/hosts foo/etc/hosts\n" "\n" "To pull /etc/hosts from the container:\n" -" lxc file pull foo/etc/hosts .\n" +" lxc file pull foo/etc/hosts ." msgstr "" +"Gérer les fichiers dans un conteneur.\n" +"\n" +"lxc file pull [-r|--recursive] [] " +"[[:]...] \n" +"lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--" +"mode=MODE] [...] [:]\n" +"lxc file edit [:]/\n" +"\n" +" dans le cas de pull, dans le cas de push et in the " +"dans le cas d'edit sont de la forme /\n" +"\n" +"Exemples :\n" +"\n" +"Pour pousser /etc/hosts dans le conteneur foo :\n" +" lxc file push /etc/hosts foo/etc/hosts\n" +"\n" +"Pour récupérer /etc/hosts du conteneur :\n" +" lxc file pull foo/etc/hosts ." #: lxc/network.go:48 msgid "" "Manage networks.\n" "\n" -"lxc network list List available networks.\n" -"lxc network show Show details of a network.\n" -"lxc network create [key=value]... Create a network.\n" -"lxc network get Get network configuration.\n" -"lxc network set Set network configuration.\n" -"lxc network unset Unset network configuration.\n" -"lxc network delete Delete a network.\n" -"lxc network edit \n" +"lxc network list [:] List available " +"networks.\n" +"lxc network show [:] Show details of a " +"network.\n" +"lxc network create [:] [key=value...] Create a network.\n" +"lxc network get [:] Get network " +"configuration.\n" +"lxc network set [:] Set network " +"configuration.\n" +"lxc network unset [:] Unset network " +"configuration.\n" +"lxc network delete [:] Delete a network.\n" +"lxc network edit [:]\n" " Edit network, either by launching external editor or reading STDIN.\n" " Example: lxc network edit # launch editor\n" " cat network.yaml | lxc network edit # read from " "network.yaml\n" "\n" -"lxc network attach [device name]\n" -"lxc network attach-profile [device name]\n" +"lxc network attach [:] [device name]\n" +"lxc network attach-profile [:] [device name]\n" "\n" -"lxc network detach [device name]\n" -"lxc network detach-profile [device name]\n" +"lxc network detach [:] [device name]\n" +"lxc network detach-profile [:] [device name]" msgstr "" +"Gérer les réseaux.\n" +"\n" +"lxc network list [:] Lister les réseaux " +"disponibles.\n" +"lxc network show [:] Afficher les " +"détails d'un réseau.\n" +"lxc network create [:] [key=value]... Créer un réseau.\n" +"lxc network get [:] Récupérer une " +"configuration du réseau.\n" +"lxc network set [:] Positionner une " +"configuration du réseau.\n" +"lxc network unset [:] Désaffecter une " +"configuration du réseau.\n" +"lxc network delete [:] Supprimer un " +"réseau.\n" +"lxc network edit [:]\n" +" Éditer le réseau, soit en lançant un éditeur externe ou en lisant " +"STDIN.\n" +" Exemple : lxc network edit # lancer l'éditeur\n" +" cat network.yaml | lxc network edit # lire depuis " +"network.yaml\n" +"\n" +"lxc network attach [:] [device name]\n" +"lxc network attach-profile [:] [device name]\n" +"\n" +"lxc network detach [:] [device name]\n" +"lxc network detach-profile [:] [device name]" -#: lxc/remote.go:38 +#: lxc/remote.go:37 msgid "" "Manage remote LXD servers.\n" "\n" -"lxc remote add [] [--accept-certificate] [--" +"lxc remote add [] [--accept-certificate] [--" "password=PASSWORD]\n" -" [--public] [--protocol=PROTOCOL] " +" [--public] [--protocol=PROTOCOL] " "Add the remote at .\n" -"lxc remote remove " +"lxc remote remove " "Remove the remote .\n" "lxc remote list " "List all remotes.\n" -"lxc remote rename " +"lxc remote rename " "Rename remote to .\n" -"lxc remote set-url " +"lxc remote set-url " "Update 's url to .\n" -"lxc remote set-default " +"lxc remote set-default " "Set the default remote.\n" "lxc remote get-default " "Print the default remote." msgstr "" +"Gérer les serveur LXD distants.\n" +"\n" +"lxc remote add [] [--accept-certificate] [--" +"password=PASSWORD]\n" +" [--public] [--protocol=PROTOCOL] " +"Ajouter le serveur distant accessible via .\n" +"lxc remote remove " +"Supprimer le serveur distant .\n" +"lxc remote list " +"Lister tous les serveurs distants.\n" +"lxc remote rename " +"Renommer le serveur distant de en .\n" +"lxc remote set-url " +"Mettre à jour l'url de en .\n" +"lxc remote set-default " +"Choisir le serveur distant par défaut.\n" +"lxc remote get-default " +"Afficher le serveur distant par défaut." #: lxc/image.go:95 msgid "" @@ -821,22 +1218,22 @@ "hash or alias name (if one is set).\n" "\n" "\n" -"lxc image import [rootfs tarball|URL] [remote:] [--public] [--" -"created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--" -"alias=ALIAS].. [prop=value]\n" +"lxc image import [|] [:] [--public] " +"[--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] " +"[--alias=ALIAS...] [prop=value]\n" " Import an image tarball (or tarballs) into the LXD image store.\n" "\n" -"lxc image copy [remote:] : [--alias=ALIAS].. [--copy-aliases] " -"[--public] [--auto-update]\n" +"lxc image copy [:] : [--alias=ALIAS...] [--copy-" +"aliases] [--public] [--auto-update]\n" " Copy an image from one LXD daemon to another over the network.\n" "\n" " The auto-update flag instructs the server to keep this image up to\n" " date. It requires the source to be an alias and for it to be public.\n" "\n" -"lxc image delete [remote:] [remote:][...]\n" +"lxc image delete [:] [[:]...]\n" " Delete one or more images from the LXD image store.\n" "\n" -"lxc image export [remote:] [target]\n" +"lxc image export [:] [target]\n" " Export an image from the LXD image store into a distributable tarball.\n" "\n" " The output target is optional and defaults to the working directory.\n" @@ -848,50 +1245,126 @@ " the appropriate extension will be appended to the provided file name\n" " based on the algorithm used to compress the image. \n" "\n" -"lxc image info [remote:]\n" +"lxc image info [:]\n" " Print everything LXD knows about a given image.\n" "\n" -"lxc image list [remote:] [filter] [--format table|json]\n" +"lxc image list [:] [filter] [--format table|json]\n" " List images in the LXD image store. Filters may be of the\n" " = form for property based filtering, or part of the image\n" " hash or part of the image alias name.\n" "\n" -"lxc image show [remote:]\n" +"lxc image show [:]\n" " Yaml output of the user modifiable properties of an image.\n" "\n" -"lxc image edit [remote:]\n" +"lxc image edit [:]\n" " Edit image, either by launching external editor or reading STDIN.\n" " Example: lxc image edit # launch editor\n" " cat image.yaml | lxc image edit # read from image.yaml\n" "\n" -"lxc image alias create [remote:] \n" +"lxc image alias create [:] \n" " Create a new alias for an existing image.\n" "\n" -"lxc image alias delete [remote:]\n" +"lxc image alias delete [:]\n" " Delete an alias.\n" "\n" -"lxc image alias list [remote:] [filter]\n" +"lxc image alias list [:] [filter]\n" " List the aliases. Filters may be part of the image hash or part of the " -"image alias name.\n" +"image alias name." msgstr "" +"Manipuler les images de conteneur.\n" +"\n" +"Dans LXD les conteneurs sont créés depuis des images. Ces images sont\n" +"elles-même générées soit depuis des conteneurs existants ou téléchargées\n" +"depuis un serveur d'image\n" +"\n" +"Lors de l'utilisation d'images distantes, LXD cachera automatiquement ces\n" +"images pour vous et les supprimera à expiration.\n" +"\n" +"L'identifiant unique d'image est l'empreinte (sha-256) de sa représentation\n" +"sous la forme d'une archive tar (ou pour les images fractionnées, la\n" +"concaténation de ses métadonnées et des archives du système de fichiers\n" +"racine).\n" +"\n" +"Les images sont référencées par leur empreinte complète, le préfixe unique\n" +"le plus court ou leur alias (s'il est positionné).\n" +"\n" +"\n" +"lxc image import [|] [:] [--public] " +"[--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] " +"[--alias=ALIAS].. [prop=value]\n" +" Importer l'archive (ou les archives) tar d'une image dans le dépôt LXD.\n" +"\n" +"lxc image copy [:] : [--alias=ALIAS].. [--copy-" +"aliases] [--public] [--auto-update]\n" +" Copier une image d'un démon LXD vers un autre au travers du réseau.\n" +"\n" +" Le booléen auto-update indique au serveur qu'il doit garder cette image\n" +" à jour. Cela requiert que la source soit un alias et qu'elle soit\n" +" publique.\n" +"\n" +"lxc image delete [:] [:][...]\n" +" Supprimer une ou plusieurs images du dépôt LXD.\n" +"\n" +"lxc image export [:] []\n" +" Exporter une image du dépôt LXD sous la forme d'une archive tar " +"distribuable.\n" +"\n" +" La cible en sortie est optionnelle et le répertoire de travail est sa\n" +" valeur par défaut. La cible peut être un répertoire existant, un nom\n" +" de fichier, ou \\-\\ pour spécifier stdout. La cible DOIT être un\n" +" répertoire lors de l'export d'une image fractionnée. Si la cible est\n" +" un répertoire, le nom de l'image (le nom de chaque partie pour une\n" +" image fractionnée) telle que présent dans la base de données sera\n" +" utilisé pour l'image exportée. Si la cible est un fichier (ni un\n" +" répertoire, ni stdout), alors l'extension appropriée sera ajoutée au\n" +" nom de fichier fourni suivant l'algorithme utilisé pour comprimer\n" +" l'image.\n" +"\n" +"lxc image info [:]\n" +" Afficher toutes les informations connues concernant une image donnée.\n" +"\n" +"lxc image list [:] [filter] [--format table|json]\n" +" Lister les images contenues dans le dépôt LXD. Les filtres peuvent\n" +" être de la forme = pour un filtrage sur les propriétés, ou\n" +" une partie de l'empreinte de l'image ou une partie de l'alias de\n" +" l'image.\n" +"\n" +"lxc image show [:]\n" +" Sortie yaml des propriétés de l'image modifiables par l'utilisateur.\n" +"\n" +"lxc image edit [:]\n" +" Éditer l'image, soit en lançant un éditeur externe ou en lisant STDIN.\n" +" Exemple : lxc image edit # lance l'éditeur\n" +" cat image.yaml | lxc image edit # lit depuis image." +"yaml\n" +"\n" +"lxc image alias create [:] \n" +" Créer un nouvel alias pour une image existante.\n" +"\n" +"lxc image alias delete [:]\n" +" Supprimer un alias.\n" +"\n" +"lxc image alias list [:] []\n" +" Lister les alias. Les filtres peuvent être une partie de l'empreinte\n" +" de l'image ou une partie de l'alias de l'image." #: lxc/info.go:161 msgid "Memory (current)" -msgstr "" +msgstr "Mémoire (courante)" #: lxc/info.go:165 msgid "Memory (peak)" -msgstr "" +msgstr "Mémoire (pointe)" #: lxc/help.go:87 msgid "Missing summary." -msgstr "Sommaire manquant." +msgstr "Résumé manquant." #: lxc/monitor.go:41 msgid "" "Monitor activity on the LXD server.\n" "\n" -"lxc monitor [remote:] [--type=TYPE...]\n" +"lxc monitor [:] [--type=TYPE...]\n" "\n" "Connects to the monitoring interface of the specified LXD server.\n" "\n" @@ -899,104 +1372,126 @@ "Specific types to listen to can be specified with --type.\n" "\n" "Example:\n" -"lxc monitor --type=logging" +" lxc monitor --type=logging" msgstr "" +"Superviser l'activité du serveur LXD.\n" +"\n" +"lxc monitor [:] [--type=TYPE...]\n" +"\n" +"Se connecte à l'interface de supervision du serveur LXD spécifié.\n" +"\n" +"Surveillera tous types de message ar défaut.\n" +"Les types à surveiller peuvent être spécifiés avec --type.\n" +"\n" +"Exemple :\n" +"lxc monitor --type=logging" -#: lxc/network.go:216 lxc/network.go:265 +#: lxc/network.go:215 lxc/network.go:264 msgid "More than one device matches, specify the device name." -msgstr "" +msgstr "Plus d'un périphérique correspond, spécifiez le nom du périphérique." -#: lxc/file.go:235 +#: lxc/file.go:237 msgid "More than one file to download, but target is not a directory" msgstr "" -"Plusieurs fichiers à télécharger mais la destination n'est pas un dossier" +"Plusieurs fichiers à télécharger, mais la destination n'est pas un dossier" #: lxc/move.go:16 msgid "" "Move containers within or in between lxd instances.\n" "\n" -"lxc move [remote:] [remote:]\n" +"lxc move [:] [:][]\n" " Move a container between two hosts, renaming it if destination name " "differs.\n" "\n" "lxc move \n" " Rename a local container.\n" +"\n" +"lxc move / /\n" +" Rename a snapshot." msgstr "" +"Déplacer les conteneurs vers ou entre des instances lxd.\n" +"\n" +"lxc move [:] [:][]\n" +" Déplace un conteneur entre deux hôtes, en le renommant si le nom\n" +" de la destination diffère de l'original.\n" +"\n" +"lxc move \n" +" Renomme un conteneur local.\n" +"\n" +"lxc move / /\n" +" Renomme un instantané." #: lxc/action.go:69 msgid "Must supply container name for: " -msgstr "" +msgstr "Vous devez spécifier le nom d'un conteneur pour : " -#: lxc/list.go:428 lxc/network.go:422 lxc/profile.go:447 lxc/remote.go:381 +#: lxc/list.go:428 lxc/network.go:421 lxc/profile.go:446 lxc/remote.go:380 msgid "NAME" -msgstr "" +msgstr "NOM" -#: lxc/network.go:408 lxc/remote.go:355 lxc/remote.go:360 +#: lxc/network.go:407 lxc/remote.go:354 lxc/remote.go:359 msgid "NO" -msgstr "" +msgstr "NON" #: lxc/info.go:89 #, c-format msgid "Name: %s" -msgstr "" +msgstr "Nom : %s" -#: lxc/network.go:190 +#: lxc/network.go:189 #, c-format msgid "Network %s created" -msgstr "" +msgstr "Le réseau %s a été créé" -#: lxc/network.go:293 +#: lxc/network.go:292 #, c-format msgid "Network %s deleted" -msgstr "" +msgstr "Le réseau % a été supprimé" #: lxc/init.go:141 lxc/init.go:142 lxc/launch.go:46 lxc/launch.go:47 msgid "Network name" -msgstr "" +msgstr "Nom du réseau" -#: lxc/image.go:168 lxc/publish.go:34 +#: lxc/image.go:167 lxc/publish.go:34 msgid "New alias to define at target" -msgstr "" +msgstr "Nouvel alias à définir sur la cible" #: lxc/config.go:284 -#, fuzzy msgid "No certificate provided to add" -msgstr "Un certificat n'a pas été fournis" +msgstr "Un certificat à ajouter n'a pas été fourni" -#: lxc/network.go:225 lxc/network.go:274 -#, fuzzy +#: lxc/network.go:224 lxc/network.go:273 msgid "No device found for this network" -msgstr "Aucun certificat pour cette connexion" +msgstr "Aucun périphérique existant pour ce réseau" #: lxc/config.go:307 msgid "No fingerprint specified." -msgstr "Aucune empreinte n'a été spécifié." +msgstr "Aucune empreinte n'a été spécifiée." -#: lxc/remote.go:121 +#: lxc/remote.go:120 msgid "Only https URLs are supported for simplestreams" -msgstr "" +msgstr "Seules les URLs https sont supportées par simplestreams" -#: lxc/image.go:434 +#: lxc/image.go:424 msgid "Only https:// is supported for remote image import." -msgstr "" +msgstr "Seul https:// est supporté par l'import d'images distantes." #: lxc/help.go:63 lxc/main.go:112 -#, fuzzy msgid "Options:" -msgstr "Opération %s" +msgstr "Options :" -#: lxc/image.go:538 +#: lxc/image.go:535 #, c-format msgid "Output is in %s" -msgstr "" +msgstr "Le résultat est dans %s" #: lxc/exec.go:55 msgid "Override the terminal mode (auto, interactive or non-interactive)" -msgstr "" +msgstr "Surcharger le mode terminal (auto, interactif ou non-interactif)" #: lxc/list.go:572 msgid "PERSISTENT" -msgstr "" +msgstr "PERSISTANT" #: lxc/list.go:429 msgid "PID" @@ -1004,761 +1499,565 @@ #: lxc/list.go:430 msgid "PROFILES" -msgstr "" +msgstr "PROFILS" -#: lxc/remote.go:383 +#: lxc/remote.go:382 msgid "PROTOCOL" -msgstr "" +msgstr "PROTOCOLE" -#: lxc/image.go:640 lxc/remote.go:384 +#: lxc/image.go:637 lxc/remote.go:383 msgid "PUBLIC" msgstr "" #: lxc/info.go:188 msgid "Packets received" -msgstr "" +msgstr "Paquets reçus" #: lxc/info.go:189 msgid "Packets sent" -msgstr "" +msgstr "Paquets émis" #: lxc/help.go:70 -#, fuzzy -msgid "Path to an alternate client configuration directory." -msgstr "Dossier de configuration alternatif." +msgid "Path to an alternate client configuration directory" +msgstr "Chemin vers un dossier de configuration client alternatif" #: lxc/help.go:71 -#, fuzzy -msgid "Path to an alternate server directory." -msgstr "Dossier de configuration alternatif." +msgid "Path to an alternate server directory" +msgstr "Chemin vers un dossier de configuration serveur alternatif" #: lxc/main.go:31 msgid "Permission denied, are you in the lxd group?" -msgstr "" +msgstr "Permission refusée, êtes-vous dans le groupe lxd ?" #: lxc/info.go:106 #, c-format msgid "Pid: %d" -msgstr "" - -#: lxc/help.go:25 -#, fuzzy -msgid "" -"Presents details on how to use LXD.\n" -"\n" -"lxd help [--all]" -msgstr "Explique comment utiliser LXD.\n" +msgstr "Pid : %d" -#: lxc/network.go:343 lxc/profile.go:219 +#: lxc/network.go:342 lxc/profile.go:218 msgid "Press enter to open the editor again" -msgstr "" +msgstr "Appuyer sur Entrée pour ouvrir à nouveau l'éditeur" -#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:735 +#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:732 msgid "Press enter to start the editor again" -msgstr "" +msgstr "Appuyer sur Entrée pour lancer à nouveau l'éditeur" + +#: lxc/manpage.go:18 +msgid "Print all the subcommands help." +msgstr "Afficher l'aide de toutes les commandes." #: lxc/help.go:65 -msgid "Print debug information." -msgstr "" +msgid "Print debug information" +msgstr "Afficher les informations de débogage" #: lxc/help.go:64 -msgid "Print less common commands." -msgstr "" +msgid "Print less common commands" +msgstr "Afficher les commandes moins communes" #: lxc/help.go:66 -msgid "Print verbose information." -msgstr "" - -#: lxc/manpage.go:18 -msgid "Prints all the subcommands help." -msgstr "" +msgid "Print verbose information" +msgstr "Afficher ds informations supplémentaires" #: lxc/version.go:18 -#, fuzzy msgid "" "Prints the version number of this client tool.\n" "\n" "lxc version" -msgstr "Montre le numéro de version de LXD.\n" +msgstr "" +"Afficher le numéro de version de cet utilitaire client.\n" +"\n" +"lxc version" #: lxc/info.go:130 -#, fuzzy, c-format +#, c-format msgid "Processes: %d" -msgstr "Mauvaise URL pour le conteneur %s" +msgstr "Processus : %d" -#: lxc/profile.go:275 -#, fuzzy, c-format +#: lxc/profile.go:274 +#, c-format msgid "Profile %s added to %s" -msgstr "Mauvaise URL pour le conteneur %s" +msgstr "Profil %s ajouté à %s" -#: lxc/profile.go:170 +#: lxc/profile.go:169 #, c-format msgid "Profile %s created" -msgstr "" +msgstr "Profil %s créé" -#: lxc/profile.go:240 +#: lxc/profile.go:239 #, c-format msgid "Profile %s deleted" -msgstr "" +msgstr "Profil %s supprimé" -#: lxc/profile.go:306 +#: lxc/profile.go:305 #, c-format msgid "Profile %s removed from %s" -msgstr "" +msgstr "Profil %s supprimé de %s" #: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:137 lxc/init.go:138 #: lxc/launch.go:42 lxc/launch.go:43 msgid "Profile to apply to the new container" -msgstr "" +msgstr "Profil à appliquer au nouveau conteneur" -#: lxc/profile.go:256 +#: lxc/profile.go:255 #, c-format msgid "Profiles %s applied to %s" -msgstr "" +msgstr "Profils %s appliqués à %s" #: lxc/info.go:104 -#, fuzzy, c-format +#, c-format msgid "Profiles: %s" -msgstr "Mauvaise URL pour le conteneur %s" +msgstr "Profils : %s" -#: lxc/image.go:361 +#: lxc/image.go:358 msgid "Properties:" -msgstr "" +msgstr "Propriétés :" -#: lxc/remote.go:55 +#: lxc/remote.go:54 msgid "Public image server" -msgstr "" +msgstr "Serveur d'images public" -#: lxc/image.go:349 +#: lxc/image.go:346 #, c-format msgid "Public: %s" -msgstr "" +msgstr "Public :" #: lxc/publish.go:26 msgid "" "Publish containers as images.\n" "\n" -"lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-" -"value]..." +"lxc publish [:][/] [:] [--" +"alias=ALIAS...] [prop-key=prop-value...]" msgstr "" +"Publier des conteneurs en tant qu'image.\n" +"\n" +"lxc publish [:][/] [:] [--" +"alias=ALIAS...] [prop-key=prop-value...]" -#: lxc/file.go:58 lxc/file.go:59 +#: lxc/file.go:57 lxc/file.go:58 msgid "Recursively push or pull files" -msgstr "" +msgstr "Pousser ou récupérer des fichiers récursivement" -#: lxc/remote.go:53 +#: lxc/remote.go:52 msgid "Remote admin password" -msgstr "" +msgstr "Mot de passe de l'administrateur distant" #: lxc/info.go:91 #, c-format msgid "Remote: %s" -msgstr "" +msgstr "Serveur distant : %s" #: lxc/delete.go:42 #, c-format msgid "Remove %s (yes/no): " -msgstr "" +msgstr "Supprimer %s (yes/no) : " #: lxc/delete.go:36 lxc/delete.go:37 -msgid "Require user confirmation." -msgstr "" +msgid "Require user confirmation" +msgstr "Requérir une confirmation de l'utilisateur" #: lxc/info.go:127 msgid "Resources:" +msgstr "Ressources :" + +#: lxc/restore.go:21 +msgid "" +"Restore a container's state to a previous snapshot.\n" +"\n" +"lxc restore [:] [--stateful]\n" +"\n" +"Restores a container from a snapshot (optionally with running state, see\n" +"snapshot help for details).\n" +"\n" +"Examples:\n" +"Create the snapshot:\n" +" lxc snapshot u1 snap0\n" +"\n" +"Restore the snapshot:\n" +" lxc restore u1 snap0" msgstr "" +"Restaurer l'état d'une ressource depuis un instantané.\n" +"\n" +"lxc restore [:] [--stateful]\n" +"\n" +"Restaure un conteneur depuis un instantané (éventuellement avec l'état\n" +"d'exécution, voir snapshot help pour les détails).\n" +"\n" +"Par exemple :\n" +"Créer un instantané :\n" +" lxc snapshot u1 snap0\n" +"\n" +"Restaurer un instantané :\n" +" lxc restore u1 snap0" -#: lxc/init.go:267 +#: lxc/init.go:239 #, c-format msgid "Retrieving image: %s" -msgstr "" +msgstr "Récupérer l'image : %s" -#: lxc/image.go:643 +#: lxc/image.go:640 msgid "SIZE" -msgstr "" +msgstr "TAILLE" #: lxc/list.go:431 msgid "SNAPSHOTS" -msgstr "" +msgstr "INSTANTANÉS" #: lxc/list.go:432 msgid "STATE" -msgstr "" +msgstr "ÉTAT" -#: lxc/remote.go:385 +#: lxc/remote.go:384 msgid "STATIC" -msgstr "" +msgstr "STATIQUE" -#: lxc/remote.go:226 +#: lxc/remote.go:225 msgid "Server certificate NACKed by user" msgstr "Le certificat serveur a été rejeté par l'utilisateur" -#: lxc/remote.go:288 +#: lxc/remote.go:287 msgid "Server doesn't trust us after adding our cert" -msgstr "Identification refuse après l'ajout du certificat client" - -#: lxc/remote.go:54 -msgid "Server protocol (lxd or simplestreams)" msgstr "" +"Le serveur ne nous fait pas confiance après l'ajout de notre certificat" -#: lxc/restore.go:21 -msgid "" -"Set the current state of a resource back to a snapshot.\n" -"\n" -"lxc restore [remote:] [--stateful]\n" -"\n" -"Restores a container from a snapshot (optionally with running state, see\n" -"snapshot help for details).\n" -"\n" -"For example:\n" -"lxc snapshot u1 snap0 # create the snapshot\n" -"lxc restore u1 snap0 # restore the snapshot" -msgstr "" +#: lxc/remote.go:53 +msgid "Server protocol (lxd or simplestreams)" +msgstr "Protocole du serveur (lxd ou simplestreams)" -#: lxc/file.go:56 +#: lxc/file.go:55 msgid "Set the file's gid on push" -msgstr "Définit le gid lors de l'envoi" +msgstr "Définir le gid du fichier lors de l'envoi" -#: lxc/file.go:57 +#: lxc/file.go:56 msgid "Set the file's perms on push" -msgstr "Définit les permissions lors de l'envoi" +msgstr "Définir les permissions du fichier lors de l'envoi" -#: lxc/file.go:55 +#: lxc/file.go:54 msgid "Set the file's uid on push" -msgstr "Définit le uid lors de l'envoi" +msgstr "Définir l'uid du fichier lors de l'envoi" #: lxc/help.go:32 msgid "Show all commands (not just interesting ones)" -msgstr "Affiche toutes les comandes (pas seulement les intéresantes)" +msgstr "Afficher toutes les comandes (pas seulement les plus intéressantes)" #: lxc/help.go:67 -msgid "Show client version." -msgstr "" +msgid "Show client version" +msgstr "Afficher la version du client" #: lxc/info.go:36 msgid "Show the container's last 100 log lines?" -msgstr "" +msgstr "Afficher les 100 dernières lignes du journal du conteneur ?" -#: lxc/image.go:347 +#: lxc/config.go:32 +msgid "Show the expanded configuration" +msgstr "Afficher ou pas la configuration étendue" + +#: lxc/image.go:344 #, c-format msgid "Size: %.2fMB" -msgstr "" +msgstr "Taille : %.2f Mo" #: lxc/info.go:208 msgid "Snapshots:" -msgstr "" +msgstr "Instantanés :" -#: lxc/image.go:371 +#: lxc/image.go:368 msgid "Source:" -msgstr "" +msgstr "Source :" -#: lxc/launch.go:142 +#: lxc/launch.go:144 #, c-format msgid "Starting %s" -msgstr "" +msgstr "Démarrage de %s" #: lxc/info.go:98 #, c-format msgid "Status: %s" -msgstr "" +msgstr "État : %s" #: lxc/publish.go:35 lxc/publish.go:36 msgid "Stop the container if currently running" -msgstr "" +msgstr "Arrêter le conteneur s'il est en cours d'exécution" #: lxc/delete.go:106 lxc/publish.go:113 msgid "Stopping container failed!" -msgstr "L'arrêt du conteneur a échoué!" +msgstr "L'arrêt du conteneur a échoué !" #: lxc/action.go:45 -#, fuzzy -msgid "Store the container state (only for stop)." -msgstr "Force l'arrêt du conteneur." +msgid "Store the container state (only for stop)" +msgstr "Forcer l'arrêt du conteneur (seulement pour stop)" #: lxc/info.go:169 msgid "Swap (current)" -msgstr "" +msgstr "Swap (courant)" #: lxc/info.go:173 msgid "Swap (peak)" -msgstr "" +msgstr "Swap (pointe)" -#: lxc/list.go:433 lxc/network.go:423 +#: lxc/list.go:433 lxc/network.go:422 msgid "TYPE" msgstr "" #: lxc/delete.go:92 msgid "The container is currently running, stop it first or pass --force." msgstr "" +"Le conteneur est en cours d'exécution, arrêtez-le d'abord ou ajoutez --force." #: lxc/publish.go:91 msgid "" "The container is currently running. Use --force to have it stopped and " "restarted." msgstr "" +"Le conteneur est en cours d'exécution. Utilisez --force pour qu'il soit " +"arrêté et redémarré." -#: lxc/init.go:313 -msgid "The container you are starting doesn’t have any network attached to it." +#: lxc/init.go:312 +msgid "The container you are starting doesn't have any network attached to it." msgstr "" +"Le conteneur que vous démarrez n'est attaché à aucune interface réseau." #: lxc/config.go:675 lxc/config.go:687 lxc/config.go:720 lxc/config.go:738 #: lxc/config.go:776 lxc/config.go:794 -#, fuzzy msgid "The device doesn't exist" -msgstr "le serveur distant %s n'existe pas" +msgstr "Le périphérique n'existe pas" -#: lxc/init.go:297 +#: lxc/init.go:296 #, c-format msgid "The local image '%s' couldn't be found, trying '%s:' instead." -msgstr "" +msgstr "L'image locale '%s' n'a pas été trouvée, essayez '%s:' à la place." #: lxc/main.go:181 msgid "The opposite of `lxc pause` is `lxc start`." -msgstr "" +msgstr "Le pendant de `lxc pause` est `lxc start`." -#: lxc/network.go:230 lxc/network.go:279 -#, fuzzy +#: lxc/network.go:229 lxc/network.go:278 msgid "The specified device doesn't exist" -msgstr "le serveur distant %s n'existe pas" +msgstr "Le périphérique indiqué n'existe pas" -#: lxc/network.go:234 lxc/network.go:283 -#, fuzzy +#: lxc/network.go:233 lxc/network.go:282 msgid "The specified device doesn't match the network" -msgstr "le serveur distant %s n'existe pas" +msgstr "le périphérique indiqué ne correspond pas au réseau" #: lxc/publish.go:64 msgid "There is no \"image name\". Did you want an alias?" -msgstr "" +msgstr "Il n'existe pas d'\"image\". Vouliez-vous un alias ?" #: lxc/action.go:41 -msgid "Time to wait for the container before killing it." -msgstr "Temps d'attente avant de tuer le conteneur." +msgid "Time to wait for the container before killing it" +msgstr "Temps d'attente du conteneur avant de le tuer" -#: lxc/image.go:350 +#: lxc/image.go:347 msgid "Timestamps:" -msgstr "" +msgstr "Horodatage :" -#: lxc/init.go:315 +#: lxc/init.go:314 msgid "To attach a network to a container, use: lxc network attach" -msgstr "" +msgstr "Pour attacher un réseau à un conteneur, utilisez : lxc network attach" -#: lxc/init.go:314 +#: lxc/init.go:313 msgid "To create a new network, use: lxc network create" -msgstr "" +msgstr "Pour créer un réseau, utilisez : lxc network create" #: lxc/main.go:137 msgid "To start your first container, try: lxc launch ubuntu:16.04" msgstr "" +"Pour démarrer votre premier conteneur, essayez : lxc launch ubuntu:16.04" -#: lxc/image.go:421 +#: lxc/image.go:426 #, c-format -msgid "Transferring image: %d%%" -msgstr "" +msgid "Transferring image: %s" +msgstr "Transfert de l'image : %s" -#: lxc/action.go:99 lxc/launch.go:155 +#: lxc/action.go:99 lxc/launch.go:157 #, c-format msgid "Try `lxc info --show-log %s` for more info" -msgstr "" +msgstr "Essayez `lxc info --show-log %s` pour plus d'informations" #: lxc/info.go:100 msgid "Type: ephemeral" -msgstr "" +msgstr "Type : éphémère" #: lxc/info.go:102 msgid "Type: persistent" -msgstr "" +msgstr "Type : persistant" -#: lxc/image.go:644 +#: lxc/image.go:641 msgid "UPLOAD DATE" -msgstr "" +msgstr "DATE DE PUBLICATION" -#: lxc/remote.go:382 +#: lxc/remote.go:381 msgid "URL" msgstr "" -#: lxc/network.go:425 lxc/profile.go:448 +#: lxc/network.go:424 lxc/profile.go:447 msgid "USED BY" -msgstr "" +msgstr "UTILISÉ PAR" -#: lxc/remote.go:96 +#: lxc/remote.go:95 msgid "Unable to read remote TLS certificate" -msgstr "" +msgstr "Impossible de lire le certificat TLS distant" -#: lxc/image.go:355 +#: lxc/image.go:352 #, c-format msgid "Uploaded: %s" -msgstr "" +msgstr "Publié : %s" #: lxc/main.go:112 -#, fuzzy, c-format +#, c-format msgid "Usage: %s" -msgstr "" -"Utilisation: %s\n" -"\n" -"Options:\n" -"\n" +msgstr "Utilisation : %s" #: lxc/help.go:48 -#, fuzzy -msgid "Usage: lxc [subcommand] [options]" -msgstr "" -"Utilisation: lxc [sous commande] [options]\n" -"Comande disponibles:\n" +msgid "Usage: lxc [options]" +msgstr "Utilisation : lxc [options]" #: lxc/delete.go:46 msgid "User aborted delete operation." -msgstr "" +msgstr "L'utilisateur a annulé l'opération de suppression." -#: lxc/restore.go:35 -#, fuzzy +#: lxc/restore.go:38 msgid "" "Whether or not to restore the container's running state from snapshot (if " "available)" msgstr "" -"Est-ce que l'état de fonctionement du conteneur doit être inclus dans " -"l'instantané (snapshot)" +"Restaurer ou pas l'état de fonctionnement du conteneur depuis l'instantané " +"(s'il est disponible)" #: lxc/snapshot.go:38 msgid "Whether or not to snapshot the container's running state" -msgstr "" -"Est-ce que l'état de fonctionement du conteneur doit être inclus dans " -"l'instantané (snapshot)" - -#: lxc/config.go:32 -msgid "Whether to show the expanded configuration" -msgstr "" +msgstr "Réaliser ou pas l'instantané de l'état de fonctionnement du conteneur" -#: lxc/network.go:410 lxc/remote.go:357 lxc/remote.go:362 +#: lxc/network.go:409 lxc/remote.go:356 lxc/remote.go:361 msgid "YES" -msgstr "" +msgstr "OUI" #: lxc/main.go:52 msgid "`lxc config profile` is deprecated, please use `lxc profile`" msgstr "" +"La commande `lxc config profile` est dépréciée, merci d'utiliser `lxc " +"profile`" -#: lxc/launch.go:127 -#, fuzzy +#: lxc/launch.go:128 msgid "bad number of things scanned from image, container or snapshot" -msgstr "nombre de propriété invalide pour la ressource" +msgstr "nombre d'éléments invalide dans l'image, conteneur ou instantané" #: lxc/action.go:95 msgid "bad result type from action" -msgstr "mauvais type de réponse pour l'action!" +msgstr "type de réponse renvoyé par l'action invalide !" #: lxc/copy.go:115 msgid "can't copy to the same container name" -msgstr "" +msgstr "impossible de copier vers le même nom de conteneur" -#: lxc/file.go:270 +#: lxc/file.go:272 msgid "can't pull a directory without --recursive" -msgstr "" +msgstr "impossible de récupérer un répertoire sans --recursive" -#: lxc/remote.go:345 +#: lxc/remote.go:344 msgid "can't remove the default remote" -msgstr "" +msgstr "impossible de supprimer le serveur distant par défaut" -#: lxc/file.go:117 +#: lxc/file.go:119 msgid "can't supply uid/gid/mode in recursive mode" -msgstr "" +msgstr "impossible de spécifier uid/gid/mode en mode récursif" -#: lxc/remote.go:371 +#: lxc/remote.go:370 msgid "default" -msgstr "" +msgstr "par défaut" #: lxc/copy.go:131 lxc/copy.go:136 lxc/copy.go:232 lxc/copy.go:237 -#: lxc/init.go:217 lxc/init.go:222 lxc/launch.go:111 lxc/launch.go:116 -#, fuzzy +#: lxc/init.go:219 lxc/init.go:224 lxc/launch.go:112 lxc/launch.go:117 msgid "didn't get any affected image, container or snapshot from server" -msgstr "N'a pas pû obtenir de resource du serveur" +msgstr "pas de réponse du serveur pour cette image, conteneur ou instantané" -#: lxc/image.go:341 +#: lxc/image.go:338 msgid "disabled" -msgstr "" +msgstr "désactivé" -#: lxc/image.go:343 +#: lxc/image.go:340 msgid "enabled" -msgstr "" +msgstr "activé" #: lxc/main.go:22 lxc/main.go:148 -#, fuzzy, c-format +#, c-format msgid "error: %v" -msgstr "erreur: %v\n" +msgstr "erreur : %v" #: lxc/help.go:40 lxc/main.go:107 -#, fuzzy, c-format +#, c-format msgid "error: unknown command: %s" -msgstr "erreur: comande inconnue: %s\n" +msgstr "erreur : commande inconnue: %s" -#: lxc/launch.go:131 +#: lxc/launch.go:132 msgid "got bad version" -msgstr "reçu une version invalide" +msgstr "version reçue invalide" -#: lxc/image.go:336 lxc/image.go:620 +#: lxc/image.go:333 lxc/image.go:617 msgid "no" -msgstr "" +msgstr "non" #: lxc/copy.go:164 msgid "not all the profiles from the source exist on the target" -msgstr "" +msgstr "tous les profils de la source n'existent pas sur la cible" -#: lxc/remote.go:219 -#, fuzzy +#: lxc/remote.go:218 msgid "ok (y/n)?" -msgstr "ok (y/n)?" +msgstr "ok (y/n) ?" #: lxc/main.go:304 lxc/main.go:308 #, c-format msgid "processing aliases failed %s\n" -msgstr "" +msgstr "l'analyse des alias a échoué %s\n" -#: lxc/file.go:311 +#: lxc/file.go:313 msgid "recursive edit doesn't make sense :(" -msgstr "" +msgstr "l'édition récursive ne fait aucun sens :(" -#: lxc/remote.go:407 +#: lxc/remote.go:406 #, c-format msgid "remote %s already exists" msgstr "le serveur distant %s existe déjà" -#: lxc/remote.go:337 lxc/remote.go:399 lxc/remote.go:434 lxc/remote.go:450 +#: lxc/remote.go:336 lxc/remote.go:398 lxc/remote.go:433 lxc/remote.go:449 #, c-format msgid "remote %s doesn't exist" msgstr "le serveur distant %s n'existe pas" -#: lxc/remote.go:320 +#: lxc/remote.go:319 #, c-format msgid "remote %s exists as <%s>" msgstr "le serveur distant %s existe en tant que <%s>" -#: lxc/remote.go:341 lxc/remote.go:403 lxc/remote.go:438 +#: lxc/remote.go:340 lxc/remote.go:402 lxc/remote.go:437 #, c-format msgid "remote %s is static and cannot be modified" -msgstr "" +msgstr "le serveur distant %s est statique et ne peut être modifié" #: lxc/info.go:219 msgid "stateful" -msgstr "" +msgstr "à suivi d'état" #: lxc/info.go:221 msgid "stateless" -msgstr "" +msgstr "sans suivi d'état" #: lxc/info.go:215 #, c-format msgid "taken at %s" -msgstr "" +msgstr "pris à %s" #: lxc/exec.go:185 msgid "unreachable return reached" -msgstr "Un retour inacessible à été atteint" +msgstr "Un retour impossible à été renvoyé" #: lxc/main.go:236 msgid "wrong number of subcommand arguments" -msgstr "nombre d'argument incorrect pour la sous-comande" +msgstr "nombre d'arguments incorrect pour la sous-comande" -#: lxc/delete.go:45 lxc/image.go:338 lxc/image.go:624 +#: lxc/delete.go:45 lxc/image.go:335 lxc/image.go:621 msgid "yes" -msgstr "" +msgstr "oui" #: lxc/copy.go:44 msgid "you must specify a source container name" -msgstr "" - -#, fuzzy -#~ msgid "Bad image property: %s" -#~ msgstr "(Image invalide: %s\n" - -#, fuzzy -#~ msgid "" -#~ "Create a read-only snapshot of a container.\n" -#~ "\n" -#~ "lxc snapshot [remote:] [--stateful]" -#~ msgstr "Prend un instantané (snapshot) en lecture seule d'un conteneur.\n" - -#, fuzzy -#~ msgid "" -#~ "Set the current state of a resource back to its state at the time the " -#~ "snapshot was created.\n" -#~ "\n" -#~ "lxc restore [remote:] [--stateful]" -#~ msgstr "Prend un instantané (snapshot) en lecture seule d'un conteneur.\n" - -#~ msgid "api version mismatch: mine: %q, daemon: %q" -#~ msgstr "Version de l'API incompatible: local: %q, distant: %q" - -#, fuzzy -#~ msgid "bad version in profile url" -#~ msgstr "version invalide dans l'URL du conteneur" - -#, fuzzy -#~ msgid "device already exists" -#~ msgstr "le serveur distant %s existe déjà" - -#, fuzzy -#~ msgid "error." -#~ msgstr "erreur: %v\n" - -#~ msgid "got bad op status %s" -#~ msgstr "reçu un status d'opration invalide %s" - -#, fuzzy -#~ msgid "got bad response type, expected %s got %s" -#~ msgstr "reçu une mauvaise réponse pour \"exec\"" - -#~ msgid "invalid wait url %s" -#~ msgstr "URL d'attente invalide %s" - -#~ msgid "no response!" -#~ msgstr "pas de réponse!" - -#~ msgid "unknown remote name: %q" -#~ msgstr "serveur distant inconnu: %q" - -#, fuzzy -#~ msgid "unknown transport type: %s" -#~ msgstr "serveur distant inconnu: %q" - -#~ msgid "cannot resolve unix socket address: %v" -#~ msgstr "Ne peut pas résoudre l'adresse du unix socket: %v" - -#, fuzzy -#~ msgid "unknown group %s" -#~ msgstr "serveur distant inconnu: %q" - -#, fuzzy -#~ msgid "Information about remotes not yet supported" -#~ msgstr "" -#~ "Il n'est pas encore possible d'obtenir de l'information sur un serveur " -#~ "distant\n" - -#~ msgid "Unknown image command %s" -#~ msgstr "Comande d'image inconnue %s" - -#~ msgid "Unknown remote subcommand %s" -#~ msgstr "Comande de serveur distant inconnue %s" - -#~ msgid "Unkonwn config trust command %s" -#~ msgstr "Comande de configuration de confiance inconnue %s" - -#, fuzzy -#~ msgid "YAML parse error %v" -#~ msgstr "erreur: %v\n" - -#~ msgid "invalid argument %s" -#~ msgstr "Arguments invalides %s" - -#, fuzzy -#~ msgid "unknown profile cmd %s" -#~ msgstr "Comande de configuration inconue %s" - -#, fuzzy -#~ msgid "Publish to remote server is not supported yet" -#~ msgstr "" -#~ "Il n'est pas encore possible d'obtenir de l'information sur un serveur " -#~ "distant\n" - -#, fuzzy -#~ msgid "Use an alternative config path." -#~ msgstr "Dossier de configuration alternatif." - -#, fuzzy -#~ msgid "" -#~ "error: %v\n" -#~ "%s\n" -#~ msgstr "" -#~ "erreur: %v\n" -#~ "%s" - -#, fuzzy -#~ msgid "Show for remotes is not yet supported\n" -#~ msgstr "" -#~ "Il n'est pas encore possible d'obtenir de l'information sur un serveur " -#~ "distant\n" - -#~ msgid "(Bad alias entry: %s\n" -#~ msgstr "(Alias invalide: %s\n" - -#~ msgid "bad container url %s" -#~ msgstr "Mauvaise URL pour le conteneur %s" - -#~ msgid "bad version in container url" -#~ msgstr "version invalide dans l'URL du conteneur" - -#, fuzzy -#~ msgid "Ephemeral containers not yet supported\n" -#~ msgstr "" -#~ "Il n'est pas encore possible d'obtenir de l'information sur un serveur " -#~ "distant\n" - -#~ msgid "(Bad image entry: %s\n" -#~ msgstr "(Image invalide: %s\n" - -#~ msgid "Certificate already stored.\n" -#~ msgstr "Le certificat a déjà été enregistré.\n" - -#, fuzzy -#~ msgid "Non-async response from delete!" -#~ msgstr "Réponse invalide (non-async) durant la suppression!" - -#, fuzzy -#~ msgid "Non-async response from init!" -#~ msgstr "" -#~ "Réponse invalide (non-async) durant la cration d'un instantané (snapshot)!" - -#~ msgid "Non-async response from snapshot!" -#~ msgstr "" -#~ "Réponse invalide (non-async) durant la cration d'un instantané (snapshot)!" - -#~ msgid "Server certificate has changed" -#~ msgstr "Le certificat serveur a changé" - -#, fuzzy -#~ msgid "Unexpected non-async response" -#~ msgstr "Réponse invalide (non-async) durant la suppression!" - -#~ msgid "bad response type from image list!" -#~ msgstr "mauvais type de réponse pour la liste d'image!" - -#~ msgid "bad response type from list!" -#~ msgstr "mauvais type de réponse pour la liste!" - -#, fuzzy -#~ msgid "got non-async response!" -#~ msgstr "Réponse invalide (non-async) durant la suppression!" - -#~ msgid "got non-sync response from containers get!" -#~ msgstr "Réponse invalide (non-async) durant le chargement!" - -#~ msgid "Delete a container or container snapshot.\n" -#~ msgstr "Supprime un conteneur ou l'instantané (snapshot) d'un conteneur.\n" - -#~ msgid "List information on containers.\n" -#~ msgstr "Liste de l'information sur les conteneurs.\n" - -#~ msgid "Lists the available resources.\n" -#~ msgstr "Liste des ressources disponibles.\n" - -#~ msgid "Manage files on a container.\n" -#~ msgstr "Gérer les fichiers du conteneur.\n" - -#~ msgid "Manage remote lxc servers.\n" -#~ msgstr "Gérer les serveurs distants.\n" - -#~ msgid "Non-async response from create!" -#~ msgstr "Réponse invalide (non-async) durant la cration!" - -#~ msgid "Only 'password' can be set currently" -#~ msgstr "Seul 'password' peut être configuré en ce moment" - -#~ msgid "" -#~ "lxc image import [target] [--created-at=ISO-8601] [--expires-" -#~ "at=ISO-8601] [--fingerprint=HASH] [prop=value]\n" -#~ msgstr "" -#~ "lxc image import [destination] [--created-at=ISO-8601] [--" -#~ "expires-at=ISO-8601] [--fingerprint=HASH] [proprit=valeur]\n" - -#~ msgid "lxc init ubuntu []\n" -#~ msgstr "lxc init ubuntu []\n" - -#~ msgid "lxc launch ubuntu []\n" -#~ msgstr "lxc launch ubuntu []\n" +msgstr "vous devez spécifier un nom de conteneur source" diff -Nru lxd-2.6.2/po/ja.po lxd-2.7/po/ja.po --- lxd-2.6.2/po/ja.po 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/po/ja.po 2016-12-21 00:52:23.000000000 +0000 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: LXD\n" "Report-Msgid-Bugs-To: lxc-devel@lists.linuxcontainers.org\n" -"POT-Creation-Date: 2016-11-04 21:31-0400\n" +"POT-Creation-Date: 2016-12-15 13:20-0500\n" "PO-Revision-Date: 2016-11-09 19:45+0900\n" "Last-Translator: KATOH Yasufumi \n" "Language-Team: Japanese \n" @@ -104,7 +104,7 @@ "### Note that the name is shown but cannot be changed" msgstr "" -#: lxc/image.go:617 +#: lxc/image.go:614 #, c-format msgid "%s (%d more)" msgstr "" @@ -113,15 +113,15 @@ msgid "'/' not allowed in snapshot name" msgstr "'/' はスナップショットの名前には使用できません。" -#: lxc/profile.go:254 +#: lxc/profile.go:253 msgid "(none)" msgstr "" -#: lxc/image.go:638 lxc/image.go:680 +#: lxc/image.go:635 lxc/image.go:677 msgid "ALIAS" msgstr "" -#: lxc/image.go:642 +#: lxc/image.go:639 msgid "ARCH" msgstr "" @@ -129,29 +129,25 @@ msgid "ARCHITECTURE" msgstr "" -#: lxc/remote.go:52 +#: lxc/remote.go:51 msgid "Accept certificate" msgstr "証明書を受け入れます" -#: lxc/remote.go:268 +#: lxc/remote.go:267 #, c-format msgid "Admin password for %s: " msgstr "%s の管理者パスワード: " -#: lxc/image.go:365 +#: lxc/image.go:362 msgid "Aliases:" msgstr "エイリアス:" -#: lxc/exec.go:54 -msgid "An environment variable of the form HOME=/home/foo" -msgstr "環境変数を HOME=/home/foo の形式で指定します" - -#: lxc/image.go:348 lxc/info.go:93 +#: lxc/image.go:345 lxc/info.go:93 #, c-format msgid "Architecture: %s" msgstr "アーキテクチャ: %s" -#: lxc/image.go:369 +#: lxc/image.go:366 #, c-format msgid "Auto update: %s" msgstr "自動更新: %s" @@ -190,27 +186,38 @@ msgid "Can't unset key '%s', it's not currently set." msgstr "キー '%s' が指定されていないので削除できません。" -#: lxc/network.go:386 lxc/profile.go:420 +#: lxc/network.go:385 lxc/profile.go:419 msgid "Cannot provide container name to list" msgstr "コンテナ名を取得できません" -#: lxc/remote.go:218 +#: lxc/remote.go:217 #, c-format msgid "Certificate fingerprint: %x" msgstr "証明書のフィンガープリント: %x" #: lxc/action.go:33 -#, c-format +#, fuzzy, c-format msgid "" -"Changes state of one or more containers to %s.\n" +"Change state of one or more containers to %s.\n" "\n" -"lxc %s [...]%s" +"lxc %s [:] [[:]...]%s" msgstr "" "1つまたは複数のコンテナの状態を %s に変更します。\n" "\n" "lxc %s [...]%s" -#: lxc/remote.go:291 +#: lxc/finger.go:15 +#, fuzzy +msgid "" +"Check if the LXD instance is up.\n" +"\n" +"lxc finger [:]" +msgstr "" +"LXDインスタンスが稼働中かを確認します。\n" +"\n" +"lxc finger " + +#: lxc/remote.go:290 msgid "Client certificate stored at server: " msgstr "クライアント証明書がサーバに格納されました: " @@ -223,8 +230,8 @@ msgid "Config key/value to apply to the new container" msgstr "新しいコンテナに適用するキー/値の設定" -#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:734 lxc/network.go:342 -#: lxc/profile.go:218 +#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:731 lxc/network.go:341 +#: lxc/profile.go:217 #, c-format msgid "Config parsing error: %s" msgstr "設定の構文エラー: %s" @@ -237,7 +244,7 @@ msgid "Container name is mandatory" msgstr "コンテナ名を指定する必要があります" -#: lxc/copy.go:140 lxc/copy.go:241 lxc/init.go:227 +#: lxc/copy.go:140 lxc/copy.go:241 lxc/init.go:229 #, c-format msgid "Container name is: %s" msgstr "コンテナ名: %s" @@ -247,15 +254,16 @@ msgid "Container published with fingerprint: %s" msgstr "コンテナは以下のフィンガープリントで publish されます: %s" -#: lxc/image.go:166 +#: lxc/image.go:165 msgid "Copy aliases from source" msgstr "ソースからエイリアスをコピーしました" #: lxc/copy.go:24 +#, fuzzy msgid "" -"Copy containers within or in between lxd instances.\n" +"Copy containers within or in between LXD instances.\n" "\n" -"lxc copy [remote:] [[remote:]] [--" +"lxc copy [:][/] [[:]] [--" "ephemeral|e] [--profile|-p ...] [--config|-c ...]" msgstr "" "LXDインスタンス内もしくはLXDインスタンス間でコンテナをコピーします。\n" @@ -263,20 +271,26 @@ "lxc copy [remote:] [remote:]\n" "[--ephemeral|e] [--profile|-p ...] [--config|-c ...]" -#: lxc/image.go:280 +#: lxc/image.go:278 #, c-format msgid "Copying the image: %s" msgstr "イメージのコピー中: %s" -#: lxc/remote.go:233 +#: lxc/remote.go:232 msgid "Could not create server cert dir" msgstr "サーバ証明書格納用のディレクトリを作成できません。" +#: lxc/file.go:83 +#, c-format +msgid "Could not sanitize path %s" +msgstr "" + #: lxc/snapshot.go:21 +#, fuzzy msgid "" "Create a read-only snapshot of a container.\n" "\n" -"lxc snapshot [remote:] [--stateful]\n" +"lxc snapshot [:] [--stateful]\n" "\n" "Creates a snapshot of the container (optionally with the container's memory\n" "state). When --stateful is used, LXD attempts to checkpoint the container's\n" @@ -289,7 +303,7 @@ "successfully).\n" "\n" "Example:\n" -"lxc snapshot u1 snap0" +" lxc snapshot u1 snap0" msgstr "" "コンテナの読み取り専用のスナップショットを作成します。\n" "\n" @@ -304,16 +318,16 @@ "例:\n" "lxc snapshot u1 snap0" -#: lxc/file.go:60 lxc/file.go:61 +#: lxc/file.go:59 lxc/file.go:60 msgid "Create any directories necessary" msgstr "必要なディレクトリをすべて作成します" -#: lxc/image.go:353 lxc/info.go:95 +#: lxc/image.go:350 lxc/info.go:95 #, c-format msgid "Created: %s" msgstr "作成日時: %s" -#: lxc/init.go:180 lxc/launch.go:134 +#: lxc/init.go:180 lxc/launch.go:135 #, c-format msgid "Creating %s" msgstr "%s を作成中" @@ -322,7 +336,7 @@ msgid "Creating the container" msgstr "コンテナを作成中" -#: lxc/image.go:641 lxc/image.go:682 +#: lxc/image.go:638 lxc/image.go:679 msgid "DESCRIPTION" msgstr "" @@ -331,10 +345,11 @@ msgstr "圧縮アルゴリズムを指定します: 圧縮アルゴリズム名 or none" #: lxc/delete.go:25 +#, fuzzy msgid "" -"Delete containers or container snapshots.\n" +"Delete containers or snapshots.\n" "\n" -"lxc delete [remote:][/] [remote:][[/" +"lxc delete [:][/] [[:][/" "]...]\n" "\n" "Destroy containers or snapshots with any attached data (configuration, " @@ -367,13 +382,20 @@ msgstr "" #: lxc/main.go:41 -msgid "Enables debug mode." +#, fuzzy +msgid "Enable debug mode" msgstr "デバッグモードを有効にします。" #: lxc/main.go:40 -msgid "Enables verbose mode." +#, fuzzy +msgid "Enable verbose mode" msgstr "詳細モードを有効にします。" +#: lxc/exec.go:54 +#, fuzzy +msgid "Environment variable to set (e.g. HOME=/home/foo)" +msgstr "環境変数を HOME=/home/foo の形式で指定します" + #: lxc/help.go:69 msgid "Environment:" msgstr "環境変数:" @@ -388,32 +410,34 @@ msgstr "Listenするイベントタイプ" #: lxc/exec.go:45 +#, fuzzy msgid "" "Execute the specified command in a container.\n" "\n" -"lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env " -"EDITOR=/usr/bin/vim]... [--] \n" +"lxc exec [:] [--mode=auto|interactive|non-interactive] [--" +"env KEY=VALUE...] [--] \n" "\n" "Mode defaults to non-interactive, interactive mode is selected if both stdin " "AND stdout are terminals (stderr is ignored)." msgstr "" "指定したコマンドをコンテナ内で実行します。\n" "\n" -"lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... [--] \n" +"lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env " +"EDITOR=/usr/bin/vim]... [--] \n" "\n" "デフォルトのモードは non-interactive です。もし標準入出力が両方ともターミナ\n" "ルの場合は interactive モードが選択されます (標準エラー出力は無視されます)。" -#: lxc/image.go:357 +#: lxc/image.go:354 #, c-format msgid "Expires: %s" msgstr "失効日時: %s" -#: lxc/image.go:359 +#: lxc/image.go:356 msgid "Expires: never" msgstr "失効日時: 失効しない" -#: lxc/config.go:272 lxc/image.go:639 lxc/image.go:681 +#: lxc/config.go:272 lxc/image.go:636 lxc/image.go:678 msgid "FINGERPRINT" msgstr "" @@ -421,41 +445,45 @@ msgid "Fast mode (same as --columns=nsacPt" msgstr "Fast モード (--columns=nsacPt と同じ)" -#: lxc/image.go:346 +#: lxc/image.go:343 #, c-format msgid "Fingerprint: %s" msgstr "証明書のフィンガープリント: %s" -#: lxc/finger.go:15 -msgid "" -"Fingers the LXD instance to check if it is up and working.\n" -"\n" -"lxc finger " -msgstr "" -"LXDインスタンスが稼働中かを確認します。\n" -"\n" -"lxc finger " - #: lxc/action.go:42 lxc/action.go:43 -msgid "Force the container to shutdown." +#, fuzzy +msgid "Force the container to shutdown" msgstr "コンテナを強制シャットダウンします。" #: lxc/delete.go:34 lxc/delete.go:35 -msgid "Force the removal of stopped containers." +#, fuzzy +msgid "Force the removal of stopped containers" msgstr "停止したコンテナを強制的に削除します。" #: lxc/main.go:42 -msgid "Force using the local unix socket." +#, fuzzy +msgid "Force using the local unix socket" msgstr "強制的にローカルのUNIXソケットを使います。" -#: lxc/image.go:169 lxc/list.go:123 +#: lxc/image.go:168 lxc/list.go:123 msgid "Format" msgstr "" -#: lxc/remote.go:66 +#: lxc/remote.go:65 msgid "Generating a client certificate. This may take a minute..." msgstr "クライアント証明書を生成します。1分ぐらいかかります..." +#: lxc/help.go:25 +#, fuzzy +msgid "" +"Help page for the LXD client.\n" +"\n" +"lxc help [--all]" +msgstr "" +"LXDの使い方の詳細を表示します。\n" +"\n" +"lxd help [--all]" + #: lxc/list.go:423 msgid "IPV4" msgstr "" @@ -474,33 +502,36 @@ msgstr "初めて LXD を使う場合、sudo lxd init と実行する必要があります" #: lxc/main.go:43 -msgid "Ignore aliases when determining what command to run." +#, fuzzy +msgid "Ignore aliases when determining what command to run" msgstr "どのコマンドを実行するか決める際にエイリアスを無視します。" #: lxc/action.go:46 -msgid "Ignore the container state (only for start)." +#, fuzzy +msgid "Ignore the container state (only for start)" msgstr "コンテナの状態を無視します (startのみ)。" -#: lxc/image.go:285 +#: lxc/image.go:281 msgid "Image copied successfully!" msgstr "イメージのコピーが成功しました!" -#: lxc/image.go:442 +#: lxc/image.go:421 lxc/image.go:433 #, c-format msgid "Image imported with fingerprint: %s" msgstr "イメージは以下のフィンガープリントでインポートされました: %s" -#: lxc/image.go:429 +#: lxc/image.go:418 #, c-format msgid "Importing the image: %s" msgstr "イメージのインポート中: %s" #: lxc/init.go:74 +#, fuzzy msgid "" "Initialize a container from a particular image.\n" "\n" -"lxc init [remote:] [remote:][] [--ephemeral|-e] [--profile|-p " -"...] [--config|-c ...] [--network|-n ]\n" +"lxc init [:] [:][] [--ephemeral|-e] [--profile|-" +"p ...] [--config|-c ...] [--network|-n ]\n" "\n" "Initializes a container using the specified image and name.\n" "\n" @@ -508,11 +539,12 @@ "Specifying \"-p\" with no argument will result in no profile.\n" "\n" "Example:\n" -"lxc init ubuntu u1" +" lxc init ubuntu u1" msgstr "" "指定したイメージからコンテナを初期化します。\n" "\n" -"lxc init [remote:] [remote:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ]\n" +"lxc init [remote:] [remote:][] [--ephemeral|-e] [--profile|-p " +"...] [--config|-c ...] [--network|-n ]\n" "\n" "指定したイメージとコンテナ名を使ってコンテナを初期化します。\n" "\n" @@ -522,7 +554,7 @@ "例:\n" "lxc init ubuntu u1" -#: lxc/remote.go:136 +#: lxc/remote.go:135 #, c-format msgid "Invalid URL scheme \"%s\" in \"%s\"" msgstr "不正な URL スキーム \"%s\" (\"%s\" 内)" @@ -535,12 +567,12 @@ msgid "Invalid configuration key" msgstr "正しくない設定項目 (key) です" -#: lxc/file.go:247 +#: lxc/file.go:249 #, c-format msgid "Invalid source %s" msgstr "不正なソース %s" -#: lxc/file.go:73 +#: lxc/file.go:72 #, c-format msgid "Invalid target %s" msgstr "不正な送り先 %s" @@ -549,7 +581,7 @@ msgid "Ips:" msgstr "IPアドレス:" -#: lxc/image.go:167 +#: lxc/image.go:166 msgid "Keep the image up to date after initial copy" msgstr "最初にコピーした後も常にイメージを最新の状態に保つ" @@ -562,11 +594,13 @@ msgstr "LXD のソケットが見つかりません。LXD が実行されていますか?" #: lxc/launch.go:22 +#, fuzzy msgid "" "Launch a container from a particular image.\n" "\n" -"lxc launch [remote:] [remote:][] [--ephemeral|-e] [--profile|-p " -"...] [--config|-c ...] [--network|-n ]\n" +"lxc launch [:] [:][] [--ephemeral|-e] [--" +"profile|-p ...] [--config|-c ...] [--network|-n " +"]\n" "\n" "Launches a container using the specified image and name.\n" "\n" @@ -574,11 +608,12 @@ "Specifying \"-p\" with no argument will result in no profile.\n" "\n" "Example:\n" -"lxc launch ubuntu:16.04 u1" +" lxc launch ubuntu:16.04 u1" msgstr "" "指定したイメージからコンテナを起動します。\n" "\n" -"lxc launch [remote:] [remote:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ]\n" +"lxc launch [remote:] [remote:][] [--ephemeral|-e] [--profile|-p " +"...] [--config|-c ...] [--network|-n ]\n" "\n" "指定したイメージと名前を使ってコンテナを起動します。\n" "\n" @@ -589,14 +624,15 @@ "lxc launch ubuntu:16.04 u1" #: lxc/info.go:25 +#, fuzzy msgid "" "List information on LXD servers and containers.\n" "\n" "For a container:\n" -" lxc info [:]container [--show-log]\n" +" lxc info [] [--show-log]\n" "\n" "For a server:\n" -" lxc info [:]" +" lxc info []" msgstr "" "LXD サーバとコンテナの情報を一覧表示します。\n" "\n" @@ -607,10 +643,12 @@ " lxc info [:]" #: lxc/list.go:68 +#, fuzzy msgid "" -"Lists the available resources.\n" +"Lists the containers.\n" "\n" -"lxc list [resource] [filters] [--format table|json] [-c columns] [--fast]\n" +"lxc list [:] [filters] [--format table|json] [-c ] [--" +"fast]\n" "\n" "The filters are:\n" "* A single keyword like \"web\" which will list any container with a name " @@ -659,20 +697,24 @@ "Default column layout: ns46tS\n" "Fast column layout: nsacPt\n" "\n" -"Example: lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile." -"eth0.hwaddr:MAC\n" +"Example:\n" +" lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile.eth0." +"hwaddr:MAC" msgstr "" "利用可能なリソースを一覧表示します。\n" "\n" "lxc list [resource] [filters] [--format table|json] [-c columns] [--fast]\n" "\n" "フィルタの指定:\n" -"* 単一の \"web\" のようなキーワードを指定すると、名前が \"web\" ではじまるコンテ\n" +"* 単一の \"web\" のようなキーワードを指定すると、名前が \"web\" ではじまるコ" +"ンテ\n" " ナが一覧表示されます。\n" "* コンテナ名の正規表現 (例: .*web.*01$)\n" -"* 設定項目のキーと値。キーの名前空間は一意に識別できる場合は短縮することがで\n" +"* 設定項目のキーと値。キーの名前空間は一意に識別できる場合は短縮することが" +"で\n" " きます:\n" -" * \"user.blah=abc\" は \"blah\" という user プロパティが \"abc\" に設定されている\n" +" * \"user.blah=abc\" は \"blah\" という user プロパティが \"abc\" に設定され" +"ている\n" " コンテナをすべて一覧表示します。\n" " * \"u.blah=abc\" は上記と同じ意味になります。\n" " * \"security.privileged=1\" は特権コンテナをすべて一覧表示します。\n" @@ -713,17 +755,18 @@ "Fast モードのカラムレイアウト: nsacPt\n" "\n" "例:\n" -"lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile.eth0.hwaddr:MAC\n" +"lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile.eth0.hwaddr:" +"MAC\n" #: lxc/info.go:239 msgid "Log:" msgstr "ログ:" -#: lxc/network.go:424 +#: lxc/network.go:423 msgid "MANAGED" msgstr "" -#: lxc/image.go:165 +#: lxc/image.go:164 msgid "Make image public" msgstr "イメージを public にする" @@ -732,25 +775,33 @@ msgstr "イメージを public にする" #: lxc/profile.go:48 +#, fuzzy msgid "" "Manage configuration profiles.\n" "\n" -"lxc profile list [filters] List available profiles.\n" -"lxc profile show Show details of a profile.\n" -"lxc profile create Create a profile.\n" -"lxc profile copy Copy the profile to the " -"specified remote.\n" -"lxc profile get Get profile configuration.\n" -"lxc profile set Set profile configuration.\n" -"lxc profile unset Unset profile configuration.\n" -"lxc profile delete Delete a profile.\n" -"lxc profile edit \n" +"lxc profile list [:] List " +"available profiles.\n" +"lxc profile show [:] Show details " +"of a profile.\n" +"lxc profile create [:] Create a " +"profile.\n" +"lxc profile copy [:] [:] Copy the " +"profile.\n" +"lxc profile get [:] Get profile " +"configuration.\n" +"lxc profile set [:] Set profile " +"configuration.\n" +"lxc profile unset [:] Unset " +"profile configuration.\n" +"lxc profile delete [:] Delete a " +"profile.\n" +"lxc profile edit [:]\n" " Edit profile, either by launching external editor or reading STDIN.\n" " Example: lxc profile edit # launch editor\n" " cat profile.yaml | lxc profile edit # read from " "profile.yaml\n" "\n" -"lxc profile assign \n" +"lxc profile assign [:] \n" " Assign a comma-separated list of profiles to a container, in order.\n" " All profiles passed in this call (and only those) will be applied\n" " to the specified container, i.e. it sets the list of profiles exactly " @@ -762,27 +813,27 @@ " lxc profile assign foo default # Only default is active\n" " lxc profile assign '' # no profiles are applied anymore\n" " lxc profile assign bar,default # Apply default second now\n" -"lxc profile add # add a profile to a container\n" -"lxc profile remove # remove the profile from a " +"lxc profile add [:] # add a profile to a " "container\n" +"lxc profile remove [:] # remove the profile " +"from a container\n" "\n" "Devices:\n" -"lxc profile device list List " -"devices in the given profile.\n" -"lxc profile device show Show " -"full device details in the given profile.\n" -"lxc profile device remove Remove a " -"device from a profile.\n" -"lxc profile device get <[remote:]profile> Get a " -"device property.\n" -"lxc profile device set <[remote:]profile> Set a " -"device property.\n" -"lxc profile device unset <[remote:]profile> Unset a " -"device property.\n" -"lxc profile device add " -"[key=value]...\n" -" Add a profile device, such as a disk or a nic, to the containers\n" -" using the specified profile." +"lxc profile device list [:] " +"List devices in the given profile.\n" +"lxc profile device show [:] " +"Show full device details in the given profile.\n" +"lxc profile device remove [:] " +"Remove a device from a profile.\n" +"lxc profile device get [:] " +"Get a device property.\n" +"lxc profile device set [:] " +"Set a device property.\n" +"lxc profile device unset [:] " +"Unset a device property.\n" +"lxc profile device add [:] [key=value...]\n" +" Add a profile device, such as a disk or a nic, to the containers using " +"the specified profile." msgstr "" "設定プロファイルを管理します。\n" "\n" @@ -805,12 +856,14 @@ "lxc profile edit \n" " プロファイルを編集します。外部エディタもしくはSTDINから読み込みます。\n" " 例: lxc profile edit # エディタの起動\n" -" cat profile.yaml | lxc profile edit # profile.yaml から読み込み\n" +" cat profile.yaml | lxc profile edit # profile.yaml から読み" +"込み\n" "\n" "lxc profile assign \n" " プロファイルのコンマ区切りのリストをコンテナに順番に割り当てます。\n" " このコマンドで指定したプロファイルだけが対象のコンテナに適用されます。\n" -" つまり、コマンドで指定したコンテナに正確にプロファイルのリストを設定します。\n" +" つまり、コマンドで指定したコンテナに正確にプロファイルのリストを設定しま" +"す。\n" " コンテナから特定のプロファイルを追加したり削除したりするには、この\n" " 後の {add|remove} を使います。\n" " 例: lxc profile assign foo default,bar # defaultとbarを適用\n" @@ -835,59 +888,68 @@ " デバイスプロパティを設定します\n" "lxc profile device unset <[remote:]profile> \n" " デバイスプロパティを削除します\n" -"lxc profile device add [key=value]...\n" +"lxc profile device add " +"[key=value]...\n" " ディスクやNICのようなプロファイルデバイスを指定したプロファイルを使って\n" " コンテナに追加します。" #: lxc/config.go:57 +#, fuzzy msgid "" "Manage configuration.\n" "\n" -"lxc config device add <[remote:]container> [key=value]... " -"Add a device to a container.\n" -"lxc config device get <[remote:]container> " -"Get a device property.\n" -"lxc config device set <[remote:]container> " -"Set a device property.\n" -"lxc config device unset <[remote:]container> " -"Unset a device property.\n" -"lxc config device list <[remote:]container> " -"List devices for container.\n" -"lxc config device show <[remote:]container> " -"Show full device details for container.\n" -"lxc config device remove <[remote:]container> " -"Remove device from container.\n" -"\n" -"lxc config get [remote:][container] " -"Get container or server configuration key.\n" -"lxc config set [remote:][container] " -"Set container or server configuration key.\n" -"lxc config unset [remote:][container] " -"Unset container or server configuration key.\n" -"lxc config show [remote:][container] [--expanded] " -"Show container or server configuration.\n" -"lxc config edit [remote:][container] " -"Edit container or server configuration in external editor.\n" +"lxc config device add [:] " +"[key=value...] Add a device to a container.\n" +"lxc config device get [:] " +" Get a device property.\n" +"lxc config device set [:] " +" Set a device property.\n" +"lxc config device unset [:] " +" Unset a device property.\n" +"lxc config device list " +"[:] List devices for " +"container.\n" +"lxc config device show " +"[:] Show full device " +"details for container.\n" +"lxc config device remove [:] " +" Remove device from container.\n" +"\n" +"lxc config get [:][container] " +" Get container or server " +"configuration key.\n" +"lxc config set [:][container] " +" Set container or server configuration " +"key.\n" +"lxc config unset [:][container] " +" Unset container or server " +"configuration key.\n" +"lxc config show [:][container] [--" +"expanded] Show container or server configuration.\n" +"lxc config edit [:]" +"[container] Edit container or server " +"configuration in external editor.\n" " Edit configuration, either by launching external editor or reading " "STDIN.\n" " Example: lxc config edit # launch editor\n" -" cat config.yaml | lxc config edit # read from config." -"yaml\n" +" cat config.yaml | lxc config edit # read from " +"config.yaml\n" "\n" -"lxc config trust list [remote] " -"List all trusted certs.\n" -"lxc config trust add [remote] " -"Add certfile.crt to trusted hosts.\n" -"lxc config trust remove [remote] [hostname|fingerprint] " -"Remove the cert from trusted hosts.\n" +"lxc config trust list " +"[:] List all trusted " +"certs.\n" +"lxc config trust add [:] Add certfile.crt to trusted hosts.\n" +"lxc config trust remove [:] [hostname|" +"fingerprint] Remove the cert from trusted hosts.\n" "\n" "Examples:\n" "To mount host's /share/c1 onto /opt in the container:\n" -" lxc config device add [remote:]container1 disk source=/" +" lxc config device add [:]container1 disk source=/" "share/c1 path=opt\n" "\n" "To set an lxc config value:\n" -" lxc config set [remote:] raw.lxc 'lxc.aa_allow_incomplete = " +" lxc config set [:] raw.lxc 'lxc.aa_allow_incomplete = " "1'\n" "\n" "To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the " @@ -924,9 +986,11 @@ " コンテナもしくはサーバの設定を表示します。\n" "lxc config edit [remote:][container]\n" " コンテナもしくはサーバの設定を外部エディタで編集します。\n" -" 設定の編集は外部エディタを起動するか、標準入力からの読み込みで行います。\n" +" 設定の編集は外部エディタを起動するか、標準入力からの読み込みで行いま" +"す。\n" " 例: lxc config edit # エディタの起動\n" -" cat config.yaml | lxc config edit # config.yamlから読み込み\n" +" cat config.yaml | lxc config edit # config.yamlから読み込" +"み\n" "\n" "lxc config trust list [remote]\n" " 信頼する証明書を全て表示します。\n" @@ -937,10 +1001,12 @@ "\n" "例:\n" "ホストの /share/c1 をコンテナ内の /opt にマウントするには:\n" -" lxc config device add [remote:]container1 disk source=/share/c1 path=opt\n" +" lxc config device add [remote:]container1 disk source=/" +"share/c1 path=opt\n" "\n" "lxc 設定項目に値を設定するには:\n" -" lxc config set [remote:] raw.lxc 'lxc.aa_allow_incomplete = 1'\n" +" lxc config set [remote:] raw.lxc 'lxc.aa_allow_incomplete = " +"1'\n" "\n" "IPv4 と IPv6 のポート 8443 で Listen するには\n" "(8443 はデフォルトなので省略できます):\n" @@ -949,30 +1015,32 @@ "サーバのパスワードを設定するには:\n" " lxc config set core.trust_password blah" -#: lxc/file.go:35 +#: lxc/file.go:36 +#, fuzzy msgid "" -"Manage files on a container.\n" +"Manage files in a container.\n" "\n" -"lxc file pull [-r|--recursive] [...] \n" +"lxc file pull [-r|--recursive] [:] " +"[[:]...] \n" "lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--" -"mode=MODE] [...] \n" -"lxc file edit \n" +"mode=MODE] [...] [:]\n" +"lxc file edit [:]/\n" "\n" " in the case of pull, in the case of push and in the " "case of edit are /\n" "\n" "Examples:\n" -"\n" "To push /etc/hosts into the container foo:\n" -" lxc file push /etc/hosts foo/etc/hosts\n" +" lxc file push /etc/hosts foo/etc/hosts\n" "\n" "To pull /etc/hosts from the container:\n" -" lxc file pull foo/etc/hosts .\n" +" lxc file pull foo/etc/hosts ." msgstr "" "コンテナ上のファイルを管理します。\n" "\n" "lxc file pull [-r|--recursive] [...] \n" -"lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--mode=MODE] [...] \n" +"lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--" +"mode=MODE] [...] \n" "lxc file edit \n" "\n" "pull の場合の 、push の場合の 、edit の場合の は、い\n" @@ -987,41 +1055,52 @@ " lxc file pull foo/etc/hosts\n" #: lxc/network.go:48 +#, fuzzy msgid "" "Manage networks.\n" "\n" -"lxc network list List available networks.\n" -"lxc network show Show details of a network.\n" -"lxc network create [key=value]... Create a network.\n" -"lxc network get Get network configuration.\n" -"lxc network set Set network configuration.\n" -"lxc network unset Unset network configuration.\n" -"lxc network delete Delete a network.\n" -"lxc network edit \n" +"lxc network list [:] List available " +"networks.\n" +"lxc network show [:] Show details of a " +"network.\n" +"lxc network create [:] [key=value...] Create a network.\n" +"lxc network get [:] Get network " +"configuration.\n" +"lxc network set [:] Set network " +"configuration.\n" +"lxc network unset [:] Unset network " +"configuration.\n" +"lxc network delete [:] Delete a network.\n" +"lxc network edit [:]\n" " Edit network, either by launching external editor or reading STDIN.\n" " Example: lxc network edit # launch editor\n" " cat network.yaml | lxc network edit # read from " "network.yaml\n" "\n" -"lxc network attach [device name]\n" -"lxc network attach-profile [device name]\n" +"lxc network attach [:] [device name]\n" +"lxc network attach-profile [:] [device name]\n" "\n" -"lxc network detach [device name]\n" -"lxc network detach-profile [device name]\n" +"lxc network detach [:] [device name]\n" +"lxc network detach-profile [:] [device name]" msgstr "" "ネットワークを管理します。<\n" "\n" -"lxc network list 利用できるネットワークを一覧します\n" -"lxc network show ネットワークの詳細を表示します\n" +"lxc network list 利用できるネットワークを一覧" +"します\n" +"lxc network show ネットワークの詳細を表示しま" +"す\n" "lxc network create [key=value]... ネットワークを作成します\n" -"lxc network get ネットワークの設定を取得します\n" +"lxc network get ネットワークの設定を取得しま" +"す\n" "lxc network set ネットワークを設定します\n" -"lxc network unset ネットワークの設定を削除します\n" +"lxc network unset ネットワークの設定を削除しま" +"す\n" "lxc network delete ネットワークを削除します\n" "lxc network edit \n" " ネットワークを編集します。外部エディタもしくはSTDINから読み込みます。\n" " 例: lxc network edit # エディタの起動\n" -" cat network.yaml | lxc network edit # network.yaml から読み込み\n" +" cat network.yaml | lxc network edit # network.yaml から読み" +"込み\n" "\n" "lxc network attach [device name]\n" "lxc network attach-profile [device name]\n" @@ -1029,23 +1108,24 @@ "lxc network detach [device name]\n" "lxc network detach-profile [device name]\n" -#: lxc/remote.go:38 +#: lxc/remote.go:37 +#, fuzzy msgid "" "Manage remote LXD servers.\n" "\n" -"lxc remote add [] [--accept-certificate] [--" +"lxc remote add [] [--accept-certificate] [--" "password=PASSWORD]\n" -" [--public] [--protocol=PROTOCOL] " +" [--public] [--protocol=PROTOCOL] " "Add the remote at .\n" -"lxc remote remove " +"lxc remote remove " "Remove the remote .\n" "lxc remote list " "List all remotes.\n" -"lxc remote rename " +"lxc remote rename " "Rename remote to .\n" -"lxc remote set-url " +"lxc remote set-url " "Update 's url to .\n" -"lxc remote set-default " +"lxc remote set-default " "Set the default remote.\n" "lxc remote get-default " "Print the default remote." @@ -1070,6 +1150,7 @@ " デフォルトに設定されているリモートホストを表示します。" #: lxc/image.go:95 +#, fuzzy msgid "" "Manipulate container images.\n" "\n" @@ -1088,22 +1169,22 @@ "hash or alias name (if one is set).\n" "\n" "\n" -"lxc image import [rootfs tarball|URL] [remote:] [--public] [--" -"created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--" -"alias=ALIAS].. [prop=value]\n" +"lxc image import [|] [:] [--public] " +"[--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] " +"[--alias=ALIAS...] [prop=value]\n" " Import an image tarball (or tarballs) into the LXD image store.\n" "\n" -"lxc image copy [remote:] : [--alias=ALIAS].. [--copy-aliases] " -"[--public] [--auto-update]\n" +"lxc image copy [:] : [--alias=ALIAS...] [--copy-" +"aliases] [--public] [--auto-update]\n" " Copy an image from one LXD daemon to another over the network.\n" "\n" " The auto-update flag instructs the server to keep this image up to\n" " date. It requires the source to be an alias and for it to be public.\n" "\n" -"lxc image delete [remote:] [remote:][...]\n" +"lxc image delete [:] [[:]...]\n" " Delete one or more images from the LXD image store.\n" "\n" -"lxc image export [remote:] [target]\n" +"lxc image export [:] [target]\n" " Export an image from the LXD image store into a distributable tarball.\n" "\n" " The output target is optional and defaults to the working directory.\n" @@ -1115,31 +1196,31 @@ " the appropriate extension will be appended to the provided file name\n" " based on the algorithm used to compress the image. \n" "\n" -"lxc image info [remote:]\n" +"lxc image info [:]\n" " Print everything LXD knows about a given image.\n" "\n" -"lxc image list [remote:] [filter] [--format table|json]\n" +"lxc image list [:] [filter] [--format table|json]\n" " List images in the LXD image store. Filters may be of the\n" " = form for property based filtering, or part of the image\n" " hash or part of the image alias name.\n" "\n" -"lxc image show [remote:]\n" +"lxc image show [:]\n" " Yaml output of the user modifiable properties of an image.\n" "\n" -"lxc image edit [remote:]\n" +"lxc image edit [:]\n" " Edit image, either by launching external editor or reading STDIN.\n" " Example: lxc image edit # launch editor\n" " cat image.yaml | lxc image edit # read from image.yaml\n" "\n" -"lxc image alias create [remote:] \n" +"lxc image alias create [:] \n" " Create a new alias for an existing image.\n" "\n" -"lxc image alias delete [remote:]\n" +"lxc image alias delete [:]\n" " Delete an alias.\n" "\n" -"lxc image alias list [remote:] [filter]\n" +"lxc image alias list [:] [filter]\n" " List the aliases. Filters may be part of the image hash or part of the " -"image alias name.\n" +"image alias name." msgstr "" "コンテナイメージを操作します。\n" "\n" @@ -1156,11 +1237,14 @@ "る場合は) エイリアスで参照できます。\n" "\n" "\n" -"lxc image import [rootfs tarball|URL] [remote:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--alias=ALIAS].. [prop=value]\n" +"lxc image import [rootfs tarball|URL] [remote:] [--public] [--" +"created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--" +"alias=ALIAS].. [prop=value]\n" " イメージの tarball (複数も可能) を LXD のイメージストアにインポートしま\n" " す。\n" "\n" -"lxc image copy [remote:] : [--alias=ALIAS].. [--copy-aliases] [--public] [--auto-update]\n" +"lxc image copy [remote:] : [--alias=ALIAS].. [--copy-aliases] " +"[--public] [--auto-update]\n" " ネットワーク経由である LXD デーモンから他の LXD デーモンへイメージを\n" " コピーします。\n" "\n" @@ -1206,7 +1290,8 @@ " エイリアスを削除します。\n" "\n" "lxc image alias list [remote:] [filter]\n" -" エイリアスを一覧表示します。イメージハッシュの一部やイメージのエイリアス\n" +" エイリアスを一覧表示します。イメージハッシュの一部やイメージのエイリア" +"ス\n" " 名の一部をフィルタとして指定できます。\n" #: lxc/info.go:161 @@ -1222,10 +1307,11 @@ msgstr "サマリーはありません。" #: lxc/monitor.go:41 +#, fuzzy msgid "" "Monitor activity on the LXD server.\n" "\n" -"lxc monitor [remote:] [--type=TYPE...]\n" +"lxc monitor [:] [--type=TYPE...]\n" "\n" "Connects to the monitoring interface of the specified LXD server.\n" "\n" @@ -1233,7 +1319,7 @@ "Specific types to listen to can be specified with --type.\n" "\n" "Example:\n" -"lxc monitor --type=logging" +" lxc monitor --type=logging" msgstr "" "LXD サーバの動作をモニタリングします。\n" "\n" @@ -1247,26 +1333,30 @@ "例:\n" "lxc monitor --type=logging" -#: lxc/network.go:216 lxc/network.go:265 +#: lxc/network.go:215 lxc/network.go:264 msgid "More than one device matches, specify the device name." msgstr "複数のデバイスとマッチします。デバイス名を指定してください。" -#: lxc/file.go:235 +#: lxc/file.go:237 msgid "More than one file to download, but target is not a directory" msgstr "" "ダウンロード対象のファイルが複数ありますが、コピー先がディレクトリではありま" "せん。" #: lxc/move.go:16 +#, fuzzy msgid "" "Move containers within or in between lxd instances.\n" "\n" -"lxc move [remote:] [remote:]\n" +"lxc move [:] [:][]\n" " Move a container between two hosts, renaming it if destination name " "differs.\n" "\n" "lxc move \n" " Rename a local container.\n" +"\n" +"lxc move / /\n" +" Rename a snapshot." msgstr "" "LXD ホスト内、もしくは LXD ホスト間でコンテナを移動します。\n" "\n" @@ -1281,11 +1371,11 @@ msgid "Must supply container name for: " msgstr "コンテナ名を指定する必要があります: " -#: lxc/list.go:428 lxc/network.go:422 lxc/profile.go:447 lxc/remote.go:381 +#: lxc/list.go:428 lxc/network.go:421 lxc/profile.go:446 lxc/remote.go:380 msgid "NAME" msgstr "" -#: lxc/network.go:408 lxc/remote.go:355 lxc/remote.go:360 +#: lxc/network.go:407 lxc/remote.go:354 lxc/remote.go:359 msgid "NO" msgstr "" @@ -1294,12 +1384,12 @@ msgid "Name: %s" msgstr "コンテナ名: %s" -#: lxc/network.go:190 +#: lxc/network.go:189 #, c-format msgid "Network %s created" msgstr "ネットワーク %s を作成しました" -#: lxc/network.go:293 +#: lxc/network.go:292 #, c-format msgid "Network %s deleted" msgstr "ネットワーク %s を削除しました" @@ -1308,7 +1398,7 @@ msgid "Network name" msgstr "ネットワーク名:" -#: lxc/image.go:168 lxc/publish.go:34 +#: lxc/image.go:167 lxc/publish.go:34 msgid "New alias to define at target" msgstr "新しいエイリアスを定義する" @@ -1316,7 +1406,7 @@ msgid "No certificate provided to add" msgstr "追加すべき証明書が提供されていません" -#: lxc/network.go:225 lxc/network.go:274 +#: lxc/network.go:224 lxc/network.go:273 msgid "No device found for this network" msgstr "このネットワークに対するデバイスがありません" @@ -1324,11 +1414,11 @@ msgid "No fingerprint specified." msgstr "フィンガープリントが指定されていません。" -#: lxc/remote.go:121 +#: lxc/remote.go:120 msgid "Only https URLs are supported for simplestreams" msgstr "simplestreams は https の URL のみサポートします" -#: lxc/image.go:434 +#: lxc/image.go:424 msgid "Only https:// is supported for remote image import." msgstr "リモートイメージのインポートは https:// のみをサポートします。" @@ -1336,7 +1426,7 @@ msgid "Options:" msgstr "オプション:" -#: lxc/image.go:538 +#: lxc/image.go:535 #, c-format msgid "Output is in %s" msgstr "%s に出力されます" @@ -1357,11 +1447,11 @@ msgid "PROFILES" msgstr "" -#: lxc/remote.go:383 +#: lxc/remote.go:382 msgid "PROTOCOL" msgstr "" -#: lxc/image.go:640 lxc/remote.go:384 +#: lxc/image.go:637 lxc/remote.go:383 msgid "PUBLIC" msgstr "" @@ -1374,11 +1464,13 @@ msgstr "送信パケット" #: lxc/help.go:70 -msgid "Path to an alternate client configuration directory." +#, fuzzy +msgid "Path to an alternate client configuration directory" msgstr "別のクライアント用設定ディレクトリ" #: lxc/help.go:71 -msgid "Path to an alternate server directory." +#, fuzzy +msgid "Path to an alternate server directory" msgstr "別のサーバ用設定ディレクトリ" #: lxc/main.go:31 @@ -1390,40 +1482,34 @@ msgid "Pid: %d" msgstr "" -#: lxc/help.go:25 -msgid "" -"Presents details on how to use LXD.\n" -"\n" -"lxd help [--all]" -msgstr "" -"LXDの使い方の詳細を表示します。\n" -"\n" -"lxd help [--all]" - -#: lxc/network.go:343 lxc/profile.go:219 +#: lxc/network.go:342 lxc/profile.go:218 msgid "Press enter to open the editor again" msgstr "再度エディタを開くためには Enter キーを押します" -#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:735 +#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:732 msgid "Press enter to start the editor again" msgstr "再度エディタを起動するには Enter キーを押します" +#: lxc/manpage.go:18 +#, fuzzy +msgid "Print all the subcommands help." +msgstr "全てのサブコマンドのヘルプを表示します。" + #: lxc/help.go:65 -msgid "Print debug information." +#, fuzzy +msgid "Print debug information" msgstr "デバッグ情報を表示します。" #: lxc/help.go:64 -msgid "Print less common commands." +#, fuzzy +msgid "Print less common commands" msgstr "全てのコマンドを表示します (主なコマンドだけではなく)。" #: lxc/help.go:66 -msgid "Print verbose information." +#, fuzzy +msgid "Print verbose information" msgstr "詳細情報を表示します。" -#: lxc/manpage.go:18 -msgid "Prints all the subcommands help." -msgstr "全てのサブコマンドのヘルプを表示します。" - #: lxc/version.go:18 msgid "" "Prints the version number of this client tool.\n" @@ -1439,22 +1525,22 @@ msgid "Processes: %d" msgstr "プロセス数: %d" -#: lxc/profile.go:275 +#: lxc/profile.go:274 #, c-format msgid "Profile %s added to %s" msgstr "プロファイル %s が %s に追加されました" -#: lxc/profile.go:170 +#: lxc/profile.go:169 #, c-format msgid "Profile %s created" msgstr "プロファイル %s を作成しました" -#: lxc/profile.go:240 +#: lxc/profile.go:239 #, c-format msgid "Profile %s deleted" msgstr "プロファイル %s を削除しました" -#: lxc/profile.go:306 +#: lxc/profile.go:305 #, c-format msgid "Profile %s removed from %s" msgstr "プロファイル %s が %s から削除されました" @@ -1464,7 +1550,7 @@ msgid "Profile to apply to the new container" msgstr "新しいコンテナに適用するプロファイル" -#: lxc/profile.go:256 +#: lxc/profile.go:255 #, c-format msgid "Profiles %s applied to %s" msgstr "プロファイル %s が %s に追加されました" @@ -1474,36 +1560,37 @@ msgid "Profiles: %s" msgstr "プロファイル: %s" -#: lxc/image.go:361 +#: lxc/image.go:358 msgid "Properties:" msgstr "プロパティ:" -#: lxc/remote.go:55 +#: lxc/remote.go:54 msgid "Public image server" msgstr "Public なイメージサーバとして設定します" -#: lxc/image.go:349 +#: lxc/image.go:346 #, c-format msgid "Public: %s" msgstr "" #: lxc/publish.go:26 +#, fuzzy msgid "" "Publish containers as images.\n" "\n" -"lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-" -"value]..." +"lxc publish [:][/] [:] [--" +"alias=ALIAS...] [prop-key=prop-value...]" msgstr "" "イメージとしてコンテナを publish します。\n" "\n" "lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-" "value]..." -#: lxc/file.go:58 lxc/file.go:59 +#: lxc/file.go:57 lxc/file.go:58 msgid "Recursively push or pull files" msgstr "再帰的にファイルをpush/pullします" -#: lxc/remote.go:53 +#: lxc/remote.go:52 msgid "Remote admin password" msgstr "リモートの管理者パスワード" @@ -1518,19 +1605,48 @@ msgstr "%s を消去しますか (yes/no): " #: lxc/delete.go:36 lxc/delete.go:37 -msgid "Require user confirmation." +#, fuzzy +msgid "Require user confirmation" msgstr "ユーザの確認を要求する。" #: lxc/info.go:127 msgid "Resources:" msgstr "リソース:" -#: lxc/init.go:267 +#: lxc/restore.go:21 +#, fuzzy +msgid "" +"Restore a container's state to a previous snapshot.\n" +"\n" +"lxc restore [:] [--stateful]\n" +"\n" +"Restores a container from a snapshot (optionally with running state, see\n" +"snapshot help for details).\n" +"\n" +"Examples:\n" +"Create the snapshot:\n" +" lxc snapshot u1 snap0\n" +"\n" +"Restore the snapshot:\n" +" lxc restore u1 snap0" +msgstr "" +"リソースの現在の状態をスナップショット時点の状態に設定します。\n" +"\n" +"lxc restore [remote:] [--stateful]\n" +"\n" +"スナップショットからコンテナをリストアします (オプションで実行状態もリスト\n" +"アします。詳しくはスナップショットのヘルプをご覧ください)。\n" +"\n" +"例:\n" +"lxc snapshot u1 snap0 # スナップショットの作成\n" +"lxc restore u1 snap0 # スナップショットからリストア" + +#: lxc/init.go:239 #, c-format msgid "Retrieving image: %s" msgstr "イメージの取得中: %s" -#: lxc/image.go:643 +#: lxc/image.go:640 msgid "SIZE" msgstr "" @@ -1542,55 +1658,31 @@ msgid "STATE" msgstr "" -#: lxc/remote.go:385 +#: lxc/remote.go:384 msgid "STATIC" msgstr "" -#: lxc/remote.go:226 +#: lxc/remote.go:225 msgid "Server certificate NACKed by user" msgstr "ユーザによりサーバ証明書が拒否されました" -#: lxc/remote.go:288 +#: lxc/remote.go:287 msgid "Server doesn't trust us after adding our cert" msgstr "サーバが我々の証明書を追加した後我々を信頼していません" -#: lxc/remote.go:54 +#: lxc/remote.go:53 msgid "Server protocol (lxd or simplestreams)" msgstr "サーバのプロトコル (lxd or simplestreams)" -#: lxc/restore.go:21 -msgid "" -"Set the current state of a resource back to a snapshot.\n" -"\n" -"lxc restore [remote:] [--stateful]\n" -"\n" -"Restores a container from a snapshot (optionally with running state, see\n" -"snapshot help for details).\n" -"\n" -"For example:\n" -"lxc snapshot u1 snap0 # create the snapshot\n" -"lxc restore u1 snap0 # restore the snapshot" -msgstr "" -"リソースの現在の状態をスナップショット時点の状態に設定します。\n" -"\n" -"lxc restore [remote:] [--stateful]\n" -"\n" -"スナップショットからコンテナをリストアします (オプションで実行状態もリスト\n" -"アします。詳しくはスナップショットのヘルプをご覧ください)。\n" -"\n" -"例:\n" -"lxc snapshot u1 snap0 # スナップショットの作成\n" -"lxc restore u1 snap0 # スナップショットからリストア" - -#: lxc/file.go:56 +#: lxc/file.go:55 msgid "Set the file's gid on push" msgstr "プッシュ時にファイルのgidを設定します" -#: lxc/file.go:57 +#: lxc/file.go:56 msgid "Set the file's perms on push" msgstr "プッシュ時にファイルのパーミションを設定します" -#: lxc/file.go:55 +#: lxc/file.go:54 msgid "Set the file's uid on push" msgstr "プッシュ時にファイルのuidを設定します" @@ -1599,14 +1691,20 @@ msgstr "全てコマンドを表示します (主なコマンドだけではなく)" #: lxc/help.go:67 -msgid "Show client version." +#, fuzzy +msgid "Show client version" msgstr "クライアントのバージョンを表示します。" #: lxc/info.go:36 msgid "Show the container's last 100 log lines?" msgstr "コンテナログの最後の 100 行を表示しますか?" -#: lxc/image.go:347 +#: lxc/config.go:32 +#, fuzzy +msgid "Show the expanded configuration" +msgstr "拡張した設定を表示するかどうか" + +#: lxc/image.go:344 #, c-format msgid "Size: %.2fMB" msgstr "サイズ: %.2fMB" @@ -1615,11 +1713,11 @@ msgid "Snapshots:" msgstr "スナップショット:" -#: lxc/image.go:371 +#: lxc/image.go:368 msgid "Source:" msgstr "取得元:" -#: lxc/launch.go:142 +#: lxc/launch.go:144 #, c-format msgid "Starting %s" msgstr "%s を起動中" @@ -1638,7 +1736,8 @@ msgstr "コンテナの停止に失敗しました!" #: lxc/action.go:45 -msgid "Store the container state (only for stop)." +#, fuzzy +msgid "Store the container state (only for stop)" msgstr "コンテナの状態を保存します (stopのみ)。" #: lxc/info.go:169 @@ -1649,7 +1748,7 @@ msgid "Swap (peak)" msgstr "Swap (ピーク)" -#: lxc/list.go:433 lxc/network.go:423 +#: lxc/list.go:433 lxc/network.go:422 msgid "TYPE" msgstr "" @@ -1665,8 +1764,9 @@ "コンテナは現在実行中です。停止して、再起動するために --force を使用してくだ\n" "さい。" -#: lxc/init.go:313 -msgid "The container you are starting doesn’t have any network attached to it." +#: lxc/init.go:312 +#, fuzzy +msgid "The container you are starting doesn't have any network attached to it." msgstr "起動しようとしたコンテナに接続されているネットワークがありません。" #: lxc/config.go:675 lxc/config.go:687 lxc/config.go:720 lxc/config.go:738 @@ -1674,7 +1774,7 @@ msgid "The device doesn't exist" msgstr "デバイスが存在しません" -#: lxc/init.go:297 +#: lxc/init.go:296 #, c-format msgid "The local image '%s' couldn't be found, trying '%s:' instead." msgstr "" @@ -1684,11 +1784,11 @@ msgid "The opposite of `lxc pause` is `lxc start`." msgstr "`lxc pause` の反対のコマンドは `lxc start` です。" -#: lxc/network.go:230 lxc/network.go:279 +#: lxc/network.go:229 lxc/network.go:278 msgid "The specified device doesn't exist" msgstr "指定したデバイスが存在しません" -#: lxc/network.go:234 lxc/network.go:283 +#: lxc/network.go:233 lxc/network.go:282 msgid "The specified device doesn't match the network" msgstr "指定したデバイスはネットワークとマッチしません" @@ -1700,20 +1800,23 @@ "さい。" #: lxc/action.go:41 -msgid "Time to wait for the container before killing it." +#, fuzzy +msgid "Time to wait for the container before killing it" msgstr "コンテナを強制停止するまでの時間" -#: lxc/image.go:350 +#: lxc/image.go:347 msgid "Timestamps:" msgstr "タイムスタンプ:" -#: lxc/init.go:315 +#: lxc/init.go:314 msgid "To attach a network to a container, use: lxc network attach" -msgstr "コンテナにネットワークを接続するには、lxc network attach を使用してください。" +msgstr "" +"コンテナにネットワークを接続するには、lxc network attach を使用してください。" -#: lxc/init.go:314 +#: lxc/init.go:313 msgid "To create a new network, use: lxc network create" -msgstr "新しいネットワークを作成するには、lxc network create を使用してください。" +msgstr "" +"新しいネットワークを作成するには、lxc network create を使用してください。" #: lxc/main.go:137 msgid "To start your first container, try: lxc launch ubuntu:16.04" @@ -1722,12 +1825,12 @@ "だ\n" "さい。" -#: lxc/image.go:421 -#, c-format -msgid "Transferring image: %d%%" +#: lxc/image.go:426 +#, fuzzy, c-format +msgid "Transferring image: %s" msgstr "イメージを転送中: %d%%" -#: lxc/action.go:99 lxc/launch.go:155 +#: lxc/action.go:99 lxc/launch.go:157 #, c-format msgid "Try `lxc info --show-log %s` for more info" msgstr "更に情報を得るために `lxc info --show-log %s` を実行してみてください" @@ -1740,23 +1843,23 @@ msgid "Type: persistent" msgstr "タイプ: persistent" -#: lxc/image.go:644 +#: lxc/image.go:641 msgid "UPLOAD DATE" msgstr "" -#: lxc/remote.go:382 +#: lxc/remote.go:381 msgid "URL" msgstr "" -#: lxc/network.go:425 lxc/profile.go:448 +#: lxc/network.go:424 lxc/profile.go:447 msgid "USED BY" msgstr "" -#: lxc/remote.go:96 +#: lxc/remote.go:95 msgid "Unable to read remote TLS certificate" msgstr "リモートの TLS 証明書を読めません" -#: lxc/image.go:355 +#: lxc/image.go:352 #, c-format msgid "Uploaded: %s" msgstr "アップロード日時: %s" @@ -1767,14 +1870,15 @@ msgstr "使い方: %s" #: lxc/help.go:48 -msgid "Usage: lxc [subcommand] [options]" +#, fuzzy +msgid "Usage: lxc [options]" msgstr "使い方: lxc [サブコマンド] [オプション]" #: lxc/delete.go:46 msgid "User aborted delete operation." msgstr "ユーザが削除操作を中断しました。" -#: lxc/restore.go:35 +#: lxc/restore.go:38 msgid "" "Whether or not to restore the container's running state from snapshot (if " "available)" @@ -1785,11 +1889,7 @@ msgid "Whether or not to snapshot the container's running state" msgstr "コンテナの稼動状態のスナップショットを取得するかどうか" -#: lxc/config.go:32 -msgid "Whether to show the expanded configuration" -msgstr "拡張した設定を表示するかどうか" - -#: lxc/network.go:410 lxc/remote.go:357 lxc/remote.go:362 +#: lxc/network.go:409 lxc/remote.go:356 lxc/remote.go:361 msgid "YES" msgstr "" @@ -1797,7 +1897,7 @@ msgid "`lxc config profile` is deprecated, please use `lxc profile`" msgstr "`lxc config profile` は廃止されました。`lxc profile` を使ってください" -#: lxc/launch.go:127 +#: lxc/launch.go:128 msgid "bad number of things scanned from image, container or snapshot" msgstr "" "イメージ、コンテナ、スナップショットのいずれかからスキャンされた数が不正" @@ -1810,34 +1910,35 @@ msgid "can't copy to the same container name" msgstr "同じコンテナ名へはコピーできません" -#: lxc/file.go:270 +#: lxc/file.go:272 msgid "can't pull a directory without --recursive" -msgstr "ディレクトリを pull する場合は --recursive オプションを使用してください。" +msgstr "" +"ディレクトリを pull する場合は --recursive オプションを使用してください。" -#: lxc/remote.go:345 +#: lxc/remote.go:344 msgid "can't remove the default remote" msgstr "デフォルトのリモートは削除できません" -#: lxc/file.go:117 +#: lxc/file.go:119 msgid "can't supply uid/gid/mode in recursive mode" msgstr "再帰 (recursive) モードでは uid/gid/mode を指定できません。" -#: lxc/remote.go:371 +#: lxc/remote.go:370 msgid "default" msgstr "" #: lxc/copy.go:131 lxc/copy.go:136 lxc/copy.go:232 lxc/copy.go:237 -#: lxc/init.go:217 lxc/init.go:222 lxc/launch.go:111 lxc/launch.go:116 +#: lxc/init.go:219 lxc/init.go:224 lxc/launch.go:112 lxc/launch.go:117 msgid "didn't get any affected image, container or snapshot from server" msgstr "" "サーバから変更されたイメージ、コンテナ、スナップショットを取得できませんで\n" "した" -#: lxc/image.go:341 +#: lxc/image.go:338 msgid "disabled" msgstr "無効" -#: lxc/image.go:343 +#: lxc/image.go:340 msgid "enabled" msgstr "有効" @@ -1851,11 +1952,11 @@ msgid "error: unknown command: %s" msgstr "エラー: 未知のコマンド: %s" -#: lxc/launch.go:131 +#: lxc/launch.go:132 msgid "got bad version" msgstr "不正なバージョンを得ました" -#: lxc/image.go:336 lxc/image.go:620 +#: lxc/image.go:333 lxc/image.go:617 msgid "no" msgstr "" @@ -1863,7 +1964,7 @@ msgid "not all the profiles from the source exist on the target" msgstr "コピー元の全てのプロファイルがターゲットに存在しません" -#: lxc/remote.go:219 +#: lxc/remote.go:218 msgid "ok (y/n)?" msgstr "ok (y/n)?" @@ -1872,26 +1973,26 @@ msgid "processing aliases failed %s\n" msgstr "エイリアスの処理が失敗しました %s\n" -#: lxc/file.go:311 +#: lxc/file.go:313 msgid "recursive edit doesn't make sense :(" msgstr "再帰的な edit は意味がありません :(" -#: lxc/remote.go:407 +#: lxc/remote.go:406 #, c-format msgid "remote %s already exists" msgstr "リモート %s は既に存在します" -#: lxc/remote.go:337 lxc/remote.go:399 lxc/remote.go:434 lxc/remote.go:450 +#: lxc/remote.go:336 lxc/remote.go:398 lxc/remote.go:433 lxc/remote.go:449 #, c-format msgid "remote %s doesn't exist" msgstr "リモート %s は存在しません" -#: lxc/remote.go:320 +#: lxc/remote.go:319 #, c-format msgid "remote %s exists as <%s>" msgstr "リモート %s は <%s> として存在します" -#: lxc/remote.go:341 lxc/remote.go:403 lxc/remote.go:438 +#: lxc/remote.go:340 lxc/remote.go:402 lxc/remote.go:437 #, c-format msgid "remote %s is static and cannot be modified" msgstr "リモート %s は static ですので変更できません" @@ -1917,7 +2018,7 @@ msgid "wrong number of subcommand arguments" msgstr "サブコマンドの引数の数が正しくありません" -#: lxc/delete.go:45 lxc/image.go:338 lxc/image.go:624 +#: lxc/delete.go:45 lxc/image.go:335 lxc/image.go:621 msgid "yes" msgstr "" diff -Nru lxd-2.6.2/po/lxd.pot lxd-2.7/po/lxd.pot --- lxd-2.6.2/po/lxd.pot 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/po/lxd.pot 2016-12-21 00:52:23.000000000 +0000 @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: lxd\n" "Report-Msgid-Bugs-To: lxc-devel@lists.linuxcontainers.org\n" - "POT-Creation-Date: 2016-11-24 20:15-0500\n" + "POT-Creation-Date: 2016-12-20 17:29+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -100,7 +100,7 @@ "### Note that the name is shown but cannot be changed" msgstr "" -#: lxc/image.go:615 +#: lxc/image.go:614 #, c-format msgid "%s (%d more)" msgstr "" @@ -109,15 +109,15 @@ msgid "'/' not allowed in snapshot name" msgstr "" -#: lxc/profile.go:254 +#: lxc/profile.go:253 msgid "(none)" msgstr "" -#: lxc/image.go:636 lxc/image.go:678 +#: lxc/image.go:635 lxc/image.go:677 msgid "ALIAS" msgstr "" -#: lxc/image.go:640 +#: lxc/image.go:639 msgid "ARCH" msgstr "" @@ -125,29 +125,25 @@ msgid "ARCHITECTURE" msgstr "" -#: lxc/remote.go:52 +#: lxc/remote.go:51 msgid "Accept certificate" msgstr "" -#: lxc/remote.go:268 +#: lxc/remote.go:267 #, c-format msgid "Admin password for %s: " msgstr "" -#: lxc/image.go:363 +#: lxc/image.go:362 msgid "Aliases:" msgstr "" -#: lxc/exec.go:54 -msgid "An environment variable of the form HOME=/home/foo" -msgstr "" - -#: lxc/image.go:346 lxc/info.go:93 +#: lxc/image.go:345 lxc/info.go:93 #, c-format msgid "Architecture: %s" msgstr "" -#: lxc/image.go:367 +#: lxc/image.go:366 #, c-format msgid "Auto update: %s" msgstr "" @@ -186,29 +182,29 @@ msgid "Can't unset key '%s', it's not currently set." msgstr "" -#: lxc/network.go:386 lxc/profile.go:420 +#: lxc/network.go:385 lxc/profile.go:419 msgid "Cannot provide container name to list" msgstr "" -#: lxc/remote.go:218 +#: lxc/remote.go:217 #, c-format msgid "Certificate fingerprint: %x" msgstr "" #: lxc/action.go:33 #, c-format -msgid "Changes state of one or more containers to %s.\n" +msgid "Change state of one or more containers to %s.\n" "\n" - "lxc %s [...]%s" + "lxc %s [:] [[:]...]%s" msgstr "" #: lxc/finger.go:15 msgid "Check if the LXD instance is up.\n" "\n" - "lxc finger " + "lxc finger [:]" msgstr "" -#: lxc/remote.go:291 +#: lxc/remote.go:290 msgid "Client certificate stored at server: " msgstr "" @@ -216,11 +212,11 @@ msgid "Columns" msgstr "" -#: lxc/copy.go:31 lxc/copy.go:32 lxc/init.go:135 lxc/init.go:136 lxc/launch.go:40 lxc/launch.go:41 +#: lxc/copy.go:31 lxc/copy.go:32 lxc/init.go:135 lxc/init.go:136 msgid "Config key/value to apply to the new container" msgstr "" -#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:732 lxc/network.go:342 lxc/profile.go:218 +#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:731 lxc/network.go:341 lxc/profile.go:217 #, c-format msgid "Config parsing error: %s" msgstr "" @@ -243,29 +239,34 @@ msgid "Container published with fingerprint: %s" msgstr "" -#: lxc/image.go:166 +#: lxc/image.go:165 msgid "Copy aliases from source" msgstr "" #: lxc/copy.go:24 -msgid "Copy containers within or in between lxd instances.\n" +msgid "Copy containers within or in between LXD instances.\n" "\n" - "lxc copy [remote:] [[remote:]] [--ephemeral|e] [--profile|-p ...] [--config|-c ...]" + "lxc copy [:][/] [[:]] [--ephemeral|e] [--profile|-p ...] [--config|-c ...]" msgstr "" -#: lxc/image.go:279 +#: lxc/image.go:278 #, c-format msgid "Copying the image: %s" msgstr "" -#: lxc/remote.go:233 +#: lxc/remote.go:232 msgid "Could not create server cert dir" msgstr "" +#: lxc/file.go:83 +#, c-format +msgid "Could not sanitize path %s" +msgstr "" + #: lxc/snapshot.go:21 msgid "Create a read-only snapshot of a container.\n" "\n" - "lxc snapshot [remote:] [--stateful]\n" + "lxc snapshot [:] [--stateful]\n" "\n" "Creates a snapshot of the container (optionally with the container's memory\n" "state). When --stateful is used, LXD attempts to checkpoint the container's\n" @@ -275,19 +276,19 @@ "successfully).\n" "\n" "Example:\n" - "lxc snapshot u1 snap0" + " lxc snapshot u1 snap0" msgstr "" -#: lxc/file.go:60 lxc/file.go:61 +#: lxc/file.go:59 lxc/file.go:60 msgid "Create any directories necessary" msgstr "" -#: lxc/image.go:351 lxc/info.go:95 +#: lxc/image.go:350 lxc/info.go:95 #, c-format msgid "Created: %s" msgstr "" -#: lxc/init.go:180 lxc/launch.go:135 +#: lxc/init.go:180 lxc/launch.go:126 #, c-format msgid "Creating %s" msgstr "" @@ -296,7 +297,7 @@ msgid "Creating the container" msgstr "" -#: lxc/image.go:639 lxc/image.go:680 +#: lxc/image.go:638 lxc/image.go:679 msgid "DESCRIPTION" msgstr "" @@ -307,7 +308,7 @@ #: lxc/delete.go:25 msgid "Delete containers or snapshots.\n" "\n" - "lxc delete [remote:][/] [remote:][[/]...]\n" + "lxc delete [:][/] [[:][/]...]\n" "\n" "Destroy containers or snapshots with any attached data (configuration, snapshots, ...)." msgstr "" @@ -331,18 +332,22 @@ msgstr "" #: lxc/main.go:41 -msgid "Enables debug mode." +msgid "Enable debug mode" msgstr "" #: lxc/main.go:40 -msgid "Enables verbose mode." +msgid "Enable verbose mode" +msgstr "" + +#: lxc/exec.go:54 +msgid "Environment variable to set (e.g. HOME=/home/foo)" msgstr "" #: lxc/help.go:69 msgid "Environment:" msgstr "" -#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:139 lxc/init.go:140 lxc/launch.go:44 lxc/launch.go:45 +#: lxc/copy.go:35 lxc/copy.go:36 lxc/init.go:139 lxc/init.go:140 msgid "Ephemeral container" msgstr "" @@ -353,21 +358,21 @@ #: lxc/exec.go:45 msgid "Execute the specified command in a container.\n" "\n" - "lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... [--] \n" + "lxc exec [:] [--mode=auto|interactive|non-interactive] [--env KEY=VALUE...] [--] \n" "\n" "Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored)." msgstr "" -#: lxc/image.go:355 +#: lxc/image.go:354 #, c-format msgid "Expires: %s" msgstr "" -#: lxc/image.go:357 +#: lxc/image.go:356 msgid "Expires: never" msgstr "" -#: lxc/config.go:272 lxc/image.go:637 lxc/image.go:679 +#: lxc/config.go:272 lxc/image.go:636 lxc/image.go:678 msgid "FINGERPRINT" msgstr "" @@ -375,31 +380,37 @@ msgid "Fast mode (same as --columns=nsacPt" msgstr "" -#: lxc/image.go:344 +#: lxc/image.go:343 #, c-format msgid "Fingerprint: %s" msgstr "" #: lxc/action.go:42 lxc/action.go:43 -msgid "Force the container to shutdown." +msgid "Force the container to shutdown" msgstr "" #: lxc/delete.go:34 lxc/delete.go:35 -msgid "Force the removal of stopped containers." +msgid "Force the removal of stopped containers" msgstr "" #: lxc/main.go:42 -msgid "Force using the local unix socket." +msgid "Force using the local unix socket" msgstr "" -#: lxc/image.go:169 lxc/list.go:123 +#: lxc/image.go:168 lxc/list.go:123 msgid "Format" msgstr "" -#: lxc/remote.go:66 +#: lxc/remote.go:65 msgid "Generating a client certificate. This may take a minute..." msgstr "" +#: lxc/help.go:25 +msgid "Help page for the LXD client.\n" + "\n" + "lxc help [--all]" +msgstr "" + #: lxc/list.go:423 msgid "IPV4" msgstr "" @@ -417,23 +428,23 @@ msgstr "" #: lxc/main.go:43 -msgid "Ignore aliases when determining what command to run." +msgid "Ignore aliases when determining what command to run" msgstr "" #: lxc/action.go:46 -msgid "Ignore the container state (only for start)." +msgid "Ignore the container state (only for start)" msgstr "" -#: lxc/image.go:282 +#: lxc/image.go:281 msgid "Image copied successfully!" msgstr "" -#: lxc/image.go:422 lxc/image.go:434 +#: lxc/image.go:421 lxc/image.go:433 #, c-format msgid "Image imported with fingerprint: %s" msgstr "" -#: lxc/image.go:419 +#: lxc/image.go:418 #, c-format msgid "Importing the image: %s" msgstr "" @@ -441,7 +452,7 @@ #: lxc/init.go:74 msgid "Initialize a container from a particular image.\n" "\n" - "lxc init [remote:] [remote:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ]\n" + "lxc init [:] [:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ]\n" "\n" "Initializes a container using the specified image and name.\n" "\n" @@ -449,10 +460,10 @@ "Specifying \"-p\" with no argument will result in no profile.\n" "\n" "Example:\n" - "lxc init ubuntu u1" + " lxc init ubuntu:16.04 u1" msgstr "" -#: lxc/remote.go:136 +#: lxc/remote.go:135 #, c-format msgid "Invalid URL scheme \"%s\" in \"%s\"" msgstr "" @@ -465,12 +476,12 @@ msgid "Invalid configuration key" msgstr "" -#: lxc/file.go:247 +#: lxc/file.go:249 #, c-format msgid "Invalid source %s" msgstr "" -#: lxc/file.go:73 +#: lxc/file.go:72 #, c-format msgid "Invalid target %s" msgstr "" @@ -479,7 +490,7 @@ msgid "Ips:" msgstr "" -#: lxc/image.go:167 +#: lxc/image.go:166 msgid "Keep the image up to date after initial copy" msgstr "" @@ -494,7 +505,7 @@ #: lxc/launch.go:22 msgid "Launch a container from a particular image.\n" "\n" - "lxc launch [remote:] [remote:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ]\n" + "lxc launch [:] [:][] [--ephemeral|-e] [--profile|-p ...] [--config|-c ...] [--network|-n ]\n" "\n" "Launches a container using the specified image and name.\n" "\n" @@ -502,23 +513,23 @@ "Specifying \"-p\" with no argument will result in no profile.\n" "\n" "Example:\n" - "lxc launch ubuntu:16.04 u1" + " lxc launch ubuntu:16.04 u1" msgstr "" #: lxc/info.go:25 msgid "List information on LXD servers and containers.\n" "\n" "For a container:\n" - " lxc info [:]container [--show-log]\n" + " lxc info [] [--show-log]\n" "\n" "For a server:\n" - " lxc info [:]" + " lxc info []" msgstr "" #: lxc/list.go:68 -msgid "Lists the available resources.\n" +msgid "Lists the containers.\n" "\n" - "lxc list [resource] [filters] [--format table|json] [-c columns] [--fast]\n" + "lxc list [:] [filters] [--format table|json] [-c ] [--fast]\n" "\n" "The filters are:\n" "* A single keyword like \"web\" which will list any container with a name starting by \"web\".\n" @@ -563,18 +574,19 @@ "Default column layout: ns46tS\n" "Fast column layout: nsacPt\n" "\n" - "Example: lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile.eth0.hwaddr:MAC\n" + "Example:\n" + " lxc list -c n,volatile.base_image:\"BASE IMAGE\":0,s46,volatile.eth0.hwaddr:MAC" msgstr "" #: lxc/info.go:239 msgid "Log:" msgstr "" -#: lxc/network.go:424 +#: lxc/network.go:423 msgid "MANAGED" msgstr "" -#: lxc/image.go:165 +#: lxc/image.go:164 msgid "Make image public" msgstr "" @@ -585,20 +597,20 @@ #: lxc/profile.go:48 msgid "Manage configuration profiles.\n" "\n" - "lxc profile list [filters] List available profiles.\n" - "lxc profile show Show details of a profile.\n" - "lxc profile create Create a profile.\n" - "lxc profile copy Copy the profile to the specified remote.\n" - "lxc profile get Get profile configuration.\n" - "lxc profile set Set profile configuration.\n" - "lxc profile unset Unset profile configuration.\n" - "lxc profile delete Delete a profile.\n" - "lxc profile edit \n" + "lxc profile list [:] List available profiles.\n" + "lxc profile show [:] Show details of a profile.\n" + "lxc profile create [:] Create a profile.\n" + "lxc profile copy [:] [:] Copy the profile.\n" + "lxc profile get [:] Get profile configuration.\n" + "lxc profile set [:] Set profile configuration.\n" + "lxc profile unset [:] Unset profile configuration.\n" + "lxc profile delete [:] Delete a profile.\n" + "lxc profile edit [:]\n" " Edit profile, either by launching external editor or reading STDIN.\n" " Example: lxc profile edit # launch editor\n" " cat profile.yaml | lxc profile edit # read from profile.yaml\n" "\n" - "lxc profile assign \n" + "lxc profile assign [:] \n" " Assign a comma-separated list of profiles to a container, in order.\n" " All profiles passed in this call (and only those) will be applied\n" " to the specified container, i.e. it sets the list of profiles exactly to\n" @@ -608,51 +620,50 @@ " lxc profile assign foo default # Only default is active\n" " lxc profile assign '' # no profiles are applied anymore\n" " lxc profile assign bar,default # Apply default second now\n" - "lxc profile add # add a profile to a container\n" - "lxc profile remove # remove the profile from a container\n" + "lxc profile add [:] # add a profile to a container\n" + "lxc profile remove [:] # remove the profile from a container\n" "\n" "Devices:\n" - "lxc profile device list List devices in the given profile.\n" - "lxc profile device show Show full device details in the given profile.\n" - "lxc profile device remove Remove a device from a profile.\n" - "lxc profile device get <[remote:]profile> Get a device property.\n" - "lxc profile device set <[remote:]profile> Set a device property.\n" - "lxc profile device unset <[remote:]profile> Unset a device property.\n" - "lxc profile device add [key=value]...\n" - " Add a profile device, such as a disk or a nic, to the containers\n" - " using the specified profile." + "lxc profile device list [:] List devices in the given profile.\n" + "lxc profile device show [:] Show full device details in the given profile.\n" + "lxc profile device remove [:] Remove a device from a profile.\n" + "lxc profile device get [:] Get a device property.\n" + "lxc profile device set [:] Set a device property.\n" + "lxc profile device unset [:] Unset a device property.\n" + "lxc profile device add [:] [key=value...]\n" + " Add a profile device, such as a disk or a nic, to the containers using the specified profile." msgstr "" #: lxc/config.go:57 msgid "Manage configuration.\n" "\n" - "lxc config device add <[remote:]container> [key=value]... Add a device to a container.\n" - "lxc config device get <[remote:]container> Get a device property.\n" - "lxc config device set <[remote:]container> Set a device property.\n" - "lxc config device unset <[remote:]container> Unset a device property.\n" - "lxc config device list <[remote:]container> List devices for container.\n" - "lxc config device show <[remote:]container> Show full device details for container.\n" - "lxc config device remove <[remote:]container> Remove device from container.\n" - "\n" - "lxc config get [remote:][container] Get container or server configuration key.\n" - "lxc config set [remote:][container] Set container or server configuration key.\n" - "lxc config unset [remote:][container] Unset container or server configuration key.\n" - "lxc config show [remote:][container] [--expanded] Show container or server configuration.\n" - "lxc config edit [remote:][container] Edit container or server configuration in external editor.\n" + "lxc config device add [:] [key=value...] Add a device to a container.\n" + "lxc config device get [:] Get a device property.\n" + "lxc config device set [:] Set a device property.\n" + "lxc config device unset [:] Unset a device property.\n" + "lxc config device list [:] List devices for container.\n" + "lxc config device show [:] Show full device details for container.\n" + "lxc config device remove [:] Remove device from container.\n" + "\n" + "lxc config get [:][container] Get container or server configuration key.\n" + "lxc config set [:][container] Set container or server configuration key.\n" + "lxc config unset [:][container] Unset container or server configuration key.\n" + "lxc config show [:][container] [--expanded] Show container or server configuration.\n" + "lxc config edit [:][container] Edit container or server configuration in external editor.\n" " Edit configuration, either by launching external editor or reading STDIN.\n" " Example: lxc config edit # launch editor\n" - " cat config.yaml | lxc config edit # read from config.yaml\n" + " cat config.yaml | lxc config edit # read from config.yaml\n" "\n" - "lxc config trust list [remote] List all trusted certs.\n" - "lxc config trust add [remote] Add certfile.crt to trusted hosts.\n" - "lxc config trust remove [remote] [hostname|fingerprint] Remove the cert from trusted hosts.\n" + "lxc config trust list [:] List all trusted certs.\n" + "lxc config trust add [:] Add certfile.crt to trusted hosts.\n" + "lxc config trust remove [:] [hostname|fingerprint] Remove the cert from trusted hosts.\n" "\n" "Examples:\n" "To mount host's /share/c1 onto /opt in the container:\n" - " lxc config device add [remote:]container1 disk source=/share/c1 path=opt\n" + " lxc config device add [:]container1 disk source=/share/c1 path=opt\n" "\n" "To set an lxc config value:\n" - " lxc config set [remote:] raw.lxc 'lxc.aa_allow_incomplete = 1'\n" + " lxc config set [:] raw.lxc 'lxc.aa_allow_incomplete = 1'\n" "\n" "To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the default):\n" " lxc config set core.https_address [::]:8443\n" @@ -661,56 +672,55 @@ " lxc config set core.trust_password blah" msgstr "" -#: lxc/file.go:35 -msgid "Manage files on a container.\n" +#: lxc/file.go:36 +msgid "Manage files in a container.\n" "\n" - "lxc file pull [-r|--recursive] [...] \n" - "lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--mode=MODE] [...] \n" - "lxc file edit \n" + "lxc file pull [-r|--recursive] [:] [[:]...] \n" + "lxc file push [-r|--recursive] [-p|--create-dirs] [--uid=UID] [--gid=GID] [--mode=MODE] [...] [:]\n" + "lxc file edit [:]/\n" "\n" " in the case of pull, in the case of push and in the case of edit are /\n" "\n" "Examples:\n" - "\n" "To push /etc/hosts into the container foo:\n" - " lxc file push /etc/hosts foo/etc/hosts\n" + " lxc file push /etc/hosts foo/etc/hosts\n" "\n" "To pull /etc/hosts from the container:\n" - " lxc file pull foo/etc/hosts .\n" + " lxc file pull foo/etc/hosts ." msgstr "" #: lxc/network.go:48 msgid "Manage networks.\n" "\n" - "lxc network list List available networks.\n" - "lxc network show Show details of a network.\n" - "lxc network create [key=value]... Create a network.\n" - "lxc network get Get network configuration.\n" - "lxc network set Set network configuration.\n" - "lxc network unset Unset network configuration.\n" - "lxc network delete Delete a network.\n" - "lxc network edit \n" + "lxc network list [:] List available networks.\n" + "lxc network show [:] Show details of a network.\n" + "lxc network create [:] [key=value...] Create a network.\n" + "lxc network get [:] Get network configuration.\n" + "lxc network set [:] Set network configuration.\n" + "lxc network unset [:] Unset network configuration.\n" + "lxc network delete [:] Delete a network.\n" + "lxc network edit [:]\n" " Edit network, either by launching external editor or reading STDIN.\n" " Example: lxc network edit # launch editor\n" " cat network.yaml | lxc network edit # read from network.yaml\n" "\n" - "lxc network attach [device name]\n" - "lxc network attach-profile [device name]\n" + "lxc network attach [:] [device name]\n" + "lxc network attach-profile [:] [device name]\n" "\n" - "lxc network detach [device name]\n" - "lxc network detach-profile [device name]\n" + "lxc network detach [:] [device name]\n" + "lxc network detach-profile [:] [device name]" msgstr "" -#: lxc/remote.go:38 +#: lxc/remote.go:37 msgid "Manage remote LXD servers.\n" "\n" - "lxc remote add [] [--accept-certificate] [--password=PASSWORD]\n" - " [--public] [--protocol=PROTOCOL] Add the remote at .\n" - "lxc remote remove Remove the remote .\n" + "lxc remote add [] [--accept-certificate] [--password=PASSWORD]\n" + " [--public] [--protocol=PROTOCOL] Add the remote at .\n" + "lxc remote remove Remove the remote .\n" "lxc remote list List all remotes.\n" - "lxc remote rename Rename remote to .\n" - "lxc remote set-url Update 's url to .\n" - "lxc remote set-default Set the default remote.\n" + "lxc remote rename Rename remote to .\n" + "lxc remote set-url Update 's url to .\n" + "lxc remote set-default Set the default remote.\n" "lxc remote get-default Print the default remote." msgstr "" @@ -732,19 +742,19 @@ "hash or alias name (if one is set).\n" "\n" "\n" - "lxc image import [rootfs tarball|URL] [remote:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--alias=ALIAS].. [prop=value]\n" + "lxc image import [|] [:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [--alias=ALIAS...] [prop=value]\n" " Import an image tarball (or tarballs) into the LXD image store.\n" "\n" - "lxc image copy [remote:] : [--alias=ALIAS].. [--copy-aliases] [--public] [--auto-update]\n" + "lxc image copy [:] : [--alias=ALIAS...] [--copy-aliases] [--public] [--auto-update]\n" " Copy an image from one LXD daemon to another over the network.\n" "\n" " The auto-update flag instructs the server to keep this image up to\n" " date. It requires the source to be an alias and for it to be public.\n" "\n" - "lxc image delete [remote:] [remote:][...]\n" + "lxc image delete [:] [[:]...]\n" " Delete one or more images from the LXD image store.\n" "\n" - "lxc image export [remote:] [target]\n" + "lxc image export [:] [target]\n" " Export an image from the LXD image store into a distributable tarball.\n" "\n" " The output target is optional and defaults to the working directory.\n" @@ -756,30 +766,30 @@ " the appropriate extension will be appended to the provided file name\n" " based on the algorithm used to compress the image. \n" "\n" - "lxc image info [remote:]\n" + "lxc image info [:]\n" " Print everything LXD knows about a given image.\n" "\n" - "lxc image list [remote:] [filter] [--format table|json]\n" + "lxc image list [:] [filter] [--format table|json]\n" " List images in the LXD image store. Filters may be of the\n" " = form for property based filtering, or part of the image\n" " hash or part of the image alias name.\n" "\n" - "lxc image show [remote:]\n" + "lxc image show [:]\n" " Yaml output of the user modifiable properties of an image.\n" "\n" - "lxc image edit [remote:]\n" + "lxc image edit [:]\n" " Edit image, either by launching external editor or reading STDIN.\n" " Example: lxc image edit # launch editor\n" " cat image.yaml | lxc image edit # read from image.yaml\n" "\n" - "lxc image alias create [remote:] \n" + "lxc image alias create [:] \n" " Create a new alias for an existing image.\n" "\n" - "lxc image alias delete [remote:]\n" + "lxc image alias delete [:]\n" " Delete an alias.\n" "\n" - "lxc image alias list [remote:] [filter]\n" - " List the aliases. Filters may be part of the image hash or part of the image alias name.\n" + "lxc image alias list [:] [filter]\n" + " List the aliases. Filters may be part of the image hash or part of the image alias name." msgstr "" #: lxc/info.go:161 @@ -797,7 +807,7 @@ #: lxc/monitor.go:41 msgid "Monitor activity on the LXD server.\n" "\n" - "lxc monitor [remote:] [--type=TYPE...]\n" + "lxc monitor [:] [--type=TYPE...]\n" "\n" "Connects to the monitoring interface of the specified LXD server.\n" "\n" @@ -805,36 +815,39 @@ "Specific types to listen to can be specified with --type.\n" "\n" "Example:\n" - "lxc monitor --type=logging" + " lxc monitor --type=logging" msgstr "" -#: lxc/network.go:216 lxc/network.go:265 +#: lxc/network.go:215 lxc/network.go:264 msgid "More than one device matches, specify the device name." msgstr "" -#: lxc/file.go:235 +#: lxc/file.go:237 msgid "More than one file to download, but target is not a directory" msgstr "" #: lxc/move.go:16 msgid "Move containers within or in between lxd instances.\n" "\n" - "lxc move [remote:] [remote:]\n" + "lxc move [:] [:][]\n" " Move a container between two hosts, renaming it if destination name differs.\n" "\n" "lxc move \n" " Rename a local container.\n" + "\n" + "lxc move / /\n" + " Rename a snapshot." msgstr "" #: lxc/action.go:69 msgid "Must supply container name for: " msgstr "" -#: lxc/list.go:428 lxc/network.go:422 lxc/profile.go:447 lxc/remote.go:381 +#: lxc/list.go:428 lxc/network.go:421 lxc/profile.go:446 lxc/remote.go:380 msgid "NAME" msgstr "" -#: lxc/network.go:408 lxc/remote.go:355 lxc/remote.go:360 +#: lxc/network.go:407 lxc/remote.go:354 lxc/remote.go:359 msgid "NO" msgstr "" @@ -843,21 +856,21 @@ msgid "Name: %s" msgstr "" -#: lxc/network.go:190 +#: lxc/network.go:189 #, c-format msgid "Network %s created" msgstr "" -#: lxc/network.go:293 +#: lxc/network.go:292 #, c-format msgid "Network %s deleted" msgstr "" -#: lxc/init.go:141 lxc/init.go:142 lxc/launch.go:46 lxc/launch.go:47 +#: lxc/init.go:141 lxc/init.go:142 msgid "Network name" msgstr "" -#: lxc/image.go:168 lxc/publish.go:34 +#: lxc/image.go:167 lxc/publish.go:34 msgid "New alias to define at target" msgstr "" @@ -865,7 +878,7 @@ msgid "No certificate provided to add" msgstr "" -#: lxc/network.go:225 lxc/network.go:274 +#: lxc/network.go:224 lxc/network.go:273 msgid "No device found for this network" msgstr "" @@ -873,11 +886,11 @@ msgid "No fingerprint specified." msgstr "" -#: lxc/remote.go:121 +#: lxc/remote.go:120 msgid "Only https URLs are supported for simplestreams" msgstr "" -#: lxc/image.go:425 +#: lxc/image.go:424 msgid "Only https:// is supported for remote image import." msgstr "" @@ -885,7 +898,7 @@ msgid "Options:" msgstr "" -#: lxc/image.go:536 +#: lxc/image.go:535 #, c-format msgid "Output is in %s" msgstr "" @@ -906,11 +919,11 @@ msgid "PROFILES" msgstr "" -#: lxc/remote.go:383 +#: lxc/remote.go:382 msgid "PROTOCOL" msgstr "" -#: lxc/image.go:638 lxc/remote.go:384 +#: lxc/image.go:637 lxc/remote.go:383 msgid "PUBLIC" msgstr "" @@ -923,11 +936,11 @@ msgstr "" #: lxc/help.go:70 -msgid "Path to an alternate client configuration directory." +msgid "Path to an alternate client configuration directory" msgstr "" #: lxc/help.go:71 -msgid "Path to an alternate server directory." +msgid "Path to an alternate server directory" msgstr "" #: lxc/main.go:31 @@ -939,34 +952,28 @@ msgid "Pid: %d" msgstr "" -#: lxc/help.go:25 -msgid "Presents details on how to use LXD.\n" - "\n" - "lxd help [--all]" -msgstr "" - -#: lxc/network.go:343 lxc/profile.go:219 +#: lxc/network.go:342 lxc/profile.go:218 msgid "Press enter to open the editor again" msgstr "" -#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:733 +#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:732 msgid "Press enter to start the editor again" msgstr "" +#: lxc/manpage.go:18 +msgid "Print all the subcommands help." +msgstr "" + #: lxc/help.go:65 -msgid "Print debug information." +msgid "Print debug information" msgstr "" #: lxc/help.go:64 -msgid "Print less common commands." +msgid "Print less common commands" msgstr "" #: lxc/help.go:66 -msgid "Print verbose information." -msgstr "" - -#: lxc/manpage.go:18 -msgid "Prints all the subcommands help." +msgid "Print verbose information" msgstr "" #: lxc/version.go:18 @@ -980,31 +987,31 @@ msgid "Processes: %d" msgstr "" -#: lxc/profile.go:275 +#: lxc/profile.go:274 #, c-format msgid "Profile %s added to %s" msgstr "" -#: lxc/profile.go:170 +#: lxc/profile.go:169 #, c-format msgid "Profile %s created" msgstr "" -#: lxc/profile.go:240 +#: lxc/profile.go:239 #, c-format msgid "Profile %s deleted" msgstr "" -#: lxc/profile.go:306 +#: lxc/profile.go:305 #, c-format msgid "Profile %s removed from %s" msgstr "" -#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:137 lxc/init.go:138 lxc/launch.go:42 lxc/launch.go:43 +#: lxc/copy.go:33 lxc/copy.go:34 lxc/init.go:137 lxc/init.go:138 msgid "Profile to apply to the new container" msgstr "" -#: lxc/profile.go:256 +#: lxc/profile.go:255 #, c-format msgid "Profiles %s applied to %s" msgstr "" @@ -1014,15 +1021,15 @@ msgid "Profiles: %s" msgstr "" -#: lxc/image.go:359 +#: lxc/image.go:358 msgid "Properties:" msgstr "" -#: lxc/remote.go:55 +#: lxc/remote.go:54 msgid "Public image server" msgstr "" -#: lxc/image.go:347 +#: lxc/image.go:346 #, c-format msgid "Public: %s" msgstr "" @@ -1030,14 +1037,14 @@ #: lxc/publish.go:26 msgid "Publish containers as images.\n" "\n" - "lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-value]..." + "lxc publish [:][/] [:] [--alias=ALIAS...] [prop-key=prop-value...]" msgstr "" -#: lxc/file.go:58 lxc/file.go:59 +#: lxc/file.go:57 lxc/file.go:58 msgid "Recursively push or pull files" msgstr "" -#: lxc/remote.go:53 +#: lxc/remote.go:52 msgid "Remote admin password" msgstr "" @@ -1052,19 +1059,35 @@ msgstr "" #: lxc/delete.go:36 lxc/delete.go:37 -msgid "Require user confirmation." +msgid "Require user confirmation" msgstr "" #: lxc/info.go:127 msgid "Resources:" msgstr "" +#: lxc/restore.go:21 +msgid "Restore a container's state to a previous snapshot.\n" + "\n" + "lxc restore [:] [--stateful]\n" + "\n" + "Restores a container from a snapshot (optionally with running state, see\n" + "snapshot help for details).\n" + "\n" + "Examples:\n" + "Create the snapshot:\n" + " lxc snapshot u1 snap0\n" + "\n" + "Restore the snapshot:\n" + " lxc restore u1 snap0" +msgstr "" + #: lxc/init.go:239 #, c-format msgid "Retrieving image: %s" msgstr "" -#: lxc/image.go:641 +#: lxc/image.go:640 msgid "SIZE" msgstr "" @@ -1076,44 +1099,31 @@ msgid "STATE" msgstr "" -#: lxc/remote.go:385 +#: lxc/remote.go:384 msgid "STATIC" msgstr "" -#: lxc/remote.go:226 +#: lxc/remote.go:225 msgid "Server certificate NACKed by user" msgstr "" -#: lxc/remote.go:288 +#: lxc/remote.go:287 msgid "Server doesn't trust us after adding our cert" msgstr "" -#: lxc/remote.go:54 +#: lxc/remote.go:53 msgid "Server protocol (lxd or simplestreams)" msgstr "" -#: lxc/restore.go:21 -msgid "Set the current state of a container back to a snapshot.\n" - "\n" - "lxc restore [remote:] [--stateful]\n" - "\n" - "Restores a container from a snapshot (optionally with running state, see\n" - "snapshot help for details).\n" - "\n" - "For example:\n" - "lxc snapshot u1 snap0 # create the snapshot\n" - "lxc restore u1 snap0 # restore the snapshot" -msgstr "" - -#: lxc/file.go:56 +#: lxc/file.go:55 msgid "Set the file's gid on push" msgstr "" -#: lxc/file.go:57 +#: lxc/file.go:56 msgid "Set the file's perms on push" msgstr "" -#: lxc/file.go:55 +#: lxc/file.go:54 msgid "Set the file's uid on push" msgstr "" @@ -1122,14 +1132,18 @@ msgstr "" #: lxc/help.go:67 -msgid "Show client version." +msgid "Show client version" msgstr "" #: lxc/info.go:36 msgid "Show the container's last 100 log lines?" msgstr "" -#: lxc/image.go:345 +#: lxc/config.go:32 +msgid "Show the expanded configuration" +msgstr "" + +#: lxc/image.go:344 #, c-format msgid "Size: %.2fMB" msgstr "" @@ -1138,11 +1152,11 @@ msgid "Snapshots:" msgstr "" -#: lxc/image.go:369 +#: lxc/image.go:368 msgid "Source:" msgstr "" -#: lxc/launch.go:144 +#: lxc/launch.go:135 #, c-format msgid "Starting %s" msgstr "" @@ -1161,7 +1175,7 @@ msgstr "" #: lxc/action.go:45 -msgid "Store the container state (only for stop)." +msgid "Store the container state (only for stop)" msgstr "" #: lxc/info.go:169 @@ -1172,7 +1186,7 @@ msgid "Swap (peak)" msgstr "" -#: lxc/list.go:433 lxc/network.go:423 +#: lxc/list.go:433 lxc/network.go:422 msgid "TYPE" msgstr "" @@ -1201,11 +1215,11 @@ msgid "The opposite of `lxc pause` is `lxc start`." msgstr "" -#: lxc/network.go:230 lxc/network.go:279 +#: lxc/network.go:229 lxc/network.go:278 msgid "The specified device doesn't exist" msgstr "" -#: lxc/network.go:234 lxc/network.go:283 +#: lxc/network.go:233 lxc/network.go:282 msgid "The specified device doesn't match the network" msgstr "" @@ -1214,10 +1228,10 @@ msgstr "" #: lxc/action.go:41 -msgid "Time to wait for the container before killing it." +msgid "Time to wait for the container before killing it" msgstr "" -#: lxc/image.go:348 +#: lxc/image.go:347 msgid "Timestamps:" msgstr "" @@ -1233,12 +1247,12 @@ msgid "To start your first container, try: lxc launch ubuntu:16.04" msgstr "" -#: lxc/image.go:427 +#: lxc/image.go:426 #, c-format msgid "Transferring image: %s" msgstr "" -#: lxc/action.go:99 lxc/launch.go:157 +#: lxc/action.go:99 lxc/launch.go:148 #, c-format msgid "Try `lxc info --show-log %s` for more info" msgstr "" @@ -1251,23 +1265,23 @@ msgid "Type: persistent" msgstr "" -#: lxc/image.go:642 +#: lxc/image.go:641 msgid "UPLOAD DATE" msgstr "" -#: lxc/remote.go:382 +#: lxc/remote.go:381 msgid "URL" msgstr "" -#: lxc/network.go:425 lxc/profile.go:448 +#: lxc/network.go:424 lxc/profile.go:447 msgid "USED BY" msgstr "" -#: lxc/remote.go:96 +#: lxc/remote.go:95 msgid "Unable to read remote TLS certificate" msgstr "" -#: lxc/image.go:353 +#: lxc/image.go:352 #, c-format msgid "Uploaded: %s" msgstr "" @@ -1278,14 +1292,14 @@ msgstr "" #: lxc/help.go:48 -msgid "Usage: lxc [subcommand] [options]" +msgid "Usage: lxc [options]" msgstr "" #: lxc/delete.go:46 msgid "User aborted delete operation." msgstr "" -#: lxc/restore.go:35 +#: lxc/restore.go:38 msgid "Whether or not to restore the container's running state from snapshot (if available)" msgstr "" @@ -1293,11 +1307,7 @@ msgid "Whether or not to snapshot the container's running state" msgstr "" -#: lxc/config.go:32 -msgid "Whether to show the expanded configuration" -msgstr "" - -#: lxc/network.go:410 lxc/remote.go:357 lxc/remote.go:362 +#: lxc/network.go:409 lxc/remote.go:356 lxc/remote.go:361 msgid "YES" msgstr "" @@ -1305,7 +1315,7 @@ msgid "`lxc config profile` is deprecated, please use `lxc profile`" msgstr "" -#: lxc/launch.go:128 +#: lxc/launch.go:119 msgid "bad number of things scanned from image, container or snapshot" msgstr "" @@ -1317,31 +1327,31 @@ msgid "can't copy to the same container name" msgstr "" -#: lxc/file.go:270 +#: lxc/file.go:272 msgid "can't pull a directory without --recursive" msgstr "" -#: lxc/remote.go:345 +#: lxc/remote.go:344 msgid "can't remove the default remote" msgstr "" -#: lxc/file.go:117 +#: lxc/file.go:119 msgid "can't supply uid/gid/mode in recursive mode" msgstr "" -#: lxc/remote.go:371 +#: lxc/remote.go:370 msgid "default" msgstr "" -#: lxc/copy.go:131 lxc/copy.go:136 lxc/copy.go:232 lxc/copy.go:237 lxc/init.go:219 lxc/init.go:224 lxc/launch.go:112 lxc/launch.go:117 +#: lxc/copy.go:131 lxc/copy.go:136 lxc/copy.go:232 lxc/copy.go:237 lxc/init.go:219 lxc/init.go:224 lxc/launch.go:103 lxc/launch.go:108 msgid "didn't get any affected image, container or snapshot from server" msgstr "" -#: lxc/image.go:339 +#: lxc/image.go:338 msgid "disabled" msgstr "" -#: lxc/image.go:341 +#: lxc/image.go:340 msgid "enabled" msgstr "" @@ -1355,11 +1365,11 @@ msgid "error: unknown command: %s" msgstr "" -#: lxc/launch.go:132 +#: lxc/launch.go:123 msgid "got bad version" msgstr "" -#: lxc/image.go:334 lxc/image.go:618 +#: lxc/image.go:333 lxc/image.go:617 msgid "no" msgstr "" @@ -1367,7 +1377,7 @@ msgid "not all the profiles from the source exist on the target" msgstr "" -#: lxc/remote.go:219 +#: lxc/remote.go:218 msgid "ok (y/n)?" msgstr "" @@ -1376,26 +1386,26 @@ msgid "processing aliases failed %s\n" msgstr "" -#: lxc/file.go:311 +#: lxc/file.go:313 msgid "recursive edit doesn't make sense :(" msgstr "" -#: lxc/remote.go:407 +#: lxc/remote.go:406 #, c-format msgid "remote %s already exists" msgstr "" -#: lxc/remote.go:337 lxc/remote.go:399 lxc/remote.go:434 lxc/remote.go:450 +#: lxc/remote.go:336 lxc/remote.go:398 lxc/remote.go:433 lxc/remote.go:449 #, c-format msgid "remote %s doesn't exist" msgstr "" -#: lxc/remote.go:320 +#: lxc/remote.go:319 #, c-format msgid "remote %s exists as <%s>" msgstr "" -#: lxc/remote.go:341 lxc/remote.go:403 lxc/remote.go:438 +#: lxc/remote.go:340 lxc/remote.go:402 lxc/remote.go:437 #, c-format msgid "remote %s is static and cannot be modified" msgstr "" @@ -1421,7 +1431,7 @@ msgid "wrong number of subcommand arguments" msgstr "" -#: lxc/delete.go:45 lxc/image.go:336 lxc/image.go:622 +#: lxc/delete.go:45 lxc/image.go:335 lxc/image.go:621 msgid "yes" msgstr "" diff -Nru lxd-2.6.2/scripts/lxc-to-lxd lxd-2.7/scripts/lxc-to-lxd --- lxd-2.6.2/scripts/lxc-to-lxd 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/scripts/lxc-to-lxd 2016-12-21 00:52:23.000000000 +0000 @@ -2,12 +2,97 @@ import argparse import http.client import json -import lxc import os import socket import subprocess import sys +try: + import lxc +except ImportError: + print("You must have python3-lxc installed for this script to work.") + sys.exit(1) + + +# Whitelist of keys we either need to check or allow setting in LXD. The latter +# is strictly only true for 'lxc.aa_profile'. +keys_to_check = [ + 'lxc.pts', + # 'lxc.tty', + # 'lxc.devttydir', + # 'lxc.kmsg', + 'lxc.aa_profile', + # 'lxc.cgroup.', + 'lxc.loglevel', + # 'lxc.logfile', + 'lxc.mount.auto', + 'lxc.mount', + # 'lxc.rootfs.mount', + # 'lxc.rootfs.options', + # 'lxc.pivotdir', + # 'lxc.hook.pre-start', + # 'lxc.hook.pre-mount', + # 'lxc.hook.mount', + # 'lxc.hook.autodev', + # 'lxc.hook.start', + # 'lxc.hook.stop', + # 'lxc.hook.post-stop', + # 'lxc.hook.clone', + # 'lxc.hook.destroy', + # 'lxc.hook', + 'lxc.network.type', + 'lxc.network.flags', + 'lxc.network.link', + 'lxc.network.name', + 'lxc.network.macvlan.mode', + 'lxc.network.veth.pair', + # 'lxc.network.script.up', + # 'lxc.network.script.down', + 'lxc.network.hwaddr', + 'lxc.network.mtu', + # 'lxc.network.vlan.id', + # 'lxc.network.ipv4.gateway', + # 'lxc.network.ipv4', + # 'lxc.network.ipv6.gateway', + # 'lxc.network.ipv6', + # 'lxc.network.', + # 'lxc.network', + # 'lxc.console.logfile', + # 'lxc.console', + 'lxc.include', + 'lxc.start.auto', + 'lxc.start.delay', + 'lxc.start.order', + # 'lxc.monitor.unshare', + # 'lxc.group', + 'lxc.environment', + # 'lxc.init_cmd', + # 'lxc.init_uid', + # 'lxc.init_gid', + # 'lxc.ephemeral', + # 'lxc.syslog', + # 'lxc.no_new_privs', + + # Additional keys that are either set by this script or are used to report + # helpful errors to users. + 'lxc.arch', + 'lxc.id_map', + 'lxd.migrated', + 'lxc.rootfs.backend', + 'lxc.rootfs', + 'lxc.utsname', + 'lxc.aa_allow_incomplete', + 'lxc.autodev', + 'lxc.haltsignal', + 'lxc.rebootsignal', + 'lxc.stopsignal', + 'lxc.mount.entry', + 'lxc.cap.drop' + # 'lxc.cap.keep', + # 'lxc.seccomp', + # 'lxc.se_context', + ] + # Unix connection to LXD class UnixHTTPConnection(http.client.HTTPConnection): @@ -35,6 +120,17 @@ return result +def config_keys(config): + keys = [] + for line in config: + fields = line.split("=", 1) + cur = fields[0].strip() + if cur and not cur.startswith("#") and cur.startswith("lxc."): + keys.append(cur) + + return keys + + # Parse a LXC configuration file, called recursively for includes def config_parse(path): config = [] @@ -72,12 +168,13 @@ with open(value, "r") as fd: for line in fd: line = line.strip() - if line and not line.startswith("#"): + if (line and not line.startswith("#") and + line.startswith("lxc.")): config.append("lxc.mount.entry = %s" % line) continue # Proces normal configuration keys - if line and not line.startswith("#"): + if line and not line.strip().startswith("#"): config.append(line) return config @@ -128,6 +225,17 @@ # As some keys can't be queried over the API, parse the config ourselves print("Parsing LXC configuration") lxc_config = config_parse(container.config_file_name) + found_keys = config_keys(lxc_config) + + # Generic check for any invalid LXC configuration keys. + print("Checking for unsupported LXC configuration keys") + diff = list(set(found_keys) - set(keys_to_check)) + for d in diff: + if (not d.startswith('lxc.network.') and not + d.startswith('lxc.cgroup.')): + print("Found at least one unsupported config key %s: " % d) + print("Not importing this container, skipping...") + return False if args.debug: print("Container configuration:") @@ -135,6 +243,8 @@ print("\n ".join(lxc_config)) print("") + # Check for keys that have values differing from the LXD defaults. + print("Checking whether container has already been migrated") if config_get(lxc_config, "lxd.migrated"): print("Container has already been migrated, skipping...") return False @@ -145,6 +255,12 @@ print("Container already exists, skipping...") return False + # Validating lxc.id_map: must be unset. + print("Validating container mode") + if config_get(lxc_config, "lxc.id_map"): + print("Unprivileged containers aren't supported, skipping...") + return False + # Validate lxc.utsname print("Validating container name") value = config_get(lxc_config, "lxc.utsname") @@ -152,17 +268,40 @@ print("Container name doesn't match lxc.utsname, skipping...") return False - # Detect privileged containers - print("Validating container mode") - if config_get(lxc_config, "lxc.id_map"): - print("Unprivileged containers aren't supported, skipping...") + # Validate lxc.aa_allow_incomplete: must be set to 0 or unset. + print("Validating whether incomplete AppArmor support is enabled") + value = config_get(lxc_config, "lxc.aa_allow_incomplete") + if value and int(value[0]) != 0: + print("Container allows incomplete AppArmor support, skipping...") + return False + + # Validate lxc.autodev: must be set to 1 or unset. + print("Validating whether mounting a minimal /dev is enabled") + value = config_get(lxc_config, "lxc.autodev") + if value and int(value[0]) != 1: + print("Container doesn't mount a minimal /dev filesystem, skipping...") + return False + + # Validate lxc.haltsignal: must be unset. + print("Validating that no custom haltsignal is set") + value = config_get(lxc_config, "lxc.haltsignal") + if value: + print("Container sets custom halt signal, skipping...") return False - # Detect hooks in config - for line in lxc_config: - if line.startswith("lxc.hook."): - print("Hooks aren't supported, skipping...") - return False + # Validate lxc.rebootsignal: must be unset. + print("Validating that no custom rebootsignal is set") + value = config_get(lxc_config, "lxc.rebootsignal") + if value: + print("Container sets custom reboot signal, skipping...") + return False + + # Validate lxc.stopsignal: must be unset. + print("Validating that no custom stopsignal is set") + value = config_get(lxc_config, "lxc.stopsignal") + if value: + print("Container sets custom stop signal, skipping...") + return False # Extract and valid rootfs key print("Validating container rootfs") @@ -228,7 +367,7 @@ device['parent'] = dev.link if dev.mtu: - device['mtu'] = int(dev.mtu) + device['mtu'] = dev.mtu if dev.name: device['name'] = dev.name @@ -394,7 +533,16 @@ lxd_rootfs = os.path.join(args.lxdpath, "containers", container_name, "rootfs") - if args.copy_rootfs: + if args.move_rootfs: + if os.path.exists(lxd_rootfs): + os.rmdir(lxd_rootfs) + + if subprocess.call(["mv", rootfs, lxd_rootfs]) != 0: + print("Failed to move the container rootfs, skipping...") + return False + + os.mkdir(rootfs) + else: print("Copying container rootfs") if not os.path.exists(lxd_rootfs): os.mkdir(lxd_rootfs) @@ -404,15 +552,6 @@ "%s/" % rootfs, "%s/" % lxd_rootfs]) != 0: print("Failed to transfer the container rootfs, skipping...") return False - else: - if os.path.exists(lxd_rootfs): - os.rmdir(lxd_rootfs) - - if subprocess.call(["mv", rootfs, lxd_rootfs]) != 0: - print("Failed to move the container rootfs, skipping...") - return False - - os.mkdir(rootfs) # Delete the source if args.delete: @@ -436,7 +575,7 @@ help="Import all containers") parser.add_argument("--delete", action="store_true", default=False, help="Delete the source container") -parser.add_argument("--copy-rootfs", action="store_true", default=False, +parser.add_argument("--move-rootfs", action="store_true", default=False, help="Copy the container rootfs rather than moving it") parser.add_argument("--lxcpath", type=str, default=False, help="Alternate LXC path") diff -Nru lxd-2.6.2/shared/architectures.go lxd-2.7/shared/architectures.go --- lxd-2.6.2/shared/architectures.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/architectures.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -package shared - -import ( - "fmt" -) - -const ( - ARCH_UNKNOWN = 0 - ARCH_32BIT_INTEL_X86 = 1 - ARCH_64BIT_INTEL_X86 = 2 - ARCH_32BIT_ARMV7_LITTLE_ENDIAN = 3 - ARCH_64BIT_ARMV8_LITTLE_ENDIAN = 4 - ARCH_32BIT_POWERPC_BIG_ENDIAN = 5 - ARCH_64BIT_POWERPC_BIG_ENDIAN = 6 - ARCH_64BIT_POWERPC_LITTLE_ENDIAN = 7 - ARCH_64BIT_S390_BIG_ENDIAN = 8 -) - -var architectureNames = map[int]string{ - ARCH_32BIT_INTEL_X86: "i686", - ARCH_64BIT_INTEL_X86: "x86_64", - ARCH_32BIT_ARMV7_LITTLE_ENDIAN: "armv7l", - ARCH_64BIT_ARMV8_LITTLE_ENDIAN: "aarch64", - ARCH_32BIT_POWERPC_BIG_ENDIAN: "ppc", - ARCH_64BIT_POWERPC_BIG_ENDIAN: "ppc64", - ARCH_64BIT_POWERPC_LITTLE_ENDIAN: "ppc64le", - ARCH_64BIT_S390_BIG_ENDIAN: "s390x", -} - -var architectureAliases = map[int][]string{ - ARCH_32BIT_INTEL_X86: []string{"i386"}, - ARCH_64BIT_INTEL_X86: []string{"amd64"}, - ARCH_32BIT_ARMV7_LITTLE_ENDIAN: []string{"armel", "armhf"}, - ARCH_64BIT_ARMV8_LITTLE_ENDIAN: []string{"arm64"}, - ARCH_32BIT_POWERPC_BIG_ENDIAN: []string{"powerpc"}, - ARCH_64BIT_POWERPC_BIG_ENDIAN: []string{"powerpc64"}, - ARCH_64BIT_POWERPC_LITTLE_ENDIAN: []string{"ppc64el"}, -} - -var architecturePersonalities = map[int]string{ - ARCH_32BIT_INTEL_X86: "linux32", - ARCH_64BIT_INTEL_X86: "linux64", - ARCH_32BIT_ARMV7_LITTLE_ENDIAN: "linux32", - ARCH_64BIT_ARMV8_LITTLE_ENDIAN: "linux64", - ARCH_32BIT_POWERPC_BIG_ENDIAN: "linux32", - ARCH_64BIT_POWERPC_BIG_ENDIAN: "linux64", - ARCH_64BIT_POWERPC_LITTLE_ENDIAN: "linux64", - ARCH_64BIT_S390_BIG_ENDIAN: "linux64", -} - -var architectureSupportedPersonalities = map[int][]int{ - ARCH_32BIT_INTEL_X86: []int{}, - ARCH_64BIT_INTEL_X86: []int{ARCH_32BIT_INTEL_X86}, - ARCH_32BIT_ARMV7_LITTLE_ENDIAN: []int{}, - ARCH_64BIT_ARMV8_LITTLE_ENDIAN: []int{ARCH_32BIT_ARMV7_LITTLE_ENDIAN}, - ARCH_32BIT_POWERPC_BIG_ENDIAN: []int{}, - ARCH_64BIT_POWERPC_BIG_ENDIAN: []int{ARCH_32BIT_POWERPC_BIG_ENDIAN}, - ARCH_64BIT_POWERPC_LITTLE_ENDIAN: []int{}, - ARCH_64BIT_S390_BIG_ENDIAN: []int{}, -} - -const ArchitectureDefault = "x86_64" - -func ArchitectureName(arch int) (string, error) { - arch_name, exists := architectureNames[arch] - if exists { - return arch_name, nil - } - - return "unknown", fmt.Errorf("Architecture isn't supported: %d", arch) -} - -func ArchitectureId(arch string) (int, error) { - for arch_id, arch_name := range architectureNames { - if arch_name == arch { - return arch_id, nil - } - } - - for arch_id, arch_aliases := range architectureAliases { - for _, arch_name := range arch_aliases { - if arch_name == arch { - return arch_id, nil - } - } - } - - return 0, fmt.Errorf("Architecture isn't supported: %s", arch) -} - -func ArchitecturePersonality(arch int) (string, error) { - arch_personality, exists := architecturePersonalities[arch] - if exists { - return arch_personality, nil - } - - return "", fmt.Errorf("Architecture isn't supported: %d", arch) -} - -func ArchitecturePersonalities(arch int) ([]int, error) { - personalities, exists := architectureSupportedPersonalities[arch] - if exists { - return personalities, nil - } - - return []int{}, fmt.Errorf("Architecture isn't supported: %d", arch) -} diff -Nru lxd-2.6.2/shared/architectures_linux.go lxd-2.7/shared/architectures_linux.go --- lxd-2.6.2/shared/architectures_linux.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/architectures_linux.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -// +build linux - -package shared - -import ( - "syscall" -) - -func ArchitectureGetLocal() (string, error) { - uname := syscall.Utsname{} - if err := syscall.Uname(&uname); err != nil { - return ArchitectureDefault, err - } - - architectureName := "" - for _, c := range uname.Machine { - if c == 0 { - break - } - architectureName += string(byte(c)) - } - - return architectureName, nil -} diff -Nru lxd-2.6.2/shared/architectures_others.go lxd-2.7/shared/architectures_others.go --- lxd-2.6.2/shared/architectures_others.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/architectures_others.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -// +build !linux - -package shared - -func ArchitectureGetLocal() (string, error) { - return ArchitectureDefault, nil -} diff -Nru lxd-2.6.2/shared/cert.go lxd-2.7/shared/cert.go --- lxd-2.6.2/shared/cert.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/cert.go 2016-12-21 00:52:23.000000000 +0000 @@ -8,6 +8,7 @@ import ( "crypto/rand" "crypto/rsa" + "crypto/sha256" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -211,3 +212,21 @@ return x509.ParseCertificate(certBlock.Bytes) } + +func CertFingerprint(cert *x509.Certificate) string { + return fmt.Sprintf("%x", sha256.Sum256(cert.Raw)) +} + +func CertFingerprintStr(c string) (string, error) { + pemCertificate, _ := pem.Decode([]byte(c)) + if pemCertificate == nil { + return "", fmt.Errorf("invalid certificate") + } + + cert, err := x509.ParseCertificate(pemCertificate.Bytes) + if err != nil { + return "", err + } + + return CertFingerprint(cert), nil +} diff -Nru lxd-2.6.2/shared/flex.go lxd-2.7/shared/flex.go --- lxd-2.6.2/shared/flex.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/flex.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -/* This is a FLEXible file which can be used by both client and daemon. - * Teehee. - */ -package shared - -var Version = "2.6.2" -var UserAgent = "LXD " + Version - -/* - * Please increment the api compat number every time you change the API. - * - * Version 1.0: ping - */ -var APIVersion = "1.0" diff -Nru lxd-2.6.2/shared/idmapset_linux.go lxd-2.7/shared/idmapset_linux.go --- lxd-2.6.2/shared/idmapset_linux.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/idmapset_linux.go 2016-12-21 00:52:23.000000000 +0000 @@ -188,7 +188,6 @@ func (m IdmapSet) Intersects(i IdmapEntry) bool { for _, e := range m.Idmap { if i.Intersects(e) { - fmt.Printf("%v and %v intersect\n", i, e) return true } } diff -Nru lxd-2.6.2/shared/idmapset_linux_test.go lxd-2.7/shared/idmapset_linux_test.go --- lxd-2.6.2/shared/idmapset_linux_test.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/idmapset_linux_test.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,107 @@ +package shared + +import ( + "fmt" + "testing" +) + +func TestIdmapSetAddSafe_split(t *testing.T) { + orig := IdmapSet{Idmap: []IdmapEntry{IdmapEntry{Isuid: true, Hostid: 1000, Nsid: 0, Maprange: 1000}}} + + if err := orig.AddSafe(IdmapEntry{Isuid: true, Hostid: 500, Nsid: 500, Maprange: 10}); err != nil { + t.Error(err) + return + } + + if orig.Idmap[0].Hostid != 1000 || orig.Idmap[0].Nsid != 0 || orig.Idmap[0].Maprange != 500 { + t.Error(fmt.Errorf("bad range: %v", orig.Idmap[0])) + return + } + + if orig.Idmap[1].Hostid != 500 || orig.Idmap[1].Nsid != 500 || orig.Idmap[1].Maprange != 10 { + t.Error(fmt.Errorf("bad range: %v", orig.Idmap[1])) + return + } + + if orig.Idmap[2].Hostid != 1510 || orig.Idmap[2].Nsid != 510 || orig.Idmap[2].Maprange != 490 { + t.Error(fmt.Errorf("bad range: %v", orig.Idmap[2])) + return + } + + if len(orig.Idmap) != 3 { + t.Error("too many idmap entries") + return + } +} + +func TestIdmapSetAddSafe_lower(t *testing.T) { + orig := IdmapSet{Idmap: []IdmapEntry{IdmapEntry{Isuid: true, Hostid: 1000, Nsid: 0, Maprange: 1000}}} + + if err := orig.AddSafe(IdmapEntry{Isuid: true, Hostid: 500, Nsid: 0, Maprange: 10}); err != nil { + t.Error(err) + return + } + + if orig.Idmap[0].Hostid != 500 || orig.Idmap[0].Nsid != 0 || orig.Idmap[0].Maprange != 10 { + t.Error(fmt.Errorf("bad range: %v", orig.Idmap[0])) + return + } + + if orig.Idmap[1].Hostid != 1010 || orig.Idmap[1].Nsid != 10 || orig.Idmap[1].Maprange != 990 { + t.Error(fmt.Errorf("bad range: %v", orig.Idmap[1])) + return + } + + if len(orig.Idmap) != 2 { + t.Error("too many idmap entries") + return + } +} + +func TestIdmapSetAddSafe_upper(t *testing.T) { + orig := IdmapSet{Idmap: []IdmapEntry{IdmapEntry{Isuid: true, Hostid: 1000, Nsid: 0, Maprange: 1000}}} + + if err := orig.AddSafe(IdmapEntry{Isuid: true, Hostid: 500, Nsid: 995, Maprange: 10}); err != nil { + t.Error(err) + return + } + + if orig.Idmap[0].Hostid != 1000 || orig.Idmap[0].Nsid != 0 || orig.Idmap[0].Maprange != 995 { + t.Error(fmt.Errorf("bad range: %v", orig.Idmap[0])) + return + } + + if orig.Idmap[1].Hostid != 500 || orig.Idmap[1].Nsid != 995 || orig.Idmap[1].Maprange != 10 { + t.Error(fmt.Errorf("bad range: %v", orig.Idmap[1])) + return + } + + if len(orig.Idmap) != 2 { + t.Error("too many idmap entries") + return + } +} + +func TestIdmapSetIntersects(t *testing.T) { + orig := IdmapSet{Idmap: []IdmapEntry{IdmapEntry{Isuid: true, Hostid: 165536, Nsid: 0, Maprange: 65536}}} + + if !orig.Intersects(IdmapEntry{Isuid: true, Hostid: 231071, Nsid: 0, Maprange: 65536}) { + t.Error("ranges don't intersect") + return + } + + if !orig.Intersects(IdmapEntry{Isuid: true, Hostid: 231072, Nsid: 0, Maprange: 65536}) { + t.Error("ranges don't intersect") + return + } + + if !orig.Intersects(IdmapEntry{Isuid: true, Hostid: 231072, Nsid: 65535, Maprange: 65536}) { + t.Error("ranges don't intersect") + return + } + + if orig.Intersects(IdmapEntry{Isuid: true, Hostid: 231072, Nsid: 65536, Maprange: 65536}) { + t.Error("ranges intersect") + return + } +} diff -Nru lxd-2.6.2/shared/idmapset_test_linux.go lxd-2.7/shared/idmapset_test_linux.go --- lxd-2.6.2/shared/idmapset_test_linux.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/idmapset_test_linux.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -package shared - -import ( - "fmt" - "testing" -) - -func TestIdmapSetAddSafe_split(t *testing.T) { - orig := IdmapSet{Idmap: []IdmapEntry{IdmapEntry{Isuid: true, Hostid: 1000, Nsid: 0, Maprange: 1000}}} - - if err := orig.AddSafe(IdmapEntry{Isuid: true, Hostid: 500, Nsid: 500, Maprange: 10}); err != nil { - t.Error(err) - return - } - - if orig.Idmap[0].Hostid != 1000 || orig.Idmap[0].Nsid != 0 || orig.Idmap[0].Maprange != 500 { - t.Error(fmt.Errorf("bad range: %v", orig.Idmap[0])) - return - } - - if orig.Idmap[1].Hostid != 500 || orig.Idmap[1].Nsid != 500 || orig.Idmap[1].Maprange != 10 { - t.Error(fmt.Errorf("bad range: %v", orig.Idmap[1])) - return - } - - if orig.Idmap[2].Hostid != 1510 || orig.Idmap[2].Nsid != 510 || orig.Idmap[2].Maprange != 490 { - t.Error(fmt.Errorf("bad range: %v", orig.Idmap[2])) - return - } - - if len(orig.Idmap) != 3 { - t.Error("too many idmap entries") - return - } -} - -func TestIdmapSetAddSafe_lower(t *testing.T) { - orig := IdmapSet{Idmap: []IdmapEntry{IdmapEntry{Isuid: true, Hostid: 1000, Nsid: 0, Maprange: 1000}}} - - if err := orig.AddSafe(IdmapEntry{Isuid: true, Hostid: 500, Nsid: 0, Maprange: 10}); err != nil { - t.Error(err) - return - } - - if orig.Idmap[0].Hostid != 500 || orig.Idmap[0].Nsid != 0 || orig.Idmap[0].Maprange != 10 { - t.Error(fmt.Errorf("bad range: %v", orig.Idmap[0])) - return - } - - if orig.Idmap[1].Hostid != 1010 || orig.Idmap[1].Nsid != 10 || orig.Idmap[1].Maprange != 990 { - t.Error(fmt.Errorf("bad range: %v", orig.Idmap[1])) - return - } - - if len(orig.Idmap) != 2 { - t.Error("too many idmap entries") - return - } -} - -func TestIdmapSetAddSafe_upper(t *testing.T) { - orig := IdmapSet{Idmap: []IdmapEntry{IdmapEntry{Isuid: true, Hostid: 1000, Nsid: 0, Maprange: 1000}}} - - if err := orig.AddSafe(IdmapEntry{Isuid: true, Hostid: 500, Nsid: 995, Maprange: 10}); err != nil { - t.Error(err) - return - } - - if orig.Idmap[0].Hostid != 1000 || orig.Idmap[0].Nsid != 0 || orig.Idmap[0].Maprange != 995 { - t.Error(fmt.Errorf("bad range: %v", orig.Idmap[0])) - return - } - - if orig.Idmap[1].Hostid != 500 || orig.Idmap[1].Nsid != 995 || orig.Idmap[1].Maprange != 10 { - t.Error(fmt.Errorf("bad range: %v", orig.Idmap[1])) - return - } - - if len(orig.Idmap) != 2 { - t.Error("too many idmap entries") - return - } -} - -func TestIdmapSetIntersects(t *testing.T) { - orig := IdmapSet{Idmap: []IdmapEntry{IdmapEntry{Isuid: true, Hostid: 165536, Nsid: 0, Maprange: 65536}}} - - if orig.Intersects(IdmapEntry{Isuid: true, Hostid: 231072, Nsid: 0, Maprange: 65536}) { - t.Error("ranges don't intersect") - return - } -} diff -Nru lxd-2.6.2/shared/ioprogress/reader.go lxd-2.7/shared/ioprogress/reader.go --- lxd-2.6.2/shared/ioprogress/reader.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/ioprogress/reader.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,23 @@ +package ioprogress + +import ( + "io" +) + +type ProgressReader struct { + io.ReadCloser + Tracker *ProgressTracker +} + +func (pt *ProgressReader) Read(p []byte) (int, error) { + // Do normal reader tasks + n, err := pt.ReadCloser.Read(p) + + // Do the actual progress tracking + if pt.Tracker != nil { + pt.Tracker.total += int64(n) + pt.Tracker.Update(n) + } + + return n, err +} diff -Nru lxd-2.6.2/shared/ioprogress/tracker.go lxd-2.7/shared/ioprogress/tracker.go --- lxd-2.6.2/shared/ioprogress/tracker.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/ioprogress/tracker.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,76 @@ +package ioprogress + +import ( + "time" +) + +type ProgressTracker struct { + Length int64 + Handler func(int64, int64) + + percentage float64 + total int64 + start *time.Time + last *time.Time +} + +func (pt *ProgressTracker) Update(n int) { + // Skip the rest if no handler attached + if pt.Handler == nil { + return + } + + // Initialize start time if needed + if pt.start == nil { + cur := time.Now() + pt.start = &cur + pt.last = pt.start + } + + // Skip if no data to count + if n <= 0 { + return + } + + // Update interval handling + var percentage float64 + if pt.Length > 0 { + // If running in relative mode, check that we increased by at least 1% + percentage = float64(pt.total) / float64(pt.Length) * float64(100) + if percentage-pt.percentage < 0.9 { + return + } + } else { + // If running in absolute mode, check that at least a second elapsed + interval := time.Since(*pt.last).Seconds() + if interval < 1 { + return + } + } + + // Determine speed + speedInt := int64(0) + duration := time.Since(*pt.start).Seconds() + if duration > 0 { + speed := float64(pt.total) / duration + speedInt = int64(speed) + } + + // Determine progress + progressInt := int64(0) + if pt.Length > 0 { + pt.percentage = percentage + progressInt = int64(1 - (int(percentage) % 1) + int(percentage)) + if progressInt > 100 { + progressInt = 100 + } + } else { + progressInt = pt.total + + // Update timestamp + cur := time.Now() + pt.last = &cur + } + + pt.Handler(progressInt, speedInt) +} diff -Nru lxd-2.6.2/shared/ioprogress/writer.go lxd-2.7/shared/ioprogress/writer.go --- lxd-2.6.2/shared/ioprogress/writer.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/ioprogress/writer.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,23 @@ +package ioprogress + +import ( + "io" +) + +type ProgressWriter struct { + io.WriteCloser + Tracker *ProgressTracker +} + +func (pt *ProgressWriter) Write(p []byte) (int, error) { + // Do normal writer tasks + n, err := pt.WriteCloser.Write(p) + + // Do the actual progress tracking + if pt.Tracker != nil { + pt.Tracker.total += int64(n) + pt.Tracker.Update(n) + } + + return n, err +} diff -Nru lxd-2.6.2/shared/logging/format.go lxd-2.7/shared/logging/format.go --- lxd-2.6.2/shared/logging/format.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/logging/format.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,191 @@ +package logging + +import ( + "bytes" + "fmt" + "reflect" + "sort" + "strconv" + "strings" + "time" + + log "gopkg.in/inconshreveable/log15.v2" +) + +const ( + timeFormat = "2006-01-02T15:04:05-0700" + floatFormat = 'f' + errorKey = "LOG15_ERROR" + termTimeFormat = "01-02|15:04:05" + termMsgJust = 40 +) + +// Imported from the log15 project + +// TerminalFormat formats log records optimized for human readability on +// a terminal with color-coded level output and terser human friendly timestamp. +// This format should only be used for interactive programs or while developing. +// +// [TIME] [LEVEL] MESAGE key=value key=value ... +// +// Example: +// +// [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002 +// +func TerminalFormat() log.Format { + return log.FormatFunc(func(r *log.Record) []byte { + var color = 0 + switch r.Lvl { + case log.LvlCrit: + color = 35 + case log.LvlError: + color = 31 + case log.LvlWarn: + color = 33 + case log.LvlInfo: + color = 32 + case log.LvlDebug: + color = 36 + } + + b := &bytes.Buffer{} + lvl := strings.ToUpper(r.Lvl.String()) + if color > 0 { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg) + } else { + fmt.Fprintf(b, "[%s] [%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg) + } + + // try to justify the log output for short messages + if len(r.Ctx) > 0 && len(r.Msg) < termMsgJust { + b.Write(bytes.Repeat([]byte{' '}, termMsgJust-len(r.Msg))) + } + + // print the keys logfmt style + logfmt(b, r.Ctx, color) + return b.Bytes() + }) +} + +func LogfmtFormat() log.Format { + return log.FormatFunc(func(r *log.Record) []byte { + common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg} + buf := &bytes.Buffer{} + logfmt(buf, append(common, r.Ctx...), 0) + return buf.Bytes() + }) +} + +func logfmt(buf *bytes.Buffer, ctx []interface{}, color int) { + entries := []string{} + + for i := 0; i < len(ctx); i += 2 { + k, ok := ctx[i].(string) + v := formatLogfmtValue(ctx[i+1]) + if !ok { + k, v = errorKey, formatLogfmtValue(k) + } + + // XXX: we should probably check that all of your key bytes aren't invalid + if color > 0 { + entries = append(entries, fmt.Sprintf("\x1b[%dm%s\x1b[0m=%s", color, k, v)) + } else { + entries = append(entries, fmt.Sprintf("%s=%s", k, v)) + } + } + + sort.Strings(entries) + + for i, v := range entries { + if i != 0 { + buf.WriteByte(' ') + } + + fmt.Fprint(buf, v) + } + + buf.WriteByte('\n') +} + +func formatShared(value interface{}) (result interface{}) { + defer func() { + if err := recover(); err != nil { + if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { + result = "nil" + } else { + panic(err) + } + } + }() + + switch v := value.(type) { + case time.Time: + return v.Format(timeFormat) + + case error: + return v.Error() + + case fmt.Stringer: + return v.String() + + default: + return v + } +} + +// formatValue formats a value for serialization +func formatLogfmtValue(value interface{}) string { + if value == nil { + return "nil" + } + + value = formatShared(value) + switch v := value.(type) { + case bool: + return strconv.FormatBool(v) + case float32: + return strconv.FormatFloat(float64(v), floatFormat, 3, 64) + case float64: + return strconv.FormatFloat(v, floatFormat, 3, 64) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return fmt.Sprintf("%d", value) + case string: + return escapeString(v) + default: + return escapeString(fmt.Sprintf("%+v", value)) + } +} + +func escapeString(s string) string { + needQuotes := false + e := bytes.Buffer{} + e.WriteByte('"') + for _, r := range s { + if r <= ' ' || r == '=' || r == '"' { + needQuotes = true + } + + switch r { + case '\\', '"': + e.WriteByte('\\') + e.WriteByte(byte(r)) + case '\n': + e.WriteByte('\\') + e.WriteByte('n') + case '\r': + e.WriteByte('\\') + e.WriteByte('r') + case '\t': + e.WriteByte('\\') + e.WriteByte('t') + default: + e.WriteRune(r) + } + } + e.WriteByte('"') + start, stop := 0, e.Len() + if !needQuotes { + start, stop = 1, stop-1 + } + return string(e.Bytes()[start:stop]) +} diff -Nru lxd-2.6.2/shared/logging/log.go lxd-2.7/shared/logging/log.go --- lxd-2.6.2/shared/logging/log.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/logging/log.go 2016-12-21 00:52:23.000000000 +0000 @@ -6,6 +6,7 @@ "path/filepath" "github.com/lxc/lxd/shared" + "gopkg.in/inconshreveable/log15.v2/term" log "gopkg.in/inconshreveable/log15.v2" ) @@ -15,11 +16,16 @@ Log := log.New() var handlers []log.Handler - var syshandler log.Handler + // Format handler + format := LogfmtFormat() + if term.IsTty(os.Stderr.Fd()) { + format = TerminalFormat() + } + // System specific handler - syshandler = getSystemHandler(syslog, debug) + syshandler = getSystemHandler(syslog, debug, format) if syshandler != nil { handlers = append(handlers, syshandler) } @@ -35,11 +41,11 @@ handlers, log.LvlFilterHandler( log.LvlInfo, - log.Must.FileHandler(logfile, log.LogfmtFormat()), + log.Must.FileHandler(logfile, format), ), ) } else { - handlers = append(handlers, log.Must.FileHandler(logfile, log.LogfmtFormat())) + handlers = append(handlers, log.Must.FileHandler(logfile, format)) } } @@ -50,18 +56,18 @@ handlers, log.LvlFilterHandler( log.LvlInfo, - log.StderrHandler, + log.StreamHandler(os.Stderr, format), ), ) } else { - handlers = append(handlers, log.StderrHandler) + handlers = append(handlers, log.StreamHandler(os.Stderr, format)) } } else { handlers = append( handlers, log.LvlFilterHandler( log.LvlWarn, - log.StderrHandler, + log.StreamHandler(os.Stderr, format), ), ) } diff -Nru lxd-2.6.2/shared/logging/log_posix.go lxd-2.7/shared/logging/log_posix.go --- lxd-2.6.2/shared/logging/log_posix.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/logging/log_posix.go 2016-12-21 00:52:23.000000000 +0000 @@ -7,16 +7,16 @@ ) // getSystemHandler on Linux writes messages to syslog. -func getSystemHandler(syslog string, debug bool) log.Handler { +func getSystemHandler(syslog string, debug bool, format log.Format) log.Handler { // SyslogHandler if syslog != "" { if !debug { return log.LvlFilterHandler( log.LvlInfo, - log.Must.SyslogHandler(syslog, log.LogfmtFormat()), + log.Must.SyslogHandler(syslog, format), ) } else { - return log.Must.SyslogHandler(syslog, log.LogfmtFormat()) + return log.Must.SyslogHandler(syslog, format) } } diff -Nru lxd-2.6.2/shared/logging/log_windows.go lxd-2.7/shared/logging/log_windows.go --- lxd-2.6.2/shared/logging/log_windows.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/logging/log_windows.go 2016-12-21 00:52:23.000000000 +0000 @@ -7,6 +7,6 @@ ) // getSystemHandler on Windows does nothing. -func getSystemHandler(syslog string, debug bool) log.Handler { +func getSystemHandler(syslog string, debug bool, format log.Format) log.Handler { return nil } diff -Nru lxd-2.6.2/shared/log.go lxd-2.7/shared/log.go --- lxd-2.6.2/shared/log.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/log.go 2016-12-21 00:52:23.000000000 +0000 @@ -93,5 +93,5 @@ func PrintStack() { buf := make([]byte, 1<<16) runtime.Stack(buf, true) - LogDebugf("%s", buf) + LogErrorf("%s", buf) } diff -Nru lxd-2.6.2/shared/network.go lxd-2.7/shared/network.go --- lxd-2.6.2/shared/network.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/network.go 2016-12-21 00:52:23.000000000 +0000 @@ -8,6 +8,7 @@ "io" "io/ioutil" "net" + "net/http" "time" "github.com/gorilla/websocket" @@ -227,84 +228,105 @@ return ch } +func defaultReader(conn *websocket.Conn, r io.ReadCloser, readDone chan<- bool) { + /* For now, we don't need to adjust buffer sizes in + * WebsocketMirror, since it's used for interactive things like + * exec. + */ + in := ReaderToChannel(r, -1) + for { + buf, ok := <-in + if !ok { + r.Close() + LogDebugf("sending write barrier") + conn.WriteMessage(websocket.TextMessage, []byte{}) + readDone <- true + return + } + w, err := conn.NextWriter(websocket.BinaryMessage) + if err != nil { + LogDebugf("Got error getting next writer %s", err) + break + } + + _, err = w.Write(buf) + w.Close() + if err != nil { + LogDebugf("Got err writing %s", err) + break + } + } + closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "") + conn.WriteMessage(websocket.CloseMessage, closeMsg) + readDone <- true + r.Close() +} + +func defaultWriter(conn *websocket.Conn, w io.WriteCloser, writeDone chan<- bool) { + for { + mt, r, err := conn.NextReader() + if err != nil { + LogDebugf("Got error getting next reader %s, %s", err, w) + break + } + + if mt == websocket.CloseMessage { + LogDebugf("Got close message for reader") + break + } + + if mt == websocket.TextMessage { + LogDebugf("Got message barrier, resetting stream") + break + } + + buf, err := ioutil.ReadAll(r) + if err != nil { + LogDebugf("Got error writing to writer %s", err) + break + } + i, err := w.Write(buf) + if i != len(buf) { + LogDebugf("Didn't write all of buf") + break + } + if err != nil { + LogDebugf("Error writing buf %s", err) + break + } + } + writeDone <- true + w.Close() +} + // WebsocketMirror allows mirroring a reader to a websocket and taking the // result and writing it to a writer. This function allows for multiple // mirrorings and correctly negotiates stream endings. However, it means any // websocket.Conns passed to it are live when it returns, and must be closed // explicitly. -func WebsocketMirror(conn *websocket.Conn, w io.WriteCloser, r io.ReadCloser) (chan bool, chan bool) { +type WebSocketMirrorReader func(conn *websocket.Conn, r io.ReadCloser, readDone chan<- bool) +type WebSocketMirrorWriter func(conn *websocket.Conn, w io.WriteCloser, writeDone chan<- bool) + +func WebsocketMirror(conn *websocket.Conn, w io.WriteCloser, r io.ReadCloser, Reader WebSocketMirrorReader, Writer WebSocketMirrorWriter) (chan bool, chan bool) { readDone := make(chan bool, 1) writeDone := make(chan bool, 1) - go func(conn *websocket.Conn, w io.WriteCloser) { - for { - mt, r, err := conn.NextReader() - if err != nil { - LogDebugf("Got error getting next reader %s, %s", err, w) - break - } - - if mt == websocket.CloseMessage { - LogDebugf("Got close message for reader") - break - } - - if mt == websocket.TextMessage { - LogDebugf("Got message barrier, resetting stream") - break - } - buf, err := ioutil.ReadAll(r) - if err != nil { - LogDebugf("Got error writing to writer %s", err) - break - } - i, err := w.Write(buf) - if i != len(buf) { - LogDebugf("Didn't write all of buf") - break - } - if err != nil { - LogDebugf("Error writing buf %s", err) - break - } - } - writeDone <- true - w.Close() - }(conn, w) + ReadFunc := Reader + if ReadFunc == nil { + ReadFunc = defaultReader + } - go func(conn *websocket.Conn, r io.ReadCloser) { - /* For now, we don't need to adjust buffer sizes in - * WebsocketMirror, since it's used for interactive things like - * exec. - */ - in := ReaderToChannel(r, -1) - for { - buf, ok := <-in - if !ok { - r.Close() - LogDebugf("sending write barrier") - conn.WriteMessage(websocket.TextMessage, []byte{}) - readDone <- true - return - } - w, err := conn.NextWriter(websocket.BinaryMessage) - if err != nil { - LogDebugf("Got error getting next writer %s", err) - break - } + WriteFunc := Writer + if WriteFunc == nil { + WriteFunc = defaultWriter + } - _, err = w.Write(buf) - w.Close() - if err != nil { - LogDebugf("Got err writing %s", err) - break - } - } - closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "") - conn.WriteMessage(websocket.CloseMessage, closeMsg) - readDone <- true - r.Close() - }(conn, r) + go ReadFunc(conn, r, readDone) + go WriteFunc(conn, w, writeDone) return readDone, writeDone } + +var WebsocketUpgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { return true }, +} diff -Nru lxd-2.6.2/shared/network_linux.go lxd-2.7/shared/network_linux.go --- lxd-2.6.2/shared/network_linux.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/network_linux.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,48 @@ +// +build linux + +package shared + +import ( + "io" + + "github.com/gorilla/websocket" +) + +func WebsocketExecMirror(conn *websocket.Conn, w io.WriteCloser, r io.ReadCloser, exited chan bool, fd int) (chan bool, chan bool) { + readDone := make(chan bool, 1) + writeDone := make(chan bool, 1) + + go defaultWriter(conn, w, writeDone) + + go func(conn *websocket.Conn, r io.ReadCloser) { + in := ExecReaderToChannel(r, -1, exited, fd) + for { + buf, ok := <-in + if !ok { + r.Close() + LogDebugf("sending write barrier") + conn.WriteMessage(websocket.TextMessage, []byte{}) + readDone <- true + return + } + w, err := conn.NextWriter(websocket.BinaryMessage) + if err != nil { + LogDebugf("Got error getting next writer %s", err) + break + } + + _, err = w.Write(buf) + w.Close() + if err != nil { + LogDebugf("Got err writing %s", err) + break + } + } + closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "") + conn.WriteMessage(websocket.CloseMessage, closeMsg) + readDone <- true + r.Close() + }(conn, r) + + return readDone, writeDone +} diff -Nru lxd-2.6.2/shared/operation.go lxd-2.7/shared/operation.go --- lxd-2.6.2/shared/operation.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/operation.go 2016-12-21 00:52:23.000000000 +0000 @@ -1,16 +1,9 @@ package shared import ( - "net/http" "time" - - "github.com/gorilla/websocket" ) -var WebsocketUpgrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { return true }, -} - type Operation struct { Id string `json:"id"` Class string `json:"class"` diff -Nru lxd-2.6.2/shared/osarch/architectures.go lxd-2.7/shared/osarch/architectures.go --- lxd-2.6.2/shared/osarch/architectures.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/osarch/architectures.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,107 @@ +package osarch + +import ( + "fmt" +) + +const ( + ARCH_UNKNOWN = 0 + ARCH_32BIT_INTEL_X86 = 1 + ARCH_64BIT_INTEL_X86 = 2 + ARCH_32BIT_ARMV7_LITTLE_ENDIAN = 3 + ARCH_64BIT_ARMV8_LITTLE_ENDIAN = 4 + ARCH_32BIT_POWERPC_BIG_ENDIAN = 5 + ARCH_64BIT_POWERPC_BIG_ENDIAN = 6 + ARCH_64BIT_POWERPC_LITTLE_ENDIAN = 7 + ARCH_64BIT_S390_BIG_ENDIAN = 8 +) + +var architectureNames = map[int]string{ + ARCH_32BIT_INTEL_X86: "i686", + ARCH_64BIT_INTEL_X86: "x86_64", + ARCH_32BIT_ARMV7_LITTLE_ENDIAN: "armv7l", + ARCH_64BIT_ARMV8_LITTLE_ENDIAN: "aarch64", + ARCH_32BIT_POWERPC_BIG_ENDIAN: "ppc", + ARCH_64BIT_POWERPC_BIG_ENDIAN: "ppc64", + ARCH_64BIT_POWERPC_LITTLE_ENDIAN: "ppc64le", + ARCH_64BIT_S390_BIG_ENDIAN: "s390x", +} + +var architectureAliases = map[int][]string{ + ARCH_32BIT_INTEL_X86: []string{"i386"}, + ARCH_64BIT_INTEL_X86: []string{"amd64"}, + ARCH_32BIT_ARMV7_LITTLE_ENDIAN: []string{"armel", "armhf"}, + ARCH_64BIT_ARMV8_LITTLE_ENDIAN: []string{"arm64"}, + ARCH_32BIT_POWERPC_BIG_ENDIAN: []string{"powerpc"}, + ARCH_64BIT_POWERPC_BIG_ENDIAN: []string{"powerpc64"}, + ARCH_64BIT_POWERPC_LITTLE_ENDIAN: []string{"ppc64el"}, +} + +var architecturePersonalities = map[int]string{ + ARCH_32BIT_INTEL_X86: "linux32", + ARCH_64BIT_INTEL_X86: "linux64", + ARCH_32BIT_ARMV7_LITTLE_ENDIAN: "linux32", + ARCH_64BIT_ARMV8_LITTLE_ENDIAN: "linux64", + ARCH_32BIT_POWERPC_BIG_ENDIAN: "linux32", + ARCH_64BIT_POWERPC_BIG_ENDIAN: "linux64", + ARCH_64BIT_POWERPC_LITTLE_ENDIAN: "linux64", + ARCH_64BIT_S390_BIG_ENDIAN: "linux64", +} + +var architectureSupportedPersonalities = map[int][]int{ + ARCH_32BIT_INTEL_X86: []int{}, + ARCH_64BIT_INTEL_X86: []int{ARCH_32BIT_INTEL_X86}, + ARCH_32BIT_ARMV7_LITTLE_ENDIAN: []int{}, + ARCH_64BIT_ARMV8_LITTLE_ENDIAN: []int{ARCH_32BIT_ARMV7_LITTLE_ENDIAN}, + ARCH_32BIT_POWERPC_BIG_ENDIAN: []int{}, + ARCH_64BIT_POWERPC_BIG_ENDIAN: []int{ARCH_32BIT_POWERPC_BIG_ENDIAN}, + ARCH_64BIT_POWERPC_LITTLE_ENDIAN: []int{}, + ARCH_64BIT_S390_BIG_ENDIAN: []int{}, +} + +const ArchitectureDefault = "x86_64" + +func ArchitectureName(arch int) (string, error) { + arch_name, exists := architectureNames[arch] + if exists { + return arch_name, nil + } + + return "unknown", fmt.Errorf("Architecture isn't supported: %d", arch) +} + +func ArchitectureId(arch string) (int, error) { + for arch_id, arch_name := range architectureNames { + if arch_name == arch { + return arch_id, nil + } + } + + for arch_id, arch_aliases := range architectureAliases { + for _, arch_name := range arch_aliases { + if arch_name == arch { + return arch_id, nil + } + } + } + + return 0, fmt.Errorf("Architecture isn't supported: %s", arch) +} + +func ArchitecturePersonality(arch int) (string, error) { + arch_personality, exists := architecturePersonalities[arch] + if exists { + return arch_personality, nil + } + + return "", fmt.Errorf("Architecture isn't supported: %d", arch) +} + +func ArchitecturePersonalities(arch int) ([]int, error) { + personalities, exists := architectureSupportedPersonalities[arch] + if exists { + return personalities, nil + } + + return []int{}, fmt.Errorf("Architecture isn't supported: %d", arch) +} diff -Nru lxd-2.6.2/shared/osarch/architectures_linux.go lxd-2.7/shared/osarch/architectures_linux.go --- lxd-2.6.2/shared/osarch/architectures_linux.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/osarch/architectures_linux.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,24 @@ +// +build linux + +package osarch + +import ( + "syscall" +) + +func ArchitectureGetLocal() (string, error) { + uname := syscall.Utsname{} + if err := syscall.Uname(&uname); err != nil { + return ArchitectureDefault, err + } + + architectureName := "" + for _, c := range uname.Machine { + if c == 0 { + break + } + architectureName += string(byte(c)) + } + + return architectureName, nil +} diff -Nru lxd-2.6.2/shared/osarch/architectures_others.go lxd-2.7/shared/osarch/architectures_others.go --- lxd-2.6.2/shared/osarch/architectures_others.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/osarch/architectures_others.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,7 @@ +// +build !linux + +package osarch + +func ArchitectureGetLocal() (string, error) { + return ArchitectureDefault, nil +} diff -Nru lxd-2.6.2/shared/simplestreams/simplestreams.go lxd-2.7/shared/simplestreams/simplestreams.go --- lxd-2.6.2/shared/simplestreams/simplestreams.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/simplestreams/simplestreams.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,663 @@ +package simplestreams + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/ioprogress" + "github.com/lxc/lxd/shared/osarch" +) + +type ssSortImage []shared.ImageInfo + +func (a ssSortImage) Len() int { + return len(a) +} + +func (a ssSortImage) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a ssSortImage) Less(i, j int) bool { + if a[i].Properties["os"] == a[j].Properties["os"] { + if a[i].Properties["release"] == a[j].Properties["release"] { + if a[i].CreationDate.UTC().Unix() == 0 { + return true + } + + if a[j].CreationDate.UTC().Unix() == 0 { + return false + } + + return a[i].CreationDate.UTC().Unix() > a[j].CreationDate.UTC().Unix() + } + + if a[i].Properties["release"] == "" { + return false + } + + if a[j].Properties["release"] == "" { + return true + } + + return a[i].Properties["release"] < a[j].Properties["release"] + } + + if a[i].Properties["os"] == "" { + return false + } + + if a[j].Properties["os"] == "" { + return true + } + + return a[i].Properties["os"] < a[j].Properties["os"] +} + +var ssDefaultOS = map[string]string{ + "https://cloud-images.ubuntu.com": "ubuntu", +} + +type SimpleStreamsManifest struct { + Updated string `json:"updated"` + DataType string `json:"datatype"` + Format string `json:"format"` + License string `json:"license"` + Products map[string]SimpleStreamsManifestProduct `json:"products"` +} + +func (s *SimpleStreamsManifest) ToLXD() ([]shared.ImageInfo, map[string][][]string) { + downloads := map[string][][]string{} + + images := []shared.ImageInfo{} + nameLayout := "20060102" + eolLayout := "2006-01-02" + + for _, product := range s.Products { + // Skip unsupported architectures + architecture, err := osarch.ArchitectureId(product.Architecture) + if err != nil { + continue + } + + architectureName, err := osarch.ArchitectureName(architecture) + if err != nil { + continue + } + + for name, version := range product.Versions { + // Short of anything better, use the name as date (see format above) + if len(name) < 8 { + continue + } + + creationDate, err := time.Parse(nameLayout, name[0:8]) + if err != nil { + continue + } + + size := int64(0) + filename := "" + fingerprint := "" + + metaPath := "" + metaHash := "" + rootfsPath := "" + rootfsHash := "" + + found := 0 + for _, item := range version.Items { + // Skip the files we don't care about + if !shared.StringInSlice(item.FileType, []string{"root.tar.xz", "lxd.tar.xz", "squashfs"}) { + continue + } + found += 1 + + if fingerprint == "" { + if item.LXDHashSha256SquashFs != "" { + fingerprint = item.LXDHashSha256SquashFs + } else if item.LXDHashSha256RootXz != "" { + fingerprint = item.LXDHashSha256RootXz + } else if item.LXDHashSha256 != "" { + fingerprint = item.LXDHashSha256 + } + } + + if item.FileType == "lxd.tar.xz" { + fields := strings.Split(item.Path, "/") + filename = fields[len(fields)-1] + metaPath = item.Path + metaHash = item.HashSha256 + + size += item.Size + } + + if rootfsPath == "" || rootfsHash == "" { + if item.FileType == "squashfs" { + rootfsPath = item.Path + rootfsHash = item.HashSha256 + } + + if item.FileType == "root.tar.xz" { + rootfsPath = item.Path + rootfsHash = item.HashSha256 + } + + size += item.Size + } + } + + if found < 2 || size == 0 || filename == "" || fingerprint == "" { + // Invalid image + continue + } + + // Generate the actual image entry + description := fmt.Sprintf("%s %s %s", product.OperatingSystem, product.ReleaseTitle, product.Architecture) + if version.Label != "" { + description = fmt.Sprintf("%s (%s)", description, version.Label) + } + description = fmt.Sprintf("%s (%s)", description, name) + + image := shared.ImageInfo{} + image.Architecture = architectureName + image.Public = true + image.Size = size + image.CreationDate = creationDate + image.UploadDate = creationDate + image.Filename = filename + image.Fingerprint = fingerprint + image.Properties = map[string]string{ + "os": product.OperatingSystem, + "release": product.Release, + "version": product.Version, + "architecture": product.Architecture, + "label": version.Label, + "serial": name, + "description": description, + } + + // Add the provided aliases + if product.Aliases != "" { + image.Aliases = []shared.ImageAlias{} + for _, entry := range strings.Split(product.Aliases, ",") { + image.Aliases = append(image.Aliases, shared.ImageAlias{Name: entry}) + } + } + + // Clear unset properties + for k, v := range image.Properties { + if v == "" { + delete(image.Properties, k) + } + } + + // Attempt to parse the EOL + image.ExpiryDate = time.Unix(0, 0).UTC() + if product.SupportedEOL != "" { + eolDate, err := time.Parse(eolLayout, product.SupportedEOL) + if err == nil { + image.ExpiryDate = eolDate + } + } + + downloads[fingerprint] = [][]string{[]string{metaPath, metaHash, "meta"}, []string{rootfsPath, rootfsHash, "root"}} + images = append(images, image) + } + } + + return images, downloads +} + +type SimpleStreamsManifestProduct struct { + Aliases string `json:"aliases"` + Architecture string `json:"arch"` + OperatingSystem string `json:"os"` + Release string `json:"release"` + ReleaseCodename string `json:"release_codename"` + ReleaseTitle string `json:"release_title"` + Supported bool `json:"supported"` + SupportedEOL string `json:"support_eol"` + Version string `json:"version"` + Versions map[string]SimpleStreamsManifestProductVersion `json:"versions"` +} + +type SimpleStreamsManifestProductVersion struct { + PublicName string `json:"pubname"` + Label string `json:"label"` + Items map[string]SimpleStreamsManifestProductVersionItem `json:"items"` +} + +type SimpleStreamsManifestProductVersionItem struct { + Path string `json:"path"` + FileType string `json:"ftype"` + HashMd5 string `json:"md5"` + HashSha256 string `json:"sha256"` + LXDHashSha256 string `json:"combined_sha256"` + LXDHashSha256RootXz string `json:"combined_rootxz_sha256"` + LXDHashSha256SquashFs string `json:"combined_squashfs_sha256"` + Size int64 `json:"size"` +} + +type SimpleStreamsIndex struct { + Format string `json:"format"` + Index map[string]SimpleStreamsIndexStream `json:"index"` + Updated string `json:"updated"` +} + +type SimpleStreamsIndexStream struct { + Updated string `json:"updated"` + DataType string `json:"datatype"` + Path string `json:"path"` + Products []string `json:"products"` +} + +func NewClient(url string, httpClient http.Client, useragent string) *SimpleStreams { + return &SimpleStreams{ + http: &httpClient, + url: url, + cachedManifest: map[string]*SimpleStreamsManifest{}, + useragent: useragent, + } +} + +type SimpleStreams struct { + http *http.Client + url string + useragent string + + cachedIndex *SimpleStreamsIndex + cachedManifest map[string]*SimpleStreamsManifest + cachedImages []shared.ImageInfo + cachedAliases map[string]*shared.ImageAliasesEntry +} + +func (s *SimpleStreams) parseIndex() (*SimpleStreamsIndex, error) { + if s.cachedIndex != nil { + return s.cachedIndex, nil + } + + req, err := http.NewRequest("GET", fmt.Sprintf("%s/streams/v1/index.json", s.url), nil) + if err != nil { + return nil, err + } + + if s.useragent != "" { + req.Header.Set("User-Agent", s.useragent) + } + + r, err := s.http.Do(req) + if err != nil { + return nil, err + } + defer r.Body.Close() + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + + // Parse the idnex + ssIndex := SimpleStreamsIndex{} + err = json.Unmarshal(body, &ssIndex) + if err != nil { + return nil, err + } + + s.cachedIndex = &ssIndex + + return &ssIndex, nil +} + +func (s *SimpleStreams) parseManifest(path string) (*SimpleStreamsManifest, error) { + if s.cachedManifest[path] != nil { + return s.cachedManifest[path], nil + } + + req, err := http.NewRequest("GET", fmt.Sprintf("%s/%s", s.url, path), nil) + if err != nil { + return nil, err + } + + if s.useragent != "" { + req.Header.Set("User-Agent", s.useragent) + } + + r, err := s.http.Do(req) + if err != nil { + return nil, err + } + defer r.Body.Close() + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + + // Parse the idnex + ssManifest := SimpleStreamsManifest{} + err = json.Unmarshal(body, &ssManifest) + if err != nil { + return nil, err + } + + s.cachedManifest[path] = &ssManifest + + return &ssManifest, nil +} + +func (s *SimpleStreams) applyAliases(images []shared.ImageInfo) ([]shared.ImageInfo, map[string]*shared.ImageAliasesEntry, error) { + aliases := map[string]*shared.ImageAliasesEntry{} + + sort.Sort(ssSortImage(images)) + + defaultOS := "" + for k, v := range ssDefaultOS { + if strings.HasPrefix(s.url, k) { + defaultOS = v + break + } + } + + addAlias := func(name string, fingerprint string) *shared.ImageAlias { + if defaultOS != "" { + name = strings.TrimPrefix(name, fmt.Sprintf("%s/", defaultOS)) + } + + if aliases[name] != nil { + return nil + } + + alias := shared.ImageAliasesEntry{} + alias.Name = name + alias.Target = fingerprint + aliases[name] = &alias + + return &shared.ImageAlias{Name: name} + } + + architectureName, _ := osarch.ArchitectureGetLocal() + + newImages := []shared.ImageInfo{} + for _, image := range images { + if image.Aliases != nil { + // Build a new list of aliases from the provided ones + aliases := image.Aliases + image.Aliases = nil + + for _, entry := range aliases { + // Short + if image.Architecture == architectureName { + alias := addAlias(fmt.Sprintf("%s", entry.Name), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + } + + // Medium + alias := addAlias(fmt.Sprintf("%s/%s", entry.Name, image.Properties["architecture"]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + } + } + + newImages = append(newImages, image) + } + + return newImages, aliases, nil +} + +func (s *SimpleStreams) getImages() ([]shared.ImageInfo, map[string]*shared.ImageAliasesEntry, error) { + if s.cachedImages != nil && s.cachedAliases != nil { + return s.cachedImages, s.cachedAliases, nil + } + + images := []shared.ImageInfo{} + + // Load the main index + ssIndex, err := s.parseIndex() + if err != nil { + return nil, nil, err + } + + // Iterate through the various image manifests + for _, entry := range ssIndex.Index { + // We only care about images + if entry.DataType != "image-downloads" { + continue + } + + // No point downloading an empty image list + if len(entry.Products) == 0 { + continue + } + + manifest, err := s.parseManifest(entry.Path) + if err != nil { + return nil, nil, err + } + + manifestImages, _ := manifest.ToLXD() + + for _, image := range manifestImages { + images = append(images, image) + } + } + + // Setup the aliases + images, aliases, err := s.applyAliases(images) + if err != nil { + return nil, nil, err + } + + s.cachedImages = images + s.cachedAliases = aliases + + return images, aliases, nil +} + +func (s *SimpleStreams) getPaths(fingerprint string) ([][]string, error) { + // Load the main index + ssIndex, err := s.parseIndex() + if err != nil { + return nil, err + } + + // Iterate through the various image manifests + for _, entry := range ssIndex.Index { + // We only care about images + if entry.DataType != "image-downloads" { + continue + } + + // No point downloading an empty image list + if len(entry.Products) == 0 { + continue + } + + manifest, err := s.parseManifest(entry.Path) + if err != nil { + return nil, err + } + + manifestImages, downloads := manifest.ToLXD() + + for _, image := range manifestImages { + if strings.HasPrefix(image.Fingerprint, fingerprint) { + urls := [][]string{} + for _, path := range downloads[image.Fingerprint] { + urls = append(urls, []string{path[0], path[1], path[2]}) + } + return urls, nil + } + } + } + + return nil, fmt.Errorf("Couldn't find the requested image") +} + +func (s *SimpleStreams) downloadFile(path string, hash string, target string, progress func(int64, int64)) error { + download := func(url string, hash string, target string) error { + out, err := os.Create(target) + if err != nil { + return err + } + defer out.Close() + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + if s.useragent != "" { + req.Header.Set("User-Agent", s.useragent) + } + + resp, err := s.http.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("invalid simplestreams source: got %d looking for %s", resp.StatusCode, path) + } + + body := &ioprogress.ProgressReader{ + ReadCloser: resp.Body, + Tracker: &ioprogress.ProgressTracker{ + Length: resp.ContentLength, + Handler: progress, + }, + } + + sha256 := sha256.New() + _, err = io.Copy(io.MultiWriter(out, sha256), body) + if err != nil { + return err + } + + result := fmt.Sprintf("%x", sha256.Sum(nil)) + if result != hash { + os.Remove(target) + return fmt.Errorf("Hash mismatch for %s: %s != %s", path, result, hash) + } + + return nil + } + + // Try http first + if strings.HasPrefix(s.url, "https://") { + err := download(fmt.Sprintf("http://%s/%s", strings.TrimPrefix(s.url, "https://"), path), hash, target) + if err == nil { + return nil + } + } + + err := download(fmt.Sprintf("%s/%s", s.url, path), hash, target) + if err != nil { + return err + } + + return nil +} + +func (s *SimpleStreams) ListAliases() (shared.ImageAliases, error) { + _, aliasesMap, err := s.getImages() + if err != nil { + return nil, err + } + + aliases := shared.ImageAliases{} + + for _, alias := range aliasesMap { + aliases = append(aliases, *alias) + } + + return aliases, nil +} + +func (s *SimpleStreams) ListImages() ([]shared.ImageInfo, error) { + images, _, err := s.getImages() + return images, err +} + +func (s *SimpleStreams) GetAlias(name string) string { + _, aliasesMap, err := s.getImages() + if err != nil { + return "" + } + + alias, ok := aliasesMap[name] + if !ok { + return "" + } + + return alias.Target +} + +func (s *SimpleStreams) GetImageInfo(fingerprint string) (*shared.ImageInfo, error) { + images, _, err := s.getImages() + if err != nil { + return nil, err + } + + for _, image := range images { + if strings.HasPrefix(image.Fingerprint, fingerprint) { + return &image, nil + } + } + + return nil, fmt.Errorf("The requested image couldn't be found.") +} + +func (s *SimpleStreams) ExportImage(image string, target string) (string, error) { + if !shared.IsDir(target) { + return "", fmt.Errorf("Split images can only be written to a directory.") + } + + paths, err := s.getPaths(image) + if err != nil { + return "", err + } + + for _, path := range paths { + fields := strings.Split(path[0], "/") + targetFile := filepath.Join(target, fields[len(fields)-1]) + + err := s.downloadFile(path[0], path[1], targetFile, nil) + if err != nil { + return "", err + } + } + + return target, nil +} + +func (s *SimpleStreams) Download(image string, file string, target string, progress func(int64, int64)) error { + paths, err := s.getPaths(image) + if err != nil { + return err + } + + for _, path := range paths { + if file != path[2] { + continue + } + + return s.downloadFile(path[0], path[1], target, progress) + } + + return fmt.Errorf("The file couldn't be found.") +} diff -Nru lxd-2.6.2/shared/simplestreams.go lxd-2.7/shared/simplestreams.go --- lxd-2.6.2/shared/simplestreams.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/simplestreams.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,664 +0,0 @@ -package shared - -import ( - "crypto/sha256" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "path/filepath" - "sort" - "strings" - "time" -) - -type ssSortImage []ImageInfo - -func (a ssSortImage) Len() int { - return len(a) -} - -func (a ssSortImage) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a ssSortImage) Less(i, j int) bool { - if a[i].Properties["os"] == a[j].Properties["os"] { - if a[i].Properties["release"] == a[j].Properties["release"] { - if a[i].CreationDate.UTC().Unix() == 0 { - return true - } - - if a[j].CreationDate.UTC().Unix() == 0 { - return false - } - - return a[i].CreationDate.UTC().Unix() > a[j].CreationDate.UTC().Unix() - } - - if a[i].Properties["release"] == "" { - return false - } - - if a[j].Properties["release"] == "" { - return true - } - - return a[i].Properties["release"] < a[j].Properties["release"] - } - - if a[i].Properties["os"] == "" { - return false - } - - if a[j].Properties["os"] == "" { - return true - } - - return a[i].Properties["os"] < a[j].Properties["os"] -} - -var ssDefaultOS = map[string]string{ - "https://cloud-images.ubuntu.com": "ubuntu", -} - -type SimpleStreamsManifest struct { - Updated string `json:"updated"` - DataType string `json:"datatype"` - Format string `json:"format"` - License string `json:"license"` - Products map[string]SimpleStreamsManifestProduct `json:"products"` -} - -func (s *SimpleStreamsManifest) ToLXD() ([]ImageInfo, map[string][][]string) { - downloads := map[string][][]string{} - - images := []ImageInfo{} - nameLayout := "20060102" - eolLayout := "2006-01-02" - - for _, product := range s.Products { - // Skip unsupported architectures - architecture, err := ArchitectureId(product.Architecture) - if err != nil { - continue - } - - architectureName, err := ArchitectureName(architecture) - if err != nil { - continue - } - - for name, version := range product.Versions { - // Short of anything better, use the name as date (see format above) - if len(name) < 8 { - continue - } - - creationDate, err := time.Parse(nameLayout, name[0:8]) - if err != nil { - continue - } - - size := int64(0) - filename := "" - fingerprint := "" - - metaPath := "" - metaHash := "" - rootfsPath := "" - rootfsHash := "" - - found := 0 - for _, item := range version.Items { - // Skip the files we don't care about - if !StringInSlice(item.FileType, []string{"root.tar.xz", "lxd.tar.xz", "squashfs"}) { - continue - } - found += 1 - - if fingerprint == "" { - if item.LXDHashSha256SquashFs != "" { - fingerprint = item.LXDHashSha256SquashFs - } else if item.LXDHashSha256RootXz != "" { - fingerprint = item.LXDHashSha256RootXz - } else if item.LXDHashSha256 != "" { - fingerprint = item.LXDHashSha256 - } - } - - if item.FileType == "lxd.tar.xz" { - fields := strings.Split(item.Path, "/") - filename = fields[len(fields)-1] - metaPath = item.Path - metaHash = item.HashSha256 - - size += item.Size - } - - if rootfsPath == "" || rootfsHash == "" { - if item.FileType == "squashfs" { - rootfsPath = item.Path - rootfsHash = item.HashSha256 - } - - if item.FileType == "root.tar.xz" { - rootfsPath = item.Path - rootfsHash = item.HashSha256 - } - - size += item.Size - } - } - - if found < 2 || size == 0 || filename == "" || fingerprint == "" { - // Invalid image - continue - } - - // Generate the actual image entry - description := fmt.Sprintf("%s %s %s", product.OperatingSystem, product.ReleaseTitle, product.Architecture) - if version.Label != "" { - description = fmt.Sprintf("%s (%s)", description, version.Label) - } - description = fmt.Sprintf("%s (%s)", description, name) - - image := ImageInfo{} - image.Architecture = architectureName - image.Public = true - image.Size = size - image.CreationDate = creationDate - image.UploadDate = creationDate - image.Filename = filename - image.Fingerprint = fingerprint - image.Properties = map[string]string{ - "os": product.OperatingSystem, - "release": product.Release, - "version": product.Version, - "architecture": product.Architecture, - "label": version.Label, - "serial": name, - "description": description, - } - - // Add the provided aliases - if product.Aliases != "" { - image.Aliases = []ImageAlias{} - for _, entry := range strings.Split(product.Aliases, ",") { - image.Aliases = append(image.Aliases, ImageAlias{Name: entry}) - } - } - - // Clear unset properties - for k, v := range image.Properties { - if v == "" { - delete(image.Properties, k) - } - } - - // Attempt to parse the EOL - image.ExpiryDate = time.Unix(0, 0).UTC() - if product.SupportedEOL != "" { - eolDate, err := time.Parse(eolLayout, product.SupportedEOL) - if err == nil { - image.ExpiryDate = eolDate - } - } - - downloads[fingerprint] = [][]string{[]string{metaPath, metaHash, "meta"}, []string{rootfsPath, rootfsHash, "root"}} - images = append(images, image) - } - } - - return images, downloads -} - -type SimpleStreamsManifestProduct struct { - Aliases string `json:"aliases"` - Architecture string `json:"arch"` - OperatingSystem string `json:"os"` - Release string `json:"release"` - ReleaseCodename string `json:"release_codename"` - ReleaseTitle string `json:"release_title"` - Supported bool `json:"supported"` - SupportedEOL string `json:"support_eol"` - Version string `json:"version"` - Versions map[string]SimpleStreamsManifestProductVersion `json:"versions"` -} - -type SimpleStreamsManifestProductVersion struct { - PublicName string `json:"pubname"` - Label string `json:"label"` - Items map[string]SimpleStreamsManifestProductVersionItem `json:"items"` -} - -type SimpleStreamsManifestProductVersionItem struct { - Path string `json:"path"` - FileType string `json:"ftype"` - HashMd5 string `json:"md5"` - HashSha256 string `json:"sha256"` - LXDHashSha256 string `json:"combined_sha256"` - LXDHashSha256RootXz string `json:"combined_rootxz_sha256"` - LXDHashSha256SquashFs string `json:"combined_squashfs_sha256"` - Size int64 `json:"size"` -} - -type SimpleStreamsIndex struct { - Format string `json:"format"` - Index map[string]SimpleStreamsIndexStream `json:"index"` - Updated string `json:"updated"` -} - -type SimpleStreamsIndexStream struct { - Updated string `json:"updated"` - DataType string `json:"datatype"` - Path string `json:"path"` - Products []string `json:"products"` -} - -func SimpleStreamsClient(url string, proxy func(*http.Request) (*url.URL, error)) (*SimpleStreams, error) { - // Setup a http client - tlsConfig, err := GetTLSConfig("", "", "", nil) - if err != nil { - return nil, err - } - - tr := &http.Transport{ - TLSClientConfig: tlsConfig, - Dial: RFC3493Dialer, - Proxy: proxy, - } - - myHttp := http.Client{ - Transport: tr, - } - - return &SimpleStreams{ - http: &myHttp, - url: url, - cachedManifest: map[string]*SimpleStreamsManifest{}}, nil -} - -type SimpleStreams struct { - http *http.Client - url string - - cachedIndex *SimpleStreamsIndex - cachedManifest map[string]*SimpleStreamsManifest - cachedImages []ImageInfo - cachedAliases map[string]*ImageAliasesEntry -} - -func (s *SimpleStreams) parseIndex() (*SimpleStreamsIndex, error) { - if s.cachedIndex != nil { - return s.cachedIndex, nil - } - - req, err := http.NewRequest("GET", fmt.Sprintf("%s/streams/v1/index.json", s.url), nil) - if err != nil { - return nil, err - } - req.Header.Set("User-Agent", UserAgent) - - r, err := s.http.Do(req) - if err != nil { - return nil, err - } - defer r.Body.Close() - - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, err - } - - // Parse the idnex - ssIndex := SimpleStreamsIndex{} - err = json.Unmarshal(body, &ssIndex) - if err != nil { - return nil, err - } - - s.cachedIndex = &ssIndex - - return &ssIndex, nil -} - -func (s *SimpleStreams) parseManifest(path string) (*SimpleStreamsManifest, error) { - if s.cachedManifest[path] != nil { - return s.cachedManifest[path], nil - } - - req, err := http.NewRequest("GET", fmt.Sprintf("%s/%s", s.url, path), nil) - if err != nil { - return nil, err - } - req.Header.Set("User-Agent", UserAgent) - - r, err := s.http.Do(req) - if err != nil { - return nil, err - } - defer r.Body.Close() - - body, err := ioutil.ReadAll(r.Body) - if err != nil { - return nil, err - } - - // Parse the idnex - ssManifest := SimpleStreamsManifest{} - err = json.Unmarshal(body, &ssManifest) - if err != nil { - return nil, err - } - - s.cachedManifest[path] = &ssManifest - - return &ssManifest, nil -} - -func (s *SimpleStreams) applyAliases(images []ImageInfo) ([]ImageInfo, map[string]*ImageAliasesEntry, error) { - aliases := map[string]*ImageAliasesEntry{} - - sort.Sort(ssSortImage(images)) - - defaultOS := "" - for k, v := range ssDefaultOS { - if strings.HasPrefix(s.url, k) { - defaultOS = v - break - } - } - - addAlias := func(name string, fingerprint string) *ImageAlias { - if defaultOS != "" { - name = strings.TrimPrefix(name, fmt.Sprintf("%s/", defaultOS)) - } - - if aliases[name] != nil { - return nil - } - - alias := ImageAliasesEntry{} - alias.Name = name - alias.Target = fingerprint - aliases[name] = &alias - - return &ImageAlias{Name: name} - } - - architectureName, _ := ArchitectureGetLocal() - - newImages := []ImageInfo{} - for _, image := range images { - if image.Aliases != nil { - // Build a new list of aliases from the provided ones - aliases := image.Aliases - image.Aliases = nil - - for _, entry := range aliases { - // Short - if image.Architecture == architectureName { - alias := addAlias(fmt.Sprintf("%s", entry.Name), image.Fingerprint) - if alias != nil { - image.Aliases = append(image.Aliases, *alias) - } - } - - // Medium - alias := addAlias(fmt.Sprintf("%s/%s", entry.Name, image.Properties["architecture"]), image.Fingerprint) - if alias != nil { - image.Aliases = append(image.Aliases, *alias) - } - } - } - - newImages = append(newImages, image) - } - - return newImages, aliases, nil -} - -func (s *SimpleStreams) getImages() ([]ImageInfo, map[string]*ImageAliasesEntry, error) { - if s.cachedImages != nil && s.cachedAliases != nil { - return s.cachedImages, s.cachedAliases, nil - } - - images := []ImageInfo{} - - // Load the main index - ssIndex, err := s.parseIndex() - if err != nil { - return nil, nil, err - } - - // Iterate through the various image manifests - for _, entry := range ssIndex.Index { - // We only care about images - if entry.DataType != "image-downloads" { - continue - } - - // No point downloading an empty image list - if len(entry.Products) == 0 { - continue - } - - manifest, err := s.parseManifest(entry.Path) - if err != nil { - return nil, nil, err - } - - manifestImages, _ := manifest.ToLXD() - - for _, image := range manifestImages { - images = append(images, image) - } - } - - // Setup the aliases - images, aliases, err := s.applyAliases(images) - if err != nil { - return nil, nil, err - } - - s.cachedImages = images - s.cachedAliases = aliases - - return images, aliases, nil -} - -func (s *SimpleStreams) getPaths(fingerprint string) ([][]string, error) { - // Load the main index - ssIndex, err := s.parseIndex() - if err != nil { - return nil, err - } - - // Iterate through the various image manifests - for _, entry := range ssIndex.Index { - // We only care about images - if entry.DataType != "image-downloads" { - continue - } - - // No point downloading an empty image list - if len(entry.Products) == 0 { - continue - } - - manifest, err := s.parseManifest(entry.Path) - if err != nil { - return nil, err - } - - manifestImages, downloads := manifest.ToLXD() - - for _, image := range manifestImages { - if strings.HasPrefix(image.Fingerprint, fingerprint) { - urls := [][]string{} - for _, path := range downloads[image.Fingerprint] { - urls = append(urls, []string{path[0], path[1], path[2]}) - } - return urls, nil - } - } - } - - return nil, fmt.Errorf("Couldn't find the requested image") -} - -func (s *SimpleStreams) downloadFile(path string, hash string, target string, progress func(int64, int64)) error { - download := func(url string, hash string, target string) error { - out, err := os.Create(target) - if err != nil { - return err - } - defer out.Close() - - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return err - } - req.Header.Set("User-Agent", UserAgent) - - resp, err := s.http.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("invalid simplestreams source: got %d looking for %s", resp.StatusCode, path) - } - - body := &ProgressReader{ - ReadCloser: resp.Body, - Tracker: &ProgressTracker{ - Length: resp.ContentLength, - Handler: progress, - }, - } - - sha256 := sha256.New() - _, err = io.Copy(io.MultiWriter(out, sha256), body) - if err != nil { - return err - } - - result := fmt.Sprintf("%x", sha256.Sum(nil)) - if result != hash { - os.Remove(target) - return fmt.Errorf("Hash mismatch for %s: %s != %s", path, result, hash) - } - - return nil - } - - // Try http first - if strings.HasPrefix(s.url, "https://") { - err := download(fmt.Sprintf("http://%s/%s", strings.TrimPrefix(s.url, "https://"), path), hash, target) - if err == nil { - return nil - } - } - - err := download(fmt.Sprintf("%s/%s", s.url, path), hash, target) - if err != nil { - return err - } - - return nil -} - -func (s *SimpleStreams) ListAliases() (ImageAliases, error) { - _, aliasesMap, err := s.getImages() - if err != nil { - return nil, err - } - - aliases := ImageAliases{} - - for _, alias := range aliasesMap { - aliases = append(aliases, *alias) - } - - return aliases, nil -} - -func (s *SimpleStreams) ListImages() ([]ImageInfo, error) { - images, _, err := s.getImages() - return images, err -} - -func (s *SimpleStreams) GetAlias(name string) string { - _, aliasesMap, err := s.getImages() - if err != nil { - return "" - } - - alias, ok := aliasesMap[name] - if !ok { - return "" - } - - return alias.Target -} - -func (s *SimpleStreams) GetImageInfo(fingerprint string) (*ImageInfo, error) { - images, _, err := s.getImages() - if err != nil { - return nil, err - } - - for _, image := range images { - if strings.HasPrefix(image.Fingerprint, fingerprint) { - return &image, nil - } - } - - return nil, fmt.Errorf("The requested image couldn't be found.") -} - -func (s *SimpleStreams) ExportImage(image string, target string) (string, error) { - if !IsDir(target) { - return "", fmt.Errorf("Split images can only be written to a directory.") - } - - paths, err := s.getPaths(image) - if err != nil { - return "", err - } - - for _, path := range paths { - fields := strings.Split(path[0], "/") - targetFile := filepath.Join(target, fields[len(fields)-1]) - - err := s.downloadFile(path[0], path[1], targetFile, nil) - if err != nil { - return "", err - } - } - - return target, nil -} - -func (s *SimpleStreams) Download(image string, file string, target string, progress func(int64, int64)) error { - paths, err := s.getPaths(image) - if err != nil { - return err - } - - for _, path := range paths { - if file != path[2] { - continue - } - - return s.downloadFile(path[0], path[1], target, progress) - } - - return fmt.Errorf("The file couldn't be found.") -} diff -Nru lxd-2.6.2/shared/status.go lxd-2.7/shared/status.go --- lxd-2.6.2/shared/status.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/status.go 2016-12-21 00:52:23.000000000 +0000 @@ -47,22 +47,3 @@ func (o StatusCode) IsFinal() bool { return int(o) >= 200 } - -/* - * Create a StatusCode from an lxc.State code. N.B.: we accept an int instead - * of a lxc.State so that the shared code doesn't depend on lxc, which depends - * on liblxc, etc. - */ -func FromLXCState(state int) StatusCode { - return map[int]StatusCode{ - 1: Stopped, - 2: Starting, - 3: Running, - 4: Stopping, - 5: Aborting, - 6: Freezing, - 7: Frozen, - 8: Thawed, - 9: Error, - }[state] -} diff -Nru lxd-2.6.2/shared/util.go lxd-2.7/shared/util.go --- lxd-2.6.2/shared/util.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/util.go 2016-12-21 00:52:23.000000000 +0000 @@ -19,7 +19,6 @@ "regexp" "strconv" "strings" - "time" ) const SnapshotDelimiter = "/" @@ -738,113 +737,6 @@ return s } -type ProgressTracker struct { - Length int64 - Handler func(int64, int64) - - percentage float64 - total int64 - start *time.Time - last *time.Time -} - -func (pt *ProgressTracker) Update(n int) { - // Skip the rest if no handler attached - if pt.Handler == nil { - return - } - - // Initialize start time if needed - if pt.start == nil { - cur := time.Now() - pt.start = &cur - pt.last = pt.start - } - - // Skip if no data to count - if n <= 0 { - return - } - - // Update interval handling - var percentage float64 - if pt.Length > 0 { - // If running in relative mode, check that we increased by at least 1% - percentage = float64(pt.total) / float64(pt.Length) * float64(100) - if percentage-pt.percentage < 0.9 { - return - } - } else { - // If running in absolute mode, check that at least a second elapsed - interval := time.Since(*pt.last).Seconds() - if interval < 1 { - return - } - } - - // Determine speed - speedInt := int64(0) - duration := time.Since(*pt.start).Seconds() - if duration > 0 { - speed := float64(pt.total) / duration - speedInt = int64(speed) - } - - // Determine progress - progressInt := int64(0) - if pt.Length > 0 { - pt.percentage = percentage - progressInt = int64(1 - (int(percentage) % 1) + int(percentage)) - if progressInt > 100 { - progressInt = 100 - } - } else { - progressInt = pt.total - - // Update timestamp - cur := time.Now() - pt.last = &cur - } - - pt.Handler(progressInt, speedInt) -} - -type ProgressReader struct { - io.ReadCloser - Tracker *ProgressTracker -} - -func (pt *ProgressReader) Read(p []byte) (int, error) { - // Do normal reader tasks - n, err := pt.ReadCloser.Read(p) - - // Do the actual progress tracking - if pt.Tracker != nil { - pt.Tracker.total += int64(n) - pt.Tracker.Update(n) - } - - return n, err -} - -type ProgressWriter struct { - io.WriteCloser - Tracker *ProgressTracker -} - -func (pt *ProgressWriter) Write(p []byte) (int, error) { - // Do normal writer tasks - n, err := pt.WriteCloser.Write(p) - - // Do the actual progress tracking - if pt.Tracker != nil { - pt.Tracker.total += int64(n) - pt.Tracker.Update(n) - } - - return n, err -} - func RunCommand(name string, arg ...string) error { output, err := exec.Command(name, arg...).CombinedOutput() if err != nil { diff -Nru lxd-2.6.2/shared/util_linux.go lxd-2.7/shared/util_linux.go --- lxd-2.6.2/shared/util_linux.go 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/shared/util_linux.go 2016-12-21 00:52:23.000000000 +0000 @@ -6,9 +6,12 @@ import ( "errors" "fmt" + "io" "os" "os/exec" "strings" + "sync" + "sync/atomic" "syscall" "unsafe" ) @@ -25,6 +28,7 @@ #include #include #include +#include #include #include @@ -181,9 +185,50 @@ close(fd); return 0; } + +int get_poll_revents(int lfd, int timeout, int flags, int *revents, int *saved_errno) +{ + int ret; + struct pollfd pfd = {lfd, flags, 0}; + +again: + ret = poll(&pfd, 1, timeout); + if (ret < 0) { + if (errno == EINTR) + goto again; + + *saved_errno = errno; + fprintf(stderr, "Failed to poll() on file descriptor.\n"); + return -1; + } + + *revents = pfd.revents; + + return ret; +} */ import "C" +const POLLIN int = C.POLLIN +const POLLPRI int = C.POLLPRI +const POLLNVAL int = C.POLLNVAL +const POLLERR int = C.POLLERR +const POLLHUP int = C.POLLHUP +const POLLRDHUP int = C.POLLRDHUP + +func GetPollRevents(fd int, timeout int, flags int) (int, int, error) { + var err error + revents := C.int(0) + saved_errno := C.int(0) + + ret := C.get_poll_revents(C.int(fd), C.int(timeout), C.int(flags), &revents, &saved_errno) + if int(ret) < 0 { + err = syscall.Errno(saved_errno) + } + + return int(ret), int(revents), err +} + func ShiftOwner(basepath string, path string, uid int, gid int) error { cbasepath := C.CString(basepath) defer C.free(unsafe.Pointer(cbasepath)) @@ -482,3 +527,178 @@ return xattrs, nil } + +// Extensively commented directly in the code. Please leave the comments! +// Looking at this in a couple of months noone will know why and how this works +// anymore. +func ExecReaderToChannel(r io.Reader, bufferSize int, exited <-chan bool, fd int) <-chan []byte { + if bufferSize <= (128 * 1024) { + bufferSize = (128 * 1024) + } + + ch := make(chan ([]byte)) + + // Takes care that the closeChannel() function is exactly executed once. + // This allows us to avoid using a mutex. + var once sync.Once + closeChannel := func() { + close(ch) + } + + // COMMENT(brauner): + // [1]: This function has just one job: Dealing with the case where we + // are running an interactive shell session where we put a process in + // the background that does hold stdin/stdout open, but does not + // generate any output at all. This case cannot be dealt with in the + // following function call. Here's why: Assume the above case, now the + // attached child (the shell in this example) exits. This will not + // generate any poll() event: We won't get POLLHUP because the + // background process is holding stdin/stdout open and noone is writing + // to it. So we effectively block on GetPollRevents() in the function + // below. Hence, we use another go routine here who's only job is to + // handle that case: When we detect that the child has exited we check + // whether a POLLIN or POLLHUP event has been generated. If not, we know + // that there's nothing buffered on stdout and exit. + var attachedChildIsDead int32 = 0 + go func() { + <-exited + + atomic.StoreInt32(&attachedChildIsDead, 1) + + ret, revents, err := GetPollRevents(fd, 0, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP)) + if ret < 0 { + LogErrorf("Failed to poll(POLLIN | POLLPRI | POLLHUP | POLLRDHUP) on file descriptor: %s.", err) + } else if ret > 0 { + if (revents & POLLERR) > 0 { + LogWarnf("Detected poll(POLLERR) event.") + } + } else if ret == 0 { + LogDebugf("No data in stdout: exiting.") + once.Do(closeChannel) + return + } + }() + + go func() { + readSize := (128 * 1024) + offset := 0 + buf := make([]byte, bufferSize) + avoidAtomicLoad := false + + defer once.Do(closeChannel) + for { + nr := 0 + var err error + + ret, revents, err := GetPollRevents(fd, -1, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP)) + if ret < 0 { + // COMMENT(brauner): + // This condition is only reached in cases where we are massively f*cked since we even handle + // EINTR in the underlying C wrapper around poll(). So let's exit here. + LogErrorf("Failed to poll(POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP) on file descriptor: %s. Exiting.", err) + return + } + + // COMMENT(brauner): + // [2]: If the process exits before all its data has been read by us and no other process holds stdin or + // stdout open, then we will observe a (POLLHUP | POLLRDHUP | POLLIN) event. This means, we need to + // keep on reading from the pty file descriptor until we get a simple POLLHUP back. + both := ((revents & (POLLIN | POLLPRI)) > 0) && ((revents & (POLLHUP | POLLRDHUP)) > 0) + if both { + LogDebugf("Detected poll(POLLIN | POLLPRI | POLLHUP | POLLRDHUP) event.") + read := buf[offset : offset+readSize] + nr, err = r.Read(read) + } + + if (revents & POLLERR) > 0 { + LogWarnf("Detected poll(POLLERR) event: exiting.") + return + } + + if ((revents & (POLLIN | POLLPRI)) > 0) && !both { + // COMMENT(brauner): + // This might appear unintuitive at first but is actually a nice trick: Assume we are running + // a shell session in a container and put a process in the background that is writing to + // stdout. Now assume the attached process (aka the shell in this example) exits because we + // used Ctrl+D to send EOF or something. If no other process would be holding stdout open we + // would expect to observe either a (POLLHUP | POLLRDHUP | POLLIN | POLLPRI) event if there + // is still data buffered from the previous process or a simple (POLLHUP | POLLRDHUP) if + // no data is buffered. The fact that we only observe a (POLLIN | POLLPRI) event means that + // another process is holding stdout open and is writing to it. + // One counter argument that can be leveraged is (brauner looks at tycho :)) + // "Hey, you need to write at least one additional tty buffer to make sure that + // everything that the attached child has written is actually shown." + // The answer to that is: + // "This case can only happen if the process has exited and has left data in stdout which + // would generate a (POLLIN | POLLPRI | POLLHUP | POLLRDHUP) event and this case is already + // handled and triggers another codepath. (See [2].)" + if avoidAtomicLoad || atomic.LoadInt32(&attachedChildIsDead) == 1 { + avoidAtomicLoad = true + // COMMENT(brauner): + // Handle race between atomic.StorInt32() in the go routine + // explained in [1] and atomic.LoadInt32() in the go routine + // here: + // We need to check for (POLLHUP | POLLRDHUP) here again since we might + // still be handling a pure POLLIN event from a write prior to the childs + // exit. But the child might have exited right before and performed + // atomic.StoreInt32() to update attachedChildIsDead before we + // performed our atomic.LoadInt32(). This means we accidently hit this + // codepath and are misinformed about the available poll() events. So we + // need to perform a non-blocking poll() again to exclude that case: + // + // - If we detect no (POLLHUP | POLLRDHUP) event we know the child + // has already exited but someone else is holding stdin/stdout open and + // writing to it. + // Note that his case should only ever be triggered in situations like + // running a shell and doing stuff like: + // > ./lxc exec xen1 -- bash + // root@xen1:~# yes & + // . + // . + // . + // now send Ctrl+D or type "exit". By the time the Ctrl+D/exit event is + // triggered, we will have read all of the childs data it has written to + // stdout and so we can assume that anything that comes now belongs to + // the process that is holding stdin/stdout open. + // + // - If we detect a (POLLHUP | POLLRDHUP) event we know that we've + // hit this codepath on accident caused by the race between + // atomic.StoreInt32() in the go routine explained in [1] and + // atomic.LoadInt32() in this go routine. So the next call to + // GetPollRevents() will either return + // (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP) + // or (POLLHUP | POLLRDHUP). Both will trigger another codepath (See [2].) + // that takes care that all data of the child that is buffered in + // stdout is written out. + ret, revents, err := GetPollRevents(fd, 0, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP)) + if ret < 0 { + LogErrorf("Failed to poll(POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP) on file descriptor: %s. Exiting.", err) + return + } else if (revents & (POLLHUP | POLLRDHUP)) == 0 { + LogDebugf("Exiting but background processes are still running.") + return + } + } + read := buf[offset : offset+readSize] + nr, err = r.Read(read) + } + + // COMMENT(brauner): + // The attached process has exited and we have read all data that may have + // been buffered. + if ((revents & (POLLHUP | POLLRDHUP)) > 0) && !both { + LogDebugf("Detected poll(POLLHUP) event: exiting.") + return + } + + offset += nr + if offset > 0 && (offset+readSize >= bufferSize || err != nil) { + ch <- buf[0:offset] + offset = 0 + buf = make([]byte, bufferSize) + } + } + }() + + return ch +} diff -Nru lxd-2.6.2/shared/version/flex.go lxd-2.7/shared/version/flex.go --- lxd-2.6.2/shared/version/flex.go 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/shared/version/flex.go 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,14 @@ +/* This is a FLEXible file which can be used by both client and daemon. + * Teehee. + */ +package version + +var Version = "2.7" +var UserAgent = "LXD " + Version + +/* + * Please increment the api compat number every time you change the API. + * + * Version 1.0: ping + */ +var APIVersion = "1.0" diff -Nru lxd-2.6.2/test/main.sh lxd-2.7/test/main.sh --- lxd-2.6.2/test/main.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/main.sh 2016-12-21 00:52:23.000000000 +0000 @@ -103,7 +103,11 @@ fi echo "==> Setting up networking" - LXD_DIR="${lxddir}" lxc network attach-profile lxdbr0 default eth0 + bad=0 + ip link show lxdbr0 || bad=1 + if [ "${bad}" -eq 0 ]; then + LXD_DIR="${lxddir}" lxc network attach-profile lxdbr0 default eth0 + fi echo "==> Configuring storage backend" "$LXD_BACKEND"_configure "${lxddir}" @@ -306,10 +310,10 @@ # Allow for inspection if [ -n "${LXD_INSPECT:-}" ]; then - echo "==> Test result: ${TEST_RESULT}" if [ "${TEST_RESULT}" != "success" ]; then - echo "failed test: ${TEST_CURRENT}" + echo "==> TEST DONE: ${TEST_CURRENT_DESCRIPTION}" fi + echo "==> Test result: ${TEST_RESULT}" # shellcheck disable=SC2086 printf "To poke around, use:\n LXD_DIR=%s LXD_CONF=%s sudo -E %s/bin/lxc COMMAND\n" "${LXD_DIR}" "${LXD_CONF}" ${GOPATH:-} @@ -340,10 +344,10 @@ echo "" echo "" - echo "==> Test result: ${TEST_RESULT}" if [ "${TEST_RESULT}" != "success" ]; then - echo "failed test: ${TEST_CURRENT}" + echo "==> TEST DONE: ${TEST_CURRENT_DESCRIPTION}" fi + echo "==> Test result: ${TEST_RESULT}" } wipe() { @@ -409,125 +413,48 @@ LXD2_ADDR=$(cat "${LXD2_DIR}/lxd.addr") export LXD2_ADDR + +run_test() { + TEST_CURRENT=${1} + TEST_CURRENT_DESCRIPTION=${2:-${1}} + + echo "==> TEST BEGIN: ${TEST_CURRENT_DESCRIPTION}" + ${TEST_CURRENT} + echo "==> TEST DONE: ${TEST_CURRENT_DESCRIPTION}" +} + # allow for running a specific set of tests if [ "$#" -gt 0 ]; then - "test_${1}" + run_test "test_${1}" TEST_RESULT=success exit fi -echo "==> TEST: doing static analysis of commits" -TEST_CURRENT=test_static_analysis -test_static_analysis - -echo "==> TEST: checking dependencies" -TEST_CURRENT=test_check_deps -test_check_deps - -echo "==> TEST: Database schema update" -TEST_CURRENT=test_database_update -test_database_update - -echo "==> TEST: lxc remote url" -TEST_CURRENT=test_remote_url -test_remote_url - -echo "==> TEST: lxc remote administration" -TEST_CURRENT=test_remote_admin -test_remote_admin - -echo "==> TEST: basic usage" -TEST_CURRENT=test_basic_usage -test_basic_usage - -echo "==> TEST: security" -TEST_CURRENT=test_security -test_security - -echo "==> TEST: images (and cached image expiry)" -TEST_CURRENT=test_image_expiry -test_image_expiry - -if [ -n "${LXD_CONCURRENT:-}" ]; then - echo "==> TEST: concurrent exec" - TEST_CURRENT=test_concurrent_exec - test_concurrent_exec - - echo "==> TEST: concurrent startup" - TEST_CURRENT=test_concurrent - test_concurrent -fi - -echo "==> TEST: lxc remote usage" -TEST_CURRENT=test_remote_usage -test_remote_usage - -echo "==> TEST: snapshots" -TEST_CURRENT=test_snapshots -test_snapshots - -echo "==> TEST: snapshot restore" -TEST_CURRENT=test_snap_restore -test_snap_restore - -echo "==> TEST: profiles, devices and configuration" -TEST_CURRENT=test_config_profiles -test_config_profiles - -echo "==> TEST: server config" -TEST_CURRENT=test_server_config -test_server_config - -echo "==> TEST: filemanip" -TEST_CURRENT=test_filemanip -test_filemanip - -echo "==> TEST: network" -TEST_CURRENT=test_network -test_network - -echo "==> TEST: idmap" -TEST_CURRENT=test_idmap -test_idmap - -echo "==> TEST: template" -TEST_CURRENT=test_template -test_template - -echo "==> TEST: devlxd" -TEST_CURRENT=test_devlxd -test_devlxd - -if which fuidshift >/dev/null 2>&1; then - echo "==> TEST: uidshift" - TEST_CURRENT=test_fuidshift - test_fuidshift -else - echo "==> SKIP: fuidshift (binary missing)" -fi - -echo "==> TEST: migration" -TEST_CURRENT=test_migration -test_migration - -curversion=$(dpkg -s lxc | awk '/^Version/ { print $2 }') -if dpkg --compare-versions "${curversion}" gt 1.1.2-0ubuntu3; then - echo "==> TEST: fdleak" - TEST_CURRENT=test_fdleak - test_fdleak -else - # We temporarily skip the fdleak test because a bug in lxc is - # known to make it # fail without lxc commit - # 858377e: # logs: introduce a thread-local 'current' lxc_config (v2) - echo "==> SKIPPING TEST: fdleak" -fi - -echo "==> TEST: cpu profiling" -TEST_CURRENT=test_cpu_profiling -test_cpu_profiling - -echo "==> TEST: memory profiling" -TEST_CURRENT=test_mem_profiling -test_mem_profiling +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_admin "remote administration" +run_test test_remote_usage "remote usage" +run_test test_basic_usage "basic usage" +run_test test_security "security features" +run_test test_image_expiry "image expiry" +run_test test_concurrent_exec "concurrent exec" +run_test test_concurrent "concurrent startup" +run_test test_snapshots "container snapshots" +run_test test_snap_restore "snapshot restores" +run_test test_config_profiles "profiles and configuration" +run_test test_server_config "server configuration" +run_test test_filemanip "file manipulations" +run_test test_network "network management" +run_test test_idmap "id mapping" +run_test test_template "file templating" +run_test test_pki "PKI mode" +run_test test_devlxd "/dev/lxd" +run_test test_fuidshift "fuidshift" +run_test test_migration "migration" +run_test test_fdleak "fd leak" +run_test test_cpu_profiling "CPU profiling" +run_test test_mem_profiling "memory profiling" TEST_RESULT=success diff -Nru lxd-2.6.2/test/suites/basic.sh lxd-2.7/test/suites/basic.sh --- lxd-2.6.2/test/suites/basic.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/suites/basic.sh 2016-12-21 00:52:23.000000000 +0000 @@ -215,8 +215,9 @@ [ "$(my_curl "https://${LXD_ADDR}/1.0/containers/configtest" | jq -r .metadata.config[\"raw.lxc\"])" = "lxc.hook.clone=/bin/true" ] lxc delete configtest - # Test socket activation + # Test activateifneeded/shutdown LXD_ACTIVATION_DIR=$(mktemp -d -p "${TEST_DIR}" XXX) + chmod +x "${LXD_ACTIVATION_DIR}" spawn_lxd "${LXD_ACTIVATION_DIR}" ( set -e @@ -230,7 +231,27 @@ lxd activateifneeded --debug 2>&1 | grep -q -v "activating..." lxc config set autostart boot.autostart true --force-local lxd activateifneeded --debug 2>&1 | grep -q "Daemon has auto-started containers, activating..." - lxc delete autostart --force-local + + lxc config unset autostart boot.autostart --force-local + lxd activateifneeded --debug 2>&1 | grep -q -v "activating..." + + lxc start autostart --force-local + PID=$(lxc info autostart --force-local | grep ^Pid | awk '{print $2}') + lxd shutdown + [ -d "/proc/${PID}" ] && false + + lxd activateifneeded --debug 2>&1 | grep -q "Daemon has auto-started containers, activating..." + + # shellcheck disable=SC2086 + lxd --logfile "${LXD_DIR}/lxd.log" ${DEBUG-} "$@" 2>&1 & + LXD_PID=$! + echo "${LXD_PID}" > "${LXD_DIR}/lxd.pid" + echo "${LXD_DIR}" >> "${TEST_DIR}/daemons" + lxd waitready --timeout=300 + + lxc list --force-local autostart | grep -q RUNNING + + lxc delete autostart --force --force-local ) # shellcheck disable=SC2031 LXD_DIR=${LXD_DIR} diff -Nru lxd-2.6.2/test/suites/concurrent.sh lxd-2.7/test/suites/concurrent.sh --- lxd-2.6.2/test/suites/concurrent.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/suites/concurrent.sh 2016-12-21 00:52:23.000000000 +0000 @@ -1,6 +1,11 @@ #!/bin/sh test_concurrent() { + if [ -z "${LXD_CONCURRENT:-}" ]; then + echo "==> SKIP: LXD_CONCURRENT isn't set" + return + fi + ensure_import_testimage spawn_container() { diff -Nru lxd-2.6.2/test/suites/config.sh lxd-2.7/test/suites/config.sh --- lxd-2.6.2/test/suites/config.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/suites/config.sh 2016-12-21 00:52:23.000000000 +0000 @@ -79,6 +79,24 @@ sed -i "\|^${lpath}|d" "${TEST_DIR}/loops" } +test_mount_order() { + mkdir -p "${TEST_DIR}/order/empty" + mkdir -p "${TEST_DIR}/order/full" + touch "${TEST_DIR}/order/full/filler" + + # The idea here is that sometimes (depending on how golang randomizes the + # config) the empty dir will have the contents of full in it, but sometimes + # it won't depending on whether the devices below are processed in order or + # not. This should not be racy, and they should *always* be processed in path + # order, so the filler file should always be there. + lxc config device add foo order disk source="${TEST_DIR}/order" path=/mnt + lxc config device add foo orderFull disk source="${TEST_DIR}/order/full" path=/mnt/empty + + lxc start foo + lxc exec foo -- cat /mnt/empty/filler + lxc stop foo --force +} + test_config_profiles() { ensure_import_testimage @@ -185,6 +203,8 @@ testloopmounts + test_mount_order + lxc delete foo lxc init testimage foo diff -Nru lxd-2.6.2/test/suites/exec.sh lxd-2.7/test/suites/exec.sh --- lxd-2.6.2/test/suites/exec.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/suites/exec.sh 2016-12-21 00:52:23.000000000 +0000 @@ -1,6 +1,11 @@ #!/bin/sh test_concurrent_exec() { + if [ -z "${LXD_CONCURRENT:-}" ]; then + echo "==> SKIP: LXD_CONCURRENT isn't set" + return + fi + ensure_import_testimage name=x1 diff -Nru lxd-2.6.2/test/suites/filemanip.sh lxd-2.7/test/suites/filemanip.sh --- lxd-2.6.2/test/suites/filemanip.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/suites/filemanip.sh 2016-12-21 00:52:23.000000000 +0000 @@ -19,6 +19,7 @@ # lxc {push|pull} -r mkdir "${TEST_DIR}"/source + mkdir "${TEST_DIR}"/source/another_level echo "foo" > "${TEST_DIR}"/source/foo echo "bar" > "${TEST_DIR}"/source/bar @@ -28,6 +29,34 @@ [ "$(lxc exec filemanip -- stat -c "%g" /tmp/ptest/source)" = "$(id -g)" ] [ "$(lxc exec filemanip -- stat -c "%a" /tmp/ptest/source)" = "755" ] + lxc exec filemanip -- rm -rf /tmp/ptest/source + + # Special case where we are in the same directory as the one we are currently + # created. + oldcwd=$(pwd) + cd "${TEST_DIR}" + + lxc file push -r source filemanip/tmp/ptest + + [ "$(lxc exec filemanip -- stat -c "%u" /tmp/ptest/source)" = "$(id -u)" ] + [ "$(lxc exec filemanip -- stat -c "%g" /tmp/ptest/source)" = "$(id -g)" ] + [ "$(lxc exec filemanip -- stat -c "%a" /tmp/ptest/source)" = "755" ] + + lxc exec filemanip -- rm -rf /tmp/ptest/source + + # Special case where we are in the same directory as the one we are currently + # created. + cd source + + lxc file push -r ../source filemanip/tmp/ptest + + [ "$(lxc exec filemanip -- stat -c "%u" /tmp/ptest/source)" = "$(id -u)" ] + [ "$(lxc exec filemanip -- stat -c "%g" /tmp/ptest/source)" = "$(id -g)" ] + [ "$(lxc exec filemanip -- stat -c "%a" /tmp/ptest/source)" = "755" ] + + # Switch back to old working directory. + cd "${oldcwd}" + mkdir "${TEST_DIR}"/dest lxc file pull -r filemanip/tmp/ptest/source "${TEST_DIR}"/dest diff -Nru lxd-2.6.2/test/suites/fuidshift.sh lxd-2.7/test/suites/fuidshift.sh --- lxd-2.6.2/test/suites/fuidshift.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/suites/fuidshift.sh 2016-12-21 00:52:23.000000000 +0000 @@ -48,6 +48,11 @@ } test_fuidshift() { + if ! which fuidshift >/dev/null 2>&1; then + echo "==> SKIP: No fuidshift binary could be found" + return + fi + if [ "$(id -u)" -ne 0 ]; then test_nonroot_fuidshift else diff -Nru lxd-2.6.2/test/suites/migration.sh lxd-2.7/test/suites/migration.sh --- lxd-2.6.2/test/suites/migration.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/suites/migration.sh 2016-12-21 00:52:23.000000000 +0000 @@ -21,6 +21,16 @@ lxc_remote move l1:nonlive l2: lxc_remote config show l2:nonlive/snap0 | grep user.tester | grep foo + # test `lxd import` + if [ "${LXD_BACKEND}" = "zfs" ]; then + lxc_remote init testimage backup + lxc_remote snapshot backup + sqlite3 "${LXD_DIR}/lxd.db" "DELETE FROM containers WHERE name='backup'" + sqlite3 "${LXD_DIR}/lxd.db" "DELETE FROM containers WHERE name='backup/snap0'" + lxd import backup + lxc_remote info backup | grep snap0 + fi + # FIXME: make this backend agnostic if [ "${LXD_BACKEND}" != "lvm" ]; then [ -d "${LXD2_DIR}/containers/nonlive/rootfs" ] diff -Nru lxd-2.6.2/test/suites/pki.sh lxd-2.7/test/suites/pki.sh --- lxd-2.6.2/test/suites/pki.sh 1970-01-01 00:00:00.000000000 +0000 +++ lxd-2.7/test/suites/pki.sh 2016-12-21 00:52:23.000000000 +0000 @@ -0,0 +1,50 @@ +#!/bin/sh + +test_pki() { + if [ ! -d "/usr/share/easy-rsa/" ]; then + echo "==> SKIP: The pki test requires easy-rsa to be installed" + return + fi + + # Setup the PKI + cp -R /usr/share/easy-rsa "${TEST_DIR}/pki" + ( + set -e + cd "${TEST_DIR}/pki" + ls + # shellcheck disable=SC1091 + . ./vars + ./clean-all + ./pkitool --initca + ./pkitool --server 127.0.0.1 + ./pkitool lxd-client + ) + + # Setup the daemon + LXD5_DIR=$(mktemp -d -p "${TEST_DIR}" XXX) + chmod +x "${LXD5_DIR}" + cat "${TEST_DIR}/pki/keys/127.0.0.1.crt" "${TEST_DIR}/pki/keys/ca.crt" > "${LXD5_DIR}/server.crt" + cp "${TEST_DIR}/pki/keys/127.0.0.1.key" "${LXD5_DIR}/server.key" + cp "${TEST_DIR}/pki/keys/ca.crt" "${LXD5_DIR}/server.ca" + spawn_lxd "${LXD5_DIR}" + LXD5_ADDR=$(cat "${LXD5_DIR}/lxd.addr") + + # Setup the client + LXC5_DIR=$(mktemp -d -p "${TEST_DIR}" XXX) + cp "${TEST_DIR}/pki/keys/lxd-client.crt" "${LXC5_DIR}/client.crt" + cp "${TEST_DIR}/pki/keys/lxd-client.key" "${LXC5_DIR}/client.key" + cp "${TEST_DIR}/pki/keys/ca.crt" "${LXC5_DIR}/client.ca" + + # Confirm that a valid client certificate works + ( + set -e + export LXD_CONF=${LXC5_DIR} + lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=foo + lxc_remote info pki-lxd: + ) + + # Confirm that a normal, non-PKI certificate doesn't + ! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=foo + + kill_lxd "${LXD5_DIR}" +} diff -Nru lxd-2.6.2/test/suites/remote.sh lxd-2.7/test/suites/remote.sh --- lxd-2.6.2/test/suites/remote.sh 2016-11-25 04:22:21.000000000 +0000 +++ lxd-2.7/test/suites/remote.sh 2016-12-21 00:52:23.000000000 +0000 @@ -132,9 +132,9 @@ lxc_remote delete lxd2:c1 # Test that local and public servers can be accessed without a client cert - mv "${LXD_CONF}/client.crt" "${LXD_CONF}/client.crt.bak" + mv "${LXD_CONF}/client.crt" "${LXD_CONF}/client.crt.bak" mv "${LXD_CONF}/client.key" "${LXD_CONF}/client.key.bak" - + # testimage should still exist on the local server. Count the number of # matches so the output isn't polluted with the results. lxc_remote image list local: | grep -c testimage