Files
cloudpods/pkg/hostman/diskutils/libguestfs/driver.go
Jian Qiu 4375bad382 fix: deploy encrypted disk (#14187)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2022-05-02 09:07:03 +08:00

253 lines
6.0 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package libguestfs
import (
"fmt"
"io/ioutil"
"math/rand"
"path"
"path/filepath"
"strings"
"time"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/sortedmap"
"yunion.io/x/onecloud/pkg/hostman/diskutils/fsutils"
"yunion.io/x/onecloud/pkg/hostman/diskutils/libguestfs/guestfish"
"yunion.io/x/onecloud/pkg/hostman/diskutils/nbd"
"yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
"yunion.io/x/onecloud/pkg/hostman/guestfs/guestfishpart"
"yunion.io/x/onecloud/pkg/hostman/guestfs/kvmpart"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/qemuimg"
)
const (
DiskLabelLength = 6
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func init() {
rand.Seed(time.Now().UnixNano())
}
type SLibguestfsDriver struct {
imageInfo qemuimg.SImageInfo
nbddev string
diskLabel string
lvmParts []string
fsmap *sortedmap.SSortedMap
fish *guestfish.Guestfish
device string
parts []fsdriver.IDiskPartition
}
func NewLibguestfsDriver(imageInfo qemuimg.SImageInfo) *SLibguestfsDriver {
return &SLibguestfsDriver{
imageInfo: imageInfo,
}
}
func (d *SLibguestfsDriver) Connect() error {
fish, err := guestfsManager.AcquireFish()
if err != nil {
return err
}
d.fish = fish
d.nbddev = nbd.GetNBDManager().AcquireNbddev()
if err != nil {
return errors.Errorf("Cannot get nbd device")
}
log.Debugf("acquired device %s", d.nbddev)
err = nbd.QemuNbdConnect(d.imageInfo, d.nbddev)
if err != nil {
return err
}
lable := RandStringBytes(DiskLabelLength)
err = fish.AddDrive(d.nbddev, lable, false)
if err != nil {
return err
}
d.diskLabel = lable
if err = fish.LvmClearFilter(); err != nil {
return err
}
devices, err := fish.ListDevices()
if err != nil {
return err
}
if len(devices) == 0 {
return errors.Errorf("fish list devices no device found")
}
d.device = devices[0]
fsmap, err := fish.ListFilesystems()
if err != nil {
return err
}
d.fsmap = fsmap
log.Debugf("fsmap output %#v", d.fsmap)
lvs, err := fish.Lvs()
if err != nil {
return err
}
d.lvmParts = lvs
keys := d.fsmap.Keys()
for i := 0; i < len(keys); i++ {
partDev := keys[i]
ifs, _ := d.fsmap.Get(keys[i])
fs := ifs.(string)
log.Debugf("new partition %s %s %s", d.device, partDev, fs)
/* guestfish run ntfs mount to host is too slow
* use host nbd partition replace */
if fs == "ntfs" && len(d.lvmParts) == 0 {
log.Infof("has ntfs, use nbd parts")
d.parts, err = d.findNbdPartitions()
if err != nil {
return err
}
if err = guestfsManager.ReleaseFish(d.fish); err != nil {
log.Errorf("release fish failed %s", err)
}
d.diskLabel = ""
d.fish = nil
break
}
part := guestfishpart.NewGuestfishDiskPartition(d.device, partDev, fs, fish)
d.parts = append(d.parts, part)
}
return nil
}
func (d *SLibguestfsDriver) findNbdPartitions() ([]fsdriver.IDiskPartition, error) {
if len(d.nbddev) == 0 {
return nil, fmt.Errorf("Want find partitions but dosen't have nbd dev")
}
dev := filepath.Base(d.nbddev)
devpath := filepath.Dir(d.nbddev)
files, err := ioutil.ReadDir(devpath)
if err != nil {
return nil, errors.Wrapf(err, "read dir %s", devpath)
}
parts := make([]fsdriver.IDiskPartition, 0)
for i := 0; i < len(files); i++ {
if files[i].Name() != dev && strings.HasPrefix(files[i].Name(), dev+"p") {
var part = kvmpart.NewKVMGuestDiskPartition(path.Join(devpath, files[i].Name()), "", false)
parts = append(parts, part)
}
}
return parts, nil
}
func (d *SLibguestfsDriver) Disconnect() error {
if len(d.diskLabel) > 0 {
if err := guestfsManager.ReleaseFish(d.fish); err != nil {
log.Errorf("release fish failed %s", err)
}
d.diskLabel = ""
d.fish = nil
}
if len(d.nbddev) > 0 {
if err := nbd.QemuNbdDisconnect(d.nbddev); err != nil {
return err
}
nbd.GetNBDManager().ReleaseNbddev(d.nbddev)
}
return nil
}
func (d *SLibguestfsDriver) GetPartitions() []fsdriver.IDiskPartition {
return d.parts
}
func (d *SLibguestfsDriver) IsLVMPartition() bool {
return len(d.lvmParts) > 0
}
func (d *SLibguestfsDriver) Zerofree() {
startTime := time.Now()
for _, part := range d.parts {
part.Zerofree()
}
log.Infof("libguestfs zerofree %d partitions takes %f seconds",
len(d.parts), time.Now().Sub(startTime).Seconds())
}
func (d *SLibguestfsDriver) ResizePartition() error {
if d.IsLVMPartition() {
// do not try to resize LVM partition
return nil
}
return fsutils.ResizeDiskFs(d.nbddev, 0)
}
func (d *SLibguestfsDriver) FormatPartition(fs, uuid string) error {
return fsutils.FormatPartition(fmt.Sprintf("%sp1", d.nbddev), fs, uuid)
}
func (d *SLibguestfsDriver) MakePartition(fsFormat string) error {
return fsutils.Mkpartition(d.nbddev, fsFormat)
}
func (d *SLibguestfsDriver) FormatPartition2(fs, uuid string) error {
partDev := fmt.Sprintf("%s1", d.device)
switch fs {
case "swap":
return d.fish.Mkswap(partDev, uuid, "")
case "ext2", "ext3", "ext4", "xfs", "fat":
return d.fish.Mkfs(partDev, fs)
}
return errors.Errorf("Unknown fs %s", fs)
}
func (d *SLibguestfsDriver) MakePartition2(fsFormat string) error {
var (
labelType = "gpt"
diskType = fileutils2.FsFormatToDiskType(fsFormat)
)
if len(diskType) == 0 {
return errors.Errorf("Unknown fsFormat %s", fsFormat)
}
err := d.fish.PartDisk(d.device, labelType)
if err != nil {
return err
}
return nil
}