diff --git a/pkg/cloudcommon/storagetypes/storagetypes.go b/pkg/cloudcommon/storagetypes/storagetypes.go new file mode 100644 index 0000000000..eaf96c2a1e --- /dev/null +++ b/pkg/cloudcommon/storagetypes/storagetypes.go @@ -0,0 +1,13 @@ +package storagetypes + +// TODO: move models/storages.go storage types to this file +const ( + STORAGE_LOCAL = "local" + STORAGE_BAREMETAL = "baremetal" + STORAGE_SHEEPDOG = "sheepdog" + STORAGE_RBD = "rbd" + STORAGE_DOCKER = "docker" + STORAGE_NAS = "nas" + STORAGE_VSAN = "vsan" + STORAGE_NFS = "nfs" +) diff --git a/pkg/cloudcommon/workmanager/manager.go b/pkg/cloudcommon/workmanager/manager.go index e8a7ff5497..9d18686653 100644 --- a/pkg/cloudcommon/workmanager/manager.go +++ b/pkg/cloudcommon/workmanager/manager.go @@ -61,7 +61,3 @@ func (w *SWorkManager) Stop() { func NewWorkManger() *SWorkManager { return &SWorkManager{} } - -// type ITaskPramas interface { -// To -// } diff --git a/pkg/hostman/guestman/guesthandler.go b/pkg/hostman/guestman/guesthandler.go index c5b7765920..1f0667b0fe 100644 --- a/pkg/hostman/guestman/guesthandler.go +++ b/pkg/hostman/guestman/guesthandler.go @@ -59,7 +59,6 @@ func deleteGuest(ctx context.Context, w http.ResponseWriter, r *http.Request) { if err != nil { response(ctx, w, err) } else { - // TODO: CleanGuest wm.DelayTask(ctx, guest.CleanGuest, migrated) response(ctx, w, map[string]bool{"delay_clean": true}) } diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index b9492ce665..bfac06509e 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -23,6 +23,8 @@ import ( "yunion.io/x/pkg/util/seclib" ) +const VNC_PORT_BASE = 5900 + type SGuestManager struct { ServersPath string Servers map[string]*SKVMGuestInstance @@ -282,12 +284,10 @@ func (m *SGuestManager) Delete(sid string) (*SKVMGuestInstance, error) { func (m *SGuestManager) Start(ctx context.Context, sid string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { if guest, ok := m.Servers[sid]; ok { if desc, err := body.Get("desc"); err != nil { - // TODO guest.SaveDesc(desc) } if guest.IsStopped() { params, _ := body.Get("params") - // TODO if err := guest.StartGuest(ctx, params); err != nil { return nil, httperrors.NewBadRequestError("Failed to start server") } else { @@ -309,6 +309,26 @@ func (m *SGuestManager) Start(ctx context.Context, sid string, body jsonutils.JS } } +func (m *SGuestManager) GetFreeVncPort() int64 { + vncPorts := make(map[int]struct{}, 0) + for _, guest := range m.Servers { + inUsePort := guest.GetVncPort() + if inUsePort > 0 { + vncPorts[inUsePort] = struct{}{} + } + } + var port = 1 + for { + if _, ok := vncPorts[port]; !ok && !cloudcommon.IsTcpPortUsed("0.0.0.0", VNC_PORT_BASE+port) && + !cloudcommon.IsTcpPortUsed("0.0.0.0", MONITOR_PORT_BASE+port) { + break + } else { + port += 1 + } + } + return port +} + func initGuestManager(serversPath string) { if guestManger == nil { guestManger = NewGuestManager(serversPath) diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/kvm.go similarity index 87% rename from pkg/hostman/guestman/qemu-kvm.go rename to pkg/hostman/guestman/kvm.go index 7a1da1bbdd..473eefe0ab 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/kvm.go @@ -18,6 +18,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon" "yunion.io/x/onecloud/pkg/cloudcommon/httpclients" + "yunion.io/x/onecloud/pkg/cloudcommon/storagetypes" "yunion.io/x/onecloud/pkg/hostman/guestfs" "yunion.io/x/onecloud/pkg/hostman/hostinfo" "yunion.io/x/onecloud/pkg/hostman/monitor" @@ -169,19 +170,35 @@ func (s *SKVMGuestInstance) DirtyServerRequestStart() { } } -// Must called in new goroutine -func (s *SKVMGuestInstance) asyncScriptStart(ctx context.Context, params *jsonutils.JSONDict) { - // TODO - // hostinof.instace().clean_deleted_ports +// Delay Process +func (s *SKVMGuestInstance) asyncScriptStart(ctx context.Context, params interface{}) { + data, ok := params.(*jsonutils.JSONDict) + if !ok { + log.Errorln("asyncScriptStart params error") + return + } + + // TODO hostinof.instace().clean_deleted_ports + time.Sleep(100 * time.Millisecond) var isStarted, tried, err = false, 0, nil for !isStarted && tried < MAX_TRY { tried += 1 + vncPort := s.manager.GetFreeVncPort() - s.saveVncPort(vncPort) - params.Set("vnc_port", jsonutils.NewInt(vncPort)) - s.saveScripts(params) - isStarted, err = s.scriptStart() + if err = s.saveVncPort(vncPort); err != nil { + goto finally + } else { + data.Set("vnc_port", jsonutils.NewInt(vncPort)) + } + + if err = s.saveScripts(data); err != nil { + goto finally + } else { + isStarted, err = s.scriptStart() + } + + finally: if !isStarted { log.Errorf("Start VM failed: %s", err) time.Sleep((1 << (tried - 1)) * time.Seconde) @@ -189,6 +206,7 @@ func (s *SKVMGuestInstance) asyncScriptStart(ctx context.Context, params *jsonut log.Infof("VM started ...") } } + s.onAsyncScriptStart(ctx, isStarted, err) } @@ -205,6 +223,29 @@ func (s *SKVMGuestInstance) onAsyncScriptStart(ctx context.Context, isStarted bo } } +func (s *SKVMGuestInstance) saveScripts(data *jsonutils.JSONDict) error { + startScript, err := s.generateStartScript(data) + if err != nil { + return err + } + if err := cloudcommon.FilePutContents(s.GetStartScriptPath, startScript, false); err != nil { + return err + } + stopScript, err := s.generateStopScript(data) + if err != nil { + return err + } + return cloudcommon.FilePutContents(s.GetStopScriptPath, stopScript, false) +} + +func (s *SKVMGuestInstance) GetStartScriptPath() string { + return path.Join(s.HomeDir(), "startvm") +} + +func (s *SKVMGuestInstance) GetStopScriptPath() string { + return path.Join(s.HomeDir(), "stopvm") +} + func (s *SKVMGuestInstance) ImportServer(pendingDelete bool) { if s.IsDirtyShotdown() && !pendingDelete { log.Infof("Server dirty shotdown %s", s.GetName()) @@ -334,6 +375,10 @@ func (s *SKVMGuestInstance) GetVncPort() int { return -1 } +func (s *SKVMGuestInstance) saveVncPort(port int64) error { + return cloudcommon.FilePutContents(s.GetVncFilePath(), fmt.Sprintf("%d", port), false) +} + func (s *SKVMGuestInstance) SyncStatus() { if s.IsRunning() { s.monitor.GetBlockJobs(s.CheckBlockOrRunning) @@ -360,9 +405,7 @@ func (s *SKVMGuestInstance) SaveDesc(desc jsonutils.JSONObject) error { } func (s *SKVMGuestInstance) StartGuest(ctx context.Context, params jsonutils.JSONObject) { - wm.DelayTask(func() { - s.asyncScriptStart(ctx, params) - }) + wm.DelayTask(ctx, s.asyncScriptStart, params) } func (s *SKVMGuestInstance) DeployFs(deployInfo *guestfs.SDeployInfo) (jsonutils.JSONObject, error) { @@ -372,12 +415,13 @@ func (s *SKVMGuestInstance) DeployFs(deployInfo *guestfs.SDeployInfo) (jsonutils diskId, _ := disks[0].GetString("disk_id") disk := hostinfo.GetStorageManager().GetStorageDisk(storageId, diskId) - return disk.DeployGuestFs(s.Desc, deployInfo) + return disk.DeployGuestFs(disk.GetPath, s.Desc, deployInfo) } else { return nil, fmt.Errorf("Guest dosen't have disk ??") } } +// Delay process func (s *SKVMGuestInstance) CleanGuest(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { migrated, ok := params.(bool) if !ok { @@ -443,8 +487,12 @@ func (s *SKVMGuestInstance) delTmpDisks(ctx context.Context, migrated bool) { // TODO GetDisksByPath, storagetypes, deleteallsnapshot, delete d := hostinfo.GetStorageManager().GetDiskByPath(diskPath) if d != nil && d.GetType == storagetypes.STORAGE_LOCAL && migrated { - d.DeleteAllSnapshot() - d.Delete(ctx) + if err := d.DeleteAllSnapshot(); err != nil { + log.Errorln(err) + } + if err := d.Delete(ctx); err != nil { + log.Errorln(err) + } } } } diff --git a/pkg/hostman/guestman/kvmhelper.go b/pkg/hostman/guestman/kvmhelper.go new file mode 100644 index 0000000000..bf434d9049 --- /dev/null +++ b/pkg/hostman/guestman/kvmhelper.go @@ -0,0 +1,45 @@ +package guestman + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/hostman/options" +) + +var ( + OS_NAME_LINUX = "Linux" + OS_NAME_WINDOWS = "Windows" + OS_NAME_MACOS = "macOS" + OS_NAME_ANDROID = "Android" + OS_NAME_VMWARE = "VMWare" +) + +func (s *SKVMGuestInstance) getOsname() string { + if s.Desc.Contains("metadata") { + metadata, _ := s.Desc.Get("metadata") + if metadata.Contains("os_name") { + osname, _ := metadata.GetString("os_name") + return osname + } + } + return OS_NAME_LINUX +} + +func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) (string, error) { + osname := s.GetOsname() + if osname == OS_NAME_MACOS { + s.Desc.Set("machine", jsonutils.NewString("q35")) + s.Desc.Set("bios", jsonutils.NewString("UEFI")) + } + + vncPort, _ := data.Int("vnc_port") + + qemuVersion := options.HostOptions.DefaultQemuVersion + if data.Contains("qemu_version") { + qemuVersion, _ := data.GetString("qemu_version") + } + if qemuVersion == "latest" { + qemuVersion = "" + } + + // TODO: isolatedDevsParams := hostinfo.Instance()... +} diff --git a/pkg/hostman/options/options.go b/pkg/hostman/options/options.go index f83ea98b3c..2828a40433 100644 --- a/pkg/hostman/options/options.go +++ b/pkg/hostman/options/options.go @@ -51,6 +51,8 @@ type SHostOptions struct { PrivatePrefixes []string `help:"IPv4 private prefixes"` LocalImagePath []string `help:"Local image storage paths"` SharedStorages []string `help:"Path of shared storages"` + + DefaultQemuVersion string `help:"Default qemu version" default:"2.9.1"` } var HostOptions SHostOptions diff --git a/pkg/hostman/storageman/diskbase.go b/pkg/hostman/storageman/diskbase.go index 5f63b7ad3d..c9a1cfdf22 100644 --- a/pkg/hostman/storageman/diskbase.go +++ b/pkg/hostman/storageman/diskbase.go @@ -1,6 +1,7 @@ package storageman import ( + "context" "fmt" "path" @@ -12,8 +13,18 @@ import ( type IDisk interface { GetId() string Probe() bool - DeployGuestFs(guestDesc *jsonutils.JSONDict, - deployInfo *guestfs.SDeployInfo) (jsonutils.JSONObject, error) + + DeleteAllSnapshot() error + Delete() error + + GetPath() string + CreateFromUrl(context.Context, string) error + // CreateFromSnapshot + CreateFromTemplate(context.Context, string, string, int64) error + Resize(context.Context, int64) error + + // @params: diskPath, guestDesc, deployInfo + DeployGuestFs(string, *jsonutils.JSONDict, *guestfs.SDeployInfo) (jsonutils.JSONObject, error) } type SBaseDisk struct { @@ -28,14 +39,37 @@ func NewBaseDisk(storage IStorage, id string) *SBaseDisk { return ret } -func (d *SBaseDisk) getPath() string { +func (d *SBaseDisk) GetId() string { + return d.Id +} + +func (d *SBaseDisk) GetPath() string { return path.Join(d.Storage.GetPath(), d.Id) } -func (d *SBaseDisk) DeployGuestFs( - guestDesc *jsonutils.JSONDict, +func (d *SBaseDisk) Probe() error { + return fmt.Errorf("Not implemented") +} + +func (d *SBaseDisk) Delete() error { + return fmt.Errorf("Not implemented") +} + +func (d *SBaseDisk) CreateFromUrl(context.Context, string) error { + return fmt.Errorf("Not implemented") +} + +func (d *SBaseDisk) CreateFromTemplate(context.Context, string, string, int64) error { + return fmt.Errorf("Not implemented") +} + +func (d *SBaseDisk) Resize(context.Context, int64) error { + return fmt.Errorf("Not implemented") +} + +func (d *SBaseDisk) DeployGuestFs(diskPath string, guestDesc *jsonutils.JSONDict, deployInfo *guestfs.SDeployInfo) (jsonutils.JSONObject, error) { - var kvmDisk = NewKVMGuestDisk(d.getPath()) + var kvmDisk = NewKVMGuestDisk(diskPath) if kvmDisk.Connect() { defer kvmDisk.Disconnect() log.Infof("Kvm Disk Connect Success !!") diff --git a/pkg/hostman/storageman/disklocal.go b/pkg/hostman/storageman/disklocal.go new file mode 100644 index 0000000000..dab3ff7841 --- /dev/null +++ b/pkg/hostman/storageman/disklocal.go @@ -0,0 +1,70 @@ +package storageman + +import ( + "fmt" + "os" + "path" + + "yunion.io/x/log" +) + +var _ALTER_SUFFIX_ = ".alter" + +type SLocalDisk struct { + SBaseDisk + isAlter bool +} + +func NewLocalDisk(storage IStorage, id string) *SLocalDisk { + var ret = new(SLocalDisk) + ret.SBaseDisk = *NewBaseDisk(storage, id) + return ret +} + +func (d *SLocalDisk) getPath() string { + return path.Join(d.Storage.GetPath(), d.Id) +} + +func (d *SLocalDisk) getAlterPath() string { + return path.Join(d.Storage.GetPath(), d.Id, _ALTER_SUFFIX_) +} + +func (d *SLocalDisk) GetPath() string { + if d.isAlter { + return d.getAlterPath() + } else { + return d.getPath() + } +} + +func (d *SLocalDisk) Probe() error { + if _, err := os.Stat(d.getPath()); !os.IsNotExist(err) { + d.isAlter = false + return nil + } else if _, err := os.Stat(d.getAlterPath()); !os.IsNotExist(err) { + d.isAlter = true + return nil + } + return fmt.Errorf("Disk not found") +} + +func (d *SLocalDisk) Delete() error { + dpath := d.GetPath() + log.Infof("Delete guest disk %s", dpath) + if err := d.Storage.DeleteDiskfile(dpath); err != nil { + return err + } + // TODO: PostCreateFromImageFuse umount fuse fs + d.UmountImageFuse() + /* ???????????????? + files = os.listdir(self.storage.path) + for f in files: + if f.startswith(self.id): + if not re.match(r'[a-z0-9\-]*\.\d{14}', f): + path = os.path.join(self.storage.path, f) + print 'delete backing-file:', path + self.storage.delete_diskfile(path) + */ + d.Storage.RemoveDisk(d) + return nil +} diff --git a/pkg/hostman/storageman/diskutils.go b/pkg/hostman/storageman/diskutils.go index 3e7ac3b433..1ad02da48b 100644 --- a/pkg/hostman/storageman/diskutils.go +++ b/pkg/hostman/storageman/diskutils.go @@ -10,6 +10,7 @@ import ( "time" "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/qemutils" "yunion.io/x/onecloud/pkg/hostman/guestfs" ) diff --git a/pkg/hostman/storageman/imagecachebase.go b/pkg/hostman/storageman/imagecachebase.go new file mode 100644 index 0000000000..c64f1c480a --- /dev/null +++ b/pkg/hostman/storageman/imagecachebase.go @@ -0,0 +1,46 @@ +package storageman + +import "context" + +type IImageCache interface { + GetPath() string + Load() error + Acquire(context.Context, string, string) error + Release() + Remove() error + GetImageId() string +} + +type SImageCacheDesc struct { + name string + format string + id string + chksum string + path string + size int64 +} + +type SLocalImageCache struct { + imageId string + Manager IImagecacheManager + Size int64 + Desc *SImageCacheDesc + + consumerCount int +} + +func NewLocalImageCache(imageId string, imagecacheManager IImageCacheManger) *SLocalImageCache { + imageCache := new(SLocalImageCache) + imageCache.imageId = imageId + imageCache.Manager = imagecacheManager + return imageCache +} + +func (l *SLocalImageCache) Load() error { + // TODO +} + +type SRbdImageCache struct { + imageId string + Manager IImagecacheManager +} diff --git a/pkg/hostman/storageman/imagecachemanager.go b/pkg/hostman/storageman/imagecachemanager.go new file mode 100644 index 0000000000..b0694ebb37 --- /dev/null +++ b/pkg/hostman/storageman/imagecachemanager.go @@ -0,0 +1,86 @@ +package storageman + +import ( + "io/ioutil" + "os" + "os/exec" + "sync" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/util/regutils" +) + +type IImageCacheManger interface { + // LoadCache() error ??? + PrefetchImageCache(ctx, data jsonutils.JSONObject) error + DeleteImageCache(ctx, data jsonutils.JSONObject) error +} + +type SBaseImageCacheManager struct { + storagemanager *SStorageManager + storagecacaheId string + cachePath string + cachedImages map[string]IImageCache + mutex *sync.Mutex +} + +type SLocalImageCacheManager struct { + SBaseImageCacheManager + limit int + isTemplate bool +} + +func NewLocalImageCacheManager(manager *SStorageManager, cachePath string, limit int, isTemplete bool, storagecacheId string) *SLocalImageCacheManager { + imageCacheManager := new(SLocalImageCacheManager) + imageCacheManager.storagemanager = manager + imageCacheManager.storagecacaheId = storagecacheId + imageCacheManager.cachePath = cachePath + imageCacheManager.limit = limit + imageCacheManager.isTemplate = isTemplete + imageCacheManager.cachedImages = make(map[string]IImageCache, 0) + imageCacheManager.mutex = new(sync.Mutex) + if _, err := os.Stat(cachePath); os.IsNotExist(err) { + exec.Command("mkdir", "-p", cachePath).Run() + } + imageCacheManager.loadCache() +} + +func (c *SLocalImageCacheManager) loadCache() { + if len(c.cachePath) == 0 { + return + } + c.mutex.Lock() + defer c.mutex.Unlock() + files, _ := ioutil.ReadDir(c.cachePath) + for _, f := range files { + if regutils.MatchUUIDExact(f.Name()) { + c.loadImageCache(f) + } + } +} + +func (c *SLocalImageCacheManager) loadImageCache(file string) { + imageCache := NewLocalImageCache(file, c) + if err := imageCache.Load(); err != nil { + c.cachedImages[imageCache.GetImageId()] = imageCache + } +} + +// TODO: AgentImageCacheManager +type SAgentImageCacheManager struct { + storagemanager *SStorageManager +} + +func NewAgentImageCacheManager(storagemanager *SStorageManager) *SAgentImageCacheManager { + return &SAgentImageCacheManager{storagemanager} +} + +type SRbdImageCacheManager struct { + SBaseImageCacheManager + pool, prefix string + storage IStorage +} + +func NewRbdImageCacheManager() *SRbdImageCacheManager { + // TODO +} diff --git a/pkg/hostman/storageman/localdisk.go b/pkg/hostman/storageman/localdisk.go deleted file mode 100644 index 1e387a312a..0000000000 --- a/pkg/hostman/storageman/localdisk.go +++ /dev/null @@ -1,25 +0,0 @@ -package storageman - -import "os" - -type SLocalDisk struct { - *SBaseDisk -} - -func NewLocalDisk(storage IStorage, id string) *SLocalDisk { - var ret = new(SLocalDisk) - ret.SBaseDisk = NewBaseDisk(storage, id) - return ret -} - -func (d *SLocalDisk) GetId() string { - return d.Id -} - -func (d *SLocalDisk) Probe() bool { - if _, err := os.Stat(d.getPath()); !os.IsNotExist(err) { - return true - } - // TODO alter ?? - return false -} diff --git a/pkg/hostman/storageman/storagebase.go b/pkg/hostman/storageman/storagebase.go index 76b94ee5da..96cb9c921b 100644 --- a/pkg/hostman/storageman/storagebase.go +++ b/pkg/hostman/storageman/storagebase.go @@ -9,6 +9,7 @@ import ( type IStorage interface { StorageType() string GetPath() string + GetFreeSizeMb() int // Find owner disks first, if not found, call create disk GetDiskById(diskId string) IDisk diff --git a/pkg/hostman/storageman/localstorage.go b/pkg/hostman/storageman/storagelocal.go similarity index 77% rename from pkg/hostman/storageman/localstorage.go rename to pkg/hostman/storageman/storagelocal.go index 2a9e52b90f..525b652d51 100644 --- a/pkg/hostman/storageman/localstorage.go +++ b/pkg/hostman/storageman/storagelocal.go @@ -1,18 +1,20 @@ package storageman +import "yunion.io/x/log" + type SLocalStorage struct { - *SBaseStorage + SBaseStorage } func NewLocalStorage(manager *SStorageManager, path string) *SLocalStorage { var ret = new(SLocalStorage) - ret.SBaseStorage = NewBaseStorage(manager, path) + ret.SBaseStorage = *NewBaseStorage(manager, path) ret.StartSnapshotRecycle() return ret } func (s *SLocalStorage) StorageType() string { - return "local" + return storagetypes.STORAGE_LOCAL } func (s *SLocalStorage) GetDiskById(diskId string) IDisk { @@ -28,11 +30,13 @@ func (s *SLocalStorage) CreateDisk(diskId string) IDisk { s.DiskLock.Lock() defer s.DiskLock.Unlock() var disk = NewLocalDisk(s, diskId) - if disk.Probe() { + if err := disk.Probe(); err != nil { s.Disks = append(s.Disks, disk) return disk + } else { + log.Errorln(err) + return nil } - return nil } func (s *SLocalStorage) StartSnapshotRecycle() { diff --git a/pkg/hostman/storageman/storagemanager.go b/pkg/hostman/storageman/storagemanager.go index 95b2ff7576..a55ab407cf 100644 --- a/pkg/hostman/storageman/storagemanager.go +++ b/pkg/hostman/storageman/storagemanager.go @@ -1,22 +1,25 @@ package storageman import ( - "yunion.io/x/log" + "fmt" + "os" + "path" + "strings" + "yunion.io/x/onecloud/pkg/hostman/options" ) -/***************************************************************************/ -/****************************StorageManager*********************************/ -/***************************************************************************/ - const MINIMAL_FREE_SPACE = 128 type SStorageManager struct { - storages []IStorage - LocalStorageImagecache IStorageCache + storages []IStorage + AgentStorage IStorage + LocalStorageImagecacheManager IImageCacheManger + AgentStorageImagecacheManager IImageCacheManger + NfsStorageImagecacheMangers []IImageCacheManger } -func NewStorageManager() *SStorageManager { +func NewStorageManager() (*SStorageManager, error) { var ret = new(SStorageManager) ret.storages = make([]IStorage, 0) var allFull = true @@ -36,12 +39,97 @@ func NewStorageManager() *SStorageManager { allFull = False } if allFull { - log.Fatalf("Not enough storage space!") + return nil, fmt.Errorf("Not enough storage space!") + } + if err := ret.initLocalStorageImagecache(); err != nil { + return nil, fmt.Errorf("Init Local storage image cache failed: %s", err) } - ret.initLocalStorageImagecache() ret.initAgentStorageImagecache() - ret.initAgentStorage() - return ret + if err := ret.initAgentStorage(); err != nil { + return nil, fmt.Errorf("Init agent storage failed: %s", err) + } + return ret, nil +} + +func (s *SStorageManager) getLeasedUsedLocalStorage(cacheDir string, limit int) (string, error) { + var maxFree int + var spath string + var maxStorage IStorage + for _, storage := range s.storages { + if _, ok := storage.(*SLocalStorage); ok { + cachePath := path.Join(storage.GetPath(), cacheDir) + if _, err := os.Stat(cachePath); !os.IsNotExist(err) { + spath = cachePath + break + } + free := storage.GetFreeSizeMb() + if maxFree < free { + maxFree = free + maxStorage = storage + } + } + } + if len(spath) == 0 { + if maxFree >= limit*1024 { + spath = path.Join(maxStorage.GetPath(), cacheDir) + } else { + return "", fmt.Errorf("No local storage has free space larger than %dGB", limit) + } + } + return spath, nil +} + +func (s *SStorageManager) initLocalStorageImagecache() error { + var cacheDir = "image_cache" + cachePath := options.HostOptions.ImageCachePath + limit := options.HostOptions.ImageCacheLimit + if len(cachePath) == 0 { + var err error + cachePath, err = s.getLeasedUsedLocalStorage(cacheDir, limit) + if err != nil { + return err + } + } + if len(cachePath) == 0 { + s.LocalStorageImagecacheManager = NewLocalImageCacheManager(s, cachePath, limit, true, "") + return nil + } else { + fmt.Errorf("Cannot allocate image cache storage") + } +} + +func (s *SStorageManager) initAgentStorageImagecache() { + s.AgentStorageImagecacheManager = NewAgentImageCacheManager(s) +} + +func (s *SStorageManager) initAgentStorage() error { + var cacheDir = "agent_tmp" + var spath = options.HostOptions.AgentTempPath + var limit = options.HostOptions.AgentTempLimit + if len(spath) == 0 { + var err error + spath, err = s.getLeasedUsedLocalStorage(cacheDir, limit) + if err != nil { + return err + } + } + if len(spath) != nil { + // TODO: NewAgentStorage + s.AgentStorage = NewAgentStorage(s, spath) + } else { + return fmt.Errorf("Cannot allocate agent storage") + } +} + +func (s *SStorageManager) AddNfsStorage(storagecacheId, cachePath string) { + if len(cachePath) == 0 { + return + } + if s.NfsStorageImagecacheMangers == nil { + s.NfsStorageImagecacheMangers = make(map[string]IImageCacheManger, 0) + } + s.NfsStorageImagecacheMangers[storagecacheId] = NewLocalImageCacheManager(s, cachePath, + options.HostOptions.ImageCacheLimit, true, storagecacheId) } func (s *SStorageManager) GetStorageDisk(storageId, diskId string) IDisk { @@ -51,17 +139,26 @@ func (s *SStorageManager) GetStorageDisk(storageId, diskId string) IDisk { return nil } -func (s *SStorageManager) initLocalStorageImagecache() { - var cacheDir = "image_cache" - cachePath := options.HostOptions.ImageCachePath - limit := options.HostOptions.ImageCacheLimit - if len(cachePath) == 0 { - cachePath = s.getLeasedUsedLocalStorage(cacheDir, limit) - } - if len(cachePath) == 0 { - // TODO NewLocalImageCacheManager - s.LocalStorageImagecache = NewLocalImageCacheManager(s, cachePath, limit, true) - } else { - log.Fatalf("Cannot allocate image cache storage") +func (s *SStorageManager) GetStorageByPath(sPath string) IStorage { + for _, storage := range s.storages { + if storage.GetPath() == sPath { + return storage + } } + return nil +} + +func (s *SStorageManager) GetDiskByPath(diskPath string) IDisk { + pos := strings.LastIndex(diskPath, "/") + sPath := path[:pos] + diskId := path[pos+1:] + pos = strings.LastIndex(diskId, ".") + if pos > 0 { + diskId = diskId[:pos] + } + storage := s.GetStorageByPath(sPath) + if storage != nil { + return storage.GetDiskById(diskId) + } + return nil }