| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- // 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 storageman
- import (
- "context"
- "fmt"
- "io/ioutil"
- "path"
- "strings"
- "sync"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/qemuimgfmt"
- "yunion.io/x/pkg/util/regutils"
- "yunion.io/x/onecloud/pkg/apis"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- hostapi "yunion.io/x/onecloud/pkg/apis/host"
- imageapi "yunion.io/x/onecloud/pkg/apis/image"
- "yunion.io/x/onecloud/pkg/hostman/guestman/desc"
- deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
- "yunion.io/x/onecloud/pkg/hostman/hostutils"
- "yunion.io/x/onecloud/pkg/hostman/options"
- "yunion.io/x/onecloud/pkg/hostman/storageman/storageutils"
- "yunion.io/x/onecloud/pkg/httperrors"
- modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
- "yunion.io/x/onecloud/pkg/mcclient/modules/image"
- "yunion.io/x/onecloud/pkg/util/procutils"
- "yunion.io/x/onecloud/pkg/util/qemuimg"
- )
- const (
- _RECYCLE_BIN_ = "recycle_bin"
- _IMGSAVE_BACKUPS_ = "imgsave_backups"
- _SNAPSHOT_PATH_ = "snapshots"
- _BACKUP_PATH_ = "backups"
- ErrStorageTimeout = constError("storage accessible check timeout")
- TempBindMountPath = "/opt/cloud/workspace/temp-bind"
- )
- type constError string
- func (e constError) Error() string { return string(e) }
- var DELETEING_SNAPSHOTS = sync.Map{}
- type IStorageFactory interface {
- NewStorage(manager *SStorageManager, mountPoint string) IStorage
- StorageType() string
- }
- var (
- storagesFactories = make([]IStorageFactory, 0)
- )
- // for shared storages
- func registerStorageFactory(factory IStorageFactory) {
- storagesFactories = append(storagesFactories, factory)
- }
- func NewStorage(manager *SStorageManager, mountPoint, storageType string) IStorage {
- for i := range storagesFactories {
- if storageType == storagesFactories[i].StorageType() || strings.HasPrefix(mountPoint, storagesFactories[i].StorageType()) {
- return storagesFactories[i].NewStorage(manager, mountPoint)
- }
- }
- log.Errorf("no storage driver for %s found", storageType)
- return nil
- }
- type IStorage interface {
- GetId() string
- GetStorageName() string
- GetZoneId() string
- SetStorageInfo(storageId, storageName string, conf jsonutils.JSONObject) error
- SyncStorageInfo() (jsonutils.JSONObject, error)
- SyncStorageSize() (api.SHostStorageStat, error)
- StorageType() string
- GetStorageConf() *jsonutils.JSONDict
- GetStoragecacheId() string
- SetStoragecacheId(storagecacheId string)
- IsLocal() bool
- Lvmlockd() bool
- SetPath(string)
- GetPath() string
- GetSnapshotDir() string
- GetSnapshotPathByIds(diskId, snapshotId string) string
- DeleteSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
- DeleteSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
- IsSnapshotExist(diskId, snapshotId string) (bool, error)
- GetBackupDir() string
- StorageBackup(ctx context.Context, params *SStorageBackup) (jsonutils.JSONObject, error)
- StorageBackupRecovery(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
- GetFreeSizeMb() int
- GetCapacityMb() int
- // Find owner disks first, if not found, call create disk
- GetDiskById(diskId string) (IDisk, error)
- CreateDisk(diskId string) IDisk
- RemoveDisk(IDisk)
- GetDisksPath() ([]string, error)
- // DeleteDisk(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
- // *SDiskCreateByDiskinfo
- CreateDiskByDiskinfo(context.Context, interface{}) (jsonutils.JSONObject, error)
- SaveToGlance(context.Context, interface{}) (jsonutils.JSONObject, error)
- CreateDiskFromSnapshot(context.Context, IDisk, *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error)
- CreateDiskFromExistingPath(context.Context, IDisk, *SDiskCreateByDiskinfo) error
- CreateDiskFromBackup(context.Context, IDisk, *SDiskCreateByDiskinfo) error
- DiskMigrate(context.Context, interface{}) (jsonutils.JSONObject, error)
- // GetCloneTargetDiskPath generate target disk path by target disk id
- GetCloneTargetDiskPath(ctx context.Context, targetDiskId string) string
- // CloneDiskFromStorage clone disk from other storage
- CloneDiskFromStorage(ctx context.Context, srcStorage IStorage, srcDisk IDisk, targetDiskId string, fullCopy bool, encInfo apis.SEncryptInfo) (*hostapi.ServerCloneDiskFromStorageResponse, error)
- CreateSnapshotFormUrl(ctx context.Context, snapshotUrl, diskId, snapshotPath string) error
- DeleteDiskfile(diskPath string, skipRecycle bool) error
- GetFuseTmpPath() string
- GetFuseMountPath() string
- GetImgsaveBackupPath() string
- DestinationPrepareMigrate(ctx context.Context, liveMigrate bool, disksUri string, snapshotsUri string,
- disksBackingFile, diskSnapsChain, outChainSnaps jsonutils.JSONObject, rebaseDisks bool, diskDesc *desc.SGuestDisk, serverId string, idx, totalDiskCount int, encInfo *apis.SEncryptInfo, sysDiskHasTemplate bool) error
- Accessible() error
- Detach() error
- CleanRecycleDiskfiles(ctx context.Context)
- }
- type SBaseStorage struct {
- Manager *SStorageManager
- StorageId string
- Path string
- StorageName string
- StorageConf *jsonutils.JSONDict
- StoragecacheId string
- isSetStorageInfo bool
- Disks []IDisk
- DiskLock *sync.Mutex
- }
- func NewBaseStorage(manager *SStorageManager, path string) *SBaseStorage {
- var ret = new(SBaseStorage)
- ret.Disks = make([]IDisk, 0)
- ret.DiskLock = new(sync.Mutex)
- ret.Manager = manager
- ret.Path = path
- return ret
- }
- func (s *SBaseStorage) GetId() string {
- return s.StorageId
- }
- func (s *SBaseStorage) Lvmlockd() bool {
- return false
- }
- func (s *SBaseStorage) GetFuseTmpPath() string {
- return path.Join(s.Path, _FUSE_TMP_PATH_)
- }
- func (s *SBaseStorage) GetFuseMountPath() string {
- return path.Join(s.Path, _FUSE_MOUNT_PATH_)
- }
- func (s *SBaseStorage) GetStorageName() string {
- return s.StorageName
- }
- func (s *SBaseStorage) GetStoragecacheId() string {
- return s.StoragecacheId
- }
- func (s *SBaseStorage) SetStoragecacheId(storagecacheId string) {
- s.StoragecacheId = storagecacheId
- }
- func (s *SBaseStorage) StorageBackup(ctx context.Context, params *SStorageBackup) (jsonutils.JSONObject, error) {
- return nil, nil
- }
- func (s *SBaseStorage) StorageBackupRecovery(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
- return nil, nil
- }
- func (s *SBaseStorage) GetName(generateName func() string) string {
- if len(s.StorageName) > 0 {
- return s.StorageName
- } else {
- return generateName()
- }
- }
- func (s *SBaseStorage) GetPath() string {
- return s.Path
- }
- func (s *SBaseStorage) SetPath(p string) {
- s.Path = p
- }
- func (s *SBaseStorage) GetZoneId() string {
- return s.Manager.GetZoneId()
- }
- func (d *SBaseStorage) GetDisksPath() ([]string, error) {
- spath := d.GetPath()
- files, err := ioutil.ReadDir(spath)
- if err != nil {
- return nil, err
- }
- disksPath := make([]string, 0)
- for i := range files {
- if !files[i].IsDir() && regutils.MatchUUIDExact(files[i].Name()) {
- disksPath = append(disksPath, path.Join(spath, files[i].Name()))
- }
- }
- return disksPath, nil
- }
- func (s *SBaseStorage) GetCapacityMb() int {
- return s.GetAvailSizeMb()
- }
- func (s *SBaseStorage) GetStorageConf() *jsonutils.JSONDict {
- return s.StorageConf
- }
- func (s *SBaseStorage) GetAvailSizeMb() int {
- return s.GetTotalSizeMb()
- }
- func (s *SBaseStorage) GetUsedSizeMb() int {
- size, err := storageutils.GetUsedSizeMb(s.Path)
- if err != nil {
- log.Errorf("failed get %s used size: %s", s.Path, err)
- return -1
- }
- return size
- }
- /*func (s *SBaseStorage) GetMediumType() string {
- return s.Manager.GetMediumType()
- }*/
- func (s *SBaseStorage) GetFreeSizeMb() int {
- size, err := storageutils.GetFreeSizeMb(s.Path)
- if err != nil {
- log.Errorf("failed get %s free size: %s", s.Path, err)
- return -1
- }
- return size
- }
- func (s *SBaseStorage) GetTotalSizeMb() int {
- size, err := storageutils.GetTotalSizeMb(s.Path)
- if err != nil {
- log.Errorf("failed get %s total size: %s", s.Path, err)
- return -1
- }
- return size
- }
- func (s *SBaseStorage) SetStorageInfo(storageId, storageName string, conf jsonutils.JSONObject) error {
- s.StorageId = storageId
- s.StorageName = storageName
- if dconf, ok := conf.(*jsonutils.JSONDict); ok {
- s.StorageConf = dconf
- }
- s.BindMountStoragePath(s.Path)
- return nil
- }
- func (s *SBaseStorage) SyncStorageSize() (api.SHostStorageStat, error) {
- stat := api.SHostStorageStat{
- StorageId: s.StorageId,
- }
- stat.CapacityMb = int64(s.GetCapacityMb())
- stat.ActualCapacityUsedMb = int64(s.GetUsedSizeMb())
- return stat, nil
- }
- func (s *SBaseStorage) BindMountStoragePath(sPath string) error {
- if strings.HasPrefix(sPath, "/opt/cloud") {
- return nil
- }
- if s.isSetStorageInfo || !options.HostOptions.EnableRemoteExecutor {
- return nil
- }
- err := s.bindMountTo(sPath)
- if err == nil {
- s.isSetStorageInfo = true
- }
- return err
- }
- func (s *SBaseStorage) bindMountTo(sPath string) error {
- tempPath := path.Join(TempBindMountPath, sPath)
- out, err := procutils.NewCommand("mkdir", "-p", tempPath).Output()
- if err != nil {
- return errors.Errorf("mkdir temp mount path %s failed %s", tempPath, out)
- }
- out, err = procutils.NewCommand("mkdir", "-p", sPath).Output()
- if err != nil {
- return errors.Errorf("mkdir mount path %s failed %s", sPath, out)
- }
- if procutils.NewCommand("mountpoint", tempPath).Run() != nil {
- out, err = procutils.NewRemoteCommandAsFarAsPossible("mount", "--bind", sPath, tempPath).Output()
- if err != nil {
- return errors.Errorf("bind mount to temp path failed %s", out)
- }
- }
- if procutils.NewCommand("mountpoint", sPath).Run() != nil {
- out, err = procutils.NewCommand("mount", "--bind", tempPath, sPath).Output()
- if err != nil {
- return errors.Errorf("bind mount temp path to local image path failed %s", out)
- }
- }
- log.Infof("bind mount %s -> %s", tempPath, sPath)
- return nil
- }
- func (s *SBaseStorage) RemoveDisk(d IDisk) {
- s.DiskLock.Lock()
- defer s.DiskLock.Unlock()
- for i := 0; i < len(s.Disks); i++ {
- if s.Disks[i].GetId() == d.GetId() {
- s.Disks = append(s.Disks[:i], s.Disks[i+1:]...)
- break
- }
- }
- }
- func (s *SBaseStorage) DeleteDiskfile(diskpath string, skipRecycle bool) error {
- return fmt.Errorf("Not Implement")
- }
- func (s *SBaseStorage) CreateDiskByDiskinfo(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
- createParams, ok := params.(*SDiskCreateByDiskinfo)
- if !ok {
- return nil, hostutils.ParamsError
- }
- if createParams.Disk != nil {
- if !createParams.DiskInfo.Rebuild {
- return nil, fmt.Errorf("Disk exist")
- }
- if createParams.DiskInfo.Backup != nil && createParams.DiskInfo.Backup.BackupAsTar != nil {
- log.Infof("skip OnRebuildRoot step when backup_as_tar is provided")
- } else {
- if err := createParams.Disk.OnRebuildRoot(ctx, createParams.DiskInfo); err != nil {
- return nil, err
- }
- }
- }
- disk := createParams.Storage.CreateDisk(createParams.DiskId)
- if disk == nil {
- return nil, fmt.Errorf("Fail to Create disk %s", createParams.DiskId)
- }
- log.Infof("storage %s %s(%s) start create disk", createParams.Storage.StorageType(), createParams.Storage.GetStorageName(), createParams.Storage.GetId())
- switch {
- case len(createParams.DiskInfo.SnapshotId) > 0:
- log.Infof("CreateDiskFromSnpashot %s", createParams)
- return s.CreateDiskFromSnpashot(ctx, disk, createParams)
- case len(createParams.DiskInfo.ImageId) > 0 && createParams.DiskInfo.ImageFormat != imageapi.IMAGE_DISK_FORMAT_TGZ:
- log.Infof("CreateDiskFromTemplate %s", createParams)
- return s.CreateDiskFromTemplate(ctx, disk, createParams)
- case createParams.DiskInfo.Backup != nil:
- log.Infof("CreateDiskFromBackup %s", createParams)
- return s.createDiskFromBackup(ctx, disk, createParams)
- case len(createParams.DiskInfo.ExistingPath) > 0:
- return s.createDiskFromExistingPath(ctx, disk, createParams)
- case createParams.DiskInfo.DiskSizeMb > 0:
- log.Infof("CreateRawDisk %s", createParams)
- return s.CreateRawDisk(ctx, disk, createParams)
- default:
- return nil, fmt.Errorf("CreateDiskByDiskinfo with params %s empty snapshot_id, image_id or disk_size_mb", jsonutils.Marshal(createParams.DiskInfo))
- }
- }
- func (s *SBaseStorage) CreateRawDisk(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
- var encryptInfo *apis.SEncryptInfo
- if input.DiskInfo.Encryption {
- encryptInfo = &input.DiskInfo.EncryptInfo
- }
- return disk.CreateRaw(ctx, input.DiskInfo.DiskSizeMb,
- input.DiskInfo.Format,
- input.DiskInfo.FsFormat,
- input.DiskInfo.FsFeatures,
- encryptInfo, input.DiskId, "")
- }
- func (s *SBaseStorage) CreateDiskFromTemplate(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
- var (
- format = "qcow2" // force qcow2
- )
- var encryptInfo *apis.SEncryptInfo
- if input.DiskInfo.Encryption {
- encryptInfo = &input.DiskInfo.EncryptInfo
- }
- return disk.CreateFromTemplate(ctx, input.DiskInfo.ImageId, format, int64(input.DiskInfo.DiskSizeMb), encryptInfo)
- }
- func (s *SBaseStorage) CreateDiskFromSnpashot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
- var storage = input.Storage
- if len(input.DiskInfo.SnapshotUrl) == 0 {
- return nil, httperrors.NewMissingParameterError("snapshot_url")
- }
- return storage.CreateDiskFromSnapshot(ctx, disk, input)
- }
- func (s *SBaseStorage) createDiskFromExistingPath(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
- var storage = input.Storage
- if len(input.DiskInfo.ExistingPath) == 0 {
- return nil, httperrors.NewMissingParameterError("existing_path")
- }
- if !strings.HasPrefix(input.DiskInfo.ExistingPath, storage.GetPath()) {
- return nil, errors.Errorf("disk %s not in storage %s", input.DiskInfo.ExistingPath, storage.GetPath())
- }
- err := storage.CreateDiskFromExistingPath(ctx, disk, input)
- if err != nil {
- return nil, errors.Wrapf(err, "CreateDiskFromExistingPath")
- }
- return disk.GetDiskDesc(), nil
- }
- func (s *SBaseStorage) createDiskFromBackup(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) (jsonutils.JSONObject, error) {
- var storage = input.Storage
- if input.DiskInfo.Backup == nil {
- return nil, httperrors.NewMissingParameterError("Backup")
- }
- err := storage.CreateDiskFromBackup(ctx, disk, input)
- if err != nil {
- return nil, errors.Wrapf(err, "CreateDiskFromBackup")
- }
- return disk.GetDiskDesc(), nil
- }
- func (s *SBaseStorage) DestinationPrepareMigrate(
- ctx context.Context, liveMigrate bool, disksUri string, snapshotsUri string,
- disksBackingFile, diskSnapsChain, outChainSnaps jsonutils.JSONObject, rebaseDisks bool, diskinfo *desc.SGuestDisk, serverId string, idx, totalDiskCount int, encInfo *apis.SEncryptInfo, sysDiskHasTemplate bool,
- ) error {
- return nil
- }
- func (s *SBaseStorage) GetCloneTargetDiskPath(ctx context.Context, targetDiskId string) string {
- return ""
- }
- func (s *SBaseStorage) CloneDiskFromStorage(
- ctx context.Context, srcStorage IStorage, srcDisk IDisk, targetDiskId string, fullCopy bool, encInfo apis.SEncryptInfo,
- ) (*hostapi.ServerCloneDiskFromStorageResponse, error) {
- return nil, httperrors.ErrNotImplemented
- }
- func (s *SBaseStorage) GetBackupDir() string {
- return path.Join(s.Path, _BACKUP_PATH_)
- }
- func (s *SBaseStorage) CreateDiskFromBackup(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) error {
- info := input.DiskInfo
- backupPath := path.Join(s.GetBackupDir(), info.Backup.BackupId)
- img, err := qemuimg.NewQemuImage(backupPath)
- if err != nil {
- log.Errorf("unable to new qemu image for %s: %s", backupPath, err.Error())
- return errors.Wrapf(err, "unable to new qemu image for %s", backupPath)
- }
- _, err = img.Clone(disk.GetPath(), qemuimgfmt.QCOW2, false)
- return err
- }
- func (s *SBaseStorage) DiskMigrate(context.Context, interface{}) (jsonutils.JSONObject, error) {
- return nil, httperrors.ErrNotImplemented
- }
- func (s *SBaseStorage) onSaveToGlanceFailed(ctx context.Context, imageId string, reason string) {
- params := jsonutils.NewDict()
- params.Set("status", jsonutils.NewString("killed"))
- params.Set("reason", jsonutils.NewString(reason))
- _, err := image.Images.PerformAction(
- hostutils.GetImageSession(ctx),
- imageId, "update-status", params,
- )
- if err != nil {
- log.Errorln(err)
- }
- }
- func releaseInfoToParams(relInfo *deployapi.ReleaseInfo, params *jsonutils.JSONDict) {
- if relInfo != nil {
- params.Set("os_distribution", jsonutils.NewString(relInfo.Distro))
- if len(relInfo.Version) > 0 {
- params.Set("os_version", jsonutils.NewString(relInfo.Version))
- }
- if len(relInfo.Arch) > 0 {
- params.Set("os_arch", jsonutils.NewString(relInfo.Arch))
- }
- if len(relInfo.Version) > 0 {
- params.Set("os_language", jsonutils.NewString(relInfo.Language))
- }
- if len(relInfo.CurrentVersion) > 0 {
- params.Set("os_current_version", jsonutils.NewString(relInfo.CurrentVersion))
- }
- }
- }
- func requestDeleteSnapshot(
- storage IStorage, diskId, snapshotPath, deleteSnapshot, convertSnapshotPath,
- outfile string, pendingDelete bool,
- ) {
- deleteSnapshotPath := path.Join(snapshotPath, deleteSnapshot)
- DELETEING_SNAPSHOTS.Store(diskId, true)
- defer DELETEING_SNAPSHOTS.Delete(diskId)
- _, err := modules.Snapshots.PerformAction(hostutils.GetComputeSession(context.Background()),
- deleteSnapshot, "deleted", nil)
- if err != nil {
- log.Errorln(err)
- return
- }
- if out, err := procutils.NewCommand("rm", "-f", convertSnapshotPath).Output(); err != nil {
- log.Errorf("%s", out)
- return
- }
- if out, err := procutils.NewCommand("mv", "-f", outfile, convertSnapshotPath).Output(); err != nil {
- log.Errorf("%s", out)
- return
- }
- if !pendingDelete {
- if err := storage.DeleteDiskfile(deleteSnapshotPath, false); err != nil {
- log.Errorln(err)
- return
- }
- }
- }
|