diff --git a/go.mod b/go.mod index d8f722e960..7a73679d26 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 7359382440..faf684ab59 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/pkg/apis/webconsole/consts.go b/pkg/apis/webconsole/consts.go index 2b984f594d..740c1e3ac5 100644 --- a/pkg/apis/webconsole/consts.go +++ b/pkg/apis/webconsole/consts.go @@ -35,4 +35,5 @@ const ( APSARA = "apsara" JDCLOUD = "jdcloud" CLOUDPODS = "cloudpods" + PROXMOX = "proxmox" ) diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index 1f70f4faea..d5824b66d4 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -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 } diff --git a/pkg/compute/guestdrivers/proxmox.go b/pkg/compute/guestdrivers/proxmox.go index 535cfa84e7..d94586fe45 100644 --- a/pkg/compute/guestdrivers/proxmox.go +++ b/pkg/compute/guestdrivers/proxmox.go @@ -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 { diff --git a/pkg/compute/hostdrivers/baremetal.go b/pkg/compute/hostdrivers/baremetal.go index ddb99f4ba7..05535c8d3c 100644 --- a/pkg/compute/hostdrivers/baremetal.go +++ b/pkg/compute/hostdrivers/baremetal.go @@ -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) diff --git a/pkg/compute/hostdrivers/esxi.go b/pkg/compute/hostdrivers/esxi.go index e729fbd977..e4ace3020c 100644 --- a/pkg/compute/hostdrivers/esxi.go +++ b/pkg/compute/hostdrivers/esxi.go @@ -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 { diff --git a/pkg/compute/hostdrivers/kvm.go b/pkg/compute/hostdrivers/kvm.go index 8c465582a3..c149a54d5c 100644 --- a/pkg/compute/hostdrivers/kvm.go +++ b/pkg/compute/hostdrivers/kvm.go @@ -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) diff --git a/pkg/compute/hostdrivers/managedvirtual.go b/pkg/compute/hostdrivers/managedvirtual.go index 151941426f..5260160e84 100644 --- a/pkg/compute/hostdrivers/managedvirtual.go +++ b/pkg/compute/hostdrivers/managedvirtual.go @@ -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) diff --git a/pkg/compute/hostdrivers/proxmox.go b/pkg/compute/hostdrivers/proxmox.go index 2a148a0e3d..a482571bc3 100644 --- a/pkg/compute/hostdrivers/proxmox.go +++ b/pkg/compute/hostdrivers/proxmox.go @@ -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 +} diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index ac4ef9fa2a..60b3d00b8a 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -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) } } } diff --git a/pkg/compute/models/guestcdrom.go b/pkg/compute/models/guestcdrom.go index e6e7387a3a..b89fcd6b4f 100644 --- a/pkg/compute/models/guestcdrom.go +++ b/pkg/compute/models/guestcdrom.go @@ -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 { diff --git a/pkg/compute/models/hostdrivers.go b/pkg/compute/models/hostdrivers.go index f7ed8c775e..f016e66b9f 100644 --- a/pkg/compute/models/hostdrivers.go +++ b/pkg/compute/models/hostdrivers.go @@ -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) diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index dfb6bdbace..afa5e4b9ba 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -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) diff --git a/pkg/compute/tasks/guest_create_disk_task.go b/pkg/compute/tasks/guest_create_disk_task.go index ef8e9e3f50..0d8e71d7f4 100644 --- a/pkg/compute/tasks/guest_create_disk_task.go +++ b/pkg/compute/tasks/guest_create_disk_task.go @@ -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{}) } diff --git a/pkg/compute/tasks/guest_insert_iso_task.go b/pkg/compute/tasks/guest_insert_iso_task.go index 8cc9e623d0..6e9b471370 100644 --- a/pkg/compute/tasks/guest_insert_iso_task.go +++ b/pkg/compute/tasks/guest_insert_iso_task.go @@ -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") diff --git a/pkg/compute/tasks/storage_cache_image_task.go b/pkg/compute/tasks/storage_cache_image_task.go index c47b304f87..379bbab431 100644 --- a/pkg/compute/tasks/storage_cache_image_task.go +++ b/pkg/compute/tasks/storage_cache_image_task.go @@ -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) diff --git a/pkg/mcclient/options/compute/servers.go b/pkg/mcclient/options/compute/servers.go index 3aabfc8e63..e9492e35fa 100644 --- a/pkg/mcclient/options/compute/servers.go +++ b/pkg/mcclient/options/compute/servers.go @@ -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"` diff --git a/pkg/webconsole/handlers.go b/pkg/webconsole/handlers.go index 49dc7f7f5b..3af732dd91 100644 --- a/pkg/webconsole/handlers.go +++ b/pkg/webconsole/handlers.go @@ -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) diff --git a/pkg/webconsole/server/server.go b/pkg/webconsole/server/server.go index 58a24bd070..50aaa5d32f 100644 --- a/pkg/webconsole/server/server.go +++ b/pkg/webconsole/server/server.go @@ -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) diff --git a/pkg/webconsole/session/remote_console.go b/pkg/webconsole/session/remote_console.go index 4b22628e47..2b8f31554f 100644 --- a/pkg/webconsole/session/remote_console.go +++ b/pkg/webconsole/session/remote_console.go @@ -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) diff --git a/vendor/modules.txt b/vendor/modules.txt index 4974d9c9e6..4d4ab793f3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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 diff --git a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/instance.go b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/instance.go index 3bd1342139..9ca7a028f4 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/instance.go +++ b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/instance.go @@ -108,6 +108,7 @@ type GuestDiskCreateOptions struct { SizeMb int UUID string Driver string + Idx int StorageId string } diff --git a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go index 4d2993dbdd..bedc8b9ab4 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go +++ b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/storagecache.go index 7699347171..f120c1ccc0 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/storagecache.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/apsara/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/apsara/storagecache.go index 56b0eede4f..22c2739541 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/apsara/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/apsara/storagecache.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/storagecache.go index 65b65a56fc..898f05ccc9 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/storagecache.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/storagecache.go index 2f9a63d5f1..44e1c7f23b 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/storagecache.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/bingocloud/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/bingocloud/storagecache.go index 25c9a565b5..aa10aaa86f 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/bingocloud/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/bingocloud/storagecache.go @@ -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 } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/ctyun/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/ctyun/storagecache.go index edd9c4e868..322962debf 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/ctyun/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/ctyun/storagecache.go @@ -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 } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/ecloud/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/ecloud/storagecache.go index 57673668f4..4ef8d90142 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/ecloud/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/ecloud/storagecache.go @@ -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 } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/esxi/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/esxi/storagecache.go index 225251b882..cd9d39a378 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/esxi/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/esxi/storagecache.go @@ -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 } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/google/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/google/storagecache.go index 069ca59bb5..a764eec3a4 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/google/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/google/storagecache.go @@ -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 diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/hcso/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/hcso/storagecache.go index 2828ccadcc..1c2d787006 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/hcso/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/hcso/storagecache.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/huawei/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/huawei/storagecache.go index 2cff3ee623..7e779886a9 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/huawei/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/huawei/storagecache.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/jdcloud/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/jdcloud/storagecache.go index 1083e23941..9baaf77462 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/jdcloud/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/jdcloud/storagecache.go @@ -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 } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/storagecache.go index 03d2df0701..70d0aee81b 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/storagecache.go @@ -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 { diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/openstack/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/openstack/storagecache.go index c2cde7a3cf..11c12437c8 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/openstack/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/openstack/storagecache.go @@ -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 -} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/cluster.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/cluster.go index d411a853a7..b76f5339bf 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/cluster.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/cluster.go @@ -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 } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/disk.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/disk.go index 28209e1936..878d669d17 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/disk.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/disk.go @@ -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)) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/host.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/host.go index 5755d0fecd..bbda499be1 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/host.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/host.go @@ -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 } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/image.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/image.go index c7f95f97ad..1f07242e89 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/image.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/image.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/instance.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/instance.go index ab99df9b3f..fc41ad2118 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/instance.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/instance.go @@ -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) +} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/proxmox.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/proxmox.go index 553523513e..dd95b45d21 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/proxmox.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/proxmox.go @@ -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 diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/region.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/region.go index 2d9be0df76..bc578d5d05 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/region.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/region.go @@ -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 { diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/storage.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/storage.go index 37cef91ae1..c7bf80dcce 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/storage.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/storage.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/storagecache.go index f22eb18b31..2040e2aa9d 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/storagecache.go @@ -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) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/storagecache.go index 528790a561..90e9bcf853 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/storagecache.go @@ -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 } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/ucloud/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/ucloud/storagecache.go index 53c368943e..468b393f0a 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/ucloud/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/ucloud/storagecache.go @@ -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) diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/zstack/storagecache.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/zstack/storagecache.go index ec474c2d19..7e1bbd25a5 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/zstack/storagecache.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/zstack/storagecache.go @@ -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 -}