[lxc-devel] [lxd/master] Add the lxd-p2c tool
stgraber on Github
lxc-bot at linuxcontainers.org
Thu Feb 22 08:16:42 UTC 2018
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180222/871229b8/attachment.bin>
-------------- next part --------------
From 1551e180ab6d1808ab3cff15b4ac56088a4e9bbc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Feb 2018 18:21:09 -0500
Subject: [PATCH 1/7] shared/eagain: Make our EAGAIN code a new package
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
shared/eagain/file.go | 52 ++++++++++++++++++++++++++++++++++++++++++
test/suites/static_analysis.sh | 1 +
2 files changed, 53 insertions(+)
create mode 100644 shared/eagain/file.go
diff --git a/shared/eagain/file.go b/shared/eagain/file.go
new file mode 100644
index 000000000..9e3eac9c0
--- /dev/null
+++ b/shared/eagain/file.go
@@ -0,0 +1,52 @@
+package eagain
+
+import (
+ "io"
+ "syscall"
+
+ "github.com/lxc/lxd/shared"
+)
+
+// Reader represents an io.Reader that handles EAGAIN
+type Reader struct {
+ Reader io.Reader
+}
+
+// Read behaves like io.Reader.Read but will retry on EAGAIN
+func (er Reader) Read(p []byte) (int, error) {
+again:
+ n, err := er.Reader.Read(p)
+ if err == nil {
+ return n, nil
+ }
+
+ // keep retrying on EAGAIN
+ errno, ok := shared.GetErrno(err)
+ if ok && errno == syscall.EAGAIN {
+ goto again
+ }
+
+ return n, err
+}
+
+// Writer represents an io.Writer that handles EAGAIN
+type Writer struct {
+ Writer io.Writer
+}
+
+// Write behaves like io.Writer.Write but will retry on EAGAIN
+func (ew Writer) Write(p []byte) (int, error) {
+again:
+ n, err := ew.Writer.Write(p)
+ if err == nil {
+ return n, nil
+ }
+
+ // keep retrying on EAGAIN
+ errno, ok := shared.GetErrno(err)
+ if ok && errno == syscall.EAGAIN {
+ goto again
+ }
+
+ return n, err
+}
diff --git a/test/suites/static_analysis.sh b/test/suites/static_analysis.sh
index ce311c401..598d77113 100644
--- a/test/suites/static_analysis.sh
+++ b/test/suites/static_analysis.sh
@@ -83,6 +83,7 @@ test_static_analysis() {
golint -set_exit_status shared/api/
golint -set_exit_status shared/cancel/
golint -set_exit_status shared/cmd/
+ golint -set_exit_status shared/eagain/
golint -set_exit_status shared/gnuflag/
golint -set_exit_status shared/i18n/
golint -set_exit_status shared/ioprogress/
From f4dfb99569dd6ca434c66732e00a4a593c8e2abf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Feb 2018 18:22:30 -0500
Subject: [PATCH 2/7] lxd/netcat: Port to using shared/eagain
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/main_netcat.go | 46 +++-------------------------------------------
1 file changed, 3 insertions(+), 43 deletions(-)
diff --git a/lxd/main_netcat.go b/lxd/main_netcat.go
index 2fac5b338..26e2e8b0c 100644
--- a/lxd/main_netcat.go
+++ b/lxd/main_netcat.go
@@ -6,9 +6,9 @@ import (
"net"
"os"
"sync"
- "syscall"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/eagain"
)
// Netcat is called with:
@@ -50,7 +50,7 @@ func cmdNetcat(args *Args) error {
wg.Add(1)
go func() {
- _, err := io.Copy(eagainWriter{os.Stdout}, eagainReader{conn})
+ _, err := io.Copy(eagain.Writer{Writer: os.Stdout}, eagain.Reader{Reader: conn})
if err != nil {
logFile.WriteString(fmt.Sprintf("Error while copying from stdout to unix domain socket \"%s\": %s.\n", args.Params[0], err))
}
@@ -59,7 +59,7 @@ func cmdNetcat(args *Args) error {
}()
go func() {
- _, err := io.Copy(eagainWriter{conn}, eagainReader{os.Stdin})
+ _, err := io.Copy(eagain.Writer{Writer: conn}, eagain.Reader{Reader: os.Stdin})
if err != nil {
logFile.WriteString(fmt.Sprintf("Error while copying from unix domain socket \"%s\" to stdin: %s.\n", args.Params[0], err))
}
@@ -69,43 +69,3 @@ func cmdNetcat(args *Args) error {
return nil
}
-
-type eagainReader struct {
- r io.Reader
-}
-
-func (er eagainReader) Read(p []byte) (int, error) {
-again:
- n, err := er.r.Read(p)
- if err == nil {
- return n, nil
- }
-
- // keep retrying on EAGAIN
- errno, ok := shared.GetErrno(err)
- if ok && errno == syscall.EAGAIN {
- goto again
- }
-
- return n, err
-}
-
-type eagainWriter struct {
- w io.Writer
-}
-
-func (ew eagainWriter) Write(p []byte) (int, error) {
-again:
- n, err := ew.w.Write(p)
- if err == nil {
- return n, nil
- }
-
- // keep retrying on EAGAIN
- errno, ok := shared.GetErrno(err)
- if ok && errno == syscall.EAGAIN {
- goto again
- }
-
- return n, err
-}
From 4de1243fe80fc9421d60c60c6ba176169195d68f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Feb 2018 18:50:35 -0500
Subject: [PATCH 3/7] lxd: Move migration code to own package
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
Makefile | 2 +-
lxd/containers_post.go | 3 +-
lxd/migrate.go | 104 ++++++++++---------------
lxd/{ => migration}/migrate.pb.go | 159 +++++++++++++++++++-------------------
lxd/{ => migration}/migrate.proto | 2 +-
lxd/migration/wsproto.go | 71 +++++++++++++++++
lxd/storage.go | 5 +-
lxd/storage_btrfs.go | 9 ++-
lxd/storage_ceph_migration.go | 7 +-
lxd/storage_dir.go | 7 +-
lxd/storage_lvm.go | 7 +-
lxd/storage_migration.go | 5 +-
lxd/storage_mock.go | 8 +-
lxd/storage_zfs.go | 7 +-
test/suites/static_analysis.sh | 1 +
15 files changed, 227 insertions(+), 170 deletions(-)
rename lxd/{ => migration}/migrate.pb.go (70%)
rename lxd/{ => migration}/migrate.proto (99%)
create mode 100644 lxd/migration/wsproto.go
diff --git a/Makefile b/Makefile
index 891088074..9d7a37226 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@ debug:
# it's not a default build step.
.PHONY: protobuf
protobuf:
- protoc --go_out=. ./lxd/migrate.proto
+ protoc --go_out=. ./lxd/migration/migrate.proto
.PHONY: check
check: default
diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index 76d2a615a..55875277f 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -12,6 +12,7 @@ import (
"github.com/gorilla/websocket"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -316,7 +317,7 @@ func createFromMigration(d *Daemon, req *api.ContainersPost) Response {
return InternalError(err)
}
- if ps.MigrationType() == MigrationFSType_RSYNC {
+ if ps.MigrationType() == migration.MigrationFSType_RSYNC {
c, err = containerCreateFromImage(d.State(), args, req.Source.BaseImage)
if err != nil {
return InternalError(err)
diff --git a/lxd/migrate.go b/lxd/migrate.go
index 6bedadcb1..62078766e 100644
--- a/lxd/migrate.go
+++ b/lxd/migrate.go
@@ -24,6 +24,7 @@ import (
"github.com/gorilla/websocket"
"gopkg.in/lxc/go-lxc.v2"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -63,36 +64,17 @@ func (c *migrationFields) send(m proto.Message) error {
*/
c.controlLock.Lock()
defer c.controlLock.Unlock()
- w, err := c.controlConn.NextWriter(websocket.BinaryMessage)
- if err != nil {
- return err
- }
- defer w.Close()
- data, err := proto.Marshal(m)
+ err := migration.ProtoSend(c.controlConn, m)
if err != nil {
return err
}
- return shared.WriteAll(w, data)
+ return nil
}
func (c *migrationFields) recv(m proto.Message) error {
- mt, r, err := c.controlConn.NextReader()
- if err != nil {
- return err
- }
-
- if mt != websocket.BinaryMessage {
- return fmt.Errorf("Only binary messages allowed")
- }
-
- buf, err := ioutil.ReadAll(r)
- if err != nil {
- return err
- }
-
- return proto.Unmarshal(buf, m)
+ return migration.ProtoRecv(c.controlConn, m)
}
func (c *migrationFields) disconnect() {
@@ -123,26 +105,20 @@ func (c *migrationFields) disconnect() {
}
func (c *migrationFields) sendControl(err error) {
- message := ""
- if err != nil {
- message = err.Error()
- }
+ c.controlLock.Lock()
+ defer c.controlLock.Unlock()
- msg := MigrationControl{
- Success: proto.Bool(err == nil),
- Message: proto.String(message),
- }
- c.send(&msg)
+ migration.ProtoSendControl(c.controlConn, err)
if err != nil {
c.disconnect()
}
}
-func (c *migrationFields) controlChannel() <-chan MigrationControl {
- ch := make(chan MigrationControl)
+func (c *migrationFields) controlChannel() <-chan migration.MigrationControl {
+ ch := make(chan migration.MigrationControl)
go func() {
- msg := MigrationControl{}
+ msg := migration.MigrationControl{}
err := c.recv(&msg)
if err != nil {
logger.Debugf("Got error reading migration control socket %s", err)
@@ -321,24 +297,24 @@ fi
return err
}
-func snapshotToProtobuf(c container) *Snapshot {
- config := []*Config{}
+func snapshotToProtobuf(c container) *migration.Snapshot {
+ config := []*migration.Config{}
for k, v := range c.LocalConfig() {
kCopy := string(k)
vCopy := string(v)
- config = append(config, &Config{Key: &kCopy, Value: &vCopy})
+ config = append(config, &migration.Config{Key: &kCopy, Value: &vCopy})
}
- devices := []*Device{}
+ devices := []*migration.Device{}
for name, d := range c.LocalDevices() {
- props := []*Config{}
+ props := []*migration.Config{}
for k, v := range d {
kCopy := string(k)
vCopy := string(v)
- props = append(props, &Config{Key: &kCopy, Value: &vCopy})
+ props = append(props, &migration.Config{Key: &kCopy, Value: &vCopy})
}
- devices = append(devices, &Device{Name: &name, Config: props})
+ devices = append(devices, &migration.Device{Name: &name, Config: props})
}
parts := strings.SplitN(c.Name(), shared.SnapshotDelimiter, 2)
@@ -346,7 +322,7 @@ func snapshotToProtobuf(c container) *Snapshot {
arch := int32(c.Architecture())
stateful := c.IsStateful()
- return &Snapshot{
+ return &migration.Snapshot{
Name: &parts[len(parts)-1],
LocalConfig: config,
Profiles: c.Profiles(),
@@ -444,7 +420,7 @@ func readCriuStatsDump(path string) (uint64, uint64, error) {
size := binary.LittleEndian.Uint32(in[8:12])
logger.Debugf("stats-dump payload size %d", size)
- statsEntry := &StatsEntry{}
+ statsEntry := &migration.StatsEntry{}
if err = proto.Unmarshal(in[12:12+size], statsEntry); err != nil {
logger.Errorf("Failed to parse CRIU's 'stats-dump' file: %s", err.Error())
return 0, 0, err
@@ -534,7 +510,7 @@ func (s *migrationSourceWs) preDumpLoop(args *preDumpLoopArgs) (bool, error) {
// expects a message to know if this was the
// last pre-dump
logger.Debugf("Sending another header")
- sync := MigrationSync{
+ sync := migration.MigrationSync{
FinalPreDump: proto.Bool(final),
}
@@ -557,11 +533,11 @@ func (s *migrationSourceWs) preDumpLoop(args *preDumpLoopArgs) (bool, error) {
func (s *migrationSourceWs) Do(migrateOp *operation) error {
<-s.allConnected
- criuType := CRIUType_CRIU_RSYNC.Enum()
+ criuType := migration.CRIUType_CRIU_RSYNC.Enum()
if !s.live {
criuType = nil
if s.container.IsRunning() {
- criuType = CRIUType_NONE.Enum()
+ criuType = migration.CRIUType_NONE.Enum()
}
}
@@ -575,7 +551,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
defer s.container.StorageStop()
}
- idmaps := make([]*IDMapType, 0)
+ idmaps := make([]*migration.IDMapType, 0)
idmapset, err := s.container.IdmapSet()
if err != nil {
@@ -584,7 +560,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
if idmapset != nil {
for _, ctnIdmap := range idmapset.Idmap {
- idmap := IDMapType{
+ idmap := migration.IDMapType{
Isuid: proto.Bool(ctnIdmap.Isuid),
Isgid: proto.Bool(ctnIdmap.Isgid),
Hostid: proto.Int32(int32(ctnIdmap.Hostid)),
@@ -598,7 +574,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
driver, fsErr := s.container.Storage().MigrationSource(s.container, s.containerOnly)
- snapshots := []*Snapshot{}
+ snapshots := []*migration.Snapshot{}
snapshotNames := []string{}
// Only send snapshots when requested.
if !s.containerOnly {
@@ -620,7 +596,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
// The protocol says we have to send a header no matter what, so let's
// do that, but then immediately send an error.
myType := s.container.Storage().MigrationType()
- header := MigrationHeader{
+ header := migration.MigrationHeader{
Fs: &myType,
Criu: criuType,
Idmap: idmaps,
@@ -648,7 +624,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
bwlimit := ""
if *header.Fs != myType {
- myType = MigrationFSType_RSYNC
+ myType = migration.MigrationFSType_RSYNC
header.Fs = &myType
driver, _ = rsyncMigrationSource(s.container, s.containerOnly)
@@ -693,7 +669,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
if s.live {
if header.Criu == nil {
return abort(fmt.Errorf("Got no CRIU socket type for live migration"))
- } else if *header.Criu != CRIUType_CRIU_RSYNC {
+ } else if *header.Criu != migration.CRIUType_CRIU_RSYNC {
return abort(fmt.Errorf("Formats other than criu rsync not understood"))
}
@@ -865,7 +841,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
}
}
- if s.live || (header.Criu != nil && *header.Criu == CRIUType_NONE) {
+ if s.live || (header.Criu != nil && *header.Criu == migration.CRIUType_NONE) {
err = driver.SendAfterCheckpoint(s.fsConn, bwlimit)
if err != nil {
return abort(err)
@@ -874,7 +850,7 @@ func (s *migrationSourceWs) Do(migrateOp *operation) error {
driver.Cleanup()
- msg := MigrationControl{}
+ msg := migration.MigrationControl{}
err = s.recv(&msg)
if err != nil {
s.disconnect()
@@ -1091,7 +1067,7 @@ func (c *migrationSink) Do(migrateOp *operation) error {
controller = c.dest.sendControl
}
- header := MigrationHeader{}
+ header := migration.MigrationHeader{}
if err := receiver(&header); err != nil {
controller(err)
return err
@@ -1102,9 +1078,9 @@ func (c *migrationSink) Do(migrateOp *operation) error {
live = c.dest.live
}
- criuType := CRIUType_CRIU_RSYNC.Enum()
- if header.Criu != nil && *header.Criu == CRIUType_NONE {
- criuType = CRIUType_NONE.Enum()
+ criuType := migration.CRIUType_CRIU_RSYNC.Enum()
+ if header.Criu != nil && *header.Criu == migration.CRIUType_NONE {
+ criuType = migration.CRIUType_NONE.Enum()
} else {
if !live {
criuType = nil
@@ -1113,7 +1089,7 @@ func (c *migrationSink) Do(migrateOp *operation) error {
mySink := c.src.container.Storage().MigrationSink
myType := c.src.container.Storage().MigrationType()
- resp := MigrationHeader{
+ resp := migration.MigrationHeader{
Fs: &myType,
Criu: criuType,
}
@@ -1122,7 +1098,7 @@ func (c *migrationSink) Do(migrateOp *operation) error {
// we have to use rsync.
if *header.Fs != *resp.Fs {
mySink = rsyncMigrationSink
- myType = MigrationFSType_RSYNC
+ myType = migration.MigrationFSType_RSYNC
resp.Fs = &myType
}
@@ -1163,7 +1139,7 @@ func (c *migrationSink) Do(migrateOp *operation) error {
*/
fsTransfer := make(chan error)
go func() {
- snapshots := []*Snapshot{}
+ snapshots := []*migration.Snapshot{}
/* Legacy: we only sent the snapshot names, so we just
* copy the container's config over, same as we used to
@@ -1191,7 +1167,7 @@ func (c *migrationSink) Do(migrateOp *operation) error {
sendFinalFsDelta = true
}
- if criuType != nil && *criuType == CRIUType_NONE {
+ if criuType != nil && *criuType == migration.CRIUType_NONE {
sendFinalFsDelta = true
}
@@ -1229,7 +1205,7 @@ func (c *migrationSink) Do(migrateOp *operation) error {
criuConn = c.src.criuConn
}
- sync := &MigrationSync{
+ sync := &migration.MigrationSync{
FinalPreDump: proto.Bool(false),
}
@@ -1307,7 +1283,7 @@ func (c *migrationSink) Do(migrateOp *operation) error {
restore <- nil
}(c)
- var source <-chan MigrationControl
+ var source <-chan migration.MigrationControl
if c.push {
source = c.dest.controlChannel()
} else {
diff --git a/lxd/migrate.pb.go b/lxd/migration/migrate.pb.go
similarity index 70%
rename from lxd/migrate.pb.go
rename to lxd/migration/migrate.pb.go
index 85368b02d..9e9f4a465 100644
--- a/lxd/migrate.pb.go
+++ b/lxd/migration/migrate.pb.go
@@ -1,12 +1,11 @@
-// Code generated by protoc-gen-go.
-// source: lxd/migrate.proto
-// DO NOT EDIT!
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: lxd/migration/migrate.proto
/*
-Package main is a generated protocol buffer package.
+Package migration is a generated protocol buffer package.
It is generated from these files:
- lxd/migrate.proto
+ lxd/migration/migrate.proto
It has these top-level messages:
IDMapType
@@ -20,7 +19,7 @@ It has these top-level messages:
RestoreStatsEntry
StatsEntry
*/
-package main
+package migration
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
@@ -279,8 +278,8 @@ func (m *Snapshot) GetStateful() bool {
}
type MigrationHeader struct {
- Fs *MigrationFSType `protobuf:"varint,1,req,name=fs,enum=main.MigrationFSType" json:"fs,omitempty"`
- Criu *CRIUType `protobuf:"varint,2,opt,name=criu,enum=main.CRIUType" json:"criu,omitempty"`
+ Fs *MigrationFSType `protobuf:"varint,1,req,name=fs,enum=migration.MigrationFSType" json:"fs,omitempty"`
+ Criu *CRIUType `protobuf:"varint,2,opt,name=criu,enum=migration.CRIUType" json:"criu,omitempty"`
Idmap []*IDMapType `protobuf:"bytes,3,rep,name=idmap" json:"idmap,omitempty"`
SnapshotNames []string `protobuf:"bytes,4,rep,name=snapshotNames" json:"snapshotNames,omitempty"`
Snapshots []*Snapshot `protobuf:"bytes,5,rep,name=snapshots" json:"snapshots,omitempty"`
@@ -551,78 +550,78 @@ func (m *StatsEntry) GetRestore() *RestoreStatsEntry {
}
func init() {
- proto.RegisterType((*IDMapType)(nil), "main.IDMapType")
- proto.RegisterType((*Config)(nil), "main.Config")
- proto.RegisterType((*Device)(nil), "main.Device")
- proto.RegisterType((*Snapshot)(nil), "main.Snapshot")
- proto.RegisterType((*MigrationHeader)(nil), "main.MigrationHeader")
- proto.RegisterType((*MigrationControl)(nil), "main.MigrationControl")
- proto.RegisterType((*MigrationSync)(nil), "main.MigrationSync")
- proto.RegisterType((*DumpStatsEntry)(nil), "main.dump_stats_entry")
- proto.RegisterType((*RestoreStatsEntry)(nil), "main.restore_stats_entry")
- proto.RegisterType((*StatsEntry)(nil), "main.stats_entry")
- proto.RegisterEnum("main.MigrationFSType", MigrationFSType_name, MigrationFSType_value)
- proto.RegisterEnum("main.CRIUType", CRIUType_name, CRIUType_value)
-}
-
-func init() { proto.RegisterFile("lxd/migrate.proto", fileDescriptor0) }
+ proto.RegisterType((*IDMapType)(nil), "migration.IDMapType")
+ proto.RegisterType((*Config)(nil), "migration.Config")
+ proto.RegisterType((*Device)(nil), "migration.Device")
+ proto.RegisterType((*Snapshot)(nil), "migration.Snapshot")
+ proto.RegisterType((*MigrationHeader)(nil), "migration.MigrationHeader")
+ proto.RegisterType((*MigrationControl)(nil), "migration.MigrationControl")
+ proto.RegisterType((*MigrationSync)(nil), "migration.MigrationSync")
+ proto.RegisterType((*DumpStatsEntry)(nil), "migration.dump_stats_entry")
+ proto.RegisterType((*RestoreStatsEntry)(nil), "migration.restore_stats_entry")
+ proto.RegisterType((*StatsEntry)(nil), "migration.stats_entry")
+ proto.RegisterEnum("migration.MigrationFSType", MigrationFSType_name, MigrationFSType_value)
+ proto.RegisterEnum("migration.CRIUType", CRIUType_name, CRIUType_value)
+}
+
+func init() { proto.RegisterFile("lxd/migration/migrate.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 888 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x94, 0xcd, 0x6e, 0xdb, 0x46,
- 0x10, 0xc7, 0x4b, 0x89, 0xb2, 0xc5, 0xa1, 0x2c, 0x2b, 0xdb, 0x34, 0x60, 0x8b, 0x16, 0x55, 0x19,
- 0x1b, 0x10, 0x8c, 0xc2, 0x31, 0x94, 0x53, 0x8f, 0xb5, 0x5c, 0x23, 0x01, 0x12, 0xd7, 0x58, 0x39,
- 0x28, 0xda, 0x0b, 0xb1, 0x25, 0x87, 0xf2, 0xc2, 0xfc, 0xc2, 0x2e, 0x65, 0x57, 0xbe, 0xf4, 0x69,
- 0xfa, 0x48, 0x7d, 0x90, 0x5e, 0x7b, 0x2a, 0x76, 0x96, 0xa4, 0xa5, 0x22, 0xb9, 0xed, 0xfc, 0xe6,
- 0xcf, 0x99, 0x9d, 0x0f, 0x2e, 0x3c, 0xcb, 0xfe, 0x48, 0x5e, 0xe5, 0x72, 0xa5, 0x44, 0x8d, 0xa7,
- 0x95, 0x2a, 0xeb, 0x92, 0xb9, 0xb9, 0x90, 0x45, 0xf8, 0x27, 0x78, 0x6f, 0x2f, 0xde, 0x8b, 0xea,
- 0x66, 0x53, 0x21, 0x7b, 0x0e, 0x03, 0xa9, 0xd7, 0x32, 0x09, 0x9c, 0x69, 0x6f, 0x36, 0xe4, 0xd6,
- 0xb0, 0x74, 0x25, 0x93, 0xa0, 0xd7, 0xd2, 0x95, 0x4c, 0xd8, 0x0b, 0xd8, 0xbb, 0x2d, 0x75, 0x2d,
- 0x93, 0xa0, 0x3f, 0xed, 0xcd, 0x06, 0xbc, 0xb1, 0x18, 0x03, 0xb7, 0xd0, 0x32, 0x09, 0x5c, 0xa2,
- 0x74, 0x66, 0x5f, 0xc1, 0x30, 0x17, 0x95, 0x12, 0xc5, 0x0a, 0x83, 0x01, 0xf1, 0xce, 0x0e, 0xcf,
- 0x60, 0x6f, 0x51, 0x16, 0xa9, 0x5c, 0xb1, 0x09, 0xf4, 0xef, 0x70, 0x43, 0xb9, 0x3d, 0x6e, 0x8e,
- 0x26, 0xf3, 0xbd, 0xc8, 0xd6, 0x48, 0x99, 0x3d, 0x6e, 0x8d, 0xf0, 0x1c, 0xf6, 0x2e, 0xf0, 0x5e,
- 0xc6, 0x48, 0xb9, 0x44, 0x8e, 0xcd, 0x27, 0x74, 0x66, 0x47, 0xb0, 0x17, 0x53, 0xbc, 0xa0, 0x37,
- 0xed, 0xcf, 0xfc, 0xf9, 0xe8, 0xd4, 0xd4, 0x79, 0x6a, 0x73, 0xf0, 0xc6, 0x17, 0xfe, 0xeb, 0xc0,
- 0x70, 0x59, 0x88, 0x4a, 0xdf, 0x96, 0xf5, 0x47, 0xc3, 0x9c, 0x82, 0x9f, 0x95, 0xb1, 0xc8, 0x16,
- 0x9f, 0x8e, 0xb5, 0x2d, 0x30, 0x25, 0x56, 0xaa, 0x4c, 0x65, 0x86, 0x3a, 0xe8, 0x4f, 0xfb, 0x33,
- 0x8f, 0x77, 0x36, 0xfb, 0x1a, 0x3c, 0xac, 0x6e, 0x31, 0x47, 0x25, 0x32, 0xea, 0xcb, 0x90, 0x3f,
- 0x01, 0x76, 0x06, 0x23, 0x0a, 0x64, 0x6b, 0xd2, 0xc1, 0x60, 0x3b, 0x95, 0x85, 0x7c, 0x47, 0xc1,
- 0x42, 0x18, 0x09, 0x15, 0xdf, 0xca, 0x1a, 0xe3, 0x7a, 0xad, 0x30, 0xd8, 0xa3, 0x96, 0xee, 0x30,
- 0x73, 0x1f, 0x5d, 0x8b, 0x1a, 0xd3, 0x75, 0x16, 0xec, 0x53, 0xca, 0xce, 0x0e, 0xff, 0x71, 0xe0,
- 0xf0, 0x3d, 0xed, 0x82, 0x2c, 0x8b, 0x37, 0x28, 0x12, 0x54, 0xec, 0x18, 0x7a, 0xa9, 0xa6, 0x0e,
- 0x8c, 0xe7, 0x5f, 0xd8, 0xdc, 0x9d, 0xe4, 0x72, 0x69, 0xb6, 0x83, 0xf7, 0x52, 0x93, 0xda, 0x8d,
- 0x95, 0x5c, 0x07, 0xbd, 0xa9, 0x33, 0x1b, 0xcf, 0xc7, 0x4d, 0x3f, 0xf8, 0xdb, 0x0f, 0xa4, 0x20,
- 0x1f, 0x3b, 0x86, 0x81, 0x4c, 0x72, 0x51, 0x51, 0x1f, 0xfc, 0xf9, 0xa1, 0x15, 0x75, 0x5b, 0xc6,
- 0xad, 0x97, 0x1d, 0xc1, 0x81, 0x6e, 0x26, 0x70, 0x25, 0x72, 0xd4, 0x81, 0x4b, 0x6d, 0xdb, 0x85,
- 0xec, 0x7b, 0xf0, 0x5a, 0xd0, 0xb6, 0xa6, 0xc9, 0xda, 0x8e, 0x8f, 0x3f, 0x09, 0x58, 0x00, 0xfb,
- 0x95, 0xc2, 0x64, 0x9d, 0x57, 0xc1, 0xfe, 0xd4, 0x99, 0x0d, 0x79, 0x6b, 0x86, 0x97, 0x30, 0xe9,
- 0xea, 0x59, 0x94, 0x45, 0xad, 0xca, 0xcc, 0xa8, 0xf5, 0x3a, 0x8e, 0x51, 0xeb, 0x66, 0xe1, 0x5b,
- 0xd3, 0x78, 0x72, 0xd4, 0x5a, 0xac, 0x90, 0x2a, 0xf5, 0x78, 0x6b, 0x86, 0xaf, 0xe1, 0xa0, 0x8b,
- 0xb3, 0xdc, 0x14, 0xb1, 0x19, 0x46, 0x2a, 0x0b, 0x91, 0x5d, 0x2b, 0xbc, 0x30, 0x79, 0x6d, 0xa4,
- 0x1d, 0x16, 0xfe, 0xd5, 0x87, 0x89, 0xb9, 0x45, 0x64, 0x46, 0xa0, 0x23, 0x2c, 0x6a, 0xb5, 0x61,
- 0x2f, 0xe1, 0x20, 0x55, 0x88, 0x8f, 0xb2, 0x58, 0x45, 0xb5, 0x6c, 0xd6, 0xef, 0x80, 0x8f, 0x5a,
- 0x78, 0x23, 0x73, 0x64, 0xdf, 0x82, 0x9f, 0xaa, 0xf2, 0x11, 0x0b, 0x2b, 0xe9, 0x91, 0x04, 0x2c,
- 0x22, 0xc1, 0x77, 0x30, 0xca, 0x31, 0xa7, 0xe0, 0xa4, 0xe8, 0x93, 0xc2, 0x6f, 0x18, 0x49, 0x5e,
- 0xc2, 0x41, 0x8e, 0xf9, 0x83, 0x92, 0x35, 0x5a, 0x8d, 0x6b, 0x13, 0xb5, 0xb0, 0x15, 0x55, 0x62,
- 0x85, 0x3a, 0xd2, 0xb1, 0x28, 0x0a, 0x4c, 0xe8, 0x3f, 0x75, 0xf9, 0x88, 0xe0, 0xd2, 0x32, 0x76,
- 0x06, 0xcf, 0x1b, 0xd1, 0x9d, 0xac, 0x2a, 0x4c, 0xa2, 0x4a, 0x28, 0x2c, 0x6a, 0x5a, 0x40, 0x97,
- 0x33, 0xab, 0xb5, 0xae, 0x6b, 0xf2, 0x3c, 0x85, 0x35, 0x99, 0x6a, 0x2c, 0x68, 0x17, 0xdb, 0xb0,
- 0xbf, 0x58, 0x66, 0x44, 0x52, 0xe5, 0xa2, 0x8a, 0x14, 0xea, 0x32, 0xbb, 0xc7, 0x60, 0x38, 0x75,
- 0xcc, 0x05, 0x09, 0x72, 0xcb, 0xd8, 0x37, 0x00, 0x36, 0x52, 0x26, 0x1e, 0x37, 0x81, 0x47, 0x61,
- 0x3c, 0x22, 0xef, 0xc4, 0xe3, 0xa6, 0x75, 0x47, 0x95, 0xac, 0x50, 0x07, 0x30, 0x75, 0x5a, 0xf7,
- 0xb5, 0x01, 0xec, 0x08, 0xc6, 0x9d, 0x3b, 0xfa, 0x7d, 0x9d, 0xea, 0xc0, 0x27, 0xc9, 0xa8, 0x95,
- 0x9c, 0xaf, 0x53, 0x1d, 0xfe, 0xed, 0xc0, 0xe7, 0x0a, 0x75, 0x5d, 0x2a, 0xdc, 0x19, 0xd5, 0xb1,
- 0xfd, 0x5a, 0x47, 0x71, 0x99, 0x9b, 0x92, 0xed, 0x03, 0xe9, 0x72, 0x5b, 0xdb, 0xa2, 0x81, 0xec,
- 0x04, 0x9e, 0xed, 0xb6, 0x27, 0x2e, 0x1f, 0x68, 0x64, 0x2e, 0x3f, 0xdc, 0xee, 0xcd, 0xa2, 0x7c,
- 0x30, 0x73, 0x4b, 0x4b, 0x75, 0xd7, 0x0d, 0xbf, 0x99, 0x5b, 0xc3, 0xda, 0xd1, 0xb6, 0x97, 0xd9,
- 0x1a, 0x9b, 0xdf, 0x30, 0x92, 0x74, 0x17, 0x6b, 0xa0, 0x19, 0x9b, 0xd3, 0x5d, 0x8c, 0x37, 0x30,
- 0x2c, 0xc0, 0xdf, 0x2e, 0xe7, 0x04, 0xdc, 0xc4, 0xae, 0xaa, 0x33, 0xf3, 0xe7, 0x2f, 0xec, 0xef,
- 0xf4, 0xff, 0xfd, 0xe4, 0xa4, 0x61, 0xaf, 0x61, 0xbf, 0x89, 0x4d, 0x7f, 0x82, 0x3f, 0xff, 0xd2,
- 0xca, 0x3f, 0xd2, 0x26, 0xde, 0x2a, 0x4f, 0x7e, 0xd8, 0x7a, 0x5f, 0xec, 0xe3, 0xc1, 0x3c, 0x18,
- 0xf0, 0xe5, 0xaf, 0x57, 0x8b, 0xc9, 0x67, 0xe6, 0x78, 0x7e, 0xc3, 0x2f, 0x97, 0x13, 0x87, 0xed,
- 0x43, 0xff, 0xb7, 0xcb, 0xe5, 0xa4, 0x67, 0x0e, 0xfc, 0xfc, 0x62, 0xd2, 0x3f, 0x79, 0x05, 0xc3,
- 0xf6, 0x39, 0x61, 0x63, 0x00, 0x73, 0x8e, 0xb6, 0x3e, 0xbc, 0x7e, 0xf3, 0xe3, 0x87, 0x77, 0x13,
- 0x87, 0x0d, 0xc1, 0xbd, 0xfa, 0xf9, 0xea, 0xa7, 0x49, 0xef, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff,
- 0x0d, 0x9a, 0xe8, 0xf0, 0xda, 0x06, 0x00, 0x00,
+ // 894 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0x5b, 0x6f, 0xdb, 0x36,
+ 0x14, 0x9e, 0x64, 0xf9, 0xa2, 0x23, 0x3b, 0x75, 0xd9, 0x60, 0x10, 0xda, 0x5d, 0x3c, 0xb5, 0xc3,
+ 0x3c, 0x3f, 0x24, 0x9d, 0x8b, 0x01, 0xdb, 0xe3, 0xe2, 0x2c, 0x6b, 0x81, 0x36, 0x0b, 0xe8, 0x14,
+ 0xc3, 0xf6, 0x22, 0x70, 0xd2, 0x91, 0x43, 0x44, 0x37, 0x90, 0x72, 0x52, 0xe7, 0x65, 0x2f, 0xdb,
+ 0x4f, 0xd9, 0x4f, 0xda, 0xff, 0x19, 0x48, 0x4a, 0x8a, 0xbc, 0x15, 0xe8, 0x1b, 0xcf, 0x77, 0x3e,
+ 0x9e, 0xef, 0xdc, 0x48, 0x78, 0x92, 0xbe, 0x8b, 0x8f, 0x33, 0xbe, 0x11, 0xac, 0xe2, 0x45, 0x5e,
+ 0x9f, 0xf0, 0xa8, 0x14, 0x45, 0x55, 0x10, 0xb7, 0x75, 0x04, 0x7f, 0x80, 0xfb, 0xea, 0xf4, 0x0d,
+ 0x2b, 0x2f, 0x77, 0x25, 0x92, 0x43, 0xe8, 0x73, 0xb9, 0xe5, 0xb1, 0x6f, 0xcd, 0xec, 0xf9, 0x88,
+ 0x1a, 0xc3, 0xa0, 0x1b, 0x1e, 0xfb, 0x76, 0x83, 0x6e, 0x78, 0x4c, 0x3e, 0x86, 0xc1, 0x55, 0x21,
+ 0x2b, 0x1e, 0xfb, 0xbd, 0x99, 0x3d, 0xef, 0xd3, 0xda, 0x22, 0x04, 0x9c, 0x5c, 0xf2, 0xd8, 0x77,
+ 0x34, 0xaa, 0xcf, 0xe4, 0x31, 0x8c, 0x32, 0x56, 0x0a, 0x96, 0x6f, 0xd0, 0xef, 0x6b, 0xbc, 0xb5,
+ 0x83, 0xe7, 0x30, 0x58, 0x15, 0x79, 0xc2, 0x37, 0x64, 0x0a, 0xbd, 0x6b, 0xdc, 0x69, 0x6d, 0x97,
+ 0xaa, 0xa3, 0x52, 0xbe, 0x61, 0xe9, 0x16, 0xb5, 0xb2, 0x4b, 0x8d, 0x11, 0xfc, 0x04, 0x83, 0x53,
+ 0xbc, 0xe1, 0x11, 0x6a, 0x2d, 0x96, 0x61, 0x7d, 0x45, 0x9f, 0xc9, 0xd7, 0x30, 0x88, 0x74, 0x3c,
+ 0xdf, 0x9e, 0xf5, 0xe6, 0xde, 0xf2, 0xe1, 0x51, 0x5b, 0xec, 0x91, 0x11, 0xa2, 0x35, 0x21, 0xf8,
+ 0xd3, 0x86, 0xd1, 0x3a, 0x67, 0xa5, 0xbc, 0x2a, 0xaa, 0xf7, 0xc6, 0x7a, 0x01, 0x5e, 0x5a, 0x44,
+ 0x2c, 0x5d, 0x7d, 0x20, 0x60, 0x97, 0xa5, 0x8a, 0x2d, 0x45, 0x91, 0xf0, 0x14, 0xa5, 0xdf, 0x9b,
+ 0xf5, 0xe6, 0x2e, 0x6d, 0x6d, 0xf2, 0x09, 0xb8, 0x58, 0x5e, 0x61, 0x86, 0x82, 0xa5, 0xba, 0x43,
+ 0x23, 0x7a, 0x0f, 0x90, 0x6f, 0x61, 0xac, 0x03, 0x99, 0xea, 0xa4, 0xdf, 0xff, 0x9f, 0x9e, 0xf1,
+ 0xd0, 0x3d, 0x1a, 0x09, 0x60, 0xcc, 0x44, 0x74, 0xc5, 0x2b, 0x8c, 0xaa, 0xad, 0x40, 0x7f, 0xa0,
+ 0x3b, 0xbc, 0x87, 0xa9, 0xa4, 0x64, 0xc5, 0x2a, 0x4c, 0xb6, 0xa9, 0x3f, 0xd4, 0xba, 0xad, 0x1d,
+ 0xfc, 0x65, 0xc3, 0x83, 0x37, 0x8d, 0xc4, 0x4b, 0x64, 0x31, 0x0a, 0xb2, 0x00, 0x3b, 0x91, 0xba,
+ 0x17, 0x07, 0xcb, 0xc7, 0x9d, 0x04, 0x5a, 0xde, 0xd9, 0x5a, 0x6d, 0x0c, 0xb5, 0x13, 0x49, 0xbe,
+ 0x02, 0x27, 0x12, 0x7c, 0xeb, 0xdb, 0x33, 0x6b, 0x7e, 0xb0, 0x7c, 0xd4, 0x6d, 0x0f, 0x7d, 0xf5,
+ 0x56, 0xd3, 0x34, 0x81, 0x2c, 0xa0, 0xcf, 0xe3, 0x8c, 0x95, 0xba, 0x2d, 0xde, 0xf2, 0xb0, 0xc3,
+ 0x6c, 0x77, 0x90, 0x1a, 0x0a, 0x79, 0x06, 0x13, 0x59, 0x8f, 0xe6, 0x9c, 0x65, 0x28, 0x7d, 0x47,
+ 0xb7, 0x72, 0x1f, 0x24, 0xdf, 0x80, 0xdb, 0x00, 0x4d, 0xbb, 0xba, 0xfa, 0xcd, 0x70, 0xe9, 0x3d,
+ 0x8b, 0xf8, 0x30, 0x2c, 0x05, 0xc6, 0xdb, 0xac, 0xf4, 0x87, 0x33, 0x6b, 0x3e, 0xa2, 0x8d, 0x19,
+ 0x9c, 0xc1, 0xb4, 0x2d, 0x6f, 0x55, 0xe4, 0x95, 0x28, 0x52, 0xc5, 0x96, 0xdb, 0x28, 0x42, 0x29,
+ 0xeb, 0x37, 0xd1, 0x98, 0xca, 0x93, 0xa1, 0x94, 0x6c, 0x83, 0xba, 0x70, 0x97, 0x36, 0x66, 0xf0,
+ 0x02, 0x26, 0x6d, 0x9c, 0xf5, 0x2e, 0x8f, 0xd4, 0x80, 0x12, 0x9e, 0xb3, 0xf4, 0x42, 0xe0, 0xa9,
+ 0xd2, 0x35, 0x91, 0xf6, 0xb0, 0xe0, 0xef, 0x1e, 0x4c, 0x55, 0x16, 0xa1, 0x1a, 0x8b, 0x0c, 0x31,
+ 0xaf, 0xc4, 0x8e, 0x3c, 0x85, 0x49, 0x22, 0x10, 0xef, 0x78, 0xbe, 0x09, 0x2b, 0x5e, 0x2f, 0xe7,
+ 0x84, 0x8e, 0x1b, 0xf0, 0x92, 0x67, 0x48, 0x3e, 0x07, 0x2f, 0x11, 0xc5, 0x1d, 0xe6, 0x86, 0x62,
+ 0x6b, 0x0a, 0x18, 0x48, 0x13, 0xbe, 0x80, 0x71, 0x86, 0x99, 0x0e, 0xae, 0x19, 0x3d, 0xcd, 0xf0,
+ 0x6a, 0x4c, 0x53, 0x9e, 0xc2, 0x24, 0xc3, 0xec, 0x56, 0xf0, 0x0a, 0x0d, 0xc7, 0x31, 0x42, 0x0d,
+ 0xd8, 0x90, 0x4a, 0xb6, 0x41, 0x19, 0xca, 0x88, 0xe5, 0x39, 0xc6, 0xfa, 0x29, 0x3b, 0x74, 0xac,
+ 0xc1, 0xb5, 0xc1, 0xc8, 0x73, 0x38, 0xac, 0x49, 0xd7, 0xbc, 0x2c, 0x31, 0x0e, 0x4b, 0x26, 0x30,
+ 0xaf, 0xf4, 0x52, 0x3a, 0x94, 0x18, 0xae, 0x71, 0x5d, 0x68, 0xcf, 0x7d, 0x58, 0xa5, 0x54, 0x61,
+ 0xae, 0xf7, 0xb3, 0x09, 0xfb, 0x8b, 0xc1, 0x14, 0x89, 0x8b, 0x8c, 0x95, 0xa1, 0x40, 0x59, 0xa4,
+ 0x37, 0xe8, 0x8f, 0x66, 0x96, 0x4a, 0x50, 0x83, 0xd4, 0x60, 0xe4, 0x53, 0x00, 0x13, 0x29, 0x65,
+ 0x77, 0x3b, 0xdf, 0xd5, 0x61, 0x5c, 0x8d, 0xbc, 0x66, 0x77, 0xbb, 0xc6, 0x1d, 0x96, 0xbc, 0x44,
+ 0xe9, 0xc3, 0xcc, 0x6a, 0xdc, 0x17, 0x0a, 0x20, 0xcf, 0xe0, 0xa0, 0x75, 0x87, 0xbf, 0x6f, 0x13,
+ 0xe9, 0x7b, 0x9a, 0x32, 0x6e, 0x28, 0x27, 0xdb, 0x44, 0x06, 0xff, 0x58, 0xf0, 0x48, 0xa0, 0xac,
+ 0x0a, 0x81, 0x7b, 0xa3, 0xfa, 0xd2, 0xdc, 0x96, 0x61, 0x54, 0x64, 0xaa, 0x64, 0xf3, 0x87, 0x3a,
+ 0xd4, 0xd4, 0xb6, 0xaa, 0x41, 0xb2, 0x80, 0x87, 0xfb, 0xed, 0x89, 0x8a, 0x5b, 0x3d, 0x32, 0x87,
+ 0x3e, 0xe8, 0xf6, 0x66, 0x55, 0xdc, 0xaa, 0xb9, 0x25, 0x85, 0xb8, 0x6e, 0x87, 0x5f, 0xcf, 0xad,
+ 0xc6, 0x9a, 0xd1, 0x36, 0xc9, 0x74, 0xc6, 0xe6, 0xd5, 0x98, 0xa6, 0xb4, 0x89, 0xd5, 0xa0, 0x1a,
+ 0x9b, 0xd5, 0x26, 0x46, 0x6b, 0x30, 0x78, 0x07, 0x5e, 0xb7, 0x9c, 0x63, 0x70, 0x62, 0xb3, 0xaa,
+ 0xd6, 0xdc, 0x5b, 0x3e, 0xe9, 0xbc, 0xa9, 0xff, 0x2e, 0x29, 0xd5, 0x44, 0xf2, 0x1d, 0x0c, 0x6b,
+ 0x01, 0xfd, 0x1c, 0xbc, 0xe5, 0x67, 0x9d, 0x3b, 0xef, 0x69, 0x18, 0x6d, 0xe8, 0x8b, 0xef, 0x3b,
+ 0xbf, 0x8f, 0xf9, 0x55, 0x88, 0x0b, 0x7d, 0xba, 0xfe, 0xf5, 0x7c, 0x35, 0xfd, 0x48, 0x1d, 0x4f,
+ 0x2e, 0xe9, 0xd9, 0x7a, 0x6a, 0x91, 0x21, 0xf4, 0x7e, 0x3b, 0x5b, 0x4f, 0x6d, 0x75, 0xa0, 0x27,
+ 0xa7, 0xd3, 0xde, 0xe2, 0x18, 0x46, 0xcd, 0x17, 0x43, 0x0e, 0x00, 0xd4, 0x39, 0xec, 0x5c, 0xbc,
+ 0x78, 0xf9, 0xc3, 0xdb, 0xd7, 0x53, 0x8b, 0x8c, 0xc0, 0x39, 0xff, 0xf9, 0xfc, 0xc7, 0xa9, 0xfd,
+ 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa0, 0x86, 0x78, 0x5b, 0x16, 0x07, 0x00, 0x00,
}
diff --git a/lxd/migrate.proto b/lxd/migration/migrate.proto
similarity index 99%
rename from lxd/migrate.proto
rename to lxd/migration/migrate.proto
index 8bcfdc295..bfb35eca2 100644
--- a/lxd/migrate.proto
+++ b/lxd/migration/migrate.proto
@@ -1,7 +1,7 @@
// silence the protobuf compiler warning by setting the default
syntax = "proto2";
-package main;
+package migration;
enum MigrationFSType {
RSYNC = 0;
diff --git a/lxd/migration/wsproto.go b/lxd/migration/wsproto.go
new file mode 100644
index 000000000..7ba37b29f
--- /dev/null
+++ b/lxd/migration/wsproto.go
@@ -0,0 +1,71 @@
+package migration
+
+import (
+ "fmt"
+ "io/ioutil"
+
+ "github.com/golang/protobuf/proto"
+ "github.com/gorilla/websocket"
+
+ "github.com/lxc/lxd/shared"
+)
+
+// ProtoRecv gets a protobuf message from a websocket
+func ProtoRecv(ws *websocket.Conn, msg proto.Message) error {
+ mt, r, err := ws.NextReader()
+ if err != nil {
+ return err
+ }
+
+ if mt != websocket.BinaryMessage {
+ return fmt.Errorf("Only binary messages allowed")
+ }
+
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ return err
+ }
+
+ err = proto.Unmarshal(buf, msg)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ProtoSend sends a protobuf message over a websocket
+func ProtoSend(ws *websocket.Conn, msg proto.Message) error {
+ w, err := ws.NextWriter(websocket.BinaryMessage)
+ if err != nil {
+ return err
+ }
+ defer w.Close()
+
+ data, err := proto.Marshal(msg)
+ if err != nil {
+ return err
+ }
+
+ err = shared.WriteAll(w, data)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ProtoSendControl sends a migration control message over a websocket
+func ProtoSendControl(ws *websocket.Conn, err error) {
+ message := ""
+ if err != nil {
+ message = err.Error()
+ }
+
+ msg := MigrationControl{
+ Success: proto.Bool(err == nil),
+ Message: proto.String(message),
+ }
+
+ ProtoSend(ws, &msg)
+}
diff --git a/lxd/storage.go b/lxd/storage.go
index 5df78f7c6..f7b04ebf0 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -12,6 +12,7 @@ import (
"github.com/gorilla/websocket"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/lxd/state"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -196,7 +197,7 @@ type storage interface {
StorageEntitySetQuota(volumeType int, size int64, data interface{}) error
// Functions dealing with migration.
- MigrationType() MigrationFSType
+ MigrationType() migration.MigrationFSType
// Does this storage backend preserve inodes when it is moved across LXD
// hosts?
PreservesInodes() bool
@@ -222,7 +223,7 @@ type storage interface {
MigrationSink(
live bool,
c container,
- objects []*Snapshot,
+ objects []*migration.Snapshot,
conn *websocket.Conn,
srcIdmap *idmap.IdmapSet,
op *operation,
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index 2eca2c6a8..996ca3a85 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -16,6 +16,7 @@ import (
"github.com/gorilla/websocket"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -1972,12 +1973,12 @@ func (s *btrfsMigrationSourceDriver) Cleanup() {
}
}
-func (s *storageBtrfs) MigrationType() MigrationFSType {
+func (s *storageBtrfs) MigrationType() migration.MigrationFSType {
if s.s.OS.RunningInUserNS {
- return MigrationFSType_RSYNC
+ return migration.MigrationFSType_RSYNC
}
- return MigrationFSType_BTRFS
+ return migration.MigrationFSType_BTRFS
}
func (s *storageBtrfs) PreservesInodes() bool {
@@ -2023,7 +2024,7 @@ func (s *storageBtrfs) MigrationSource(c container, containerOnly bool) (Migrati
return driver, nil
}
-func (s *storageBtrfs) MigrationSink(live bool, container container, snapshots []*Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
+func (s *storageBtrfs) MigrationSink(live bool, container container, snapshots []*migration.Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
if s.s.OS.RunningInUserNS {
return rsyncMigrationSink(live, container, snapshots, conn, srcIdmap, op, containerOnly)
}
diff --git a/lxd/storage_ceph_migration.go b/lxd/storage_ceph_migration.go
index 000a91a9c..4e4016203 100644
--- a/lxd/storage_ceph_migration.go
+++ b/lxd/storage_ceph_migration.go
@@ -8,6 +8,7 @@ import (
"github.com/gorilla/websocket"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/idmap"
"github.com/lxc/lxd/shared/logger"
@@ -169,8 +170,8 @@ func (s *rbdMigrationSourceDriver) SendWhileRunning(conn *websocket.Conn,
return nil
}
-func (s *storageCeph) MigrationType() MigrationFSType {
- return MigrationFSType_RBD
+func (s *storageCeph) MigrationType() migration.MigrationFSType {
+ return migration.MigrationFSType_RBD
}
func (s *storageCeph) PreservesInodes() bool {
@@ -240,7 +241,7 @@ func (s *storageCeph) MigrationSource(c container, containerOnly bool) (Migratio
}
func (s *storageCeph) MigrationSink(live bool, c container,
- snapshots []*Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet,
+ snapshots []*migration.Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet,
op *operation, containerOnly bool) error {
// Check that we received a valid root disk device with a pool property
// set.
diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go
index 771153d70..6e4ddd42e 100644
--- a/lxd/storage_dir.go
+++ b/lxd/storage_dir.go
@@ -9,6 +9,7 @@ import (
"github.com/gorilla/websocket"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/idmap"
@@ -1030,8 +1031,8 @@ func (s *storageDir) ImageUmount(fingerprint string) (bool, error) {
return true, nil
}
-func (s *storageDir) MigrationType() MigrationFSType {
- return MigrationFSType_RSYNC
+func (s *storageDir) MigrationType() migration.MigrationFSType {
+ return migration.MigrationFSType_RSYNC
}
func (s *storageDir) PreservesInodes() bool {
@@ -1042,7 +1043,7 @@ func (s *storageDir) MigrationSource(container container, containerOnly bool) (M
return rsyncMigrationSource(container, containerOnly)
}
-func (s *storageDir) MigrationSink(live bool, container container, snapshots []*Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
+func (s *storageDir) MigrationSink(live bool, container container, snapshots []*migration.Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
return rsyncMigrationSink(live, container, snapshots, conn, srcIdmap, op, containerOnly)
}
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index d74b24d2f..3eedf4fa0 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -9,6 +9,7 @@ import (
"github.com/gorilla/websocket"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/idmap"
@@ -1724,8 +1725,8 @@ func (s *storageLvm) ImageUmount(fingerprint string) (bool, error) {
return true, nil
}
-func (s *storageLvm) MigrationType() MigrationFSType {
- return MigrationFSType_RSYNC
+func (s *storageLvm) MigrationType() migration.MigrationFSType {
+ return migration.MigrationFSType_RSYNC
}
func (s *storageLvm) PreservesInodes() bool {
@@ -1736,7 +1737,7 @@ func (s *storageLvm) MigrationSource(container container, containerOnly bool) (M
return rsyncMigrationSource(container, containerOnly)
}
-func (s *storageLvm) MigrationSink(live bool, container container, snapshots []*Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
+func (s *storageLvm) MigrationSink(live bool, container container, snapshots []*migration.Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
return rsyncMigrationSink(live, container, snapshots, conn, srcIdmap, op, containerOnly)
}
diff --git a/lxd/storage_migration.go b/lxd/storage_migration.go
index 76308e96d..e8a966319 100644
--- a/lxd/storage_migration.go
+++ b/lxd/storage_migration.go
@@ -6,6 +6,7 @@ import (
"github.com/gorilla/websocket"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/idmap"
@@ -96,7 +97,7 @@ func rsyncMigrationSource(c container, containerOnly bool) (MigrationStorageSour
return rsyncStorageSourceDriver{c, snapshots}, nil
}
-func snapshotProtobufToContainerArgs(containerName string, snap *Snapshot) db.ContainerArgs {
+func snapshotProtobufToContainerArgs(containerName string, snap *migration.Snapshot) db.ContainerArgs {
config := map[string]string{}
for _, ent := range snap.LocalConfig {
@@ -126,7 +127,7 @@ func snapshotProtobufToContainerArgs(containerName string, snap *Snapshot) db.Co
}
}
-func rsyncMigrationSink(live bool, container container, snapshots []*Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
+func rsyncMigrationSink(live bool, container container, snapshots []*migration.Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
ourStart, err := container.StorageStart()
if err != nil {
return err
diff --git a/lxd/storage_mock.go b/lxd/storage_mock.go
index 5019237f7..1fa402f87 100644
--- a/lxd/storage_mock.go
+++ b/lxd/storage_mock.go
@@ -5,6 +5,7 @@ import (
"github.com/gorilla/websocket"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/idmap"
"github.com/lxc/lxd/shared/logger"
@@ -204,8 +205,8 @@ func (s *storageMock) ImageUmount(fingerprint string) (bool, error) {
return true, nil
}
-func (s *storageMock) MigrationType() MigrationFSType {
- return MigrationFSType_RSYNC
+func (s *storageMock) MigrationType() migration.MigrationFSType {
+ return migration.MigrationFSType_RSYNC
}
func (s *storageMock) PreservesInodes() bool {
@@ -215,7 +216,8 @@ func (s *storageMock) PreservesInodes() bool {
func (s *storageMock) MigrationSource(container container, containerOnly bool) (MigrationStorageSourceDriver, error) {
return nil, fmt.Errorf("not implemented")
}
-func (s *storageMock) MigrationSink(live bool, container container, snapshots []*Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
+
+func (s *storageMock) MigrationSink(live bool, container container, snapshots []*migration.Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
return nil
}
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index 46d9b6c50..9e4dec3eb 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -13,6 +13,7 @@ import (
"github.com/gorilla/websocket"
+ "github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -2160,8 +2161,8 @@ func (s *zfsMigrationSourceDriver) Cleanup() {
}
}
-func (s *storageZfs) MigrationType() MigrationFSType {
- return MigrationFSType_ZFS
+func (s *storageZfs) MigrationType() migration.MigrationFSType {
+ return migration.MigrationFSType_ZFS
}
func (s *storageZfs) PreservesInodes() bool {
@@ -2219,7 +2220,7 @@ func (s *storageZfs) MigrationSource(ct container, containerOnly bool) (Migratio
return &driver, nil
}
-func (s *storageZfs) MigrationSink(live bool, container container, snapshots []*Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
+func (s *storageZfs) MigrationSink(live bool, container container, snapshots []*migration.Snapshot, conn *websocket.Conn, srcIdmap *idmap.IdmapSet, op *operation, containerOnly bool) error {
poolName := s.getOnDiskPoolName()
zfsRecv := func(zfsName string, writeWrapper func(io.WriteCloser) io.WriteCloser) error {
zfsFsName := fmt.Sprintf("%s/%s", poolName, zfsName)
diff --git a/test/suites/static_analysis.sh b/test/suites/static_analysis.sh
index 598d77113..4f9d0c892 100644
--- a/test/suites/static_analysis.sh
+++ b/test/suites/static_analysis.sh
@@ -72,6 +72,7 @@ test_static_analysis() {
golint -set_exit_status lxd/debug
golint -set_exit_status lxd/endpoints
golint -set_exit_status lxd/maas
+ golint -set_exit_status lxd/migration
golint -set_exit_status lxd/node
golint -set_exit_status lxd/state
golint -set_exit_status lxd/sys
From 51eb1f60363f524daa87fcbe4fdce39ffc0c3d28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 22 Feb 2018 00:26:15 -0500
Subject: [PATCH 4/7] lxd/daemon: Cleanup startup code a bit
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/main_daemon.go | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lxd/main_daemon.go b/lxd/main_daemon.go
index 4b0948544..eec8d9e4e 100644
--- a/lxd/main_daemon.go
+++ b/lxd/main_daemon.go
@@ -24,23 +24,24 @@ func cmdDaemon(args *Args) error {
dbg.Memory(args.MemProfile),
dbg.Goroutines(args.PrintGoroutinesEvery),
)
- defer stop()
if err != nil {
- fmt.Printf("%v\n", err)
- return nil
+ return err
}
+ defer stop()
+
neededPrograms := []string{"setfacl", "rsync", "tar", "unsquashfs", "xz"}
for _, p := range neededPrograms {
_, err := exec.LookPath(p)
if err != nil {
return err
}
-
}
+
c := &DaemonConfig{
Group: args.Group,
}
+
d := NewDaemon(c, sys.DefaultOS())
err = d.Init()
if err != nil {
@@ -56,7 +57,6 @@ func cmdDaemon(args *Args) error {
s := d.State()
select {
case sig := <-ch:
-
if sig == syscall.SIGPWR {
logger.Infof("Received '%s signal', shutting down containers.", sig)
containersShutdown(s)
From d3b81aab6c4abfd6540ad3a5a4434501727061d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 22 Feb 2018 01:11:45 -0500
Subject: [PATCH 5/7] lxc: Introduce a new utils package
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxc/copy.go | 3 +-
lxc/image.go | 11 +--
lxc/init.go | 5 +-
lxc/storage.go | 3 +-
lxc/utils.go | 153 -----------------------------------------
lxc/utils/cancel.go | 48 +++++++++++++
lxc/utils/progress.go | 128 ++++++++++++++++++++++++++++++++++
test/suites/static_analysis.sh | 1 +
8 files changed, 190 insertions(+), 162 deletions(-)
create mode 100644 lxc/utils/cancel.go
create mode 100644 lxc/utils/progress.go
diff --git a/lxc/copy.go b/lxc/copy.go
index 85acaeb63..8b68143ee 100644
--- a/lxc/copy.go
+++ b/lxc/copy.go
@@ -6,6 +6,7 @@ import (
"github.com/lxc/lxd/client"
"github.com/lxc/lxd/lxc/config"
+ "github.com/lxc/lxd/lxc/utils"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
@@ -195,7 +196,7 @@ func (c *copyCmd) copyContainer(conf *config.Config, sourceResource string,
}
// Watch the background operation
- progress := progressRenderer{Format: i18n.G("Transferring container: %s")}
+ progress := utils.ProgressRenderer{Format: i18n.G("Transferring container: %s")}
_, err = op.AddHandler(progress.UpdateOp)
if err != nil {
progress.Done("")
diff --git a/lxc/image.go b/lxc/image.go
index eaccbb231..eb104ec02 100644
--- a/lxc/image.go
+++ b/lxc/image.go
@@ -18,6 +18,7 @@ import (
"github.com/lxc/lxd/client"
"github.com/lxc/lxd/lxc/config"
+ "github.com/lxc/lxd/lxc/utils"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
@@ -445,7 +446,7 @@ func (c *imageCmd) run(conf *config.Config, args []string) error {
}
// Register progress handler
- progress := progressRenderer{Format: i18n.G("Copying the image: %s")}
+ progress := utils.ProgressRenderer{Format: i18n.G("Copying the image: %s")}
_, err = op.AddHandler(progress.UpdateOp)
if err != nil {
progress.Done("")
@@ -453,7 +454,7 @@ func (c *imageCmd) run(conf *config.Config, args []string) error {
}
// Wait for operation to finish
- err = cancelableWait(op, &progress)
+ err = utils.CancelableWait(op, &progress)
if err != nil {
progress.Done("")
return err
@@ -525,7 +526,7 @@ func (c *imageCmd) run(conf *config.Config, args []string) error {
}
image := c.dereferenceAlias(d, inName)
- progress := progressRenderer{Format: i18n.G("Refreshing the image: %s")}
+ progress := utils.ProgressRenderer{Format: i18n.G("Refreshing the image: %s")}
op, err := d.RefreshImage(image)
if err != nil {
return err
@@ -713,7 +714,7 @@ func (c *imageCmd) run(conf *config.Config, args []string) error {
image.Properties[strings.TrimSpace(fields[0])] = strings.TrimSpace(fields[1])
}
- progress := progressRenderer{Format: i18n.G("Transferring image: %s")}
+ progress := utils.ProgressRenderer{Format: i18n.G("Transferring image: %s")}
if strings.HasPrefix(imageFile, "https://") {
image.Source = &api.ImagesPostSource{}
image.Source.Type = "url"
@@ -918,7 +919,7 @@ func (c *imageCmd) run(conf *config.Config, args []string) error {
defer destRootfs.Close()
// Prepare the download request
- progress := progressRenderer{Format: i18n.G("Exporting the image: %s")}
+ progress := utils.ProgressRenderer{Format: i18n.G("Exporting the image: %s")}
req := lxd.ImageFileRequest{
MetaFile: io.WriteSeeker(dest),
RootfsFile: io.WriteSeeker(destRootfs),
diff --git a/lxc/init.go b/lxc/init.go
index 4b8fcf3fc..611aeb2d9 100644
--- a/lxc/init.go
+++ b/lxc/init.go
@@ -7,6 +7,7 @@ import (
"github.com/lxc/lxd/client"
"github.com/lxc/lxd/lxc/config"
+ "github.com/lxc/lxd/lxc/utils"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
@@ -286,14 +287,14 @@ func (c *initCmd) create(conf *config.Config, args []string) (lxd.ContainerServe
}
// Watch the background operation
- progress := progressRenderer{Format: i18n.G("Retrieving image: %s")}
+ progress := utils.ProgressRenderer{Format: i18n.G("Retrieving image: %s")}
_, err = op.AddHandler(progress.UpdateOp)
if err != nil {
progress.Done("")
return nil, "", err
}
- err = cancelableWait(op, &progress)
+ err = utils.CancelableWait(op, &progress)
if err != nil {
progress.Done("")
return nil, "", err
diff --git a/lxc/storage.go b/lxc/storage.go
index 735160743..c59598eb9 100644
--- a/lxc/storage.go
+++ b/lxc/storage.go
@@ -14,6 +14,7 @@ import (
"github.com/lxc/lxd/client"
"github.com/lxc/lxd/lxc/config"
+ "github.com/lxc/lxd/lxc/utils"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
@@ -1071,7 +1072,7 @@ func (c *storageCmd) doStoragePoolVolumeCopy(client lxd.ContainerServer, src str
return err
}
// Register progress handler
- progress := progressRenderer{Format: opMsg}
+ progress := utils.ProgressRenderer{Format: opMsg}
_, err = op.AddHandler(progress.UpdateOp)
if err != nil {
progress.Done("")
diff --git a/lxc/utils.go b/lxc/utils.go
index 97d367249..47d0573ae 100644
--- a/lxc/utils.go
+++ b/lxc/utils.go
@@ -5,17 +5,13 @@ import (
"net"
"net/url"
"os"
- "os/signal"
"sort"
"strings"
- "sync"
"syscall"
- "time"
"github.com/lxc/lxd/client"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/i18n"
- "github.com/lxc/lxd/shared/ioprogress"
)
// Lists
@@ -26,118 +22,6 @@ const (
listFormatYAML = "yaml"
)
-// Progress tracking
-type progressRenderer struct {
- Format string
-
- maxLength int
- wait time.Time
- done bool
- lock sync.Mutex
-}
-
-func (p *progressRenderer) Done(msg string) {
- // Acquire rendering lock
- p.lock.Lock()
- defer p.lock.Unlock()
-
- // Check if we're already done
- if p.done {
- return
- }
-
- // Mark this renderer as done
- p.done = true
-
- // Print the new message
- if msg != "" {
- msg += "\n"
- }
-
- if len(msg) > p.maxLength {
- p.maxLength = len(msg)
- } else {
- fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
- }
-
- fmt.Print("\r")
- fmt.Print(msg)
-}
-
-func (p *progressRenderer) Update(status string) {
- // Wait if needed
- timeout := p.wait.Sub(time.Now())
- if timeout.Seconds() > 0 {
- time.Sleep(timeout)
- }
-
- // Acquire rendering lock
- p.lock.Lock()
- defer p.lock.Unlock()
-
- // Check if we're already done
- if p.done {
- return
- }
-
- // Print the new message
- msg := "%s"
- if p.Format != "" {
- msg = p.Format
- }
-
- msg = fmt.Sprintf("\r"+msg, status)
-
- if len(msg) > p.maxLength {
- p.maxLength = len(msg)
- } else {
- fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
- }
-
- fmt.Print(msg)
-}
-
-func (p *progressRenderer) Warn(status string, timeout time.Duration) {
- // Acquire rendering lock
- p.lock.Lock()
- defer p.lock.Unlock()
-
- // Check if we're already done
- if p.done {
- return
- }
-
- // Render the new message
- p.wait = time.Now().Add(timeout)
- msg := fmt.Sprintf("\r%s", status)
-
- if len(msg) > p.maxLength {
- p.maxLength = len(msg)
- } else {
- fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
- }
-
- fmt.Print(msg)
-}
-
-func (p *progressRenderer) UpdateProgress(progress ioprogress.ProgressData) {
- p.Update(progress.Text)
-}
-
-func (p *progressRenderer) UpdateOp(op api.Operation) {
- if op.Metadata == nil {
- return
- }
-
- for _, key := range []string{"fs_progress", "download_progress"} {
- value, ok := op.Metadata[key]
- if ok {
- p.Update(value.(string))
- break
- }
- }
-}
-
type stringList [][]string
func (a stringList) Len() int {
@@ -343,43 +227,6 @@ func profileDeviceAdd(client lxd.ContainerServer, name string, devName string, d
return nil
}
-// Wait for an operation and cancel it on SIGINT/SIGTERM
-func cancelableWait(op *lxd.RemoteOperation, progress *progressRenderer) error {
- // Signal handling
- chSignal := make(chan os.Signal)
- signal.Notify(chSignal, os.Interrupt)
-
- // Operation handling
- chOperation := make(chan error)
- go func() {
- chOperation <- op.Wait()
- close(chOperation)
- }()
-
- count := 0
- for {
- select {
- case err := <-chOperation:
- return err
- case <-chSignal:
- err := op.CancelTarget()
- if err == nil {
- return fmt.Errorf(i18n.G("Remote operation canceled by user"))
- }
-
- count++
-
- if count == 3 {
- return fmt.Errorf(i18n.G("User signaled us three times, exiting. The remote operation will keep running."))
- }
-
- if progress != nil {
- progress.Warn(fmt.Sprintf(i18n.G("%v (interrupt two more times to force)"), err), time.Second*5)
- }
- }
- }
-}
-
// Create the specified image alises, updating those that already exist
func ensureImageAliases(client lxd.ContainerServer, aliases []api.ImageAlias, fingerprint string) error {
if len(aliases) == 0 {
diff --git a/lxc/utils/cancel.go b/lxc/utils/cancel.go
new file mode 100644
index 000000000..f1b065cbd
--- /dev/null
+++ b/lxc/utils/cancel.go
@@ -0,0 +1,48 @@
+package utils
+
+import (
+ "fmt"
+ "os"
+ "os/signal"
+ "time"
+
+ "github.com/lxc/lxd/client"
+ "github.com/lxc/lxd/shared/i18n"
+)
+
+// CancelableWait waits for an operation and cancel it on SIGINT/SIGTERM
+func CancelableWait(op *lxd.RemoteOperation, progress *ProgressRenderer) error {
+ // Signal handling
+ chSignal := make(chan os.Signal)
+ signal.Notify(chSignal, os.Interrupt)
+
+ // Operation handling
+ chOperation := make(chan error)
+ go func() {
+ chOperation <- op.Wait()
+ close(chOperation)
+ }()
+
+ count := 0
+ for {
+ select {
+ case err := <-chOperation:
+ return err
+ case <-chSignal:
+ err := op.CancelTarget()
+ if err == nil {
+ return fmt.Errorf(i18n.G("Remote operation canceled by user"))
+ }
+
+ count++
+
+ if count == 3 {
+ return fmt.Errorf(i18n.G("User signaled us three times, exiting. The remote operation will keep running."))
+ }
+
+ if progress != nil {
+ progress.Warn(fmt.Sprintf(i18n.G("%v (interrupt two more times to force)"), err), time.Second*5)
+ }
+ }
+ }
+}
diff --git a/lxc/utils/progress.go b/lxc/utils/progress.go
new file mode 100644
index 000000000..1a781895a
--- /dev/null
+++ b/lxc/utils/progress.go
@@ -0,0 +1,128 @@
+package utils
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/lxc/lxd/shared/api"
+ "github.com/lxc/lxd/shared/ioprogress"
+)
+
+// ProgressRenderer tracks the progress information
+type ProgressRenderer struct {
+ Format string
+
+ maxLength int
+ wait time.Time
+ done bool
+ lock sync.Mutex
+}
+
+// Done prints the final status and prevents any update
+func (p *ProgressRenderer) Done(msg string) {
+ // Acquire rendering lock
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ // Check if we're already done
+ if p.done {
+ return
+ }
+
+ // Mark this renderer as done
+ p.done = true
+
+ // Print the new message
+ if msg != "" {
+ msg += "\n"
+ }
+
+ if len(msg) > p.maxLength {
+ p.maxLength = len(msg)
+ } else {
+ fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
+ }
+
+ fmt.Print("\r")
+ fmt.Print(msg)
+}
+
+// Update changes the status message to the provided string
+func (p *ProgressRenderer) Update(status string) {
+ // Wait if needed
+ timeout := p.wait.Sub(time.Now())
+ if timeout.Seconds() > 0 {
+ time.Sleep(timeout)
+ }
+
+ // Acquire rendering lock
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ // Check if we're already done
+ if p.done {
+ return
+ }
+
+ // Print the new message
+ msg := "%s"
+ if p.Format != "" {
+ msg = p.Format
+ }
+
+ msg = fmt.Sprintf("\r"+msg, status)
+
+ if len(msg) > p.maxLength {
+ p.maxLength = len(msg)
+ } else {
+ fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
+ }
+
+ fmt.Print(msg)
+}
+
+// Warn shows a temporary message instead of the status
+func (p *ProgressRenderer) Warn(status string, timeout time.Duration) {
+ // Acquire rendering lock
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ // Check if we're already done
+ if p.done {
+ return
+ }
+
+ // Render the new message
+ p.wait = time.Now().Add(timeout)
+ msg := fmt.Sprintf("\r%s", status)
+
+ if len(msg) > p.maxLength {
+ p.maxLength = len(msg)
+ } else {
+ fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
+ }
+
+ fmt.Print(msg)
+}
+
+// UpdateProgress is a helper to update the status using an iopgress instance
+func (p *ProgressRenderer) UpdateProgress(progress ioprogress.ProgressData) {
+ p.Update(progress.Text)
+}
+
+// UpdateOp is a helper to update the status using a LXD API operation
+func (p *ProgressRenderer) UpdateOp(op api.Operation) {
+ if op.Metadata == nil {
+ return
+ }
+
+ for _, key := range []string{"fs_progress", "download_progress"} {
+ value, ok := op.Metadata[key]
+ if ok {
+ p.Update(value.(string))
+ break
+ }
+ }
+}
diff --git a/test/suites/static_analysis.sh b/test/suites/static_analysis.sh
index 4f9d0c892..a3cbb96ff 100644
--- a/test/suites/static_analysis.sh
+++ b/test/suites/static_analysis.sh
@@ -61,6 +61,7 @@ test_static_analysis() {
golint -set_exit_status lxc/
golint -set_exit_status lxc/config/
+ golint -set_exit_status lxc/utils/
golint -set_exit_status lxd-benchmark
golint -set_exit_status lxd-benchmark/benchmark
From 6a3c2cc0e262149c8747a086804ac8191688a940 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Fri, 9 Feb 2018 21:27:00 -0500
Subject: [PATCH 6/7] lxd-p2c: Add new tool
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd-p2c/main.go | 69 ++++++++++++++++
lxd-p2c/main_migrate.go | 172 +++++++++++++++++++++++++++++++++++++++
lxd-p2c/main_netcat.go | 61 ++++++++++++++
lxd-p2c/setns.go | 33 ++++++++
lxd-p2c/transfer.go | 107 ++++++++++++++++++++++++
lxd-p2c/utils.go | 180 +++++++++++++++++++++++++++++++++++++++++
test/suites/static_analysis.sh | 2 +
7 files changed, 624 insertions(+)
create mode 100644 lxd-p2c/main.go
create mode 100644 lxd-p2c/main_migrate.go
create mode 100644 lxd-p2c/main_netcat.go
create mode 100644 lxd-p2c/setns.go
create mode 100644 lxd-p2c/transfer.go
create mode 100644 lxd-p2c/utils.go
diff --git a/lxd-p2c/main.go b/lxd-p2c/main.go
new file mode 100644
index 000000000..5a9d71340
--- /dev/null
+++ b/lxd-p2c/main.go
@@ -0,0 +1,69 @@
+package main
+
+import (
+ "os"
+
+ "github.com/spf13/cobra"
+
+ "github.com/lxc/lxd/shared/version"
+)
+
+type cmdGlobal struct {
+ flagVersion bool
+ flagHelp bool
+}
+
+func main() {
+ app := &cobra.Command{}
+ app.Use = "lxd-p2c"
+ app.Short = "Physical to container migration tool"
+ app.Long = `Description:
+ Physical to container migration tool
+
+ This tool lets you turn any Linux filesystem (including your current one)
+ into a LXD container on a remote LXD host.
+
+ It will setup a clean mount tree made of the root filesystem and any
+ additional mount you list, then transfer this through LXD's migration
+ API to create a new container from it.
+
+ The same set of options as ` + "`lxc launch`" + ` are also supported.
+`
+ app.SilenceUsage = true
+
+ // Global flags
+ globalCmd := cmdGlobal{}
+ app.PersistentFlags().BoolVar(&globalCmd.flagVersion, "version", false, "Print version number")
+ app.PersistentFlags().BoolVarP(&globalCmd.flagHelp, "help", "h", false, "Print help")
+
+ // Version handling
+ app.SetVersionTemplate("{{.Version}}\n")
+ app.Version = version.Version
+
+ // migrate command (main)
+ migrateCmd := cmdMigrate{global: &globalCmd}
+ app.Flags().StringArrayVarP(&migrateCmd.flagConfig, "config", "c", nil, "Configuration key and value to set on the container"+"``")
+ app.Flags().StringVarP(&migrateCmd.flagNetwork, "network", "n", "", "Network to use for the container"+"``")
+ app.Flags().StringArrayVarP(&migrateCmd.flagProfile, "profile", "p", nil, "Profile to apply to the container"+"``")
+ app.Flags().StringVarP(&migrateCmd.flagStorage, "storage", "s", "", "Storage pool to use for the container"+"``")
+ app.Flags().StringVarP(&migrateCmd.flagType, "type", "t", "", "Instance type to use for the container"+"``")
+ app.Flags().BoolVar(&migrateCmd.flagNoProfiles, "no-profiles", false, "Create the container with no profiles applied")
+ app.Use = "lxd-p2c <target URL> <container name> <filesystem root> [<filesystem mounts>...]"
+ app.RunE = migrateCmd.Run
+ app.Args = cobra.ArbitraryArgs
+
+ // netcat sub-command
+ netcatCmd := cmdNetcat{global: &globalCmd}
+ appNetcat := &cobra.Command{}
+ appNetcat.Use = "netcat <address>"
+ appNetcat.Hidden = true
+ appNetcat.Short = "Sends stdin data to a unix socket"
+ appNetcat.RunE = netcatCmd.Run
+ app.AddCommand(appNetcat)
+
+ // Run the main command and handle errors
+ err := app.Execute()
+ if err != nil {
+ os.Exit(1)
+ }
+}
diff --git a/lxd-p2c/main_migrate.go b/lxd-p2c/main_migrate.go
new file mode 100644
index 000000000..660ac2437
--- /dev/null
+++ b/lxd-p2c/main_migrate.go
@@ -0,0 +1,172 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "sort"
+ "strings"
+ "syscall"
+
+ "github.com/spf13/cobra"
+
+ "github.com/lxc/lxd/lxc/utils"
+ "github.com/lxc/lxd/shared/api"
+ "github.com/lxc/lxd/shared/osarch"
+)
+
+type cmdMigrate struct {
+ global *cmdGlobal
+
+ flagConfig []string
+ flagNetwork string
+ flagProfile []string
+ flagStorage string
+ flagType string
+ flagNoProfiles bool
+}
+
+func (c *cmdMigrate) Run(cmd *cobra.Command, args []string) error {
+ // Help and usage
+ if len(args) == 0 {
+ return cmd.Help()
+ }
+
+ // Sanity checks
+ if os.Geteuid() != 0 {
+ return fmt.Errorf("This tool must be run as root")
+ }
+
+ _, err := exec.LookPath("rsync")
+ if err != nil {
+ return err
+ }
+
+ if c.flagNoProfiles && len(c.flagProfile) != 0 {
+ return fmt.Errorf("no-profiles can't be specified alongside profiles")
+ }
+
+ // Handle mandatory arguments
+ if len(args) < 3 {
+ cmd.Help()
+ return fmt.Errorf("Missing required arguments")
+ }
+
+ // Get and sort the mounts
+ mounts := args[2:]
+ sort.Strings(mounts)
+
+ // Create the temporary directory to be used for the mounts
+ path, err := ioutil.TempDir("", "lxd-p2c_mount_")
+ if err != nil {
+ return err
+ }
+
+ // Automatically clean-up the temporary path on exit
+ defer func(path string) {
+ syscall.Unmount(path, syscall.MNT_DETACH)
+ os.Remove(path)
+ }(path)
+
+ // Create the rootfs directory
+ fullPath := fmt.Sprintf("%s/rootfs", path)
+ err = os.Mkdir(fullPath, 0755)
+ if err != nil {
+ return err
+ }
+
+ // Setup the source (mounts)
+ err = setupSource(fullPath, mounts)
+ if err != nil {
+ return fmt.Errorf("Failed to setup the source: %v", err)
+ }
+
+ // Connect to the target
+ dst, err := connectTarget(args[0])
+ if err != nil {
+ return err
+ }
+
+ // Container creation request
+ apiArgs := api.ContainersPost{}
+ apiArgs.Name = args[1]
+ apiArgs.Source = api.ContainerSource{
+ Type: "migration",
+ Mode: "push",
+ }
+
+ // System architecture
+ architectureName, err := osarch.ArchitectureGetLocal()
+ if err != nil {
+ return err
+ }
+ apiArgs.Architecture = architectureName
+
+ // Instance type
+ apiArgs.InstanceType = c.flagType
+
+ // Config overrides
+ apiArgs.Config = map[string]string{}
+ for _, entry := range c.flagConfig {
+ if !strings.Contains(entry, "=") {
+ return fmt.Errorf("Bad key=value configuration: %v", entry)
+ }
+
+ fields := strings.SplitN(entry, "=", 2)
+ apiArgs.Config[fields[0]] = fields[1]
+ }
+
+ // Profiles
+ if len(c.flagProfile) != 0 {
+ apiArgs.Profiles = c.flagProfile
+ }
+
+ if c.flagNoProfiles {
+ apiArgs.Profiles = []string{}
+ }
+
+ // Devices
+ apiArgs.Devices = map[string]map[string]string{}
+
+ network := c.flagNetwork
+ if network != "" {
+ apiArgs.Devices["eth0"] = map[string]string{
+ "type": "nic",
+ "nictype": "bridged",
+ "parent": network,
+ "name": "eth0",
+ }
+ }
+
+ storage := c.flagStorage
+ if network != "" {
+ apiArgs.Devices["root"] = map[string]string{
+ "type": "disk",
+ "pool": storage,
+ "path": "/",
+ }
+ }
+
+ // Create the container
+ op, err := dst.CreateContainer(apiArgs)
+ if err != nil {
+ return err
+ }
+
+ progress := utils.ProgressRenderer{Format: "Transferring container: %s"}
+ _, err = op.AddHandler(progress.UpdateOp)
+ if err != nil {
+ progress.Done("")
+ return err
+ }
+
+ err = transferRootfs(dst, op, fullPath)
+ if err != nil {
+ return err
+ }
+
+ progress.Done(fmt.Sprintf("Container %s successfully created", apiArgs.Name))
+
+ return nil
+}
diff --git a/lxd-p2c/main_netcat.go b/lxd-p2c/main_netcat.go
new file mode 100644
index 000000000..20350a27d
--- /dev/null
+++ b/lxd-p2c/main_netcat.go
@@ -0,0 +1,61 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "sync"
+
+ "github.com/spf13/cobra"
+
+ "github.com/lxc/lxd/shared/eagain"
+)
+
+type cmdNetcat struct {
+ global *cmdGlobal
+}
+
+func (c *cmdNetcat) Run(cmd *cobra.Command, args []string) error {
+ // Help and usage
+ if len(args) == 0 {
+ cmd.Help()
+ return nil
+ }
+
+ // Handle mandatory arguments
+ if len(args) != 1 {
+ cmd.Help()
+ return fmt.Errorf("Missing required argument")
+ }
+
+ // Connect to the provided address
+ uAddr, err := net.ResolveUnixAddr("unix", args[0])
+ if err != nil {
+ return err
+ }
+
+ conn, err := net.DialUnix("unix", nil, uAddr)
+ if err != nil {
+ return err
+ }
+
+ // We'll wait until we're done reading from the socket
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+
+ go func() {
+ io.Copy(eagain.Writer{Writer: os.Stdout}, eagain.Reader{Reader: conn})
+ conn.Close()
+ wg.Done()
+ }()
+
+ go func() {
+ io.Copy(eagain.Writer{Writer: conn}, eagain.Reader{Reader: os.Stdin})
+ }()
+
+ // Wait
+ wg.Wait()
+
+ return nil
+}
diff --git a/lxd-p2c/setns.go b/lxd-p2c/setns.go
new file mode 100644
index 000000000..cd9ef1dc0
--- /dev/null
+++ b/lxd-p2c/setns.go
@@ -0,0 +1,33 @@
+package main
+
+/*
+#define _GNU_SOURCE
+#include <errno.h>
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+__attribute__((constructor)) void init(void) {
+ if (geteuid() != 0) {
+ return;
+ }
+
+ // Unshare a new mntns so our mounts don't leak
+ if (unshare(CLONE_NEWNS) < 0) {
+ fprintf(stderr, "Failed to unshare a new mount namespace: %s\n", strerror(errno));
+ _exit(1);
+ }
+
+ // Prevent mount propagation back to initial namespace
+ if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) < 0) {
+ fprintf(stderr, "Failed to mark / private: %s\n", strerror(errno));
+ _exit(1);
+ }
+
+ // We're done, jump back to Go
+}
+*/
+import "C"
diff --git a/lxd-p2c/transfer.go b/lxd-p2c/transfer.go
new file mode 100644
index 000000000..45a4eadcb
--- /dev/null
+++ b/lxd-p2c/transfer.go
@@ -0,0 +1,107 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/exec"
+
+ "github.com/gorilla/websocket"
+ "github.com/pborman/uuid"
+
+ "github.com/lxc/lxd/lxd/migration"
+ "github.com/lxc/lxd/shared"
+)
+
+// Send an rsync stream of a path over a websocket
+func rsyncSend(conn *websocket.Conn, path string) error {
+ cmd, dataSocket, stderr, err := rsyncSendSetup(path)
+ if err != nil {
+ return err
+ }
+
+ if dataSocket != nil {
+ defer dataSocket.Close()
+ }
+
+ readDone, writeDone := shared.WebsocketMirror(conn, dataSocket, io.ReadCloser(dataSocket), nil, nil)
+
+ output, err := ioutil.ReadAll(stderr)
+ if err != nil {
+ cmd.Process.Kill()
+ cmd.Wait()
+ return fmt.Errorf("Failed to rsync: %v\n%s", err, output)
+ }
+
+ err = cmd.Wait()
+ <-readDone
+ <-writeDone
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Spawn the rsync process
+func rsyncSendSetup(path string) (*exec.Cmd, net.Conn, io.ReadCloser, error) {
+ auds := fmt.Sprintf("@lxd-p2c/%s", uuid.NewRandom().String())
+ if len(auds) > shared.ABSTRACT_UNIX_SOCK_LEN-1 {
+ auds = auds[:shared.ABSTRACT_UNIX_SOCK_LEN-1]
+ }
+
+ l, err := net.Listen("unix", auds)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ execPath, err := os.Readlink("/proc/self/exe")
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ rsyncCmd := fmt.Sprintf("sh -c \"%s netcat %s\"", execPath, auds)
+
+ cmd := exec.Command("rsync",
+ "-arvP",
+ "--devices",
+ "--numeric-ids",
+ "--partial",
+ "--sparse",
+ path,
+ "localhost:/tmp/foo",
+ "-e",
+ rsyncCmd)
+
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ if err := cmd.Start(); err != nil {
+ return nil, nil, nil, err
+ }
+
+ conn, err := l.Accept()
+ if err != nil {
+ cmd.Process.Kill()
+ cmd.Wait()
+ return nil, nil, nil, err
+ }
+ l.Close()
+
+ return cmd, conn, stderr, nil
+}
+
+func protoSendError(ws *websocket.Conn, err error) {
+ migration.ProtoSendControl(ws, err)
+
+ if err != nil {
+ closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")
+ ws.WriteMessage(websocket.CloseMessage, closeMsg)
+ ws.Close()
+ }
+}
diff --git a/lxd-p2c/utils.go b/lxd-p2c/utils.go
new file mode 100644
index 000000000..dda9f2954
--- /dev/null
+++ b/lxd-p2c/utils.go
@@ -0,0 +1,180 @@
+package main
+
+import (
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+ "strings"
+ "syscall"
+
+ "golang.org/x/crypto/ssh/terminal"
+
+ "github.com/lxc/lxd/client"
+ "github.com/lxc/lxd/lxd/migration"
+ "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
+)
+
+func transferRootfs(dst lxd.ContainerServer, op *lxd.Operation, rootfs string) error {
+ // Connect to the websockets
+ wsControl, err := dst.GetOperationWebsocket(op.ID, op.Metadata["control"].(string))
+ if err != nil {
+ return err
+ }
+
+ wsFs, err := dst.GetOperationWebsocket(op.ID, op.Metadata["fs"].(string))
+ if err != nil {
+ return err
+ }
+
+ // Setup control struct
+ fs := migration.MigrationFSType_RSYNC
+ header := migration.MigrationHeader{
+ Fs: &fs,
+ }
+
+ err = migration.ProtoSend(wsControl, &header)
+ if err != nil {
+ protoSendError(wsControl, err)
+ return err
+ }
+
+ err = migration.ProtoRecv(wsControl, &header)
+ if err != nil {
+ protoSendError(wsControl, err)
+ return err
+ }
+
+ // Send the filesystem
+ abort := func(err error) error {
+ protoSendError(wsControl, err)
+ return err
+ }
+
+ err = rsyncSend(wsFs, rootfs)
+ if err != nil {
+ return abort(err)
+ }
+
+ // Check the result
+ msg := migration.MigrationControl{}
+ err = migration.ProtoRecv(wsControl, &msg)
+ if err != nil {
+ wsControl.Close()
+ return err
+ }
+
+ if !*msg.Success {
+ return fmt.Errorf(*msg.Message)
+ }
+
+ return nil
+}
+
+func connectTarget(url string) (lxd.ContainerServer, error) {
+ // Generate a new client certificate for this
+ fmt.Println("Generating a temporary client certificate. This may take a minute...")
+ clientCrt, clientKey, err := shared.GenerateMemCert(true)
+ if err != nil {
+ return nil, err
+ }
+
+ // Attempt to connect using the system CA
+ args := lxd.ConnectionArgs{}
+ args.TLSClientCert = string(clientCrt)
+ args.TLSClientKey = string(clientKey)
+ args.UserAgent = "LXD-P2C"
+ c, err := lxd.ConnectLXD(url, &args)
+
+ var certificate *x509.Certificate
+ if err != nil {
+ // Failed to connect using the system CA, so retrieve the remote certificate
+ certificate, err = shared.GetRemoteCertificate(url)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Handle certificate prompt
+ if certificate != nil {
+ digest := shared.CertFingerprint(certificate)
+
+ fmt.Printf("Certificate fingerprint: %s\n", digest)
+ fmt.Printf("ok (y/n)? ")
+ line, err := shared.ReadStdin()
+ if err != nil {
+ return nil, err
+ }
+
+ if len(line) < 1 || line[0] != 'y' && line[0] != 'Y' {
+ return nil, fmt.Errorf("Server certificate rejected by user")
+ }
+
+ serverCrt := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certificate.Raw})
+ args.TLSServerCert = string(serverCrt)
+
+ // Setup a new connection, this time with the remote certificate
+ c, err = lxd.ConnectLXD(url, &args)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Get server information
+ srv, _, err := c.GetServer()
+ if err != nil {
+ return nil, err
+ }
+
+ // Check if our cert is already trusted
+ if srv.Auth == "trusted" {
+ return c, nil
+ }
+
+ // Prompt for trust password
+ fmt.Printf("Admin password for %s: ", url)
+ pwd, err := terminal.ReadPassword(0)
+ if err != nil {
+ return nil, err
+ }
+ fmt.Println("")
+
+ // Add client certificate to trust store
+ req := api.CertificatesPost{
+ Password: string(pwd),
+ }
+ req.Type = "client"
+
+ err = c.CreateCertificate(req)
+ if err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
+
+func setupSource(path string, mounts []string) error {
+ prefix := "/"
+ if len(mounts) > 0 {
+ prefix = mounts[0]
+ }
+
+ // Mount everything
+ for _, mount := range mounts {
+ target := fmt.Sprintf("%s/%s", path, strings.TrimPrefix(mount, prefix))
+
+ // Mount the path
+ err := syscall.Mount(mount, target, "none", syscall.MS_BIND, "")
+ if err != nil {
+ return fmt.Errorf("Failed to mount %s: %v", mount, err)
+ }
+
+ // Make it read-only
+ err = syscall.Mount("", target, "none", syscall.MS_BIND|syscall.MS_RDONLY|syscall.MS_REMOUNT, "")
+ if err != nil {
+ return fmt.Errorf("Failed to make %s read-only: %v", mount, err)
+ }
+ }
+
+ return nil
+}
diff --git a/test/suites/static_analysis.sh b/test/suites/static_analysis.sh
index a3cbb96ff..aff7eeafa 100644
--- a/test/suites/static_analysis.sh
+++ b/test/suites/static_analysis.sh
@@ -66,6 +66,8 @@ test_static_analysis() {
golint -set_exit_status lxd-benchmark
golint -set_exit_status lxd-benchmark/benchmark
+ golint -set_exit_status lxd-p2c
+
golint -set_exit_status lxd/config
golint -set_exit_status lxd/db/node
golint -set_exit_status lxd/db/query
From b986027e8d3a89381eae07ae22c900fcd2b0763c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 22 Feb 2018 03:13:28 -0500
Subject: [PATCH 7/7] i18n: Look at all lxc files
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 9d7a37226..297f6bfd1 100644
--- a/Makefile
+++ b/Makefile
@@ -101,7 +101,7 @@ update-po:
update-pot:
go get -v -x github.com/snapcore/snapd/i18n/xgettext-go/
- xgettext-go -o po/$(DOMAIN).pot --add-comments-tag=TRANSLATORS: --sort-output --package-name=$(DOMAIN) --msgid-bugs-address=lxc-devel at lists.linuxcontainers.org --keyword=i18n.G --keyword-plural=i18n.NG shared/*.go lxc/*.go lxd/*.go
+ xgettext-go -o po/$(DOMAIN).pot --add-comments-tag=TRANSLATORS: --sort-output --package-name=$(DOMAIN) --msgid-bugs-address=lxc-devel at lists.linuxcontainers.org --keyword=i18n.G --keyword-plural=i18n.NG lxc/*.go lxc/*/*.go
build-mo: $(MOFILES)
More information about the lxc-devel
mailing list