| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- // 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/hostman/hostdeployer/apis"
- "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(*apis.GuestDesc, string) 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(string, string) error {
- if d.IsLVMPartition() {
- // do not try to resize LVM partition
- return nil
- }
- return fsutils.ResizeDiskFs(d.nbddev, 0, false)
- }
- func (d *SLibguestfsDriver) FormatPartition(fs, uuid string, features *apis.FsFeatures) error {
- return fsutils.FormatPartition(fmt.Sprintf("%sp1", d.nbddev), fs, uuid, features)
- }
- 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
- }
- func (d *SLibguestfsDriver) DetectIsUEFISupport(rootfs fsdriver.IRootFsDriver) bool {
- return fsutils.DetectIsUEFISupport(rootfs, d.GetPartitions())
- }
- func (d *SLibguestfsDriver) DetectIsBIOSSupport(rootfs fsdriver.IRootFsDriver) bool {
- return fsutils.DetectIsBIOSSupport(d.nbddev, rootfs)
- }
- func (d *SLibguestfsDriver) MountRootfs(readonly bool) (fsdriver.IRootFsDriver, error) {
- return fsutils.MountRootfs(readonly, d.GetPartitions())
- }
- func (d *SLibguestfsDriver) UmountRootfs(fd fsdriver.IRootFsDriver) error {
- if part := fd.GetPartition(); part != nil {
- return part.Umount()
- }
- return nil
- }
- func (d *SLibguestfsDriver) DeployGuestfs(req *apis.DeployParams) (res *apis.DeployGuestFsResponse, err error) {
- return fsutils.DeployGuestfs(d, req)
- }
- func (d *SLibguestfsDriver) ResizeFs(*apis.ResizeFsParams) (*apis.Empty, error) {
- return fsutils.ResizeFs(d, "")
- }
- func (d *SLibguestfsDriver) SaveToGlance(req *apis.SaveToGlanceParams) (*apis.SaveToGlanceResponse, error) {
- return fsutils.SaveToGlance(d, req)
- }
- func (d *SLibguestfsDriver) FormatFs(req *apis.FormatFsParams) (*apis.Empty, error) {
- return fsutils.FormatFs(d, req)
- }
- func (d *SLibguestfsDriver) ProbeImageInfo(req *apis.ProbeImageInfoPramas) (*apis.ImageInfo, error) {
- return fsutils.ProbeImageInfo(d)
- }
|