mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
fix(region): pve create
This commit is contained in:
@@ -83,7 +83,7 @@ require (
|
||||
k8s.io/client-go v0.19.3
|
||||
k8s.io/cluster-bootstrap v0.19.3
|
||||
moul.io/http2curl/v2 v2.3.0
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20230627023024-a5d9d998e793
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20230629073725-ad3f1b174b7a
|
||||
yunion.io/x/executor v0.0.0-20211018100936-39a2cd966656
|
||||
yunion.io/x/jsonutils v1.0.1-0.20230613121553-0f3b41e2ef19
|
||||
yunion.io/x/log v1.0.1-0.20230411060016-feb3f46ab361
|
||||
|
||||
@@ -1173,8 +1173,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20230627023024-a5d9d998e793 h1:743vsYYy7PoEV0ksv9dWtPfzaoNSLQI5phOuTu3efPQ=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20230627023024-a5d9d998e793/go.mod h1:crMeQeaNaZefTXfXbQkoj5SStggqkSNVABHtYBFjM3Y=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20230629073725-ad3f1b174b7a h1:5wnOJ8TiD8njxrOS6UrtSkit7nrf5sEcGRRs0gpqMkg=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20230629073725-ad3f1b174b7a/go.mod h1:crMeQeaNaZefTXfXbQkoj5SStggqkSNVABHtYBFjM3Y=
|
||||
yunion.io/x/executor v0.0.0-20211018100936-39a2cd966656 h1:0zlZD5uhZoIHgLVAWCz2aHaYk2ZrNsACCYD7R6EIBII=
|
||||
yunion.io/x/executor v0.0.0-20211018100936-39a2cd966656/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
|
||||
|
||||
@@ -35,4 +35,5 @@ const (
|
||||
APSARA = "apsara"
|
||||
JDCLOUD = "jdcloud"
|
||||
CLOUDPODS = "cloudpods"
|
||||
PROXMOX = "proxmox"
|
||||
)
|
||||
|
||||
@@ -139,6 +139,25 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.ExternalImageId) == 0 {
|
||||
image, err := func() (*models.SCachedimage, error) {
|
||||
cdrom := guest.GetCdrom()
|
||||
if cdrom == nil {
|
||||
return nil, fmt.Errorf("empty cdrom")
|
||||
}
|
||||
return cdrom.GetImage()
|
||||
}()
|
||||
if err != nil {
|
||||
log.Errorf("get cachdimage error: %v", err)
|
||||
} else {
|
||||
config.ExternalImageId = image.ExternalId
|
||||
config.OsDistribution, _ = image.Info.GetString("properties", "os_distribution")
|
||||
config.OsVersion, _ = image.Info.GetString("properties", "os_version")
|
||||
config.OsType, _ = image.Info.GetString("properties", "os_type")
|
||||
config.ImageType = image.ImageType
|
||||
}
|
||||
}
|
||||
|
||||
// 避免因同步包年包月实例billing_cycle失败,导致重置虚拟机密码异常
|
||||
if guest.BillingType == billing_api.BILLING_TYPE_PREPAID && len(guest.BillingCycle) > 0 {
|
||||
bc, err := billing.ParseBillingCycle(guest.BillingCycle)
|
||||
@@ -639,6 +658,7 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte
|
||||
return false, errors.Wrap(err, "iVM.GetIDisks")
|
||||
}
|
||||
ret = len(idisks)
|
||||
log.Debugf("wait vm disk ready, expect %d disks, return %d disks", expect, ret)
|
||||
if ret >= expect { // 有可能自定义镜像里面也有磁盘,会导致返回的磁盘多于创建时的磁盘
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@ package guestdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/pkg/util/cloudinit"
|
||||
"yunion.io/x/pkg/util/rbacscope"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
@@ -85,6 +88,16 @@ func (self *SProxmoxGuestDriver) GetGuestInitialStateAfterCreate() string {
|
||||
return api.VM_READY
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error {
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) {
|
||||
return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status)
|
||||
}
|
||||
if disk.DiskSize/1024%1 > 0 {
|
||||
return fmt.Errorf("Resize disk size must be an integer multiple of 1G")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetDefaultSysDiskBackend() string {
|
||||
return ""
|
||||
}
|
||||
@@ -102,8 +115,21 @@ func (self *SProxmoxGuestDriver) GetMaxSecurityGroupCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) RequestGuestHotAddIso(ctx context.Context, guest *models.SGuest, path string, boot bool, task taskman.ITask) error {
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) DoGuestCreateDisksTask(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
subtask, err := taskman.TaskManager.NewTask(ctx, "ProxmoxGuestCreateDiskTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return subtask.ScheduleRun(nil)
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
return []string{api.VM_READY}, nil
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetAttachDiskStatus() ([]string, error) {
|
||||
@@ -111,7 +137,7 @@ func (self *SProxmoxGuestDriver) GetAttachDiskStatus() ([]string, error) {
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
|
||||
return []string{api.VM_READY}, nil
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetRebuildRootStatus() ([]string, error) {
|
||||
@@ -126,12 +152,23 @@ func (self *SProxmoxGuestDriver) ValidateCreateEip(ctx context.Context, userCred
|
||||
return httperrors.NewInputParameterError("%s not support create eip", self.GetHypervisor())
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
|
||||
driver := models.GetDriver(input.Hypervisor)
|
||||
if len(input.UserData) > 0 && driver != nil && driver.IsNeedInjectPasswordByCloudInit() {
|
||||
_, err := cloudinit.ParseUserData(input.UserData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) IsSupportEip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) IsSupportCdrom(guest *models.SGuest) (bool, error) {
|
||||
return false, nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) RequestRemoteUpdate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, replaceTags bool) error {
|
||||
|
||||
@@ -52,7 +52,7 @@ func (self *SBaremetalHostDriver) IsDisableImageCache(host *models.SHost) (bool,
|
||||
return agent.DisableImageCache, nil
|
||||
}
|
||||
|
||||
func (self *SBaremetalHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
func (self *SBaremetalHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
input := api.CacheImageInput{}
|
||||
task.GetParams().Unmarshal(&input)
|
||||
_, err := models.CachedimageManager.FetchById(input.ImageId)
|
||||
|
||||
@@ -52,7 +52,7 @@ func (self *SESXiHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb i
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SESXiHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
func (self *SESXiHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
params := task.GetParams()
|
||||
imageId, err := params.GetString("image_id")
|
||||
if err != nil {
|
||||
|
||||
@@ -161,7 +161,7 @@ func (self *SKVMHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb in
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SKVMHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
func (self *SKVMHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
input := api.CacheImageInput{}
|
||||
task.GetParams().Unmarshal(&input)
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ type SManagedVirtualizationHostDriver struct {
|
||||
SVirtualizationHostDriver
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
func (self *SManagedVirtualizationHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
input := api.CacheImageInput{}
|
||||
task.GetParams().Unmarshal(&input)
|
||||
image := &cloudprovider.SImageCreateOption{}
|
||||
@@ -61,8 +61,8 @@ func (self *SManagedVirtualizationHostDriver) CheckAndSetCacheImage(ctx context.
|
||||
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
|
||||
lockman.LockRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, image.ImageId))
|
||||
defer lockman.ReleaseRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, image.ImageId))
|
||||
lockman.LockRawObject(ctx, models.CachedimageManager.Keyword(), fmt.Sprintf("%s-%s", storageCache.Id, image.ImageId))
|
||||
defer lockman.ReleaseRawObject(ctx, models.CachedimageManager.Keyword(), fmt.Sprintf("%s-%s", storageCache.Id, image.ImageId))
|
||||
|
||||
log.Debugf("XXX Hold lockman key %p cachedimages %s-%s", ctx, storageCache.Id, image.ImageId)
|
||||
|
||||
|
||||
@@ -15,8 +15,25 @@
|
||||
package hostdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/image"
|
||||
)
|
||||
|
||||
type SProxmoxHostDriver struct {
|
||||
@@ -43,3 +60,149 @@ func (self *SProxmoxHostDriver) ValidateDiskSize(storage *models.SStorage, sizeG
|
||||
func (driver *SProxmoxHostDriver) GetStoragecacheQuota(host *models.SHost) int {
|
||||
return 100
|
||||
}
|
||||
|
||||
func (self *SProxmoxHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
input := api.CacheImageInput{}
|
||||
task.GetParams().Unmarshal(&input)
|
||||
opts := &cloudprovider.SImageCreateOption{}
|
||||
task.GetParams().Unmarshal(&opts)
|
||||
|
||||
if len(input.ImageId) == 0 {
|
||||
return fmt.Errorf("no image_id params")
|
||||
}
|
||||
|
||||
if input.Format != "iso" {
|
||||
return fmt.Errorf("invalid image format %s", input.Format)
|
||||
}
|
||||
|
||||
imageSize := int64(0)
|
||||
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
|
||||
lockman.LockRawObject(ctx, models.CachedimageManager.Keyword(), fmt.Sprintf("%s-%s", storageCache.Id, input.ImageId))
|
||||
defer lockman.ReleaseRawObject(ctx, models.CachedimageManager.Keyword(), fmt.Sprintf("%s-%s", storageCache.Id, input.ImageId))
|
||||
|
||||
log.Debugf("XXX Hold lockman key %p cachedimages %s-%s", ctx, storageCache.Id, input.ImageId)
|
||||
|
||||
image, err := models.CachedimageManager.GetCachedimageById(ctx, userCred, input.ImageId, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "CachedimageManager.FetchById(%s)", input.ImageId)
|
||||
}
|
||||
|
||||
if len(image.ExternalId) > 0 {
|
||||
storages, err := image.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
find := false
|
||||
for i := range storages {
|
||||
iStorage, _ := storages[i].GetIStorage(ctx)
|
||||
if gotypes.IsNil(iStorage) {
|
||||
continue
|
||||
}
|
||||
iCache := iStorage.GetIStoragecache()
|
||||
if gotypes.IsNil(iCache) {
|
||||
continue
|
||||
}
|
||||
iImage, err := iCache.GetIImageById(image.ExternalId)
|
||||
if err == nil {
|
||||
imageSize = iImage.GetSizeByte()
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !find {
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, image.ExternalId)
|
||||
}
|
||||
opts.ExternalId = image.ExternalId
|
||||
} else {
|
||||
var guest *models.SGuest
|
||||
if len(input.ServerId) > 0 {
|
||||
server, _ := models.GuestManager.FetchById(input.ServerId)
|
||||
if server != nil {
|
||||
guest = server.(*models.SGuest)
|
||||
}
|
||||
}
|
||||
|
||||
callback := func(progress float32) {
|
||||
guestInfo := ""
|
||||
if guest != nil {
|
||||
guest.SetProgress(progress)
|
||||
guestInfo = fmt.Sprintf(" for server %s ", guest.Name)
|
||||
}
|
||||
log.Infof("Upload image %s from storagecache %s%s status: %.2f%%", opts.ImageName, storageCache.Name, guestInfo, progress)
|
||||
}
|
||||
|
||||
storages, err := host.GetStorages()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetStorages")
|
||||
}
|
||||
|
||||
opts.ExternalId, err = func() (string, error) {
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region)
|
||||
info, err := modules.Images.Get(s, input.ImageId, nil)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "Images.Get(%s)", input.ImageId)
|
||||
}
|
||||
opts.Description, _ = info.GetString("description")
|
||||
opts.Checksum, _ = info.GetString("checksum")
|
||||
minDiskMb, _ := info.Int("min_disk")
|
||||
opts.MinDiskMb = int(minDiskMb)
|
||||
minRamMb, _ := info.Int("min_ram")
|
||||
opts.MinRamMb = int(minRamMb)
|
||||
opts.TmpPath = options.Options.TempPath
|
||||
|
||||
opts.GetReader = func(imageId, format string) (io.Reader, int64, error) {
|
||||
_, reader, sizeByte, err := modules.Images.Download(s, imageId, format, false)
|
||||
return reader, sizeByte, err
|
||||
}
|
||||
|
||||
for i := range storages {
|
||||
cache := storages[i].GetStoragecache()
|
||||
iCache, _ := cache.GetIStorageCache(ctx)
|
||||
if gotypes.IsNil(iCache) {
|
||||
continue
|
||||
}
|
||||
|
||||
ret, err := iCache.UploadImage(ctx, opts, callback)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotSupported {
|
||||
continue
|
||||
}
|
||||
return "", errors.Wrapf(err, "UploadImage")
|
||||
}
|
||||
|
||||
region, err := host.GetRegion()
|
||||
if err != nil {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
obj, err := models.CachedimageManager.FetchById(input.ImageId)
|
||||
if err != nil {
|
||||
return ret, errors.Wrapf(err, "CachedimageManager.FetchById")
|
||||
}
|
||||
cachedImage := obj.(*models.SCachedimage)
|
||||
db.Update(cachedImage, func() error {
|
||||
cachedImage.ExternalId = ret
|
||||
return nil
|
||||
})
|
||||
|
||||
cache.SyncCloudImages(ctx, userCred, iCache, region, true)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no valid storagecache for upload image")
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("upload image %s id: %s", opts.ImageName, image.ExternalId)
|
||||
}
|
||||
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Add(jsonutils.NewString(opts.ExternalId), "image_id")
|
||||
ret.Add(jsonutils.NewInt(imageSize), "size")
|
||||
return ret, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2096,7 +2096,7 @@ func syncPublicCloudProviderInfo(
|
||||
syncResults.Add(CachedimageManager, result)
|
||||
|
||||
msg := result.Result()
|
||||
log.Infof("syncCloudImages result: %s", msg)
|
||||
log.Infof("syncCloudImages for %s result: %s", storageCachePairs[i].local.GetName(), msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -121,6 +122,17 @@ func (self *SGuestcdrom) ejectIso() bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SGuestcdrom) GetImage() (*SCachedimage, error) {
|
||||
if len(self.ImageId) == 0 {
|
||||
return nil, fmt.Errorf("empty image_id")
|
||||
}
|
||||
image, err := CachedimageManager.FetchById(self.ImageId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "CachedimageManager.FetchById(%s)", self.ImageId)
|
||||
}
|
||||
return image.(*SCachedimage), nil
|
||||
}
|
||||
|
||||
func (self *SGuestcdrom) GetDetails() string {
|
||||
if len(self.ImageId) > 0 {
|
||||
if self.Size > 0 {
|
||||
|
||||
@@ -29,7 +29,7 @@ type IHostDriver interface {
|
||||
GetHostType() string
|
||||
GetHypervisor() string
|
||||
|
||||
CheckAndSetCacheImage(ctx context.Context, host *SHost, storagecache *SStoragecache, task taskman.ITask) error
|
||||
CheckAndSetCacheImage(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, storagecache *SStoragecache, task taskman.ITask) error
|
||||
RequestUncacheImage(ctx context.Context, host *SHost, storageCache *SStoragecache, task taskman.ITask) error
|
||||
|
||||
ValidateUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, input api.DiskUpdateInput) (api.DiskUpdateInput, error)
|
||||
|
||||
@@ -892,6 +892,13 @@ func (self *SHost) GetHoststorages() []SHoststorage {
|
||||
return hoststorages
|
||||
}
|
||||
|
||||
func (self *SHost) GetStorages() ([]SStorage, error) {
|
||||
sq := HoststorageManager.Query("storage_id").Equals("host_id", self.Id).SubQuery()
|
||||
q := StorageManager.Query().In("id", sq)
|
||||
storages := []SStorage{}
|
||||
return storages, db.FetchModelObjects(StorageManager, q, &storages)
|
||||
}
|
||||
|
||||
func (self *SHost) GetHoststorageOfId(storageId string) *SHoststorage {
|
||||
hoststorage := SHoststorage{}
|
||||
hoststorage.SetModelManager(HoststorageManager, &hoststorage)
|
||||
|
||||
@@ -241,11 +241,9 @@ func (self *ManagedGuestCreateDiskTask) OnManagedDiskPrepared(ctx context.Contex
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
/*
|
||||
func (self *ManagedGuestCreateDiskTask) OnConfigSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
self.SetStageComplete(ctx, nil)
|
||||
type ProxmoxGuestCreateDiskTask struct {
|
||||
ESXiGuestCreateDiskTask
|
||||
}
|
||||
*/
|
||||
|
||||
type ESXiGuestCreateDiskTask struct {
|
||||
SGuestCreateDiskBaseTask
|
||||
@@ -294,6 +292,7 @@ func (self *ESXiGuestCreateDiskTask) OnInit(ctx context.Context, obj db.IStandal
|
||||
SizeMb: disk.DiskSize,
|
||||
UUID: disk.Id,
|
||||
Driver: d.Driver,
|
||||
Idx: d.Index,
|
||||
StorageId: storage.GetExternalId(),
|
||||
}
|
||||
_, err = ivm.CreateDisk(ctx, &opts)
|
||||
@@ -445,5 +444,6 @@ func init() {
|
||||
taskman.RegisterTask(KVMGuestCreateDiskTask{})
|
||||
taskman.RegisterTask(ManagedGuestCreateDiskTask{})
|
||||
taskman.RegisterTask(ESXiGuestCreateDiskTask{})
|
||||
taskman.RegisterTask(ProxmoxGuestCreateDiskTask{})
|
||||
taskman.RegisterTask(NutanixGuestCreateDiskTask{})
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -78,7 +79,7 @@ func (self *GuestInsertIsoTask) OnIsoPrepareComplete(ctx context.Context, obj db
|
||||
cdromOrdinal, _ := self.Params.Int("cdrom_ordinal")
|
||||
size, err := data.Int("size")
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(errors.Wrapf(err, "get image size").Error()))
|
||||
return
|
||||
}
|
||||
name, _ := data.GetString("name")
|
||||
|
||||
@@ -80,7 +80,7 @@ func (self *StorageCacheImageTask) OnRelinquishLeastUsedCachedImageComplete(ctx
|
||||
}
|
||||
}
|
||||
|
||||
err = host.GetHostDriver().CheckAndSetCacheImage(ctx, host, storageCache, self)
|
||||
err = host.GetHostDriver().CheckAndSetCacheImage(ctx, self.UserCred, host, storageCache, self)
|
||||
if err != nil {
|
||||
errData := taskman.Error2TaskData(err)
|
||||
self.OnImageCacheCompleteFailed(ctx, storageCache, errData)
|
||||
|
||||
@@ -44,7 +44,7 @@ type ServerListOptions struct {
|
||||
Gpu *bool `help:"Show gpu servers"`
|
||||
Secgroup string `help:"Secgroup ID or Name"`
|
||||
AdminSecgroup string `help:"AdminSecgroup ID or Name"`
|
||||
Hypervisor string `help:"Show server of hypervisor" choices:"kvm|esxi|container|baremetal|aliyun|azure|aws|huawei|ucloud|zstack|openstack|google|ctyun|incloudsphere|nutanix|bingocloud|cloudpods|ecloud|jdcloud|remotefile|h3c|hcs|hcso|hcsop"`
|
||||
Hypervisor string `help:"Show server of hypervisor" choices:"kvm|esxi|container|baremetal|aliyun|azure|aws|huawei|ucloud|zstack|openstack|google|ctyun|incloudsphere|nutanix|bingocloud|cloudpods|ecloud|jdcloud|remotefile|h3c|hcs|hcso|hcsop|proxmox"`
|
||||
Region string `help:"Show servers in cloudregion"`
|
||||
WithEip *bool `help:"Show Servers with EIP"`
|
||||
WithoutEip *bool `help:"Show Servers without EIP"`
|
||||
@@ -249,7 +249,7 @@ type ServerConfigs struct {
|
||||
Host string `help:"Preferred host where virtual server should be created" json:"prefer_host"`
|
||||
BackupHost string `help:"Perfered host where virtual backup server should be created"`
|
||||
|
||||
Hypervisor string `help:"Hypervisor type" choices:"kvm|esxi|baremetal|container|aliyun|azure|qcloud|aws|huawei|openstack|ucloud|zstack|google|ctyun|incloudsphere|bingocloud|cloudpods|ecloud|jdcloud|remotefile|h3c|hcs|hcso|hcsop"`
|
||||
Hypervisor string `help:"Hypervisor type" choices:"kvm|esxi|baremetal|container|aliyun|azure|qcloud|aws|huawei|openstack|ucloud|zstack|google|ctyun|incloudsphere|bingocloud|cloudpods|ecloud|jdcloud|remotefile|h3c|hcs|hcso|hcsop|proxmox"`
|
||||
ResourceType string `help:"Resource type" choices:"shared|prepaid|dedicated"`
|
||||
Backup bool `help:"Create server with backup server"`
|
||||
AutoSwitchToBackupOnHostDown bool `help:"Auto switch to backup server on host down"`
|
||||
|
||||
@@ -222,7 +222,7 @@ func handleServerRemoteConsole(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
case session.ALIYUN, session.QCLOUD, session.OPENSTACK,
|
||||
session.VMRC, session.ZSTACK, session.CTYUN,
|
||||
session.HUAWEI, session.HCS, session.APSARA,
|
||||
session.JDCLOUD, session.CLOUDPODS:
|
||||
session.JDCLOUD, session.CLOUDPODS, session.PROXMOX:
|
||||
responsePublicCloudConsole(ctx, info, w)
|
||||
case session.VNC, session.SPICE, session.WMKS:
|
||||
handleDataSession(ctx, info, w, url.Values{"password": {info.GetPassword()}}, true)
|
||||
|
||||
@@ -58,7 +58,7 @@ func (s *ConnectionServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
switch protocol {
|
||||
case session.VNC, session.SPICE:
|
||||
info := sessionObj.ISessionData.(*session.RemoteConsoleInfo)
|
||||
if info.Hypervisor == api.HYPERVISOR_OPENSTACK {
|
||||
if info.Hypervisor == api.HYPERVISOR_OPENSTACK || info.Hypervisor == api.HYPERVISOR_PROXMOX {
|
||||
srv, err = NewWebsocketProxyServer(sessionObj)
|
||||
} else {
|
||||
srv, err = NewWebsockifyServer(sessionObj)
|
||||
|
||||
@@ -45,6 +45,7 @@ const (
|
||||
APSARA = api.APSARA
|
||||
JDCLOUD = api.JDCLOUD
|
||||
CLOUDPODS = api.CLOUDPODS
|
||||
PROXMOX = api.PROXMOX
|
||||
)
|
||||
|
||||
type RemoteConsoleInfo struct {
|
||||
@@ -123,7 +124,7 @@ func (info *RemoteConsoleInfo) GetConnectParams() (string, error) {
|
||||
return info.getQcloudURL()
|
||||
case CLOUDPODS:
|
||||
return info.getCloudpodsURL()
|
||||
case OPENSTACK, VMRC, ZSTACK, CTYUN, HUAWEI, HCS, JDCLOUD:
|
||||
case OPENSTACK, VMRC, ZSTACK, CTYUN, HUAWEI, HCS, JDCLOUD, PROXMOX:
|
||||
return info.Url, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Can't convert protocol %s to connect params", info.Protocol)
|
||||
|
||||
Vendored
+1
-1
@@ -1440,7 +1440,7 @@ sigs.k8s.io/structured-merge-diff/v4/value
|
||||
# sigs.k8s.io/yaml v1.2.0
|
||||
## explicit; go 1.12
|
||||
sigs.k8s.io/yaml
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20230627023024-a5d9d998e793
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20230629073725-ad3f1b174b7a
|
||||
## explicit; go 1.18
|
||||
yunion.io/x/cloudmux/pkg/apis
|
||||
yunion.io/x/cloudmux/pkg/apis/billing
|
||||
|
||||
+1
@@ -108,6 +108,7 @@ type GuestDiskCreateOptions struct {
|
||||
SizeMb int
|
||||
UUID string
|
||||
Driver string
|
||||
Idx int
|
||||
StorageId string
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -247,8 +247,6 @@ type ICloudStoragecache interface {
|
||||
|
||||
GetPath() string
|
||||
|
||||
CreateIImage(snapshotId, imageName, osType, imageDesc string) (ICloudImage, error)
|
||||
|
||||
UploadImage(ctx context.Context, image *SImageCreateOption, callback func(float32)) (string, error)
|
||||
}
|
||||
|
||||
|
||||
-16
@@ -211,22 +211,6 @@ func (self *SStoragecache) uploadImage(ctx context.Context, image *cloudprovider
|
||||
return task.ImageId, nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
if imageId, err := self.region.createIImage(snapshoutId, imageName, imageDesc); err != nil {
|
||||
return nil, err
|
||||
} else if image, err := self.region.GetImage(imageId); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
image.storageCache = self
|
||||
iimage := make([]cloudprovider.ICloudImage, 1)
|
||||
iimage[0] = image
|
||||
if err := cloudprovider.WaitStatus(iimage[0], cloudprovider.IMAGE_STATUS_ACTIVE, 15*time.Second, 3600*time.Second); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iimage[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SRegion) CheckBucket(bucketName string) (*oss.Bucket, error) {
|
||||
return self.checkBucket(bucketName)
|
||||
}
|
||||
|
||||
-16
@@ -233,22 +233,6 @@ func (self *SStoragecache) uploadImage(ctx context.Context, image *cloudprovider
|
||||
return task.ImageId, nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
if imageId, err := self.region.createIImage(snapshoutId, imageName, imageDesc); err != nil {
|
||||
return nil, err
|
||||
} else if image, err := self.region.GetImage(imageId); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
image.storageCache = self
|
||||
iimage := make([]cloudprovider.ICloudImage, 1)
|
||||
iimage[0] = image
|
||||
if err := cloudprovider.WaitStatus(iimage[0], cloudprovider.IMAGE_STATUS_ACTIVE, 15*time.Second, 3600*time.Second); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iimage[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateImage(snapshoutId, imageName, imageDesc string) (string, error) {
|
||||
return self.createIImage(snapshoutId, imageName, imageDesc)
|
||||
}
|
||||
|
||||
-21
@@ -98,27 +98,6 @@ func (self *SStoragecache) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
imageId, err := self.region.createIImage(snapshotId, imageName, imageDesc)
|
||||
if err != nil {
|
||||
log.Errorf("createIImage %s %s %s: %s", snapshotId, imageName, imageDesc, err)
|
||||
return nil, errors.Wrap(err, "createIImage")
|
||||
}
|
||||
image, err := self.region.GetImage(imageId)
|
||||
if err != nil {
|
||||
log.Errorf("GetImage %s: %s", imageId, err)
|
||||
return nil, errors.Wrap(err, "GetImage")
|
||||
}
|
||||
image.storageCache = self
|
||||
iimage := make([]cloudprovider.ICloudImage, 1)
|
||||
iimage[0] = image
|
||||
//todo : implement me
|
||||
if err := cloudprovider.WaitStatus(iimage[0], "avaliable", 15*time.Second, 3600*time.Second); err != nil {
|
||||
return nil, errors.Wrap(err, "WaitStatus.iimage")
|
||||
}
|
||||
return iimage[0], nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) DownloadImage(imageId string, extId string, path string) (jsonutils.JSONObject, error) {
|
||||
return self.downloadImage(imageId, extId)
|
||||
}
|
||||
|
||||
-9
@@ -184,15 +184,6 @@ func (self *SStoragecache) uploadImage(ctx context.Context, image *cloudprovider
|
||||
return img.GetGlobalId(), nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
if image, err := self.region.CreateImage(snapshotId, imageName, osType, imageDesc); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
image.storageCache = self
|
||||
return image, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SStoragecache) DownloadImage(imageId string, extId string, path string) (jsonutils.JSONObject, error) {
|
||||
return self.downloadImage(imageId, extId, path)
|
||||
}
|
||||
|
||||
-4
@@ -55,10 +55,6 @@ func (self *SStoragecache) GetStatus() string {
|
||||
return "available"
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(float32)) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
-4
@@ -31,10 +31,6 @@ type SStoragecache struct {
|
||||
region *SRegion
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, update func(progress float32)) (string, error) {
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
-4
@@ -30,10 +30,6 @@ type SStoragecache struct {
|
||||
region *SRegion
|
||||
}
|
||||
|
||||
func (sc *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (sc *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
-4
@@ -277,10 +277,6 @@ func (self *SDatastoreImageCache) GetIImageById(extId string) (cloudprovider.ICl
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SDatastoreImageCache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SDatastoreImageCache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
-4
@@ -168,10 +168,6 @@ func (cache *SStoragecache) uploadImage(ctx context.Context, image *cloudprovide
|
||||
return _image.GetGlobalId(), nil
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
|
||||
cache := &SStoragecache{region: region}
|
||||
return []cloudprovider.ICloudStoragecache{cache}, nil
|
||||
|
||||
-19
@@ -119,25 +119,6 @@ func (self *SStoragecache) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 目前支持使用vhd、zvhd、vmdk、qcow2、raw、zvhd2、vhdx、qcow、vdi或qed格式镜像文件创建私有镜像。
|
||||
// 快速通道功能可快速完成镜像制作,但镜像文件需转换为raw或zvhd2格式并完成镜像优化。
|
||||
// https://support.huaweicloud.com/api-ims/zh-cn_topic_0083905788.html
|
||||
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
if imageId, err := self.region.createIImage(snapshotId, imageName, imageDesc); err != nil {
|
||||
return nil, err
|
||||
} else if image, err := self.region.GetImage(imageId); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
image.storageCache = self
|
||||
iimage := make([]cloudprovider.ICloudImage, 1)
|
||||
iimage[0] = image
|
||||
if err := cloudprovider.WaitStatus(iimage[0], "avaliable", 15*time.Second, 3600*time.Second); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iimage[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
|
||||
return self.uploadImage(ctx, image, callback)
|
||||
}
|
||||
|
||||
-19
@@ -96,25 +96,6 @@ func (self *SStoragecache) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 目前支持使用vhd、zvhd、vmdk、qcow2、raw、zvhd2、vhdx、qcow、vdi或qed格式镜像文件创建私有镜像。
|
||||
// 快速通道功能可快速完成镜像制作,但镜像文件需转换为raw或zvhd2格式并完成镜像优化。
|
||||
// https://support.huaweicloud.com/api-ims/zh-cn_topic_0083905788.html
|
||||
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
if imageId, err := self.region.createIImage(snapshotId, imageName, imageDesc); err != nil {
|
||||
return nil, err
|
||||
} else if image, err := self.region.GetImage(imageId); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
image.storageCache = self
|
||||
iimage := make([]cloudprovider.ICloudImage, 1)
|
||||
iimage[0] = image
|
||||
if err := cloudprovider.WaitStatus(iimage[0], "avaliable", 15*time.Second, 3600*time.Second); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iimage[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
|
||||
return self.uploadImage(ctx, image, callback)
|
||||
}
|
||||
|
||||
-4
@@ -91,10 +91,6 @@ func (sc *SStoragecache) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (sc *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (sc *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
-4
@@ -81,10 +81,6 @@ func (self *SStoragecache) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, opts *cloudprovider.SImageCreateOption, callback func(float32)) (string, error) {
|
||||
reader, size, err := opts.GetReader(opts.ImageId, string(qemuimgfmt.QCOW2))
|
||||
if err != nil {
|
||||
|
||||
-4
@@ -141,7 +141,3 @@ func (cache *SStoragecache) uploadImage(ctx context.Context, image *cloudprovide
|
||||
|
||||
return img.Id, nil
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
+15
-27
@@ -47,11 +47,12 @@ type SClusterResource struct {
|
||||
}
|
||||
|
||||
type SStorageResource struct {
|
||||
Id string
|
||||
Path string
|
||||
Node string
|
||||
Name string
|
||||
Shared int
|
||||
Id string
|
||||
Path string
|
||||
Node string
|
||||
Name string
|
||||
Shared int
|
||||
Content string
|
||||
}
|
||||
|
||||
type SNodeResource struct {
|
||||
@@ -75,35 +76,22 @@ func (self *SRegion) GetClusterAllResources() ([]SClusterResource, error) {
|
||||
return resources, err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClusterStoragesResources() (map[string]SStorageResource, error) {
|
||||
func (self *SRegion) GetClusterResources(resType string) ([]SClusterResource, error) {
|
||||
resources := []SClusterResource{}
|
||||
storageResources := map[string]SStorageResource{}
|
||||
err := self.get("/cluster/resources", url.Values{}, &resources)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
params := url.Values{}
|
||||
if len(resType) > 0 {
|
||||
params.Set("type", resType)
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
if res.Type == "storage" {
|
||||
sres := SStorageResource{
|
||||
Id: res.Id,
|
||||
Path: fmt.Sprintf("/nodes/%s/storage/%s", res.Node, res.Storage),
|
||||
Node: res.Node,
|
||||
Name: res.Storage,
|
||||
Shared: res.Shared,
|
||||
}
|
||||
|
||||
storageResources[sres.Name] = sres
|
||||
}
|
||||
}
|
||||
|
||||
return storageResources, nil
|
||||
err := self.get("/cluster/resources", params, &resources)
|
||||
return resources, err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClusterNodeResources() (map[string]SNodeResource, error) {
|
||||
resources := []SClusterResource{}
|
||||
nodeResources := map[string]SNodeResource{}
|
||||
err := self.get("/cluster/resources", url.Values{}, &resources)
|
||||
params := url.Values{}
|
||||
params.Set("type", "node")
|
||||
err := self.get("/cluster/resources", params, &resources)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+60
-100
@@ -20,29 +20,22 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/cloudmux/pkg/apis/compute"
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
type SDisk struct {
|
||||
multicloud.SDisk
|
||||
ProxmoxTags
|
||||
|
||||
region *SRegion
|
||||
|
||||
Storage string
|
||||
Node string
|
||||
DiskDriver string
|
||||
DriverIdx int
|
||||
VmId int
|
||||
CacheMode string
|
||||
storage *SStorage
|
||||
|
||||
Format string `json:"format"`
|
||||
Size int64 `json:"size"`
|
||||
Vmid string
|
||||
VolId string `json:"volid"`
|
||||
Name string `json:"name"`
|
||||
Parent string `json:"parent"`
|
||||
@@ -50,7 +43,14 @@ type SDisk struct {
|
||||
}
|
||||
|
||||
func (self *SDisk) GetName() string {
|
||||
return self.Name
|
||||
if len(self.Name) > 0 {
|
||||
return self.Name
|
||||
}
|
||||
info := strings.Split(self.VolId, ":")
|
||||
if len(info) == 2 {
|
||||
return info[1]
|
||||
}
|
||||
return self.VolId
|
||||
}
|
||||
|
||||
func (self *SDisk) GetId() string {
|
||||
@@ -58,19 +58,36 @@ func (self *SDisk) GetId() string {
|
||||
}
|
||||
|
||||
func (self *SDisk) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
if self.storage.Shared == 1 {
|
||||
return fmt.Sprintf("%s|%s", self.storage.Storage, self.VolId)
|
||||
}
|
||||
return fmt.Sprintf("%s|%s|%s", self.storage.Node, self.storage.Storage, self.VolId)
|
||||
}
|
||||
|
||||
func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SDisk) Refresh() error {
|
||||
disks, err := self.storage.zone.region.GetDisks(self.storage.Node, self.storage.Storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range disks {
|
||||
disks[i].storage = self.storage
|
||||
if disks[i].GetGlobalId() == self.GetGlobalId() {
|
||||
return jsonutils.Update(self, disks[i])
|
||||
}
|
||||
}
|
||||
return errors.Wrapf(cloudprovider.ErrNotFound, self.VolId)
|
||||
}
|
||||
|
||||
func (self *SDisk) Delete(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SDisk) GetCacheMode() string {
|
||||
return self.CacheMode
|
||||
return "none"
|
||||
}
|
||||
|
||||
func (self *SDisk) GetFsFormat() string {
|
||||
@@ -82,7 +99,7 @@ func (self *SDisk) GetIsNonPersistent() bool {
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDriver() string {
|
||||
return self.DiskDriver
|
||||
return "virto"
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDiskType() string {
|
||||
@@ -118,7 +135,22 @@ func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error)
|
||||
}
|
||||
|
||||
func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error {
|
||||
return self.region.ResizeDisk(self.VolId, int(sizeMb/1024))
|
||||
vm, err := self.storage.zone.region.GetInstance(self.Vmid)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance")
|
||||
}
|
||||
for _storageName, disks := range vm.QemuDisks {
|
||||
if _storageName != self.storage.Storage {
|
||||
continue
|
||||
}
|
||||
for _, disk := range disks {
|
||||
if disk.DiskId != self.VolId {
|
||||
continue
|
||||
}
|
||||
return self.storage.zone.region.ResizeDisk(vm.Node, self.Vmid, disk.Driver, int(sizeMb-int64(self.GetDiskSizeMB()))/1024)
|
||||
}
|
||||
}
|
||||
return errors.Wrapf(cloudprovider.ErrNotFound, self.VolId)
|
||||
}
|
||||
|
||||
func (self *SDisk) GetTemplateId() string {
|
||||
@@ -130,8 +162,7 @@ func (self *SDisk) GetAccessPath() string {
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
|
||||
DataStoreId := fmt.Sprintf("storage/%s/%s", self.Node, self.Storage)
|
||||
return self.region.GetStorage(DataStoreId)
|
||||
return self.storage, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
|
||||
@@ -142,95 +173,24 @@ func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
return []cloudprovider.ICloudSnapshot{}, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetDisks(storageId string) ([]SDisk, error) {
|
||||
func (self *SRegion) GetDisks(node, storageName string) ([]SDisk, error) {
|
||||
vols := []SDisk{}
|
||||
disks := []SDisk{}
|
||||
|
||||
splited := strings.Split(storageId, "/")
|
||||
nodeName := ""
|
||||
storageName := ""
|
||||
|
||||
if len(splited) == 3 {
|
||||
nodeName, storageName = splited[1], splited[2]
|
||||
}
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/storage/%s/content", nodeName, storageName)
|
||||
err := self.get(res, url.Values{}, &vols)
|
||||
params := url.Values{}
|
||||
params.Set("content", "images")
|
||||
res := fmt.Sprintf("/nodes/%s/storage/%s/content", node, storageName)
|
||||
err := self.get(res, params, &vols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range vols {
|
||||
_, diskName := ParseSubConf(vols[i].VolId, ":")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
vols[i].Storage = storageName
|
||||
vols[i].Node = nodeName
|
||||
vols[i].Name = diskName.(string)
|
||||
|
||||
disks = append(disks, vols[i])
|
||||
}
|
||||
|
||||
return disks, nil
|
||||
return vols, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetDisk(Id string) (*SDisk, error) {
|
||||
|
||||
vols := []SDisk{}
|
||||
nodeName := ""
|
||||
storageName, diskName := ParseSubConf(Id, ":")
|
||||
resources, err := self.GetClusterStoragesResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res, ok := resources[storageName]; !ok {
|
||||
return nil, errors.Errorf("self.GetDisk")
|
||||
} else {
|
||||
nodeName = res.Node
|
||||
}
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/storage/%s/content", nodeName, storageName)
|
||||
err = self.get(res, url.Values{}, &vols)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "self.GetDisk")
|
||||
}
|
||||
|
||||
for _, vol := range vols {
|
||||
if vol.VolId == Id {
|
||||
ret := &SDisk{
|
||||
region: self,
|
||||
Storage: storageName,
|
||||
Node: nodeName,
|
||||
Format: vol.Format,
|
||||
Size: vol.Size,
|
||||
VolId: vol.VolId,
|
||||
Name: diskName.(string),
|
||||
Parent: vol.Parent,
|
||||
VmId: vol.VmId,
|
||||
Content: vol.Content,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("self.GetDisk failed to get disk by %s", Id)
|
||||
}
|
||||
|
||||
func (self *SRegion) ResizeDisk(id string, sizeGb int) error {
|
||||
disk, err := self.GetDisk(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetDisk(%s)", id)
|
||||
}
|
||||
// not support unmount disk
|
||||
if disk.VmId < 1 {
|
||||
return nil
|
||||
}
|
||||
func (self *SRegion) ResizeDisk(node string, vmId string, driver string, sizeGb int) error {
|
||||
body := map[string]interface{}{
|
||||
"disk": fmt.Sprintf("%s%d", disk.DiskDriver, disk.DriverIdx),
|
||||
"size": sizeGb,
|
||||
"disk": driver,
|
||||
"size": fmt.Sprintf("+%dG", sizeGb),
|
||||
}
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/resize", disk.Node, disk.VmId)
|
||||
return self.put(res, nil, jsonutils.Marshal(body), nil)
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%s/resize", node, vmId)
|
||||
return self.put(res, nil, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
+17
-13
@@ -194,16 +194,18 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr
|
||||
vmId := self.zone.region.GetClusterVmMaxId()
|
||||
if vmId == -1 {
|
||||
return nil, errors.Errorf("failed to get vm number by %d", vmId)
|
||||
} else {
|
||||
vmId++
|
||||
}
|
||||
vmId++
|
||||
|
||||
splited := strings.Split(opts.SysDisk.StorageExternalId, "/")
|
||||
storage := splited[2]
|
||||
storage, err := self.zone.region.GetStorage(opts.SysDisk.StorageExternalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetStorage")
|
||||
}
|
||||
|
||||
body := map[string]interface{}{
|
||||
"vmid": vmId,
|
||||
"name": opts.Name,
|
||||
"ide2": fmt.Sprintf("%s,media=cdrom", opts.ExternalImageId),
|
||||
"ostype": "other",
|
||||
"sockets": 1,
|
||||
"cores": opts.Cpu,
|
||||
@@ -213,11 +215,19 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr
|
||||
"memory": opts.MemoryMB,
|
||||
"description": opts.OsDistribution,
|
||||
"scsihw": "virtio-scsi-pci",
|
||||
"scsi0": fmt.Sprintf("%s:%d", storage, opts.SysDisk.SizeGB),
|
||||
"net0": "virtio,bridge=vmbr0,firewall=1",
|
||||
"scsi0": fmt.Sprintf("%s:%d", storage.Storage, opts.SysDisk.SizeGB),
|
||||
}
|
||||
for i, disk := range opts.DataDisks {
|
||||
storage, err := self.zone.region.GetStorage(disk.StorageExternalId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body[fmt.Sprintf("scsi%d", i+1)] = fmt.Sprintf("%s:%d", storage.Storage, opts.SysDisk.SizeGB)
|
||||
}
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/qemu", self.Node)
|
||||
_, err := self.zone.region.post(res, jsonutils.Marshal(body))
|
||||
_, err = self.zone.region.post(res, jsonutils.Marshal(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -228,12 +238,6 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, _ := range vm.QemuDisks {
|
||||
_, diskName := ParseSubConf(k, ":")
|
||||
opts.SysDisk.Name = diskName.(string)
|
||||
break
|
||||
}
|
||||
|
||||
vm.host = self
|
||||
return vm, nil
|
||||
}
|
||||
@@ -282,7 +286,7 @@ func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
storages, err := self.zone.region.GetStoragesByHost(self.Id)
|
||||
storages, err := self.zone.region.GetStoragesByHost(self.Node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+20
-125
@@ -18,11 +18,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/imagetools"
|
||||
|
||||
api "yunion.io/x/cloudmux/pkg/apis/compute"
|
||||
@@ -37,11 +33,11 @@ type SImage struct {
|
||||
|
||||
imageInfo *imagetools.ImageInfo
|
||||
|
||||
VmId int
|
||||
Node string
|
||||
Name string
|
||||
Format string
|
||||
SizeGB float64
|
||||
Volid string
|
||||
Size int64
|
||||
Ctime int64
|
||||
Content string
|
||||
Format string
|
||||
}
|
||||
|
||||
func (self *SImage) GetMinRamSizeMb() int {
|
||||
@@ -49,11 +45,11 @@ func (self *SImage) GetMinRamSizeMb() int {
|
||||
}
|
||||
|
||||
func (self *SImage) GetId() string {
|
||||
return fmt.Sprintf("%d", self.VmId)
|
||||
return self.Volid
|
||||
}
|
||||
|
||||
func (self *SImage) GetName() string {
|
||||
return self.Name
|
||||
return self.Volid
|
||||
}
|
||||
|
||||
func (self *SImage) Delete(ctx context.Context) error {
|
||||
@@ -81,12 +77,12 @@ func (self *SImage) GetImageType() cloudprovider.TImageType {
|
||||
}
|
||||
|
||||
func (self *SImage) GetSizeByte() int64 {
|
||||
return int64(self.SizeGB * 1024 * 1024)
|
||||
return self.Size
|
||||
}
|
||||
|
||||
func (img *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
|
||||
if img.imageInfo == nil {
|
||||
imgInfo := imagetools.NormalizeImageInfo(img.Name, "", "", "", "")
|
||||
imgInfo := imagetools.NormalizeImageInfo(img.Volid, "", "", "", "")
|
||||
img.imageInfo = &imgInfo
|
||||
}
|
||||
return img.imageInfo
|
||||
@@ -113,7 +109,7 @@ func (img *SImage) GetOsLang() string {
|
||||
}
|
||||
|
||||
func (img *SImage) GetFullOsName() string {
|
||||
return img.Name
|
||||
return ""
|
||||
}
|
||||
|
||||
func (img *SImage) GetBios() cloudprovider.TBiosType {
|
||||
@@ -128,122 +124,21 @@ func (self *SImage) GetMinOsDiskSizeGb() int {
|
||||
}
|
||||
|
||||
func (self *SImage) GetImageFormat() string {
|
||||
return "raw"
|
||||
return self.Format
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImageList() ([]SImage, error) {
|
||||
ret := []SImage{}
|
||||
resources, err := self.GetClusterVmResources()
|
||||
func (self *SProxmoxClient) GetImages(node, storageName string) ([]SImage, error) {
|
||||
images := []SImage{}
|
||||
params := url.Values{}
|
||||
params.Set("content", "iso")
|
||||
path := fmt.Sprintf("/nodes/%s/storage/%s/content", node, storageName)
|
||||
err := self.get(path, params, &images)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, vm := range resources {
|
||||
if vm.Template == true {
|
||||
image := SImage{
|
||||
VmId: vm.VmId,
|
||||
Name: vm.Name,
|
||||
Node: vm.Node,
|
||||
}
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", image.Node, image.VmId)
|
||||
vmConfig := map[string]interface{}{}
|
||||
err := self.get(res, url.Values{}, &vmConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
diskNames := []string{}
|
||||
for k := range vmConfig {
|
||||
if diskName := regexp.MustCompile(`(virtio|scsi|sata)\d+`).FindStringSubmatch(k); len(diskName) > 0 {
|
||||
diskNames = append(diskNames, diskName[0])
|
||||
}
|
||||
}
|
||||
|
||||
for _, diskName := range diskNames {
|
||||
diskConfStr := vmConfig[diskName].(string)
|
||||
diskConfMap := ParsePMConf(diskConfStr, "volume")
|
||||
|
||||
if diskConfMap["volume"].(string) == "none" {
|
||||
continue
|
||||
}
|
||||
if diskConfMap["media"] != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
storageName, fileName := ParseSubConf(diskConfMap["volume"].(string), ":")
|
||||
diskConfMap["storage"] = storageName
|
||||
diskConfMap["file"] = fileName
|
||||
|
||||
// cloud-init disks not always have the size sent by the API, which results in a crash
|
||||
if diskConfMap["size"] == nil && strings.Contains(fileName.(string), "cloudinit") {
|
||||
diskConfMap["size"] = "4M" // default cloud-init disk size
|
||||
}
|
||||
|
||||
image.SizeGB += DiskSizeGB(diskConfMap["size"])
|
||||
|
||||
}
|
||||
ret = append(ret, image)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImage(id string) (*SImage, error) {
|
||||
image := &SImage{}
|
||||
vmId, err := strconv.Atoi(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resources, err := self.GetClusterVmResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resources[vmId].Template == false {
|
||||
return nil, errors.Errorf("self.GetDisk")
|
||||
}
|
||||
image.VmId = resources[vmId].VmId
|
||||
image.Name = resources[vmId].Name
|
||||
image.Node = resources[vmId].Node
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", image.Node, image.VmId)
|
||||
vmConfig := map[string]interface{}{}
|
||||
err = self.get(res, url.Values{}, &vmConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
diskNames := []string{}
|
||||
for k := range vmConfig {
|
||||
if diskName := regexp.MustCompile(`(virtio|scsi|sata)\d+`).FindStringSubmatch(k); len(diskName) > 0 {
|
||||
diskNames = append(diskNames, diskName[0])
|
||||
}
|
||||
}
|
||||
|
||||
for _, diskName := range diskNames {
|
||||
diskConfStr := vmConfig[diskName].(string)
|
||||
diskConfMap := ParsePMConf(diskConfStr, "volume")
|
||||
|
||||
if diskConfMap["volume"].(string) == "none" || diskConfMap["media"].(string) == "cdrom" {
|
||||
continue
|
||||
}
|
||||
|
||||
storageName, fileName := ParseSubConf(diskConfMap["volume"].(string), ":")
|
||||
diskConfMap["storage"] = storageName
|
||||
diskConfMap["file"] = fileName
|
||||
|
||||
// cloud-init disks not always have the size sent by the API, which results in a crash
|
||||
if diskConfMap["size"] == nil && strings.Contains(fileName.(string), "cloudinit") {
|
||||
diskConfMap["size"] = "4M" // default cloud-init disk size
|
||||
}
|
||||
|
||||
var sizeInTerabytes = regexp.MustCompile(`[0-9]+T`)
|
||||
// Convert to gigabytes if disk size was received in terabytes
|
||||
matched := sizeInTerabytes.MatchString(diskConfMap["size"].(string))
|
||||
if matched {
|
||||
image.SizeGB += DiskSizeGB(diskConfMap["size"])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return image, nil
|
||||
func (self *SRegion) GetImages(node, storageName string) ([]SImage, error) {
|
||||
return self.client.GetImages(node, storageName)
|
||||
}
|
||||
|
||||
+208
-194
@@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -28,6 +27,7 @@ import (
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/osprofile"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/cloudmux/pkg/apis/compute"
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
@@ -95,6 +95,11 @@ type VmBase struct {
|
||||
Sshkeys string `json:"sshkeys"`
|
||||
}
|
||||
|
||||
type SInstanceDisk struct {
|
||||
Storage string
|
||||
VolId string
|
||||
}
|
||||
|
||||
type SInstance struct {
|
||||
multicloud.SInstanceBase
|
||||
ProxmoxTags
|
||||
@@ -105,34 +110,37 @@ type SInstance struct {
|
||||
PowerState string
|
||||
Node string
|
||||
|
||||
VmID int `json:"vmid"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"desc"`
|
||||
Pool string `json:"pool,omitempty"`
|
||||
Bios string `json:"bios"`
|
||||
EFIDisk QemuDevice `json:"efidisk,omitempty"`
|
||||
Machine string `json:"machine,omitempty"`
|
||||
Onboot bool `json:"onboot"`
|
||||
Startup string `json:"startup,omitempty"`
|
||||
Tablet bool `json:"tablet"`
|
||||
Agent int `json:"agent"`
|
||||
Memory int `json:"memory"`
|
||||
Balloon int `json:"balloon"`
|
||||
QemuOs string `json:"ostype"`
|
||||
QemuCores int `json:"cores"`
|
||||
QemuSockets int `json:"sockets"`
|
||||
QemuVcpus int `json:"vcpus"`
|
||||
QemuCpu string `json:"cpu"`
|
||||
QemuNuma bool `json:"numa"`
|
||||
QemuKVM bool `json:"kvm"`
|
||||
Hotplug string `json:"hotplug"`
|
||||
QemuIso string `json:"iso"`
|
||||
QemuPxe bool `json:"pxe"`
|
||||
FullClone *int `json:"fullclone"`
|
||||
Boot string `json:"boot"`
|
||||
BootDisk string `json:"bootdisk,omitempty"`
|
||||
Scsihw string `json:"scsihw,omitempty"`
|
||||
QemuDisks QemuDevices `json:"disk"`
|
||||
VmID int `json:"vmid"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"desc"`
|
||||
Pool string `json:"pool,omitempty"`
|
||||
Bios string `json:"bios"`
|
||||
EFIDisk QemuDevice `json:"efidisk,omitempty"`
|
||||
Machine string `json:"machine,omitempty"`
|
||||
Onboot bool `json:"onboot"`
|
||||
Startup string `json:"startup,omitempty"`
|
||||
Tablet bool `json:"tablet"`
|
||||
Agent int `json:"agent"`
|
||||
Memory int `json:"memory"`
|
||||
Balloon int `json:"balloon"`
|
||||
QemuOs string `json:"ostype"`
|
||||
QemuCores int `json:"cores"`
|
||||
QemuSockets int `json:"sockets"`
|
||||
QemuVcpus int `json:"vcpus"`
|
||||
QemuCpu string `json:"cpu"`
|
||||
QemuNuma bool `json:"numa"`
|
||||
QemuKVM bool `json:"kvm"`
|
||||
Hotplug string `json:"hotplug"`
|
||||
QemuIso string `json:"iso"`
|
||||
QemuPxe bool `json:"pxe"`
|
||||
FullClone *int `json:"fullclone"`
|
||||
Boot string `json:"boot"`
|
||||
BootDisk string `json:"bootdisk,omitempty"`
|
||||
Scsihw string `json:"scsihw,omitempty"`
|
||||
QemuDisks map[string][]struct {
|
||||
Driver string
|
||||
DiskId string
|
||||
} `json:"disk"`
|
||||
QemuUnusedDisks QemuDevices `json:"unused_disk"`
|
||||
QemuVga QemuDevice `json:"vga,omitempty"`
|
||||
QemuSerials QemuDevices `json:"serial,omitempty"`
|
||||
@@ -180,6 +188,7 @@ func (self *SInstance) Refresh() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.QemuDisks = ins.QemuDisks
|
||||
return jsonutils.Update(self, ins)
|
||||
}
|
||||
|
||||
@@ -188,11 +197,48 @@ func (self *SInstance) AssignSecurityGroup(id string) error {
|
||||
}
|
||||
|
||||
func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
|
||||
return self.host.zone.region.AttachDisk(self.VmID, diskId)
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) {
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
body := map[string]string{}
|
||||
params := url.Values{}
|
||||
storage, err := self.host.zone.region.GetStorage(opts.StorageId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
driver := fmt.Sprintf("scsi%d", opts.Idx)
|
||||
body[driver] = fmt.Sprintf("%s:%d", storage.Storage, opts.SizeMb/1024)
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", self.Node, self.VmID)
|
||||
err = self.host.zone.region.put(res, params, jsonutils.Marshal(body))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = self.Refresh()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for storageName, disks := range self.QemuDisks {
|
||||
if storageName != storage.Storage {
|
||||
continue
|
||||
}
|
||||
for i := range disks {
|
||||
if disks[i].Driver != driver {
|
||||
continue
|
||||
}
|
||||
volumes, err := self.host.zone.region.GetDisks(self.Node, storage.Storage)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for i := range volumes {
|
||||
volumes[i].storage = storage
|
||||
if strings.HasSuffix(volumes[i].GetGlobalId(), "|"+volumes[i].VolId) {
|
||||
return volumes[i].GetGlobalId(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.Wrapf(cloudprovider.ErrNotFound, "after created")
|
||||
}
|
||||
|
||||
func (self *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedVMChangeConfig) error {
|
||||
@@ -208,7 +254,26 @@ func (self *SInstance) DeployVM(ctx context.Context, name string, username strin
|
||||
}
|
||||
|
||||
func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
return self.host.zone.region.DetachDisk(self.VmID, diskId)
|
||||
diskInfo := strings.Split(diskId, "|")
|
||||
storageName, volId := "", ""
|
||||
if len(diskInfo) == 2 {
|
||||
storageName, volId = diskInfo[0], diskInfo[1]
|
||||
} else if len(diskInfo) == 3 {
|
||||
storageName, volId = diskInfo[1], diskInfo[2]
|
||||
} else {
|
||||
return fmt.Errorf("invalid diskId %s", diskId)
|
||||
}
|
||||
for _storageName, disks := range self.QemuDisks {
|
||||
if storageName != _storageName {
|
||||
continue
|
||||
}
|
||||
for _, disk := range disks {
|
||||
if disk.DiskId == volId {
|
||||
return self.host.zone.region.DetachDisk(self.Node, self.VmID, disk.Driver)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SInstance) GetBios() cloudprovider.TBiosType {
|
||||
@@ -243,32 +308,42 @@ func (self *SInstance) VMIdExists(vmId int) (bool, error) {
|
||||
|
||||
func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
ret := []cloudprovider.ICloudDisk{}
|
||||
id := self.VmID
|
||||
|
||||
exist, err := self.VMIdExists(self.VmID)
|
||||
ins, err := self.host.zone.region.GetInstance(fmt.Sprintf("%d", self.VmID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exist == false {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for k, v := range self.QemuDisks {
|
||||
disk, err := self.host.zone.region.GetDisk(k)
|
||||
for storageName, disks := range ins.QemuDisks {
|
||||
diskIds := []string{}
|
||||
for i := range disks {
|
||||
if strings.HasSuffix(disks[i].DiskId, ".iso") {
|
||||
continue
|
||||
}
|
||||
diskIds = append(diskIds, disks[i].DiskId)
|
||||
}
|
||||
disks, err := self.host.zone.region.GetDisks(self.host.Node, storageName)
|
||||
if err != nil {
|
||||
continue
|
||||
return nil, errors.Wrapf(err, "GetDisks")
|
||||
}
|
||||
disk.VmId = id
|
||||
disk.DiskDriver = v["type"].(string)
|
||||
idx, _ := strconv.ParseInt(fmt.Sprintf("%d", v["slot"]), 10, 64)
|
||||
disk.DriverIdx = int(idx)
|
||||
if cache, ok := v["cache"].(string); ok {
|
||||
disk.CacheMode = cache
|
||||
storages, err := self.host.zone.region.GetStoragesByHost(self.Node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var storage *SStorage
|
||||
for i := range storages {
|
||||
if storages[i].Storage == storageName {
|
||||
storage = &storages[i]
|
||||
}
|
||||
}
|
||||
if storage == nil {
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "search storage %s", storageName)
|
||||
}
|
||||
for i := range disks {
|
||||
if utils.IsInStringArray(disks[i].VolId, diskIds) {
|
||||
disks[i].storage = storage
|
||||
ret = append(ret, &disks[i])
|
||||
}
|
||||
}
|
||||
ret = append(ret, disk)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -343,7 +418,18 @@ func (self *SInstance) GetProjectId() string {
|
||||
}
|
||||
|
||||
func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
vnc, err := self.host.zone.region.GetVNCInfo(self.Node, self.VmID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &cloudprovider.ServerVncOutput{}
|
||||
params := url.Values{}
|
||||
params.Set("port", fmt.Sprintf("%d", vnc.Port))
|
||||
params.Set("vncticket", vnc.Ticket)
|
||||
ret.Url = fmt.Sprintf("wss://%s:%d/api2/json/nodes/%s/qemu/%d/vncwebsocket?%s", self.host.zone.region.client.host, self.host.zone.region.client.port, self.Node, self.VmID, params.Encode())
|
||||
ret.Protocol = "vnc"
|
||||
ret.Hypervisor = api.HYPERVISOR_PROXMOX
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SInstance) GetVcpuCount() int {
|
||||
@@ -438,7 +524,6 @@ func (self *SRegion) GetVmPowerStatus(node string, VmId int) string {
|
||||
}
|
||||
|
||||
func (self *SRegion) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
//ret := &SInstance{}
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", node, VmId)
|
||||
vmConfig := map[string]interface{}{}
|
||||
vmBase := &VmBase{
|
||||
@@ -473,31 +558,34 @@ func (self *SRegion) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
}
|
||||
|
||||
config := SInstance{
|
||||
VmID: int(VmId),
|
||||
Name: vmBase.Name,
|
||||
Description: strings.TrimSpace(vmBase.Description),
|
||||
Tags: strings.TrimSpace(vmBase.Tags),
|
||||
Args: strings.TrimSpace(vmBase.Args),
|
||||
Bios: vmBase.Bios,
|
||||
EFIDisk: QemuDevice{},
|
||||
Machine: vmBase.Machine,
|
||||
Onboot: Itob(vmBase.OnBoot),
|
||||
Startup: vmBase.Startup,
|
||||
Tablet: Itob(vmBase.Tablet),
|
||||
QemuOs: vmBase.Ostype,
|
||||
Memory: int(vmBase.Memory),
|
||||
QemuCores: int(vmBase.Cores),
|
||||
QemuSockets: int(vmBase.Sockets),
|
||||
QemuCpu: vmBase.Cpu,
|
||||
QemuNuma: Itob(vmBase.Numa),
|
||||
QemuKVM: Itob(vmBase.Kvm),
|
||||
Hotplug: vmBase.Hotplug,
|
||||
QemuVlanTag: -1,
|
||||
Boot: vmBase.Boot,
|
||||
BootDisk: vmBase.Bootdisk,
|
||||
Scsihw: vmBase.Scsihw,
|
||||
Hookscript: vmBase.Hookscript,
|
||||
QemuDisks: QemuDevices{},
|
||||
VmID: int(VmId),
|
||||
Name: vmBase.Name,
|
||||
Description: strings.TrimSpace(vmBase.Description),
|
||||
Tags: strings.TrimSpace(vmBase.Tags),
|
||||
Args: strings.TrimSpace(vmBase.Args),
|
||||
Bios: vmBase.Bios,
|
||||
EFIDisk: QemuDevice{},
|
||||
Machine: vmBase.Machine,
|
||||
Onboot: Itob(vmBase.OnBoot),
|
||||
Startup: vmBase.Startup,
|
||||
Tablet: Itob(vmBase.Tablet),
|
||||
QemuOs: vmBase.Ostype,
|
||||
Memory: int(vmBase.Memory),
|
||||
QemuCores: int(vmBase.Cores),
|
||||
QemuSockets: int(vmBase.Sockets),
|
||||
QemuCpu: vmBase.Cpu,
|
||||
QemuNuma: Itob(vmBase.Numa),
|
||||
QemuKVM: Itob(vmBase.Kvm),
|
||||
Hotplug: vmBase.Hotplug,
|
||||
QemuVlanTag: -1,
|
||||
Boot: vmBase.Boot,
|
||||
BootDisk: vmBase.Bootdisk,
|
||||
Scsihw: vmBase.Scsihw,
|
||||
Hookscript: vmBase.Hookscript,
|
||||
QemuDisks: map[string][]struct {
|
||||
Driver string
|
||||
DiskId string
|
||||
}{},
|
||||
QemuUnusedDisks: QemuDevices{},
|
||||
QemuVga: QemuDevice{},
|
||||
QemuNetworks: []SInstanceNic{},
|
||||
@@ -543,51 +631,36 @@ func (self *SRegion) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
config.PowerState = self.GetVmPowerStatus(node, VmId)
|
||||
|
||||
// Add disks.
|
||||
diskNames := []string{}
|
||||
diskNames := map[string]string{}
|
||||
for k := range vmConfig {
|
||||
if diskName := rxDiskName.FindStringSubmatch(k); len(diskName) > 0 {
|
||||
diskNames = append(diskNames, diskName[0])
|
||||
diskNames[k] = diskName[0]
|
||||
}
|
||||
}
|
||||
|
||||
for _, diskName := range diskNames {
|
||||
for driver, diskName := range diskNames {
|
||||
diskConfStr := vmConfig[diskName].(string)
|
||||
|
||||
id := rxDeviceID.FindStringSubmatch(diskName)
|
||||
diskID, _ := strconv.Atoi(id[0])
|
||||
diskType := rxDiskType.FindStringSubmatch(diskName)[0]
|
||||
|
||||
diskConfMap := ParsePMConf(diskConfStr, "volume")
|
||||
|
||||
if diskConfMap["volume"].(string) == "none" {
|
||||
continue
|
||||
}
|
||||
|
||||
diskConfMap["slot"] = diskID
|
||||
diskConfMap["type"] = diskType
|
||||
|
||||
storageName, fileName := ParseSubConf(diskConfMap["volume"].(string), ":")
|
||||
diskConfMap["storage"] = storageName
|
||||
diskConfMap["file"] = fileName
|
||||
|
||||
volId := diskConfMap["volume"].(string)
|
||||
|
||||
// cloud-init disks not always have the size sent by the API, which results in a crash
|
||||
if diskConfMap["size"] == nil && strings.Contains(fileName.(string), "cloudinit") {
|
||||
diskConfMap["size"] = "4M" // default cloud-init disk size
|
||||
}
|
||||
|
||||
var sizeInTerabytes = regexp.MustCompile(`[0-9]+T`)
|
||||
// Convert to gigabytes if disk size was received in terabytes
|
||||
matched := sizeInTerabytes.MatchString(diskConfMap["size"].(string))
|
||||
if matched {
|
||||
diskConfMap["size"] = fmt.Sprintf("%.0fG", DiskSizeGB(diskConfMap["size"]))
|
||||
}
|
||||
|
||||
// And device config to disks map.
|
||||
if len(diskConfMap) > 0 {
|
||||
config.QemuDisks[volId] = diskConfMap
|
||||
storageName, _ := ParseSubConf(diskConfMap["volume"].(string), ":")
|
||||
_, ok := config.QemuDisks[storageName]
|
||||
if !ok {
|
||||
config.QemuDisks[storageName] = []struct {
|
||||
Driver string
|
||||
DiskId string
|
||||
}{}
|
||||
}
|
||||
config.QemuDisks[storageName] = append(config.QemuDisks[storageName], struct {
|
||||
Driver string
|
||||
DiskId string
|
||||
}{
|
||||
Driver: driver,
|
||||
DiskId: diskConfMap["volume"].(string),
|
||||
})
|
||||
}
|
||||
|
||||
// Add unused disks
|
||||
@@ -820,90 +893,12 @@ func (self *SRegion) StopVm(vmId int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) AttachDisk(vmId int, diskId string) error {
|
||||
id := strconv.Itoa(int(vmId))
|
||||
vm1, err := self.GetInstance(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance(%d)", vmId)
|
||||
}
|
||||
if _, ok := vm1.QemuUnusedDisks[diskId]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
slotsArr := []int{}
|
||||
for _, v := range vm1.QemuDisks {
|
||||
if v["type"] == "scsi" {
|
||||
slotIdx := v["slot"].(int)
|
||||
slotsArr = append(slotsArr, slotIdx)
|
||||
}
|
||||
}
|
||||
sort.Ints(slotsArr)
|
||||
minSlot := slotsArr[0]
|
||||
for idx, _ := range slotsArr {
|
||||
if slotsArr[idx] == minSlot {
|
||||
minSlot++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (self *SRegion) DetachDisk(node string, vmId int, driver string) error {
|
||||
body := map[string]string{}
|
||||
params := url.Values{}
|
||||
diskName := fmt.Sprintf("scsi%d", minSlot)
|
||||
body[diskName] = diskId
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", vm1.Node, vm1.VmID)
|
||||
err = self.put(res, params, jsonutils.Marshal(body), nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance(%d) self.put", vmId)
|
||||
}
|
||||
//clear
|
||||
vm1.QemuDisks = make(map[string]map[string]interface{})
|
||||
vm1.QemuUnusedDisks = make(map[string]map[string]interface{})
|
||||
|
||||
vm2, err := self.GetInstance(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance(%d) vm2", vmId)
|
||||
}
|
||||
vm1.QemuDisks = vm2.QemuDisks
|
||||
vm1.QemuUnusedDisks = vm2.QemuUnusedDisks
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (self *SRegion) DetachDisk(vmId int, diskId string) error {
|
||||
id := strconv.Itoa(int(vmId))
|
||||
vm1, err := self.GetInstance(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance(%d)", vmId)
|
||||
}
|
||||
if v, ok := vm1.QemuDisks[diskId]; !ok {
|
||||
return nil
|
||||
} else {
|
||||
diskName := fmt.Sprintf("%s%d", v["type"].(string), v["slot"].(int))
|
||||
body := map[string]string{}
|
||||
params := url.Values{}
|
||||
body["delete"] = diskName
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", vm1.Node, vm1.VmID)
|
||||
err := self.put(res, params, jsonutils.Marshal(body), nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance(%d) self.put", vmId)
|
||||
}
|
||||
//clear
|
||||
vm1.QemuDisks = make(map[string]map[string]interface{})
|
||||
vm1.QemuUnusedDisks = make(map[string]map[string]interface{})
|
||||
|
||||
vm2, err := self.GetInstance(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance(%d) vm2", vmId)
|
||||
}
|
||||
vm1.QemuDisks = vm2.QemuDisks
|
||||
vm1.QemuUnusedDisks = vm2.QemuUnusedDisks
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
body["delete"] = driver
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", node, vmId)
|
||||
return self.put(res, params, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
func (self *SRegion) ChangeConfig(vmId int, cpu int, memMb int) error {
|
||||
@@ -934,7 +929,7 @@ func (self *SRegion) ChangeConfig(vmId int, cpu int, memMb int) error {
|
||||
|
||||
params := url.Values{}
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", vm.Node, vmId)
|
||||
return self.put(res, params, jsonutils.Marshal(body), nil)
|
||||
return self.put(res, params, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
func (self *SRegion) ResetVmPassword(vmId int, username, password string) error {
|
||||
@@ -957,7 +952,7 @@ func (self *SRegion) ResetVmPassword(vmId int, username, password string) error
|
||||
}
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/agent/set-user-password", nodeName, vmId)
|
||||
return self.put(res, params, jsonutils.Marshal(body), nil)
|
||||
return self.put(res, params, jsonutils.Marshal(body))
|
||||
|
||||
}
|
||||
|
||||
@@ -973,6 +968,7 @@ func (self *SRegion) DeleteVM(vmId int) error {
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d", vm1.Node, vmId)
|
||||
return self.del(res, params, nil)
|
||||
}
|
||||
|
||||
func (self *SRegion) GenVM(name, node string, cores, memMB int) (*SInstance, error) {
|
||||
|
||||
vmId := self.GetClusterVmMaxId()
|
||||
@@ -1010,3 +1006,21 @@ func (self *SRegion) GenVM(name, node string, cores, memMB int) (*SInstance, err
|
||||
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
type InstanceVnc struct {
|
||||
Port int
|
||||
Ticket string
|
||||
Cert string
|
||||
}
|
||||
|
||||
func (self *SRegion) GetVNCInfo(node string, vmId int) (*InstanceVnc, error) {
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/vncproxy", node, vmId)
|
||||
resp, err := self.post(res, map[string]interface{}{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := struct {
|
||||
Data InstanceVnc
|
||||
}{}
|
||||
return &ret.Data, resp.Unmarshal(&ret)
|
||||
}
|
||||
|
||||
+97
-17
@@ -15,12 +15,18 @@
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -130,6 +136,7 @@ type ProxmoxError struct {
|
||||
Message string
|
||||
Code int
|
||||
Params []string
|
||||
Errors string
|
||||
}
|
||||
|
||||
func (self ProxmoxError) Error() string {
|
||||
@@ -144,8 +151,7 @@ func (ce *ProxmoxError) ParseErrorFromJsonResponse(statusCode int, body jsonutil
|
||||
if ce.Code == 0 && statusCode > 0 {
|
||||
ce.Code = statusCode
|
||||
}
|
||||
if ce.Code == 404 || ce.Code == 400 || ce.Code == 500 {
|
||||
log.Errorf("code: %d", ce.Code)
|
||||
if ce.Code == 404 {
|
||||
return errors.Wrap(cloudprovider.ErrNotFound, ce.Error())
|
||||
}
|
||||
return ce
|
||||
@@ -177,16 +183,18 @@ func (cli *SProxmoxClient) post(res string, params interface{}) (jsonutils.JSONO
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
taskId, err := resp.GetString("data")
|
||||
if err != nil {
|
||||
ret := struct {
|
||||
TaskId string
|
||||
}{}
|
||||
resp.Unmarshal(&ret)
|
||||
if len(ret.TaskId) > 0 && ret.TaskId != "null" {
|
||||
_, err = cli.waitTask(ret.TaskId)
|
||||
return resp, err
|
||||
}
|
||||
_, err = cli.waitTask(taskId)
|
||||
|
||||
return resp, err
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) put(res string, params url.Values, body jsonutils.JSONObject, retVal interface{}) error {
|
||||
func (cli *SProxmoxClient) put(res string, params url.Values, body jsonutils.JSONObject) error {
|
||||
if params != nil {
|
||||
res = fmt.Sprintf("%s?%s", res, params.Encode())
|
||||
}
|
||||
@@ -194,16 +202,21 @@ func (cli *SProxmoxClient) put(res string, params url.Values, body jsonutils.JSO
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
taskId, err := resp.GetString("data")
|
||||
if err != nil {
|
||||
ret := struct {
|
||||
TaskId string
|
||||
}{}
|
||||
resp.Unmarshal(&ret)
|
||||
if len(ret.TaskId) > 0 && ret.TaskId != "null" {
|
||||
_, err = cli.waitTask(ret.TaskId)
|
||||
return err
|
||||
}
|
||||
_, err = cli.waitTask(taskId)
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) get(res string, params url.Values, retVal interface{}) error {
|
||||
if len(params) > 0 {
|
||||
res = fmt.Sprintf("%s?%s", res, params.Encode())
|
||||
}
|
||||
resp, err := cli._jsonRequest(httputils.GET, res, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -273,19 +286,86 @@ func (cli *SProxmoxClient) __jsonRequest(method httputils.THttpMethod, res strin
|
||||
header.Set("CSRFPreventionToken", cli.csrfToken)
|
||||
}
|
||||
|
||||
//header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
//header.Set("Accept", "application/json")
|
||||
|
||||
req.SetHeader(header)
|
||||
oe := &ProxmoxError{}
|
||||
_, resp, err := client.Send(context.Background(), req, oe, cli.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "with params: %v", params)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) upload(node, storageName, filename string, reader io.Reader) (*SImage, error) {
|
||||
if !strings.HasSuffix(filename, ".iso") {
|
||||
filename = filename + ".iso"
|
||||
}
|
||||
filename = filepath.Base(filename)
|
||||
client := cli.getDefaultClient()
|
||||
res := fmt.Sprintf("/nodes/%s/storage/%s/upload", node, storageName)
|
||||
url := fmt.Sprintf("%s/%s", cli.authURL, strings.TrimPrefix(res, "/"))
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
err := writer.WriteField("content", "iso")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
part, err := writer.CreateFormFile("filename", filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = io.Copy(part, reader)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "io.Copy")
|
||||
}
|
||||
writer.Close()
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
if len(cli.csrfToken) > 0 && len(cli.csrfToken) > 0 && res != AUTH_ADDR {
|
||||
req.Header.Set("Cookie", "PVEAuthCookie="+cli.authTicket)
|
||||
req.Header.Set("CSRFPreventionToken", cli.csrfToken)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, err := jsonutils.Parse(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if obj.Contains("errors") {
|
||||
return nil, fmt.Errorf(string(data))
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
for now.Sub(time.Now()) < time.Minute*1 {
|
||||
images, err := cli.GetImages(node, storageName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetImageStatus")
|
||||
}
|
||||
for i := range images {
|
||||
if strings.HasSuffix(images[i].Volid, filename) {
|
||||
return &images[i], nil
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after upload")
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
subAccount := cloudprovider.SSubAccount{}
|
||||
subAccount.Name = self.cpcfg.Name
|
||||
|
||||
+23
-6
@@ -160,13 +160,30 @@ func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
|
||||
zone, err := self.GetZone()
|
||||
storages, err := self.GetStorages()
|
||||
if err != nil {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudStoragecache{}
|
||||
cache := &SStoragecache{zone: zone}
|
||||
ret = append(ret, cache)
|
||||
localMap := map[string]bool{}
|
||||
isShared := false
|
||||
for i := range storages {
|
||||
storage := &SStoragecache{
|
||||
region: self,
|
||||
Node: storages[i].Node,
|
||||
isShare: storages[i].Shared == 1,
|
||||
}
|
||||
if !isShared && storage.isShare {
|
||||
ret = append(ret, storage)
|
||||
isShared = true
|
||||
continue
|
||||
}
|
||||
_, ok := localMap[storages[i].Node]
|
||||
if !ok {
|
||||
localMap[storages[i].Node] = true
|
||||
ret = append(ret, storage)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -195,8 +212,8 @@ func (self *SRegion) post(res string, params interface{}) (jsonutils.JSONObject,
|
||||
return self.client.post(res, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) put(res string, params url.Values, body jsonutils.JSONObject, retVal interface{}) error {
|
||||
return self.client.put(res, params, body, retVal)
|
||||
func (self *SRegion) put(res string, params url.Values, body jsonutils.JSONObject) error {
|
||||
return self.client.put(res, params, body)
|
||||
}
|
||||
|
||||
func (self *SRegion) del(res string, params url.Values, retVal interface{}) error {
|
||||
|
||||
+68
-78
@@ -16,10 +16,10 @@ package proxmox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/cloudmux/pkg/apis/compute"
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
@@ -32,27 +32,30 @@ type SStorage struct {
|
||||
|
||||
zone *SZone
|
||||
|
||||
Id string
|
||||
Node string
|
||||
Storage string `json:"storage"`
|
||||
Status string
|
||||
Id string
|
||||
Node string
|
||||
|
||||
Total int64 `json:"total"`
|
||||
Storage string `json:"storage"`
|
||||
Shared int `json:"shared"`
|
||||
Used int64 `json:"used"`
|
||||
Content string `json:"content"`
|
||||
Active int `json:"active"`
|
||||
UsedFraction float64 `json:"used_fraction"`
|
||||
Avail int64 `json:"avail"`
|
||||
Enabled int `json:"enabled"`
|
||||
Type string `json:"type"`
|
||||
Shared int `json:"shared"`
|
||||
Content string `json:"content"`
|
||||
MaxDisk int64 `json:"maxdisk"`
|
||||
Disk int64 `json:"disk"`
|
||||
PluginType string `json:"plugintype"`
|
||||
}
|
||||
|
||||
func (self *SStorage) GetName() string {
|
||||
if self.Shared == 0 {
|
||||
return fmt.Sprintf("%s-%s", self.Node, self.Storage)
|
||||
}
|
||||
return self.Storage
|
||||
}
|
||||
|
||||
func (self *SStorage) GetId() string {
|
||||
return self.Id
|
||||
if self.Shared == 0 {
|
||||
return self.Id
|
||||
}
|
||||
return self.Storage
|
||||
}
|
||||
|
||||
func (self *SStorage) GetGlobalId() string {
|
||||
@@ -60,13 +63,13 @@ func (self *SStorage) GetGlobalId() string {
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
disks, err := self.zone.region.GetDisks(self.Id)
|
||||
disks, err := self.zone.region.GetDisks(self.Node, self.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudDisk{}
|
||||
for i := range disks {
|
||||
disks[i].region = self.zone.region
|
||||
disks[i].storage = self
|
||||
ret = append(ret, &disks[i])
|
||||
}
|
||||
return ret, nil
|
||||
@@ -77,29 +80,39 @@ func (self *SStorage) CreateIDisk(conf *cloudprovider.DiskCreateConfig) (cloudpr
|
||||
}
|
||||
|
||||
func (self *SStorage) GetCapacityMB() int64 {
|
||||
return int64(self.Total / 1024 / 1024)
|
||||
return int64(self.MaxDisk / 1024 / 1024)
|
||||
}
|
||||
|
||||
func (self *SStorage) GetCapacityUsedMB() int64 {
|
||||
return int64(self.Used / 1024 / 1024)
|
||||
return int64(self.Disk / 1024 / 1024)
|
||||
}
|
||||
|
||||
func (self *SStorage) GetEnabled() bool {
|
||||
return true
|
||||
if strings.Contains(self.Content, "images") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) {
|
||||
disk, err := self.zone.region.GetDisk(id)
|
||||
disks, err := self.GetIDisks()
|
||||
if err != nil {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return disk, nil
|
||||
for i := range disks {
|
||||
if disks[i].GetGlobalId() == id {
|
||||
return disks[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, id)
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
cache := &SStoragecache{zone: self.zone}
|
||||
return cache
|
||||
return &SStoragecache{
|
||||
region: self.zone.region,
|
||||
Node: self.Node,
|
||||
isShare: self.Shared == 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SStorage) GetMediumType() string {
|
||||
@@ -111,6 +124,9 @@ func (self *SStorage) GetMountPoint() string {
|
||||
}
|
||||
|
||||
func (self *SStorage) GetStatus() string {
|
||||
if self.Status != "available" {
|
||||
return api.STORAGE_OFFLINE
|
||||
}
|
||||
return api.STORAGE_ONLINE
|
||||
}
|
||||
|
||||
@@ -131,7 +147,7 @@ func (self *SStorage) GetStorageConf() jsonutils.JSONObject {
|
||||
}
|
||||
|
||||
func (self *SStorage) GetStorageType() string {
|
||||
return strings.ToLower(self.Type)
|
||||
return strings.ToLower(self.PluginType)
|
||||
}
|
||||
|
||||
func (self *SStorage) IsSysDiskStore() bool {
|
||||
@@ -153,78 +169,52 @@ func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, er
|
||||
|
||||
func (self *SRegion) GetStorages() ([]SStorage, error) {
|
||||
storages := []SStorage{}
|
||||
resources, err := self.GetClusterStoragesResources()
|
||||
resources, err := self.GetClusterResources("storage")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
storage := &SStorage{}
|
||||
status := fmt.Sprintf("%s/status", res.Path)
|
||||
err := self.get(status, url.Values{}, storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonutils.Update(&storages, resources)
|
||||
|
||||
storage.Id = res.Id
|
||||
storage.Node = res.Node
|
||||
// not support storageCache, so chanege the type name.
|
||||
if storage.Type == "rbd" {
|
||||
storage.Type = "cephrbd"
|
||||
storageMap := map[string]bool{}
|
||||
ret := []SStorage{}
|
||||
for i := range storages {
|
||||
if storages[i].Shared == 0 {
|
||||
ret = append(ret, storages[i])
|
||||
continue
|
||||
}
|
||||
if storage.Storage == "" {
|
||||
storage.Storage = res.Name
|
||||
if _, ok := storageMap[storages[i].Storage]; !ok {
|
||||
ret = append(ret, storages[i])
|
||||
storageMap[storages[i].Storage] = true
|
||||
}
|
||||
|
||||
storages = append(storages, *storage)
|
||||
}
|
||||
|
||||
return storages, nil
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetStoragesByHost(hostId string) ([]SStorage, error) {
|
||||
storages := []SStorage{}
|
||||
nodeName := ""
|
||||
splited := strings.Split(hostId, "/")
|
||||
nodeName = splited[1]
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/storage", nodeName)
|
||||
err := self.get(res, url.Values{}, &storages)
|
||||
func (self *SRegion) GetStoragesByHost(node string) ([]SStorage, error) {
|
||||
storages, err := self.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := []SStorage{}
|
||||
for i := range storages {
|
||||
id := fmt.Sprintf("storage/%s/%s", nodeName, storages[i].Storage)
|
||||
storages[i].Node = nodeName
|
||||
storages[i].Id = id
|
||||
if storages[i].Type == "rbd" {
|
||||
storages[i].Type = "cephrbd"
|
||||
if storages[i].Shared == 1 || storages[i].Node == node {
|
||||
ret = append(ret, storages[i])
|
||||
}
|
||||
}
|
||||
|
||||
return storages, nil
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetStorage(id string) (*SStorage, error) {
|
||||
ret := &SStorage{}
|
||||
|
||||
//"id": "storage/nodeNAME/strogeNAME",
|
||||
splited := strings.Split(id, "/")
|
||||
nodeName := ""
|
||||
storageName := ""
|
||||
|
||||
if len(splited) == 3 {
|
||||
nodeName, storageName = splited[1], splited[2]
|
||||
storages, err := self.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
status := fmt.Sprintf("/nodes/%s/storage/%s/status", nodeName, storageName)
|
||||
err := self.get(status, url.Values{}, ret)
|
||||
ret.Id = id
|
||||
ret.Node = nodeName
|
||||
if ret.Type == "rbd" {
|
||||
ret.Type = "cephrbd"
|
||||
for i := range storages {
|
||||
if storages[i].GetGlobalId() == id {
|
||||
return &storages[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
return ret, err
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, id)
|
||||
}
|
||||
|
||||
+63
-13
@@ -16,28 +16,42 @@ package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/qemuimgfmt"
|
||||
)
|
||||
|
||||
type SStoragecache struct {
|
||||
multicloud.SResourceBase
|
||||
ProxmoxTags
|
||||
|
||||
zone *SZone
|
||||
region *SRegion
|
||||
Node string
|
||||
isShare bool
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetGlobalId() string {
|
||||
return self.zone.GetGlobalId()
|
||||
if self.isShare {
|
||||
return fmt.Sprintf("%s-share", self.region.GetGlobalId())
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", self.region.GetGlobalId(), self.Node)
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetId() string {
|
||||
return self.zone.GetId()
|
||||
return self.region.GetId()
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetName() string {
|
||||
return self.zone.GetName()
|
||||
if self.isShare {
|
||||
return fmt.Sprintf("%s-share", self.region.GetName())
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", self.region.GetName(), self.Node)
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetStatus() string {
|
||||
@@ -46,15 +60,26 @@ func (self *SStoragecache) GetStatus() string {
|
||||
|
||||
func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) {
|
||||
ret := []cloudprovider.ICloudImage{}
|
||||
images, err := self.zone.region.GetImageList()
|
||||
storages, err := self.region.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range images {
|
||||
images[i].cache = self
|
||||
ret = append(ret, &images[i])
|
||||
for i := range storages {
|
||||
if !strings.Contains(storages[i].Content, "iso") {
|
||||
continue
|
||||
}
|
||||
if (self.isShare && storages[i].Shared != 1) || (!self.isShare && storages[i].Node != self.Node) {
|
||||
continue
|
||||
}
|
||||
images, err := self.region.GetImages(storages[i].Node, storages[i].Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range images {
|
||||
images[i].cache = self
|
||||
ret = append(ret, &images[i])
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -79,10 +104,35 @@ func (self *SStoragecache) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snpId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, opts *cloudprovider.SImageCreateOption, callback func(float32)) (string, error) {
|
||||
reader, sizeByte, err := opts.GetReader(opts.ImageId, string(qemuimgfmt.ISO))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "GetReader")
|
||||
}
|
||||
storages, err := self.region.GetStorages()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for i := range storages {
|
||||
if (self.isShare && storages[i].Shared == 0) || (!self.isShare && storages[i].Shared == 1) {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(storages[i].Content, "iso") {
|
||||
continue
|
||||
}
|
||||
if storages[i].MaxDisk-storages[i].Disk < sizeByte {
|
||||
continue
|
||||
}
|
||||
log.Debugf("upload image %s for %s %s", opts.ImageName, storages[i].Node, storages[i].Storage)
|
||||
image, err := self.region.UploadImage(storages[i].Node, storages[i].Storage, opts.ImageName, reader)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "UploadImage")
|
||||
}
|
||||
return image.GetGlobalId(), nil
|
||||
}
|
||||
return "", fmt.Errorf("no valid shared storage for upload")
|
||||
}
|
||||
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(float32)) (string, error) {
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
func (self *SRegion) UploadImage(node, storage, filename string, reader io.Reader) (*SImage, error) {
|
||||
return self.client.upload(node, storage, filename, reader)
|
||||
}
|
||||
|
||||
-17
@@ -59,23 +59,6 @@ func (self *SStoragecache) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
// if imageId, err := self.region.createIImage(snapshoutId, imageName, imageDesc); err != nil {
|
||||
// return nil, err
|
||||
// } else if image, err := self.region.GetImage(imageId); err != nil {
|
||||
// return nil, err
|
||||
// } else {
|
||||
// image.storageCache = self
|
||||
// iimage := make([]cloudprovider.ICloudImage, 1)
|
||||
// iimage[0] = image
|
||||
// if err := cloudprovider.WaitStatus(iimage[0], compute.IMAGE_STATUS_ACTIVE, 15*time.Second, 3600*time.Second); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return iimage[0], nil
|
||||
// }
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
-4
@@ -91,10 +91,6 @@ func (self *SStoragecache) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
// https://docs.ucloud.cn/api/uhost-api/import_custom_image
|
||||
func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
|
||||
return self.uploadImage(ctx, image, callback)
|
||||
|
||||
-4
@@ -121,7 +121,3 @@ func (self *SStoragecache) uploadImage(ctx context.Context, image *cloudprovider
|
||||
}
|
||||
return img.UUID, err
|
||||
}
|
||||
|
||||
func (scache *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user