diff --git a/pkg/hostman/container/storage/local_raw/doc.go b/pkg/hostman/container/storage/local_raw/doc.go new file mode 100644 index 00000000000..b53a2d36b58 --- /dev/null +++ b/pkg/hostman/container/storage/local_raw/doc.go @@ -0,0 +1 @@ +package local_raw // import "yunion.io/x/onecloud/pkg/hostman/container/storage/local_raw" diff --git a/pkg/hostman/container/storage/local_raw.go b/pkg/hostman/container/storage/local_raw/local_raw.go similarity index 86% rename from pkg/hostman/container/storage/local_raw.go rename to pkg/hostman/container/storage/local_raw/local_raw.go index 4a4e121f44a..6b738819dcc 100644 --- a/pkg/hostman/container/storage/local_raw.go +++ b/pkg/hostman/container/storage/local_raw/local_raw.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package storage +package local_raw import ( "fmt" @@ -21,12 +21,14 @@ import ( "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/onecloud/pkg/hostman/container/storage" "yunion.io/x/onecloud/pkg/util/fileutils2" "yunion.io/x/onecloud/pkg/util/losetup" + losetupioctl "yunion.io/x/onecloud/pkg/util/losetup/ioctl" ) func init() { - RegisterDriver(newLocalRaw()) + storage.RegisterDriver(newLocalRaw()) } type localRaw struct{} @@ -35,8 +37,8 @@ func newLocalRaw() *localRaw { return &localRaw{} } -func (l localRaw) GetType() StorageType { - return STORAGE_TYPE_LOCAL_RAW +func (l localRaw) GetType() storage.StorageType { + return storage.STORAGE_TYPE_LOCAL_RAW } func (l localRaw) CheckConnect(diskPath string) (string, bool, error) { @@ -76,7 +78,7 @@ func (l localRaw) DisconnectDisk(diskPath string, mountPoint string) error { for _, dev := range devs.LoopDevs { if dev.BackFile == diskPath { log.Infof("Start detach loop device %s", dev.Name) - if err := losetup.DetachDevice(dev.Name); err != nil { + if err := losetupioctl.DetachAndRemoveDevice(dev.Name); err != nil { if strings.Contains(err.Error(), "No such device or address") { return nil } diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index e3e5c63b9a0..a150568fdae 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -49,6 +49,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/cloudcommon/tsdb" "yunion.io/x/onecloud/pkg/cloudcommon/types" + _ "yunion.io/x/onecloud/pkg/hostman/container/storage/local_raw" "yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver" "yunion.io/x/onecloud/pkg/hostman/hostinfo/hostbridge" "yunion.io/x/onecloud/pkg/hostman/hostinfo/hostconsts" diff --git a/pkg/util/losetup/ioctl/doc.go b/pkg/util/losetup/ioctl/doc.go new file mode 100644 index 00000000000..3353be3514f --- /dev/null +++ b/pkg/util/losetup/ioctl/doc.go @@ -0,0 +1 @@ +package ioctl // import "yunion.io/x/onecloud/pkg/util/losetup/ioctl" diff --git a/pkg/util/losetup/ioctl/remove.go b/pkg/util/losetup/ioctl/remove.go new file mode 100644 index 00000000000..e05bb96404f --- /dev/null +++ b/pkg/util/losetup/ioctl/remove.go @@ -0,0 +1,137 @@ +// 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 ioctl + +/* +#include +*/ +import "C" +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" + + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + + fileutils "yunion.io/x/onecloud/pkg/util/fileutils2" + "yunion.io/x/onecloud/pkg/util/losetup" + "yunion.io/x/onecloud/pkg/util/mountutils" +) + +const ( + LOOP_CTL_PATH = "/dev/loop-control" + + LOOP_CTL_REMOVE = C.LOOP_CTL_REMOVE +) + +func RemoveDevice(devNumber int) error { + fd, err := os.OpenFile(LOOP_CTL_PATH, os.O_RDWR, 0644) + if err != nil { + return errors.Wrapf(err, "Open %s", LOOP_CTL_PATH) + } + defer fd.Close() + + retNum, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), LOOP_CTL_REMOVE, uintptr(devNumber)) + if err != nil { + return errors.Wrapf(err, "IOCT REMOVE %d", devNumber) + } + log.Infof("using ioctl remove loop %v", retNum) + return nil +} + +func DetachAndRemoveDevice(devPath string) error { + getDev := func() (*losetup.Device, error) { + devs, err := losetup.ListDevices() + if err != nil { + return nil, errors.Wrapf(err, "list devices") + } + dev := devs.GetDeviceByName(devPath) + return dev, nil + } + dev, err := getDev() + if err != nil { + return err + } + if dev == nil { + return nil + } + // check mountpoints + partions, err := losetup.GetDevicePartions(devPath) + if err != nil { + return errors.Wrapf(err, "get device %s partions", devPath) + } + for _, part := range partions { + checkMntCmd, _ := losetup.NewCommand("sh", "-c", fmt.Sprintf("mount | grep %s | awk '{print $3}'", part)).Run() + if out := checkMntCmd.Output(); out != "" { + mntPoints := strings.Split(out, "\n") + for _, mntPoint := range mntPoints { + if mntPoint != "" { + if err := mountutils.Unmount(mntPoint); err != nil { + return errors.Wrapf(err, "umount %s of dev: %s, part: %s", mntPoint, dev.Name, part) + } + } + } + } + } + + _, err = losetup.NewLosetupCommand().AddArgs("-d", dev.Name).Run() + if err != nil { + return errors.Wrapf(err, "detach device") + } + + removeDev := func() error { + log.Infof("Start removing device %s", dev.Name) + if fileutils.Exists(dev.Name) { + devNumStr := strings.TrimPrefix(filepath.Base(dev.Name), "loop") + devNum, err := strconv.Atoi(devNumStr) + if err != nil { + return errors.Wrapf(err, "remove device: invalid device number: %s", devNumStr) + } + if err := RemoveDevice(devNum); err != nil { + return errors.Wrapf(err, "remove device: %s", devNumStr) + } + } else { + log.Infof("Loop device %s removed", dev.Name) + } + return nil + } + + errs := []error{} + for i := 1; i <= 3; i++ { + if err := removeDev(); err != nil { + err = errors.Wrapf(err, "remove loop device, %d times", i) + log.Warningf("remove device %s: %v", devPath, err) + errs = append(errs, err) + } else { + return nil + } + time.Sleep(time.Duration(i) * time.Second) + } + return errors.NewAggregate(errs) + + // recheck + /*dev, err = getDev() + if err != nil { + return errors.Wrapf(err, "get device by %s for rechecking", devPath) + } + if dev != nil { + return errors.Errorf("device %s still exists, %s", devPath, jsonutils.Marshal(dev)) + }*/ +} diff --git a/pkg/util/losetup/losetup.go b/pkg/util/losetup/losetup.go index 09a7c90612d..acb790caa90 100644 --- a/pkg/util/losetup/losetup.go +++ b/pkg/util/losetup/losetup.go @@ -298,6 +298,7 @@ func DetachDevice(devPath string) error { if err != nil { return errors.Wrapf(err, "detach device") } + // recheck /*dev, err = getDev() if err != nil {