The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7316
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Adds support for migrating BTRFS containers that have subvolumes. WIP.
From 62ae28941db7a0674692581b420f58fc5580df3d Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 4 May 2020 10:23:08 +0100 Subject: [PATCH 01/15] lxd/storage/driver/driver/btrfs/utils: Updates snapshotSubvolume to handle sub volumes Marks snapshotted sub volumes and the parent volume as readonly when in recursive mode and readonly argument passed. Returns list of snapshot volumes. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_utils.go | 42 +++++++++++++++-------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/lxd/storage/drivers/driver_btrfs_utils.go b/lxd/storage/drivers/driver_btrfs_utils.go index 5b30f043c4..126832c026 100644 --- a/lxd/storage/drivers/driver_btrfs_utils.go +++ b/lxd/storage/drivers/driver_btrfs_utils.go @@ -86,10 +86,12 @@ func (d *btrfs) getSubvolumes(path string) ([]string, error) { return result, nil } +// snapshotSubvolume creates a snapshot of the specified path at the dest supplied. If recursion is true and +// sub volumes are found below the path then they are created at the relative location in dest. func (d *btrfs) snapshotSubvolume(path string, dest string, readonly bool, recursion bool) error { // Single subvolume deletion. - snapshot := func(path string, dest string) error { - if readonly && !d.state.OS.RunningInUserNS { + snapshot := func(path string, dest string, ro bool) error { + if ro && !d.state.OS.RunningInUserNS { _, err := shared.RunCommand("btrfs", "subvolume", "snapshot", "-r", path, dest) if err != nil { return err @@ -109,28 +111,40 @@ func (d *btrfs) snapshotSubvolume(path string, dest string, readonly bool, recur // Now snapshot all subvolumes of the root. if recursion { // Get the subvolumes list. - subsubvols, err := d.getSubvolumes(path) + subSubVols, err := d.getSubvolumes(path) if err != nil { return err } - sort.Sort(sort.StringSlice(subsubvols)) - - if len(subsubvols) > 0 && readonly { - // Creating subvolumes requires the parent to be writable. - readonly = false + sort.Sort(sort.StringSlice(subSubVols)) + + if len(subSubVols) > 0 && readonly { + // First snapshot the root volume. Creating subvolumes requires the parent to be writable. + err = snapshot(path, dest, false) + } else { + // First snapshot the root volume. + err = snapshot(path, dest, readonly) } - // First snapshot the root. - err = snapshot(path, dest) + // Check errors from snapshot. if err != nil { return err } - for _, subsubvol := range subsubvols { + for _, subSubVol := range subSubVols { + subSubVolSnapPath := filepath.Join(dest, subSubVol) + // Clear the target for the subvol to use. - os.Remove(filepath.Join(dest, subsubvol)) + os.Remove(subSubVolSnapPath) + + err := snapshot(filepath.Join(path, subSubVol), subSubVolSnapPath, readonly) + if err != nil { + return err + } + } - err := snapshot(filepath.Join(path, subsubvol), filepath.Join(dest, subsubvol)) + // Now set the root snapshot we created earlier as writable read only. + if len(subSubVols) > 0 && readonly { + _, err = shared.RunCommand("btrfs", "property", "set", "-ts", dest, "ro", "true") if err != nil { return err } @@ -140,7 +154,7 @@ func (d *btrfs) snapshotSubvolume(path string, dest string, readonly bool, recur } // Handle standalone volume. - err := snapshot(path, dest) + err := snapshot(path, dest, readonly) if err != nil { return err } From e36d81e9949f31d9817a58182c3a623cb07be4e3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 4 May 2020 10:27:01 +0100 Subject: [PATCH 02/15] lxd/storag/drivers/driver/btrfs/volumes: Updates MigrateVolume to send subvolumes Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_volumes.go | 68 +++++++++++++++++---- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index 9048a01f4c..4d72b61b4e 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -2,6 +2,7 @@ package drivers import ( "context" + "encoding/json" "fmt" "io" "io/ioutil" @@ -606,6 +607,40 @@ func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *m return nil } + // Generate migration header, containing subvolume info. + migrationHeader, err := d.migrationHeader(vol, volSrcArgs.Snapshots) + if err != nil { + return err + } + + // If we haven't negotiated subvolume support, check if we have any subvolumes in source and fail, + // otherwise we would end up not materialising all of the source's files on the target. + if !shared.StringInSlice(migration.BTRFSFeatureMigrationHeader, volSrcArgs.MigrationType.Features) || !shared.StringInSlice(migration.BTRFSFeatureSubvolumes, volSrcArgs.MigrationType.Features) { + for _, subVol := range migrationHeader.Subvolumes { + if subVol.Path != string(filepath.Separator) { + return fmt.Errorf("Subvolumes detected in source but target does not support receiving subvolumes") + } + } + } + + // Send metadata migration header frame with subvolume info if we have negotiated that feature. + if shared.StringInSlice(migration.BTRFSFeatureMigrationHeader, volSrcArgs.MigrationType.Features) { + headerJSON, err := json.Marshal(migrationHeader) + if err != nil { + return err + } + + _, err = conn.Write(headerJSON) + if err != nil { + return err + } + + err = conn.Close() //End the frame. + if err != nil { + return err + } + } + // Transfer the snapshots first. for i, snapName := range volSrcArgs.Snapshots { snapshot, _ := vol.NewSnapshot(snapName) @@ -645,12 +680,12 @@ func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *m } // Make read-only snapshot of the subvolume as writable subvolumes cannot be sent. - migrationSendSnapshot := filepath.Join(tmpVolumesMountPoint, ".migration-send") - err = d.snapshotSubvolume(vol.MountPath(), migrationSendSnapshot, true, false) + migrationSendSnapshotPrefix := filepath.Join(tmpVolumesMountPoint, ".migration-send") + err = d.snapshotSubvolume(vol.MountPath(), migrationSendSnapshotPrefix, true, true) if err != nil { return err } - defer d.deleteSubvolume(migrationSendSnapshot, true) + defer d.deleteSubvolume(migrationSendSnapshotPrefix, true) // Setup progress tracking. var wrapper *ioprogress.ProgressTracker @@ -658,16 +693,25 @@ func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *m wrapper = migration.ProgressTracker(op, "fs_progress", vol.name) } - // Compare to latest snapshot. - btrfsParent := "" - if len(volSrcArgs.Snapshots) > 0 { - btrfsParent = GetVolumeMountPath(d.name, vol.volType, GetSnapshotVolumeName(vol.name, volSrcArgs.Snapshots[len(volSrcArgs.Snapshots)-1])) - } + // Send main volume (and any subvolumes) to target. + for _, subVolume := range migrationHeader.Subvolumes { + if subVolume.Snapshot != "" { + continue // Only sending main volume now (not snapshots). + } - // Send the volume itself. - err = d.sendSubvolume(migrationSendSnapshot, btrfsParent, conn, wrapper) - if err != nil { - return err + parent := "" + + if subVolume.Path == string(filepath.Separator) && len(volSrcArgs.Snapshots) > 0 { + // Compare to latest snapshot. + parent = GetVolumeMountPath(d.name, vol.volType, GetSnapshotVolumeName(vol.name, volSrcArgs.Snapshots[len(volSrcArgs.Snapshots)-1])) + } + + source := filepath.Join(migrationSendSnapshotPrefix, subVolume.Path) + d.logger.Debug("Sending subvolume snapshot", log.Ctx{"name": vol.name, "source": source, "parent": parent, "path": subVolume.Path}) + err = d.sendSubvolume(source, parent, conn, wrapper) + if err != nil { + return err + } } return nil From cd4c96d970d61a42f73e28fef1028beb27d13b64 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 4 May 2020 12:21:13 +0100 Subject: [PATCH 03/15] lxd/storage/drivers/driver/btrfs/volumes: Fail backup when cleanup fails in BackupVolume Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_volumes.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index 4d72b61b4e..34beb638e4 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -874,6 +874,12 @@ func (d *btrfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWr return err } + // Ensure snapshot sub volumes are removed. + err = d.deleteSubvolume(targetVolume, true) + if err != nil { + return err + } + return nil } From 43f2ab1e5a6b0b8bbe4d9f746a934d2b1e319ba5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 4 May 2020 12:24:07 +0100 Subject: [PATCH 04/15] lxd/storage/drivers/driver/btrfs/volumes: Better naming of variables in unpackVolume Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_volumes.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index 34beb638e4..0d5da1c297 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -135,8 +135,8 @@ func (d *btrfs) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData i revert.Add(revertHook) // Define function to unpack a volume from a backup tarball file. - unpackVolume := func(r io.ReadSeeker, unpacker []string, srcFile string, mountPath string) error { - d.Logger().Debug("Unpacking optimized volume", log.Ctx{"source": srcFile, "target": mountPath}) + unpackVolume := func(r io.ReadSeeker, unpacker []string, srcFile string, targetPath string) error { + d.Logger().Debug("Unpacking optimized volume", log.Ctx{"source": srcFile, "target": targetPath}) tr, cancelFunc, err := shared.CompressedTarReader(context.Background(), r, unpacker) if err != nil { return err @@ -154,7 +154,7 @@ func (d *btrfs) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData i if hdr.Name == srcFile { // Extract the backup. - err = shared.RunCommandWithFds(tr, nil, "btrfs", "receive", "-e", mountPath) + err = shared.RunCommandWithFds(tr, nil, "btrfs", "receive", "-e", targetPath) if err != nil { return err } From 550ac94a193a5eaa5dc16b1343de238c3cadfcb0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 4 May 2020 12:24:34 +0100 Subject: [PATCH 05/15] lxd/storage/driversr/driver/btrfs/utils: Allow ro subvolumes to be deleted in deleteSubvolume Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_utils.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lxd/storage/drivers/driver_btrfs_utils.go b/lxd/storage/drivers/driver_btrfs_utils.go index 126832c026..bd48d3fc5a 100644 --- a/lxd/storage/drivers/driver_btrfs_utils.go +++ b/lxd/storage/drivers/driver_btrfs_utils.go @@ -193,6 +193,11 @@ func (d *btrfs) deleteSubvolume(path string, recursion bool) error { } sort.Sort(sort.Reverse(sort.StringSlice(subsubvols))) + if len(subsubvols) > 0 { + // Attempt to make the root subvolume writable so any subvolumes can be removed. + shared.RunCommand("btrfs", "property", "set", path, "ro", "false") + } + for _, subsubvol := range subsubvols { err := destroy(filepath.Join(path, subsubvol)) if err != nil { From a315873333767f69c70a394dc8ecc1e50993b69c Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 11:44:02 +0100 Subject: [PATCH 06/15] lxd/migration/migrate/proto: Adds BTRFS Features to offer header Adds following features: migration_header - indicates a migration header will be sent/recv in data channel first. header_subvolumes - indicates migration can send/recv subvolumes. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/migration/migrate.pb.go | 209 +++++++++++++++++++++++------------- lxd/migration/migrate.proto | 6 ++ 2 files changed, 140 insertions(+), 75 deletions(-) diff --git a/lxd/migration/migrate.pb.go b/lxd/migration/migrate.pb.go index cf1b5fbe4c..0e824a7be0 100644 --- a/lxd/migration/migrate.pb.go +++ b/lxd/migration/migrate.pb.go @@ -482,6 +482,53 @@ func (m *ZfsFeatures) GetCompress() bool { return false } +type BtrfsFeatures struct { + MigrationHeader *bool `protobuf:"varint,1,opt,name=migration_header,json=migrationHeader" json:"migration_header,omitempty"` + HeaderSubvolumes *bool `protobuf:"varint,2,opt,name=header_subvolumes,json=headerSubvolumes" json:"header_subvolumes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BtrfsFeatures) Reset() { *m = BtrfsFeatures{} } +func (m *BtrfsFeatures) String() string { return proto.CompactTextString(m) } +func (*BtrfsFeatures) ProtoMessage() {} +func (*BtrfsFeatures) Descriptor() ([]byte, []int) { + return fileDescriptor_fe8772548dc4b615, []int{6} +} + +func (m *BtrfsFeatures) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BtrfsFeatures.Unmarshal(m, b) +} +func (m *BtrfsFeatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BtrfsFeatures.Marshal(b, m, deterministic) +} +func (m *BtrfsFeatures) XXX_Merge(src proto.Message) { + xxx_messageInfo_BtrfsFeatures.Merge(m, src) +} +func (m *BtrfsFeatures) XXX_Size() int { + return xxx_messageInfo_BtrfsFeatures.Size(m) +} +func (m *BtrfsFeatures) XXX_DiscardUnknown() { + xxx_messageInfo_BtrfsFeatures.DiscardUnknown(m) +} + +var xxx_messageInfo_BtrfsFeatures proto.InternalMessageInfo + +func (m *BtrfsFeatures) GetMigrationHeader() bool { + if m != nil && m.MigrationHeader != nil { + return *m.MigrationHeader + } + return false +} + +func (m *BtrfsFeatures) GetHeaderSubvolumes() bool { + if m != nil && m.HeaderSubvolumes != nil { + return *m.HeaderSubvolumes + } + return false +} + type MigrationHeader struct { 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"` @@ -493,6 +540,7 @@ type MigrationHeader struct { Refresh *bool `protobuf:"varint,9,opt,name=refresh" json:"refresh,omitempty"` ZfsFeatures *ZfsFeatures `protobuf:"bytes,10,opt,name=zfsFeatures" json:"zfsFeatures,omitempty"` VolumeSize *int64 `protobuf:"varint,11,opt,name=volumeSize" json:"volumeSize,omitempty"` + BtrfsFeatures *BtrfsFeatures `protobuf:"bytes,12,opt,name=btrfsFeatures" json:"btrfsFeatures,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -502,7 +550,7 @@ func (m *MigrationHeader) Reset() { *m = MigrationHeader{} } func (m *MigrationHeader) String() string { return proto.CompactTextString(m) } func (*MigrationHeader) ProtoMessage() {} func (*MigrationHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_fe8772548dc4b615, []int{6} + return fileDescriptor_fe8772548dc4b615, []int{7} } func (m *MigrationHeader) XXX_Unmarshal(b []byte) error { @@ -593,6 +641,13 @@ func (m *MigrationHeader) GetVolumeSize() int64 { return 0 } +func (m *MigrationHeader) GetBtrfsFeatures() *BtrfsFeatures { + if m != nil { + return m.BtrfsFeatures + } + return nil +} + type MigrationControl struct { Success *bool `protobuf:"varint,1,req,name=success" json:"success,omitempty"` // optional failure message if sending a failure @@ -606,7 +661,7 @@ func (m *MigrationControl) Reset() { *m = MigrationControl{} } func (m *MigrationControl) String() string { return proto.CompactTextString(m) } func (*MigrationControl) ProtoMessage() {} func (*MigrationControl) Descriptor() ([]byte, []int) { - return fileDescriptor_fe8772548dc4b615, []int{7} + return fileDescriptor_fe8772548dc4b615, []int{8} } func (m *MigrationControl) XXX_Unmarshal(b []byte) error { @@ -652,7 +707,7 @@ func (m *MigrationSync) Reset() { *m = MigrationSync{} } func (m *MigrationSync) String() string { return proto.CompactTextString(m) } func (*MigrationSync) ProtoMessage() {} func (*MigrationSync) Descriptor() ([]byte, []int) { - return fileDescriptor_fe8772548dc4b615, []int{8} + return fileDescriptor_fe8772548dc4b615, []int{9} } func (m *MigrationSync) XXX_Unmarshal(b []byte) error { @@ -702,7 +757,7 @@ func (m *DumpStatsEntry) Reset() { *m = DumpStatsEntry{} } func (m *DumpStatsEntry) String() string { return proto.CompactTextString(m) } func (*DumpStatsEntry) ProtoMessage() {} func (*DumpStatsEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_fe8772548dc4b615, []int{9} + return fileDescriptor_fe8772548dc4b615, []int{10} } func (m *DumpStatsEntry) XXX_Unmarshal(b []byte) error { @@ -815,7 +870,7 @@ func (m *RestoreStatsEntry) Reset() { *m = RestoreStatsEntry{} } func (m *RestoreStatsEntry) String() string { return proto.CompactTextString(m) } func (*RestoreStatsEntry) ProtoMessage() {} func (*RestoreStatsEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_fe8772548dc4b615, []int{10} + return fileDescriptor_fe8772548dc4b615, []int{11} } func (m *RestoreStatsEntry) XXX_Unmarshal(b []byte) error { @@ -883,7 +938,7 @@ func (m *StatsEntry) Reset() { *m = StatsEntry{} } func (m *StatsEntry) String() string { return proto.CompactTextString(m) } func (*StatsEntry) ProtoMessage() {} func (*StatsEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_fe8772548dc4b615, []int{11} + return fileDescriptor_fe8772548dc4b615, []int{12} } func (m *StatsEntry) XXX_Unmarshal(b []byte) error { @@ -927,6 +982,7 @@ func init() { proto.RegisterType((*Snapshot)(nil), "migration.Snapshot") proto.RegisterType((*RsyncFeatures)(nil), "migration.rsyncFeatures") proto.RegisterType((*ZfsFeatures)(nil), "migration.zfsFeatures") + proto.RegisterType((*BtrfsFeatures)(nil), "migration.btrfsFeatures") proto.RegisterType((*MigrationHeader)(nil), "migration.MigrationHeader") proto.RegisterType((*MigrationControl)(nil), "migration.MigrationControl") proto.RegisterType((*MigrationSync)(nil), "migration.MigrationSync") @@ -938,73 +994,76 @@ func init() { func init() { proto.RegisterFile("lxd/migration/migrate.proto", fileDescriptor_fe8772548dc4b615) } var fileDescriptor_fe8772548dc4b615 = []byte{ - // 1080 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x55, 0xcd, 0x6e, 0xdb, 0x46, - 0x17, 0xfd, 0x24, 0x51, 0xb6, 0x78, 0x25, 0x39, 0xca, 0xc4, 0x08, 0x88, 0xe4, 0x6b, 0xaa, 0x32, - 0x29, 0xaa, 0x78, 0x61, 0xa7, 0x0a, 0x0a, 0x64, 0x55, 0x20, 0x96, 0xea, 0x26, 0xa8, 0xa3, 0x18, - 0x23, 0x1b, 0x45, 0xbb, 0x21, 0x26, 0xe4, 0xa5, 0x3c, 0x30, 0xff, 0x30, 0x43, 0xd9, 0x96, 0x36, - 0x45, 0x1f, 0xa6, 0x0f, 0xd1, 0xa7, 0xe8, 0xaa, 0xef, 0x53, 0xcc, 0x0c, 0x49, 0x93, 0x4e, 0x81, - 0xee, 0xe6, 0x9e, 0x7b, 0x78, 0xee, 0xcc, 0xfd, 0x23, 0x3c, 0x8d, 0x6e, 0x83, 0xa3, 0x98, 0xaf, - 0x04, 0xcb, 0x79, 0x9a, 0x14, 0x27, 0x3c, 0xcc, 0x44, 0x9a, 0xa7, 0xc4, 0xae, 0x1c, 0xee, 0x6f, - 0x60, 0xbf, 0x9f, 0x7f, 0x60, 0xd9, 0xf9, 0x26, 0x43, 0xb2, 0x0f, 0x5d, 0x2e, 0xd7, 0x3c, 0x70, - 0x5a, 0xe3, 0xf6, 0xa4, 0x47, 0x8d, 0x61, 0xd0, 0x15, 0x0f, 0x9c, 0x76, 0x89, 0xae, 0x78, 0x40, - 0x1e, 0xc3, 0xce, 0x65, 0x2a, 0x73, 0x1e, 0x38, 0x9d, 0x71, 0x7b, 0xd2, 0xa5, 0x85, 0x45, 0x08, - 0x58, 0x89, 0xe4, 0x81, 0x63, 0x69, 0x54, 0x9f, 0xc9, 0x13, 0xe8, 0xc5, 0x2c, 0x13, 0x2c, 0x59, - 0xa1, 0xd3, 0xd5, 0x78, 0x65, 0xbb, 0xaf, 0x60, 0x67, 0x96, 0x26, 0x21, 0x5f, 0x91, 0x11, 0x74, - 0xae, 0x70, 0xa3, 0x63, 0xdb, 0x54, 0x1d, 0x55, 0xe4, 0x6b, 0x16, 0xad, 0x51, 0x47, 0xb6, 0xa9, - 0x31, 0xdc, 0x1f, 0x61, 0x67, 0x8e, 0xd7, 0xdc, 0x47, 0x1d, 0x8b, 0xc5, 0x58, 0x7c, 0xa2, 0xcf, - 0xe4, 0x25, 0xec, 0xf8, 0x5a, 0xcf, 0x69, 0x8f, 0x3b, 0x93, 0xfe, 0xf4, 0xe1, 0x61, 0xf5, 0xd8, - 0x43, 0x13, 0x88, 0x16, 0x04, 0xf7, 0xaf, 0x36, 0xf4, 0x96, 0x09, 0xcb, 0xe4, 0x65, 0x9a, 0xff, - 0xab, 0xd6, 0x6b, 0xe8, 0x47, 0xa9, 0xcf, 0xa2, 0xd9, 0x7f, 0x08, 0xd6, 0x59, 0xea, 0xb1, 0x99, - 0x48, 0x43, 0x1e, 0xa1, 0x74, 0x3a, 0xe3, 0xce, 0xc4, 0xa6, 0x95, 0x4d, 0xfe, 0x0f, 0x36, 0x66, - 0x97, 0x18, 0xa3, 0x60, 0x91, 0xce, 0x50, 0x8f, 0xde, 0x01, 0xe4, 0x3b, 0x18, 0x68, 0x21, 0xf3, - 0x3a, 0xe9, 0x74, 0x3f, 0x8b, 0x67, 0x3c, 0xb4, 0x41, 0x23, 0x2e, 0x0c, 0x98, 0xf0, 0x2f, 0x79, - 0x8e, 0x7e, 0xbe, 0x16, 0xe8, 0xec, 0xe8, 0x0c, 0x37, 0x30, 0x75, 0x29, 0x99, 0xb3, 0x1c, 0xc3, - 0x75, 0xe4, 0xec, 0xea, 0xb8, 0x95, 0x4d, 0x9e, 0xc3, 0xd0, 0x17, 0xa8, 0x03, 0x78, 0x01, 0xcb, - 0xd1, 0xe9, 0x8d, 0x5b, 0x93, 0x0e, 0x1d, 0x94, 0xe0, 0x9c, 0xe5, 0x48, 0x5e, 0xc0, 0x5e, 0xc4, - 0x64, 0xee, 0xad, 0x25, 0x06, 0x86, 0x65, 0x1b, 0x96, 0x42, 0x2f, 0x24, 0x06, 0x8a, 0xe5, 0xfe, - 0xde, 0x82, 0xa1, 0x90, 0x9b, 0xc4, 0x3f, 0x41, 0xa6, 0xe2, 0x4a, 0xd5, 0x26, 0xb7, 0x2c, 0xcf, - 0x85, 0x74, 0x5a, 0xe3, 0xd6, 0xa4, 0x47, 0x0b, 0x4b, 0xe1, 0x01, 0x46, 0x98, 0xab, 0xda, 0x6a, - 0xdc, 0x58, 0xea, 0xa2, 0x7e, 0x1a, 0x67, 0x02, 0xa5, 0xca, 0x9e, 0xf2, 0x54, 0x36, 0x79, 0x01, - 0xc3, 0x4f, 0x3c, 0xe0, 0x02, 0x7d, 0x75, 0x2d, 0x9d, 0x41, 0x45, 0x68, 0x82, 0xee, 0x4b, 0xe8, - 0x6f, 0x43, 0x59, 0x5d, 0xa0, 0x2e, 0xd8, 0x6a, 0x0a, 0xba, 0x7f, 0x76, 0xe0, 0xc1, 0x87, 0x32, - 0xb9, 0xef, 0x90, 0x05, 0x28, 0xc8, 0x01, 0xb4, 0x43, 0xa9, 0xbb, 0x60, 0x6f, 0xfa, 0xa4, 0x96, - 0xfa, 0x8a, 0x77, 0xb2, 0x54, 0xb3, 0x42, 0xdb, 0xa1, 0x24, 0xdf, 0x80, 0xe5, 0x0b, 0xbe, 0xd6, - 0x4f, 0xd8, 0x9b, 0x3e, 0xaa, 0x37, 0x06, 0x7d, 0x7f, 0xa1, 0x69, 0x9a, 0x40, 0x0e, 0xa0, 0xcb, - 0x83, 0x98, 0x65, 0xba, 0x21, 0xfa, 0xd3, 0xfd, 0x1a, 0xb3, 0x9a, 0x3e, 0x6a, 0x28, 0xea, 0x95, - 0xb2, 0x68, 0xca, 0x05, 0x8b, 0x51, 0x3a, 0x96, 0x6e, 0xa2, 0x26, 0x48, 0xbe, 0x05, 0xbb, 0x04, - 0xca, 0x46, 0xa9, 0xc7, 0x2f, 0xdb, 0x9a, 0xde, 0xb1, 0x88, 0x03, 0xbb, 0x99, 0xc0, 0x60, 0x1d, - 0x67, 0xce, 0xae, 0x4e, 0x44, 0x69, 0x92, 0xef, 0xef, 0x55, 0x4d, 0x77, 0x40, 0x7f, 0xea, 0xd4, - 0x04, 0x1b, 0x7e, 0x7a, 0xaf, 0xc8, 0x0e, 0xec, 0x0a, 0x0c, 0x05, 0xca, 0x4b, 0xdd, 0x15, 0x3d, - 0x5a, 0x9a, 0xe4, 0x4d, 0xa3, 0x18, 0x0e, 0x68, 0xdd, 0xc7, 0x35, 0xdd, 0x9a, 0x97, 0x36, 0xea, - 0xf6, 0x0c, 0xe0, 0x3a, 0x8d, 0xd6, 0x31, 0x2e, 0xf9, 0x16, 0x9d, 0xbe, 0x6e, 0xb6, 0x1a, 0xe2, - 0x9e, 0xc0, 0xa8, 0x2a, 0xc9, 0x2c, 0x4d, 0x72, 0x91, 0x46, 0xea, 0x1e, 0x72, 0xed, 0xfb, 0xa6, - 0xd4, 0xaa, 0xc9, 0x4b, 0x53, 0x79, 0x62, 0x94, 0x92, 0xad, 0x4c, 0xbf, 0xd9, 0xb4, 0x34, 0xdd, - 0xd7, 0x30, 0xac, 0x74, 0x96, 0x9b, 0xc4, 0x57, 0xe3, 0x14, 0xf2, 0x84, 0x45, 0x67, 0x02, 0xe7, - 0x2a, 0x57, 0x46, 0xa9, 0x81, 0xb9, 0x7f, 0x74, 0x60, 0xa4, 0x32, 0xe7, 0xa9, 0x21, 0x92, 0x1e, - 0x26, 0xb9, 0xd8, 0xa8, 0x39, 0x0a, 0x05, 0xe2, 0x96, 0x27, 0x2b, 0x2f, 0xe7, 0xc5, 0x2a, 0x19, - 0xd2, 0x41, 0x09, 0x9e, 0xf3, 0x18, 0xc9, 0x97, 0xd0, 0x0f, 0x45, 0xba, 0xc5, 0xc4, 0x50, 0xda, - 0x9a, 0x02, 0x06, 0xd2, 0x84, 0xaf, 0x60, 0x10, 0x63, 0xac, 0xc5, 0x35, 0xa3, 0xa3, 0x19, 0xfd, - 0x02, 0xd3, 0x94, 0xe7, 0x30, 0x8c, 0x31, 0xbe, 0x11, 0x3c, 0x47, 0xc3, 0xb1, 0x4c, 0xa0, 0x12, - 0x2c, 0x49, 0x19, 0x5b, 0xa1, 0xf4, 0xa4, 0xcf, 0x92, 0x04, 0x03, 0xbd, 0x78, 0x2d, 0x3a, 0xd0, - 0xe0, 0xd2, 0x60, 0xe4, 0x15, 0xec, 0x17, 0xa4, 0x2b, 0x9e, 0x65, 0x18, 0x78, 0x19, 0x13, 0x98, - 0xe4, 0x7a, 0x85, 0x58, 0x94, 0x18, 0xae, 0x71, 0x9d, 0x69, 0xcf, 0x9d, 0xac, 0x8a, 0x94, 0x63, - 0xa2, 0xb7, 0x49, 0x29, 0xfb, 0xb3, 0xc1, 0x14, 0x89, 0x8b, 0x98, 0x65, 0x9e, 0x40, 0x99, 0x46, - 0xd7, 0x66, 0xa3, 0x0c, 0xe9, 0x40, 0x83, 0xd4, 0x60, 0xe4, 0x0b, 0x00, 0xa3, 0x14, 0xb1, 0xed, - 0xc6, 0xb1, 0xb5, 0x8c, 0xad, 0x91, 0x53, 0xb6, 0xdd, 0x94, 0x6e, 0x2f, 0xe3, 0x59, 0xd1, 0x38, - 0x85, 0xfb, 0x4c, 0x01, 0x6a, 0x1f, 0x55, 0x6e, 0xef, 0xd3, 0x3a, 0x94, 0xba, 0x45, 0x8a, 0x8b, - 0x28, 0xca, 0xf1, 0x3a, 0x94, 0xee, 0xdf, 0x2d, 0x78, 0x24, 0x50, 0xe6, 0xa9, 0xc0, 0x46, 0xa9, - 0xbe, 0x36, 0x5f, 0x4b, 0x4f, 0xad, 0x02, 0x26, 0xd0, 0xfc, 0xf1, 0x2c, 0x6a, 0xde, 0x36, 0x2b, - 0x40, 0x72, 0x00, 0x0f, 0x9b, 0xe9, 0xf1, 0xd3, 0x1b, 0x5d, 0x32, 0x8b, 0x3e, 0xa8, 0xe7, 0x66, - 0x96, 0xde, 0xa8, 0xba, 0x85, 0xa9, 0xb8, 0xaa, 0x8a, 0x5f, 0xd4, 0xad, 0xc0, 0xca, 0xd2, 0x96, - 0x97, 0xa9, 0x95, 0xad, 0x5f, 0x60, 0x9a, 0x52, 0x5d, 0xac, 0x00, 0x55, 0xd9, 0x5a, 0xd5, 0xc5, - 0x68, 0x01, 0xba, 0xb7, 0xd0, 0xaf, 0x3f, 0xe7, 0x08, 0xac, 0xc0, 0xb4, 0xaa, 0x1a, 0xaf, 0xa7, - 0xb5, 0xf1, 0xba, 0xdf, 0xa4, 0x54, 0x13, 0xc9, 0x1b, 0x35, 0xb0, 0x5a, 0x4b, 0x8f, 0x43, 0x7f, - 0xfa, 0xac, 0x3e, 0xea, 0x9f, 0x27, 0x8c, 0x96, 0xf4, 0x83, 0x45, 0x6d, 0x63, 0x9a, 0x4d, 0x48, - 0x6c, 0xe8, 0xd2, 0xe5, 0x2f, 0x8b, 0xd9, 0xe8, 0x7f, 0xea, 0x78, 0x7c, 0x4e, 0x4f, 0x96, 0xa3, - 0x16, 0xd9, 0x85, 0xce, 0xaf, 0x27, 0xcb, 0x51, 0x5b, 0x1d, 0xe8, 0xf1, 0x7c, 0xd4, 0x21, 0x8f, - 0xe0, 0xc1, 0xf1, 0xe9, 0xc7, 0xd9, 0x4f, 0xde, 0xdb, 0xc5, 0xdc, 0x33, 0x5f, 0x58, 0x07, 0x47, - 0xd0, 0x2b, 0x77, 0x25, 0xd9, 0x03, 0x50, 0x67, 0xaf, 0xa6, 0x76, 0xf6, 0xee, 0xed, 0xc5, 0xe9, - 0xa8, 0x45, 0x7a, 0x60, 0x2d, 0x3e, 0x2e, 0x7e, 0x18, 0xb5, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, - 0x6c, 0x4d, 0xa0, 0xee, 0xd9, 0x08, 0x00, 0x00, + // 1128 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x55, 0xdd, 0x8e, 0xd3, 0x46, + 0x14, 0x6e, 0x12, 0x67, 0x37, 0x3e, 0x49, 0x76, 0xc3, 0x80, 0x90, 0x05, 0x2d, 0x4d, 0x0d, 0x55, + 0xc3, 0x56, 0x02, 0x1a, 0x54, 0x89, 0xab, 0x4a, 0x6c, 0xd2, 0x2d, 0xa8, 0x10, 0x56, 0x13, 0x50, + 0xd5, 0xde, 0x58, 0xb3, 0xf6, 0x71, 0x32, 0xc2, 0x7f, 0x9a, 0xb1, 0x17, 0x92, 0x9b, 0xaa, 0x0f, + 0xd3, 0xe7, 0xe9, 0x55, 0x1f, 0xa6, 0x77, 0xd5, 0xcc, 0xd8, 0xc6, 0xde, 0xad, 0xd4, 0xbb, 0x39, + 0xdf, 0xf9, 0xfc, 0x9d, 0x33, 0xe7, 0x67, 0x0c, 0x77, 0xa3, 0x8f, 0xc1, 0xe3, 0x98, 0x6f, 0x04, + 0xcb, 0x79, 0x9a, 0x94, 0x27, 0x7c, 0x94, 0x89, 0x34, 0x4f, 0x89, 0x5d, 0x3b, 0xdc, 0xdf, 0xc1, + 0x7e, 0xb9, 0x7c, 0xcd, 0xb2, 0xb7, 0xbb, 0x0c, 0xc9, 0x2d, 0xe8, 0x73, 0x59, 0xf0, 0xc0, 0xe9, + 0x4c, 0xbb, 0xb3, 0x01, 0x35, 0x86, 0x41, 0x37, 0x3c, 0x70, 0xba, 0x15, 0xba, 0xe1, 0x01, 0xb9, + 0x0d, 0x07, 0xdb, 0x54, 0xe6, 0x3c, 0x70, 0x7a, 0xd3, 0xee, 0xac, 0x4f, 0x4b, 0x8b, 0x10, 0xb0, + 0x12, 0xc9, 0x03, 0xc7, 0xd2, 0xa8, 0x3e, 0x93, 0x3b, 0x30, 0x88, 0x59, 0x26, 0x58, 0xb2, 0x41, + 0xa7, 0xaf, 0xf1, 0xda, 0x76, 0x9f, 0xc0, 0xc1, 0x22, 0x4d, 0x42, 0xbe, 0x21, 0x13, 0xe8, 0xbd, + 0xc7, 0x9d, 0x8e, 0x6d, 0x53, 0x75, 0x54, 0x91, 0x2f, 0x59, 0x54, 0xa0, 0x8e, 0x6c, 0x53, 0x63, + 0xb8, 0x3f, 0xc1, 0xc1, 0x12, 0x2f, 0xb9, 0x8f, 0x3a, 0x16, 0x8b, 0xb1, 0xfc, 0x44, 0x9f, 0xc9, + 0x43, 0x38, 0xf0, 0xb5, 0x9e, 0xd3, 0x9d, 0xf6, 0x66, 0xc3, 0xf9, 0x8d, 0x47, 0xf5, 0x65, 0x1f, + 0x99, 0x40, 0xb4, 0x24, 0xb8, 0x7f, 0x75, 0x61, 0xb0, 0x4e, 0x58, 0x26, 0xb7, 0x69, 0xfe, 0x9f, + 0x5a, 0x4f, 0x61, 0x18, 0xa5, 0x3e, 0x8b, 0x16, 0xff, 0x23, 0xd8, 0x64, 0xa9, 0xcb, 0x66, 0x22, + 0x0d, 0x79, 0x84, 0xd2, 0xe9, 0x4d, 0x7b, 0x33, 0x9b, 0xd6, 0x36, 0xf9, 0x1c, 0x6c, 0xcc, 0xb6, + 0x18, 0xa3, 0x60, 0x91, 0xae, 0xd0, 0x80, 0x7e, 0x02, 0xc8, 0xf7, 0x30, 0xd2, 0x42, 0xe6, 0x76, + 0xd2, 0xe9, 0x5f, 0x8b, 0x67, 0x3c, 0xb4, 0x45, 0x23, 0x2e, 0x8c, 0x98, 0xf0, 0xb7, 0x3c, 0x47, + 0x3f, 0x2f, 0x04, 0x3a, 0x07, 0xba, 0xc2, 0x2d, 0x4c, 0x25, 0x25, 0x73, 0x96, 0x63, 0x58, 0x44, + 0xce, 0xa1, 0x8e, 0x5b, 0xdb, 0xe4, 0x3e, 0x8c, 0x7d, 0x81, 0x3a, 0x80, 0x17, 0xb0, 0x1c, 0x9d, + 0xc1, 0xb4, 0x33, 0xeb, 0xd1, 0x51, 0x05, 0x2e, 0x59, 0x8e, 0xe4, 0x01, 0x1c, 0x45, 0x4c, 0xe6, + 0x5e, 0x21, 0x31, 0x30, 0x2c, 0xdb, 0xb0, 0x14, 0xfa, 0x4e, 0x62, 0xa0, 0x58, 0xee, 0x1f, 0x1d, + 0x18, 0x0b, 0xb9, 0x4b, 0xfc, 0x33, 0x64, 0x2a, 0xae, 0x54, 0x63, 0xf2, 0x91, 0xe5, 0xb9, 0x90, + 0x4e, 0x67, 0xda, 0x99, 0x0d, 0x68, 0x69, 0x29, 0x3c, 0xc0, 0x08, 0x73, 0xd5, 0x5b, 0x8d, 0x1b, + 0x4b, 0x25, 0xea, 0xa7, 0x71, 0x26, 0x50, 0xaa, 0xea, 0x29, 0x4f, 0x6d, 0x93, 0x07, 0x30, 0xbe, + 0xe0, 0x01, 0x17, 0xe8, 0xab, 0xb4, 0x74, 0x05, 0x15, 0xa1, 0x0d, 0xba, 0x0f, 0x61, 0xb8, 0x0f, + 0x65, 0x9d, 0x40, 0x53, 0xb0, 0xd3, 0x16, 0x74, 0x37, 0x30, 0xbe, 0xc8, 0x45, 0x83, 0xfc, 0x10, + 0x26, 0x75, 0xb1, 0xbd, 0x2d, 0xb2, 0x00, 0x45, 0xf9, 0xd1, 0x71, 0x8d, 0xbf, 0xd0, 0x30, 0xf9, + 0x16, 0x6e, 0x18, 0x82, 0x27, 0x8b, 0x8b, 0xcb, 0x34, 0x2a, 0x62, 0x94, 0xe5, 0x5d, 0x26, 0xc6, + 0xb1, 0xae, 0x71, 0xf7, 0x9f, 0x1e, 0x1c, 0xbf, 0xbe, 0x22, 0x70, 0x02, 0xdd, 0x50, 0xea, 0x71, + 0x3b, 0x9a, 0xdf, 0x69, 0xf4, 0xb8, 0xe6, 0x9d, 0xad, 0xd5, 0x52, 0xd2, 0x6e, 0x28, 0xc9, 0x37, + 0x60, 0xf9, 0x82, 0x17, 0x5a, 0xff, 0x68, 0x7e, 0xb3, 0x39, 0x81, 0xf4, 0xe5, 0x3b, 0x4d, 0xd3, + 0x04, 0x72, 0x02, 0x7d, 0x1e, 0xc4, 0x2c, 0xd3, 0x93, 0x37, 0x9c, 0xdf, 0x6a, 0x30, 0xeb, 0x35, + 0xa7, 0x86, 0xa2, 0xca, 0x29, 0xcb, 0xe9, 0x5f, 0x31, 0x95, 0xbd, 0xa5, 0xa7, 0xb5, 0x0d, 0x92, + 0xef, 0xc0, 0xae, 0x80, 0x6a, 0x22, 0x9b, 0xf1, 0xab, 0xfd, 0xa1, 0x9f, 0x58, 0xc4, 0x81, 0xc3, + 0x4c, 0x60, 0x50, 0xc4, 0x99, 0x73, 0xa8, 0x0b, 0x52, 0x99, 0xe4, 0x87, 0x2b, 0xe3, 0xa1, 0x47, + 0x6d, 0x38, 0x77, 0x1a, 0x82, 0x2d, 0x3f, 0xbd, 0x32, 0x4d, 0x0e, 0x1c, 0x0a, 0x0c, 0x05, 0xca, + 0xad, 0x1e, 0xbf, 0x01, 0xad, 0x4c, 0xf2, 0xac, 0xd5, 0x75, 0x07, 0xb4, 0xee, 0xed, 0x86, 0x6e, + 0xc3, 0x4b, 0x5b, 0x03, 0x72, 0x0f, 0xc0, 0xb4, 0x69, 0xcd, 0xf7, 0xe8, 0x0c, 0xf5, 0x54, 0x37, + 0x10, 0x95, 0x73, 0x6b, 0x48, 0x9c, 0xd1, 0xb5, 0x9c, 0x5b, 0x7e, 0xda, 0xa6, 0xbb, 0x67, 0x30, + 0xa9, 0x5b, 0xba, 0x48, 0x93, 0x5c, 0xa4, 0x91, 0xba, 0x87, 0x2c, 0x7c, 0xdf, 0xcc, 0xa4, 0xda, + 0xc6, 0xca, 0x54, 0x9e, 0x18, 0xa5, 0x64, 0x1b, 0xb3, 0x18, 0x36, 0xad, 0x4c, 0xf7, 0x29, 0x8c, + 0x6b, 0x9d, 0xf5, 0x2e, 0xf1, 0xd5, 0xde, 0x87, 0x3c, 0x61, 0xd1, 0xb9, 0xc0, 0xa5, 0xaa, 0xb5, + 0x51, 0x6a, 0x61, 0xee, 0x9f, 0x3d, 0x98, 0xa8, 0xca, 0x7b, 0x6a, 0xdb, 0xa5, 0x87, 0x49, 0x2e, + 0x76, 0x6a, 0xe1, 0x43, 0x81, 0xb8, 0xe7, 0xc9, 0xc6, 0xcb, 0x79, 0xf9, 0xe6, 0x8d, 0xe9, 0xa8, + 0x02, 0xdf, 0xf2, 0x18, 0xc9, 0x97, 0x30, 0x0c, 0x45, 0xba, 0xc7, 0xc4, 0x50, 0xba, 0x9a, 0x02, + 0x06, 0xd2, 0x84, 0xaf, 0x60, 0x14, 0x63, 0xac, 0xc5, 0x35, 0xa3, 0xa7, 0x19, 0xc3, 0x12, 0xd3, + 0x94, 0xfb, 0x30, 0x8e, 0x31, 0xfe, 0x20, 0x78, 0x8e, 0x86, 0x63, 0x99, 0x40, 0x15, 0x58, 0x91, + 0x32, 0xb6, 0x41, 0xe9, 0x49, 0x9f, 0x25, 0x09, 0x06, 0xfa, 0x0f, 0x61, 0xd1, 0x91, 0x06, 0xd7, + 0x06, 0x23, 0x4f, 0xe0, 0x56, 0x49, 0x7a, 0xcf, 0xb3, 0x0c, 0x03, 0x2f, 0x63, 0x02, 0x93, 0x5c, + 0xbf, 0x75, 0x16, 0x25, 0x86, 0x6b, 0x5c, 0xe7, 0xda, 0xf3, 0x49, 0x56, 0x45, 0xca, 0x31, 0xd1, + 0xcf, 0x5e, 0x25, 0xfb, 0x8b, 0xc1, 0x14, 0x89, 0x8b, 0x98, 0x65, 0x9e, 0x40, 0x99, 0x46, 0x97, + 0xe6, 0xe9, 0x1b, 0xd3, 0x91, 0x06, 0xa9, 0xc1, 0xc8, 0x17, 0x00, 0x46, 0x29, 0x62, 0xfb, 0x9d, + 0x63, 0x6b, 0x19, 0x5b, 0x23, 0xaf, 0xd8, 0x7e, 0x57, 0xb9, 0xbd, 0x8c, 0x67, 0xe5, 0xe0, 0x95, + 0xee, 0x73, 0x05, 0xa8, 0x87, 0xb3, 0x76, 0x7b, 0x17, 0x45, 0x28, 0xf5, 0x88, 0x95, 0x89, 0x28, + 0xca, 0x69, 0x11, 0x4a, 0xf7, 0xef, 0x0e, 0xdc, 0x14, 0x28, 0xf3, 0x54, 0x60, 0xab, 0x55, 0x5f, + 0x9b, 0xaf, 0xa5, 0xa7, 0xde, 0x2c, 0x26, 0xd0, 0xfc, 0x9a, 0x2d, 0x6a, 0xee, 0xb6, 0x28, 0x41, + 0x72, 0x02, 0x37, 0xda, 0xe5, 0xf1, 0xd3, 0x0f, 0xba, 0x65, 0x16, 0x3d, 0x6e, 0xd6, 0x66, 0x91, + 0x7e, 0x50, 0x7d, 0x0b, 0x53, 0xf1, 0xbe, 0x6e, 0x7e, 0xd9, 0xb7, 0x12, 0xab, 0x5a, 0x5b, 0x25, + 0xd3, 0x68, 0xdb, 0xb0, 0xc4, 0x34, 0xa5, 0x4e, 0xac, 0x04, 0x55, 0xdb, 0x3a, 0x75, 0x62, 0xb4, + 0x04, 0xdd, 0x8f, 0x30, 0x6c, 0x5e, 0xe7, 0x31, 0x58, 0x81, 0x19, 0x55, 0xb5, 0x42, 0x77, 0x1b, + 0x2b, 0x74, 0x75, 0x48, 0xa9, 0x26, 0x92, 0x67, 0x6a, 0xe1, 0xb5, 0x96, 0x5e, 0x87, 0xe1, 0xfc, + 0x5e, 0xf3, 0xa9, 0xb8, 0x5e, 0x30, 0x5a, 0xd1, 0x4f, 0x56, 0x8d, 0x17, 0xd7, 0xbc, 0xa4, 0xc4, + 0x86, 0x3e, 0x5d, 0xff, 0xba, 0x5a, 0x4c, 0x3e, 0x53, 0xc7, 0xd3, 0xb7, 0xf4, 0x6c, 0x3d, 0xe9, + 0x90, 0x43, 0xe8, 0xfd, 0x76, 0xb6, 0x9e, 0x74, 0xd5, 0x81, 0x9e, 0x2e, 0x27, 0x3d, 0x72, 0x13, + 0x8e, 0x4f, 0x5f, 0xbd, 0x59, 0xfc, 0xec, 0x3d, 0x5f, 0x2d, 0x3d, 0xf3, 0x85, 0x75, 0xf2, 0x18, + 0x06, 0xd5, 0x5b, 0x4b, 0x8e, 0x00, 0xd4, 0xd9, 0x6b, 0xa8, 0x9d, 0xbf, 0x78, 0xfe, 0xee, 0xd5, + 0xa4, 0x43, 0x06, 0x60, 0xad, 0xde, 0xac, 0x7e, 0x9c, 0x74, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, + 0x51, 0xb3, 0x06, 0x24, 0x82, 0x09, 0x00, 0x00, } diff --git a/lxd/migration/migrate.proto b/lxd/migration/migrate.proto index 721662e424..257f038e21 100644 --- a/lxd/migration/migrate.proto +++ b/lxd/migration/migrate.proto @@ -58,6 +58,11 @@ message zfsFeatures { optional bool compress = 1; } +message btrfsFeatures { + optional bool migration_header = 1; + optional bool header_subvolumes = 2; +} + message MigrationHeader { required MigrationFSType fs = 1; optional CRIUType criu = 2; @@ -69,6 +74,7 @@ message MigrationHeader { optional bool refresh = 9; optional zfsFeatures zfsFeatures = 10; optional int64 volumeSize = 11; + optional btrfsFeatures btrfsFeatures = 12; } message MigrationControl { From 7217506b510fab252968072ab20bfedfd827f196 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 11:46:09 +0100 Subject: [PATCH 07/15] lxd/migration/utils: Adds GetBtrfsFeaturesSlice function And constants for the BTRFS features. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/migration/utils.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lxd/migration/utils.go b/lxd/migration/utils.go index 92265c2ace..9d3762dc2a 100644 --- a/lxd/migration/utils.go +++ b/lxd/migration/utils.go @@ -1,5 +1,11 @@ package migration +// BTRFSFeatureMigrationHeader indicates a migration header will be sent/recv in data channel first. +const BTRFSFeatureMigrationHeader = "migration_header" + +// BTRFSFeatureSubvolumes indicates migration can send/recv subvolumes. +const BTRFSFeatureSubvolumes = "header_subvolumes" + // GetRsyncFeaturesSlice returns a slice of strings representing the supported RSYNC features func (m *MigrationHeader) GetRsyncFeaturesSlice() []string { features := []string{} @@ -42,3 +48,23 @@ func (m *MigrationHeader) GetZfsFeaturesSlice() []string { return features } + +// GetBtrfsFeaturesSlice returns a slice of strings representing the supported BTRFS features. +func (m *MigrationHeader) GetBtrfsFeaturesSlice() []string { + features := []string{} + if m == nil { + return features + } + + if m.BtrfsFeatures != nil { + if m.BtrfsFeatures.MigrationHeader != nil && *m.BtrfsFeatures.MigrationHeader == true { + features = append(features, BTRFSFeatureMigrationHeader) + } + + if m.BtrfsFeatures.HeaderSubvolumes != nil && *m.BtrfsFeatures.HeaderSubvolumes == true { + features = append(features, BTRFSFeatureSubvolumes) + } + } + + return features +} From 58172c554abec5a8671131872e1119986536a529 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 11:46:45 +0100 Subject: [PATCH 08/15] lxd/migration/migration/volumes: Adds BTRFS feature support to TypesToHeader Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/migration/migration_volumes.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lxd/migration/migration_volumes.go b/lxd/migration/migration_volumes.go index 331b003c22..314ea99bbe 100644 --- a/lxd/migration/migration_volumes.go +++ b/lxd/migration/migration_volumes.go @@ -72,6 +72,23 @@ func TypesToHeader(types ...Type) MigrationHeader { header.ZfsFeatures = &features } + // Add BTRFS features if preferred type is BTRFS. + if preferredType.FSType == MigrationFSType_BTRFS { + features := BtrfsFeatures{ + MigrationHeader: &missingFeature, + HeaderSubvolumes: &missingFeature, + } + for _, feature := range preferredType.Features { + if feature == BTRFSFeatureMigrationHeader { + features.MigrationHeader = &hasFeature + } else if feature == BTRFSFeatureSubvolumes { + features.HeaderSubvolumes = &hasFeature + } + } + + header.BtrfsFeatures = &features + } + // Check all the types for an Rsync method, if found add its features to the header's RsyncFeatures list. for _, t := range types { if t.FSType != MigrationFSType_RSYNC && t.FSType != MigrationFSType_BLOCK_AND_RSYNC { From d3e78fc65202a5c60a8ce228f4310a587b34cde0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 11:47:03 +0100 Subject: [PATCH 09/15] lxd/migration/migration/volumes: Adds BTRFS feature support to MatchTypes Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/migration/migration_volumes.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/migration/migration_volumes.go b/lxd/migration/migration_volumes.go index 314ea99bbe..261f7eaab0 100644 --- a/lxd/migration/migration_volumes.go +++ b/lxd/migration/migration_volumes.go @@ -145,6 +145,8 @@ func MatchTypes(offer MigrationHeader, fallbackType MigrationFSType, ourTypes [] var offeredFeatures []string if offerFSType == MigrationFSType_ZFS { offeredFeatures = offer.GetZfsFeaturesSlice() + } else if offerFSType == MigrationFSType_BTRFS { + offeredFeatures = offer.GetBtrfsFeaturesSlice() } else if offerFSType == MigrationFSType_RSYNC { offeredFeatures = offer.GetRsyncFeaturesSlice() if !shared.StringInSlice("bidirectional", offeredFeatures) { From 1fd33760969d4254ae3722471dac320a3383a763 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 11:47:31 +0100 Subject: [PATCH 10/15] lxd/storage/drivers/driver/btrfs: Adds BTRFS features to MigrationTypes Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lxd/storage/drivers/driver_btrfs.go b/lxd/storage/drivers/driver_btrfs.go index 4c4e569b81..e69d0ff18b 100644 --- a/lxd/storage/drivers/driver_btrfs.go +++ b/lxd/storage/drivers/driver_btrfs.go @@ -370,6 +370,7 @@ func (d *btrfs) GetResources() (*api.ResourcesStoragePool, error) { // MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order. func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool) []migration.Type { rsyncFeatures := []string{"xattrs", "delete", "compress", "bidirectional"} + btrfsFeatures := []string{migration.BTRFSFeatureMigrationHeader, migration.BTRFSFeatureSubvolumes} // Only offer rsync for refreshes or if running in an unprivileged container. if refresh || d.state.OS.RunningInUserNS { @@ -392,7 +393,8 @@ func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool) []migratio if contentType == ContentTypeBlock { return []migration.Type{ { - FSType: migration.MigrationFSType_BTRFS, + FSType: migration.MigrationFSType_BTRFS, + Features: btrfsFeatures, }, { FSType: migration.MigrationFSType_BLOCK_AND_RSYNC, @@ -403,7 +405,8 @@ func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool) []migratio return []migration.Type{ { - FSType: migration.MigrationFSType_BTRFS, + FSType: migration.MigrationFSType_BTRFS, + Features: btrfsFeatures, }, { FSType: migration.MigrationFSType_RSYNC, From 6fb47c0e094e23e6cf54362e38ed6a161590aa7c Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 16:51:18 +0100 Subject: [PATCH 11/15] lxd/storage/memorypipe: Dont make ioutil.ReadAll panic on cancel Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/memorypipe/memory_pipe.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/storage/memorypipe/memory_pipe.go b/lxd/storage/memorypipe/memory_pipe.go index 28744d1c7b..4db48f874f 100644 --- a/lxd/storage/memorypipe/memory_pipe.go +++ b/lxd/storage/memorypipe/memory_pipe.go @@ -31,12 +31,12 @@ func (p *pipe) Read(b []byte) (int, error) { select { case msg := <-p.ch: if msg.err == io.EOF { - return -1, msg.err + return 0, msg.err } n := copy(b, msg.data) return n, msg.err case <-p.ctx.Done(): - return -1, p.ctx.Err() + return 0, p.ctx.Err() } } @@ -51,7 +51,7 @@ func (p *pipe) Write(b []byte) (int, error) { case p.otherEnd.ch <- msg: // Sent msg to the other side's Read function. return len(msg.data), msg.err case <-p.ctx.Done(): - return -1, p.ctx.Err() + return 0, p.ctx.Err() } } From 7e16de785564ade6f6e9d2100b15e53330eac55f Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 16:57:10 +0100 Subject: [PATCH 12/15] lxd/storage/drivers/driver/btrfs/utils: Kill btrfs send on error in sendSubvolume Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_utils.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/storage/drivers/driver_btrfs_utils.go b/lxd/storage/drivers/driver_btrfs_utils.go index bd48d3fc5a..bf2fd9097b 100644 --- a/lxd/storage/drivers/driver_btrfs_utils.go +++ b/lxd/storage/drivers/driver_btrfs_utils.go @@ -286,6 +286,7 @@ func (d *btrfs) sendSubvolume(path string, parent string, conn io.ReadWriteClose _, err := io.Copy(conn, stdoutPipe) chStdoutPipe <- err conn.Close() + cmd.Process.Kill() // This closes stderr. }() // Run the command. From 10d380152044cdfbf38c143ab9ad094670023b9a Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 16:57:29 +0100 Subject: [PATCH 13/15] lxd/storage/drivers/driver/btrfs/utils: Support subvolumes in receiveSubvolume Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_utils.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lxd/storage/drivers/driver_btrfs_utils.go b/lxd/storage/drivers/driver_btrfs_utils.go index bf2fd9097b..12df54820a 100644 --- a/lxd/storage/drivers/driver_btrfs_utils.go +++ b/lxd/storage/drivers/driver_btrfs_utils.go @@ -378,8 +378,15 @@ func (d *btrfs) receiveSubvolume(path string, targetPath string, conn io.ReadWri return nil } - // Handle older LXD versions. + // Location we would expect to receive the root subvolume. receivedSnapshot := fmt.Sprintf("%s/.migration-send", path) + + // Handle non-root subvolumes. + if !shared.PathExists(receivedSnapshot) { + receivedSnapshot = fmt.Sprintf("%s/%s", path, filepath.Base(targetPath)) + } + + // Handle older LXD versions. if !shared.PathExists(receivedSnapshot) { receivedSnapshot = fmt.Sprintf("%s/.root", path) } From a1b54501a15a9a52efc99c9fd17251d3e045b636 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 17:17:00 +0100 Subject: [PATCH 14/15] lxd/storage/drivers/driver/btrfs/utils: Adds migrationHeader function returning BTRFSMigrationHeader struct Scans a volume and its snapshots for BTRFS subvolumes ready for sending to migration target. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_utils.go | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/lxd/storage/drivers/driver_btrfs_utils.go b/lxd/storage/drivers/driver_btrfs_utils.go index 12df54820a..e5744b7690 100644 --- a/lxd/storage/drivers/driver_btrfs_utils.go +++ b/lxd/storage/drivers/driver_btrfs_utils.go @@ -417,3 +417,72 @@ func (d *btrfs) volumeSize(vol Volume) string { return size } + +// BTRFSSubVolume is the structure used to store information about a subvolume. +type BTRFSSubVolume struct { + Path string + Snapshot string + Readonly bool +} + +// BTRFSMigrationHeader is the meta data header sent before each volume during a migration. +type BTRFSMigrationHeader struct { + Subvolumes []BTRFSSubVolume +} + +// migrationHeader scans the volume and any specified snapshots, returning a migration header containing subvolume +// information. +func (d *btrfs) migrationHeader(vol Volume, snapshots []string) (*BTRFSMigrationHeader, error) { + var migrationHeader BTRFSMigrationHeader + + // Add snapshots to volumes list. + for _, snapName := range snapshots { + snapVol, _ := vol.NewSnapshot(snapName) + + // Add snapshot root volume to volumes list. + migrationHeader.Subvolumes = append(migrationHeader.Subvolumes, BTRFSSubVolume{ + Snapshot: snapVol.name, + Path: string(filepath.Separator), + Readonly: false, + }) + + // Find any subvolumes in snapshot volume + subVolPaths, err := d.getSubvolumes(snapVol.MountPath()) + if err != nil { + return nil, err + } + sort.Sort(sort.StringSlice(subVolPaths)) + + // Add any subvolumes under the same snapshot volume name, but with a different path. + for _, subVolPath := range subVolPaths { + migrationHeader.Subvolumes = append(migrationHeader.Subvolumes, BTRFSSubVolume{ + Snapshot: snapVol.name, + Path: fmt.Sprintf("%s%s", string(filepath.Separator), subVolPath), + Readonly: false, + }) + } + } + + // Add main root volume to volumes list. + migrationHeader.Subvolumes = append(migrationHeader.Subvolumes, BTRFSSubVolume{ + Path: string(filepath.Separator), + Readonly: false, + }) + + // Find any subvolumes in main volume. + subVolPaths, err := d.getSubvolumes(vol.MountPath()) + if err != nil { + return nil, err + } + sort.Sort(sort.StringSlice(subVolPaths)) + + // Add any subvolumes under the main volume name, but with a different path. + for _, subVolPath := range subVolPaths { + migrationHeader.Subvolumes = append(migrationHeader.Subvolumes, BTRFSSubVolume{ + Path: fmt.Sprintf("%s%s", string(filepath.Separator), subVolPath), + Readonly: false, + }) + } + + return &migrationHeader, nil +} From e08e5688ac8fa967038d005bd211286e739abe54 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 6 May 2020 17:24:43 +0100 Subject: [PATCH 15/15] lxd/storage/drivers/driver/btrfs/volumes: Updates CreateVolumeFromMigration to receive subvolumes Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_btrfs_volumes.go | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index 0d5da1c297..b87666d238 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -318,6 +318,21 @@ func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, v return ErrNotSupported } + var migrationHeader BTRFSMigrationHeader + + // Inspect negotiated features to see if we are expecting to get a metadata migration header frame. + if shared.StringInSlice(migration.BTRFSFeatureMigrationHeader, volTargetArgs.MigrationType.Features) { + buf, err := ioutil.ReadAll(conn) + if err != nil { + return errors.Wrapf(err, "Failed reading migration header") + } + + err = json.Unmarshal(buf, &migrationHeader) + if err != nil { + return errors.Wrapf(err, "Failed decoding migration header") + } + } + // Handle btrfs send/receive migration. if len(volTargetArgs.Snapshots) > 0 { snapshotsDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name) @@ -356,11 +371,25 @@ func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, v } wrapper := migration.ProgressWriter(op, "fs_progress", vol.name) + d.logger.Debug("Receiving volume", log.Ctx{"name": vol.name, "tmpPath": tmpVolumesMountPoint, "path": vol.MountPath()}) err = d.receiveSubvolume(tmpVolumesMountPoint, vol.MountPath(), conn, wrapper) if err != nil { return err } + for _, subVol := range migrationHeader.Subvolumes { + if subVol.Snapshot != "" || subVol.Path == string(filepath.Separator) { + continue // We're only looking for subvolumes of main volume. + } + + path := filepath.Join(vol.MountPath(), subVol.Path) + d.logger.Debug("Receiving volume", log.Ctx{"name": vol.name, "tmpPath": tmpVolumesMountPoint, "path": path}) + err = d.receiveSubvolume(tmpVolumesMountPoint, filepath.Join(vol.MountPath(), subVol.Path), conn, wrapper) + if err != nil { + return err + } + } + return nil }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel